/// <summary> /// Exports historical counts as a graph. /// </summary> /// <param name="Title">Title of graph.</param> /// <param name="Order">Preferred order, can be null.</param> /// <param name="Output">Export destination</param> /// <param name="Model">Simulation model</param> /// <param name="Events">Associated event objects.</param> /// <param name="Palette">Optional predefined palette</param> public void ExportCountHistoryGraph(string Title, IEnumerable <string> Order, StreamWriter Output, Model Model, IEnumerable <IEvent> Events, SKColor[] Palette = null) { lock (this.buckets) { SortedDictionary <DateTime, long> Counts = new SortedDictionary <DateTime, long>(); Duration BucketTime = null; int Count = 0; if (Order is null) { Order = this.buckets.Keys; } foreach (string ActivityId in Order) { if (this.TryGetBucket(ActivityId, out IBucket Bucket)) { if (Bucket is IPeriodBucket PeriodBucket) { if (BucketTime is null) { BucketTime = PeriodBucket.BucketTime; } else if (BucketTime != PeriodBucket.BucketTime) { throw new Exception("Graphs incompatible. Different bucket times: " + Title); } PeriodBucket.Flush(); Statistic Last = null; foreach (Statistic Rec in PeriodBucket) { if (!Counts.ContainsKey(Rec.Start)) { Counts[Rec.Start] = 0; } Last = Rec; } if (!(Last is null)) { if (!Counts.ContainsKey(Last.Stop)) { Counts[Last.Stop] = 0; } } Count++; } } } List <string> Labels = new List <string>(); List <SKColor> LabelColors = new List <SKColor>(); StringBuilder Script = new StringBuilder(); StringBuilder PlotScript = null; SKColor Color; string t; int i = 0; int j = 0; int c = Counts.Count; bool First = true; if (Palette is null) { Palette = Model.CreatePalette(Count); } Script.Append("Time:=["); foreach (DateTime TP in Counts.Keys) { if (First) { First = false; } else { Script.Append(','); } Script.Append(t = CommonTypes.Encode(Model.GetTimeCoordinates(TP))); if (j > 0 && j < c - 1) { Script.Append(','); Script.Append(t); } j++; } Script.AppendLine("];"); StringBuilder PlotScript2 = null; int Index = 0; string Alias; foreach (string ActivityId in Order) { Index++; Alias = "C" + Index.ToString(); if (this.TryGetBucket(ActivityId, out IBucket Bucket)) { if (Bucket is IPeriodBucket PeriodBucket) { foreach (Statistic Rec in PeriodBucket) { if (!Counts.TryGetValue(Rec.Stop, out long Value)) { Value = 0; } Value += Rec.Count; Counts[Rec.Stop] = Value; } First = true; Color = Palette[i++]; Script.Append(Alias); Script.Append(":=["); bool Skip = true; foreach (long Value in Counts.Values) { if (Skip) { Skip = false; continue; } if (First) { First = false; } else { Script.Append(','); } Script.Append(t = Value.ToString()); Script.Append(','); Script.Append(t); } Script.AppendLine("];"); if (PlotScript is null) { PlotScript = new StringBuilder(); PlotScript2 = new StringBuilder(); } else { PlotScript.Append('+'); PlotScript2.Append('+'); } PlotScript.Append("plot2dlinearea(Time,"); PlotScript.Append(Alias); PlotScript.Append(",rgba("); PlotScript.Append(Color.Red.ToString()); PlotScript.Append(','); PlotScript.Append(Color.Green.ToString()); PlotScript.Append(','); PlotScript.Append(Color.Blue.ToString()); PlotScript.Append(",64))"); PlotScript2.Append("plot2dline(Time,"); PlotScript2.Append(Alias); PlotScript2.Append(",rgb("); PlotScript2.Append(Color.Red.ToString()); PlotScript2.Append(','); PlotScript2.Append(Color.Green.ToString()); PlotScript2.Append(','); PlotScript2.Append(Color.Blue.ToString()); PlotScript2.Append("),3)"); Labels.Add(ActivityId); LabelColors.Add(Color); } } } if (!(PlotScript2 is null)) { PlotScript.Append('+'); PlotScript.Append(PlotScript2.ToString()); } bool PdfShown = false; if (!(Events is null)) { LinkedList <IDistribution> Distributions = null; foreach (IEvent Event in Events) { IDistribution Distribution = Event.Distribution; if (Distribution is null) { continue; } if (Distributions is null) { Distributions = new LinkedList <IDistribution>(); } Distributions.AddLast(Distribution); Distribution.ExportPdfOnceOnly(Script); } if (!(Distributions is null)) { double t2 = Model.GetTimeCoordinates(Model.EndTime); double dt = t2 / 1000; bool First2 = true; string s; Script.Append("t:=0.."); Script.Append(CommonTypes.Encode(t2)); Script.Append("|"); Script.Append(CommonTypes.Encode(dt)); Script.AppendLine(";"); if (Model.TimeCycleUnits != 1) { Script.Append("ct:=t/"); Script.Append(s = CommonTypes.Encode(Model.TimeCycleUnits)); Script.AppendLine(";"); Script.Append("ct:=(ct-floor(ct))*"); Script.Append(s); Script.AppendLine(";"); } else { Script.AppendLine("ct:=t-floor(t);"); } PlotScript.Append("+plot2dline(t,("); foreach (IDistribution Distribution in Distributions) { if (First2) { First2 = false; } else { PlotScript.Append('+'); } PlotScript.Append(Distribution.Id); PlotScript.Append("PDF(ct)"); } PlotScript.Append(')'); if (Model.BucketTimeMs != Model.TimeUnitMs) { PlotScript.Append(".*"); PlotScript.Append(CommonTypes.Encode(Model.BucketTimeMs / Model.TimeUnitMs)); } PlotScript.AppendLine(",\"Blue\",3)"); PdfShown = true; } } Script.AppendLine("GraphWidth:=1000;"); Script.AppendLine("GraphHeight:=400;"); Script.Append("G:="); Script.Append(PlotScript.ToString()); Script.AppendLine(";"); Script.Append("G.LabelX:=\"Time × "); Script.Append(Model.TimeUnitStr); Script.AppendLine("\";"); Script.Append("G.LabelY:=\"Count / "); Script.Append(Model.DurationToString(BucketTime)); Script.AppendLine("\";"); Script.Append("G.Title:=\""); Script.Append(Title); Script.AppendLine("\";"); Script.Append("G"); Output.WriteLine("{"); Output.WriteLine(Script.ToString()); Output.WriteLine("}"); Output.WriteLine(); if (PdfShown) { Labels.Add("Expected intensity"); LabelColors.Add(SKColors.Blue); CombinedGraph.ExportLegend(Output, Labels.ToArray(), LabelColors.ToArray()); } } }