root/plot/Pybrary.Plot/StackedSeries.cs

Revision 746, 10.0 kB (checked in by mfenniak, 2 years ago)

--

Line 
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using Pybrary.Plot.Data;
5 using System.Drawing;
6 using System.Drawing.Drawing2D;
7
8 namespace Pybrary.Plot
9 {
10     public class StackedSeries : Series
11     {
12         private MultipointCollection data = new MultipointCollection();
13         private IList<string> datasetNames = new List<string>();
14         private IList<BrushDescription> brushes = new List<BrushDescription>();
15         private IList<PenDescription> pens = new List<PenDescription>();
16         private FontDescription legendFont = new FontDescription("Arial", 12f, FontStyle.Regular);
17
18         private double? minY_gt_Zero;
19         private double? maxY;
20
21         public StackedSeries()
22         {
23             data.OnChanged += delegate(object sender, EventArgs a)
24             {
25                 minY_gt_Zero = null;
26                 maxY = null;
27                 raiseSeriesChanged();
28             };
29         }
30
31         public override double GetXValueByIndex(int index)
32         {
33             return data.GetX(index);
34         }
35
36         public override double? GetYValueByIndex(int index)
37         {
38             double?[] yValues = data.GetY(index);
39             double sum = 0;
40             bool hasValue = false;
41             foreach (double? y in yValues)
42             {
43                 if (y.HasValue)
44                 {
45                     hasValue = true;
46                     sum += y.Value;
47                 }
48             }
49             if (hasValue)
50                 return sum;
51             else
52                 return null;
53         }
54
55         public override void Draw(Graphics g, AxisCollection yAxisCollection, XAxis xAxis, AdvancedRect area)
56         {
57             if (data.Count == 0)
58                 return;
59
60             GraphicsState _s = g.Save();
61             g.SmoothingMode = SmoothingMode.AntiAlias;
62
63             NumericAxis yAxis = yAxisCollection[YAxisName];
64
65             double[] baseY = new double[data.Count];
66             if (yAxis.LogAxis)
67             {
68                 for (int i = 0; i < data.Count; i++)
69                     baseY[i] = yAxis.ScaleMinimum;
70             }
71             else
72             {
73                 for (int i = 0; i < data.Count; i++)
74                     baseY[i] = 0;
75             }
76
77             for (int ySet = 0; ySet < data.GetY(0).Length; ySet++)
78             {
79                 PointF[] polygon = new PointF[data.Count * 2];
80                 PointF[] line = new PointF[data.Count];
81                 int j = 0;
82                 for (int i = (data.Count - 1); i >= 0; i--, j++)
83                 {
84                     polygon[j] = new PointF(
85                         xAxis.DataToCoordinate(data.GetX(i), area),
86                         yAxis.DataToCoordinate(baseY[i], area)
87                     );
88                 }
89                 for (int i = 0; i < data.Count; i++, j++)
90                 {
91                     double? y = data.GetY(i)[ySet];
92                     if (y == null)
93                         y = baseY[i];
94                     else
95                         y = baseY[i] + y.Value;
96                     polygon[j] = new PointF(
97                         xAxis.DataToCoordinate(data.GetX(i), area),
98                         yAxis.DataToCoordinate(y.Value, area)
99                     );
100                     line[i] = polygon[j];
101                     baseY[i] = y.Value;
102                 }
103
104                 if (brushes.Count != 0)
105                 {
106                     int idx = ySet % brushes.Count;
107                     using (Brush br = brushes[idx].CreateBrush())
108                         g.FillPolygon(br, polygon);
109                 }
110                 if (pens.Count != 0 && line.Length >= 2)
111                 {
112                     int idx = ySet % pens.Count;
113                     using (Pen p = pens[idx].CreatePen())
114                         g.DrawLines(p, line);
115                 }
116             }
117
118             g.Restore(_s);
119         }
120
121         public override IEnumerable<LegendEntry> LegendEntries
122         {
123             get
124             {
125                 if (data.Count == 0)
126                     yield break;
127                 for (int ySet = 0; ySet < data.GetY(0).Length; ySet++)
128                     yield return new StackedLegendEntry(this, ySet);
129             }
130         }
131
132         public override int TextExport(List<string> rows, string name)
133         {
134             if (data.Count == 0)
135                 return 0;
136
137             string title = String.Format("{0} X\t", name);
138             for (int c = 0; c < data.GetY(0).Length; c++)
139             {
140                 string datasetname;
141                 if (c < DatasetNames.Count)
142                     datasetname = DatasetNames[c];
143                 else
144                     datasetname = String.Format("Dataset {0}", c);
145                 title += String.Format("{0}\t", datasetname);
146             }
147             rows.Add(title);
148
149             for (int r = 0; r < Data.Count; r++)
150             {
151                 string row;
152                 if (Data.IsSetFromDateTime)
153                     row = String.Format("{0:g}", DateTime.FromOADate(Data.GetX(r)));
154                 else
155                     row = String.Format("{0}", Data.GetX(r));
156                 row += "\t";
157                 for (int c = 0; c < data.GetY(0).Length; c++)
158                 {
159                     row += String.Format("{0}\t", data.GetY(r)[c]);
160                 }
161                 rows.Add(row);
162             }
163             return data.GetY(0).Length + 1;
164         }
165
166         private void calculateMinMax()
167         {
168             minY_gt_Zero = null;
169             maxY = null;
170             for (int i = 0; i < Data.Count; i++)
171             {
172                 double?[] yValues = Data.GetY(i);
173                 if (yValues[0] != null && yValues[0] > 0 && (minY_gt_Zero == null || yValues[0] < minY_gt_Zero))
174                     minY_gt_Zero = yValues[0];
175                 double? sum = null;
176                 for (int j = 0; j < yValues.Length; j++)
177                 {
178                     double? y = yValues[j];
179                     if (y.HasValue)
180                     {
181                         if (sum == null)
182                             sum = y;
183                         else
184                             sum += y;
185                     }
186                 }
187                 if (sum != null && (maxY == null || sum > maxY))
188                     maxY = sum;
189             }
190         }
191
192         public MultipointCollection Data
193         {
194             get
195             {
196                 return data;
197             }
198         }
199
200         public override double? MinY
201         {
202             get
203             {
204                 return 0;
205             }
206         }
207
208         public override double? MinY_gt_Zero
209         {
210             get
211             {
212                 if (minY_gt_Zero == null)
213                     calculateMinMax();
214                 return minY_gt_Zero;
215             }
216         }
217
218         public override double? MaxY
219         {
220             get
221             {
222                 if (maxY == null)
223                     calculateMinMax();
224                 return maxY;
225             }
226         }
227
228         public override double? MinX
229         {
230             get
231             {
232                 return Data.MinX;
233             }
234         }
235
236         public override double? MinX_gt_Zero
237         {
238             get
239             {
240                 return Data.MinX_gt_Zero;
241             }
242         }
243
244         public override double? MaxX
245         {
246             get
247             {
248                 return Data.MaxX;
249             }
250         }
251
252         public IList<BrushDescription> Brushes
253         {
254             get
255             {
256                 return brushes;
257             }
258         }
259
260         public IList<PenDescription> Pens
261         {
262             get
263             {
264                 return pens;
265             }
266         }
267
268         public IList<string> DatasetNames
269         {
270             get
271             {
272                 return datasetNames;
273             }
274         }
275
276         public FontDescription LegendFont
277         {
278             get
279             {
280                 return legendFont;
281             }
282         }
283
284         private class StackedLegendEntry : LegendEntry
285         {
286             private StackedSeries series;
287             private int ySet;
288             public StackedLegendEntry(StackedSeries series, int ySet)
289             {
290                 this.series = series;
291                 this.ySet = ySet;
292             }
293
294             public string Name
295             {
296                 get
297                 {
298                     string name;
299                     if (ySet < series.DatasetNames.Count)
300                         name = series.DatasetNames[ySet];
301                     else
302                         name = String.Format("Dataset {0}", ySet);
303                     return name;
304                 }
305             }
306
307             public SizeF CalculateSize(Graphics g, string seriesName)
308             {
309                 SizeF sz;
310                 using (Font f = series.LegendFont.CreateFont())
311                     sz = g.MeasureString(Name, f);
312                 sz.Width += 3f / 96; // space between swatch and name
313                 sz.Width += 0.2f; // swatch width
314                 sz.Height = Math.Max(0.2f, sz.Height); // swatch height
315                 sz.Width += 3f / 96; // space on right side
316                 return sz;
317             }
318
319             public void Draw(Graphics g, AdvancedRect area, string seriesName)
320             {
321                 int idx = ySet % series.Brushes.Count;
322                 using (Brush br = series.Brushes[idx].CreateBrush())
323                     g.FillRectangle(br, new RectangleF(area.TopLeft.X, area.TopLeft.Y, 0.2f, 0.2f));
324
325                 using (Font f = series.LegendFont.CreateFont())
326                 using (Brush br = series.LegendFont.CreateBrush())
327                 {
328                     SizeF sz = g.MeasureString(Name, f);
329                     g.DrawString(Name, f, br, area.TopLeft.X + 0.2f + (3f / 96), area.Center.Y - (sz.Height / 2));
330                 }
331             }
332         }
333     }
334 }
Note: See TracBrowser for help on using the browser.