root/plot/Pybrary.Plot/ScatterSeries.cs

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

--

Line 
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using System.Drawing;
5 using System.Drawing.Drawing2D;
6 using Pybrary.Plot.Data;
7
8 namespace Pybrary.Plot
9 {
10     public enum SymbolStyle
11     {
12         NoSymbols,
13
14         /// <summary>
15         /// Draw a symbol at every point.
16         /// </summary>
17         IdentifyPoints,
18
19         /// <summary>
20         /// Draw a few symbols on the line, at any location, spaced out.
21         /// The purpose of the symbols is to identify multiple similar
22         /// looking lines, not to identify the location of the points.
23         /// </summary>
24         IdentifyLine,
25     };
26
27     public class ScatterSeries : Series
28     {
29         private PenDescription line = new PenDescription(Color.Blue, 2f / 96);
30         private PointCollection data = new PointCollection();
31         private bool stepLine = false;
32         private SymbolStyle symbolStyle = SymbolStyle.NoSymbols;
33         private SymbolDescription symbol = new SymbolDescription(
34             SymbolType.Circle,
35             new PenDescription(Color.Red, 1f / 96),
36             new BrushDescription(Color.Black),
37             1f / 16);
38         private bool appearsOnLegend = true;
39         private FontDescription legendFont = new FontDescription("Arial", 12f, FontStyle.Regular);
40
41         public ScatterSeries()
42         {
43             data.OnPointCollectionChanged += delegate() { raiseSeriesChanged(); };
44             line.OnPenDescriptionChanged += delegate() { raiseSeriesChanged(); };
45             symbol.OnSymbolDescriptionChanged += delegate() { raiseSeriesChanged(); };
46         }
47
48         public override double GetXValueByIndex(int index)
49         {
50             return data.GetX(index);
51         }
52
53         public override double? GetYValueByIndex(int index)
54         {
55             return data.GetY(index);
56         }
57
58         public override void Draw(Graphics g, AxisCollection yAxisCollection, XAxis xAxis, AdvancedRect area)
59         {
60             if (data.Count == 0)
61                 return;
62
63             GraphicsState _s = g.Save();
64             g.SmoothingMode = SmoothingMode.AntiAlias;
65
66             NumericAxis yAxis = yAxisCollection[YAxisName];
67
68             using (Pen p = line.CreatePen())
69             using (Symbol s = (SymbolStyle != SymbolStyle.NoSymbols) ? symbol.CreateSymbol() : null)
70             {
71                 // find continuous groups of non-null Y points
72                 // if our yAxis is LogRate, then we need to find continuous
73                 // groups of non-null, >0 Y points.  Yay.
74                 int start = 0, end = 0, i = 0;
75                 while (i < data.Count)
76                 {
77                     // chomp all nulls
78                     // then draw all non-nulls
79                     while (i < data.Count && (data.GetY(i) == null || (yAxis.LogAxis && data.GetY(i) <= 0)))
80                         i++;
81                     // found a non-null at i.
82                     start = i;
83                     while (i < data.Count && data.GetY(i) != null && (!yAxis.LogAxis || data.GetY(i) > 0))
84                         i++;
85                     // found a null at i;
86                     end = i;
87                     if (start != end)
88                         drawLines(g, p, s, xAxis, yAxis, area, start, end);
89                 }
90             }
91
92             g.Restore(_s);
93         }
94
95         private void drawLines(Graphics g, Pen p, Symbol s, XAxis xAxis, NumericAxis yAxis, AdvancedRect area, int start, int end)
96         {
97             PointF[] pt;
98             int count = end - start;
99             if (stepLine)
100             {
101                 if (end == data.Count && count == 1)
102                     // drawing a single point at the end going nowhere,
103                     // not possible.  fixme: make a symbol or something to
104                     // indicate a single point?
105                     return;
106                 pt = new PointF[count * 2 - (end == data.Count ? 1 : 0)];
107                 for (int i = 0; i < count; i++)
108                 {
109                     pt[i * 2].X = xAxis.DataToCoordinate(data.GetX(start + i), area);
110                     pt[i * 2].Y = yAxis.DataToCoordinate(data.GetY(start + i).Value, area);
111                     if ((start + i + 1) < data.Count)
112                     {
113                         pt[(i * 2) + 1].X = xAxis.DataToCoordinate(data.GetX(start + i + 1), area);
114                         pt[(i * 2) + 1].Y = pt[i * 2].Y;
115                     }
116                 }
117             }
118             else
119             {
120                 if (count == 1)
121                     // can't really draw a line here.
122                     // fixme: make a symbol or something to indicate a single
123                     // point?
124                     return;
125                 pt = new PointF[count];
126                 for (int i = 0; i < count; i++)
127                 {
128                     pt[i].X = xAxis.DataToCoordinate(data.GetX(start + i), area);
129                     pt[i].Y = yAxis.DataToCoordinate(data.GetY(start + i).Value, area);
130                 }
131             }
132             g.DrawLines(p, pt);
133
134             switch (SymbolStyle)
135             {
136                 case SymbolStyle.IdentifyPoints:
137                     foreach (PointF x in pt)
138                         s.DrawCenteredAt(g, x);
139                     break;
140
141                 case SymbolStyle.IdentifyLine:
142                     PointF lastPt = pt[0];
143                     for (int i = 1; i < pt.Length; i++)
144                     {
145                         PointF curPt = pt[i];
146                         double dist = Math.Sqrt(Math.Pow(curPt.X - lastPt.X, 2) + Math.Pow(curPt.Y - lastPt.Y, 2));
147                         if (dist > 0.5)
148                         {
149                             s.DrawCenteredAt(g, curPt);
150                             lastPt = curPt;
151                         }
152                     }
153                     break;
154             }
155         }
156
157         //public override void DrawLegendEntry(Graphics g, AdvancedRect rect)
158         //{
159         //    using (Pen p = line.CreatePen())
160         //        g.DrawLine(p,
161         //            new PointF(rect.TopLeft.X, rect.Center.Y),
162         //            new PointF(rect.BottomRight.X, rect.Center.Y)
163         //        );
164         //    if (SymbolStyle != SymbolStyle.NoSymbols)
165         //    {
166         //        using (Symbol sym = Symbol.CreateSymbol())
167         //            sym.DrawCenteredAt(g, rect.Center);
168         //    }
169         //}
170
171         public override IEnumerable<LegendEntry> LegendEntries
172         {
173             get
174             {
175                 if (appearsOnLegend)
176                     yield return new ScatterLegendEntry(this);
177             }
178         }
179
180         private class ScatterLegendEntry : LegendEntry
181         {
182             private ScatterSeries series;
183             public ScatterLegendEntry(ScatterSeries series)
184             {
185                 this.series = series;
186             }
187
188             public SizeF CalculateSize(Graphics g, string seriesName)
189             {
190                 SizeF sz;
191                 using (Font f = series.LegendFont.CreateFont())
192                     sz = g.MeasureString(seriesName, f);
193                 sz.Width += 3f / 96; // space between swatch and name
194                 sz.Width += 0.2f; // swatch width
195                 sz.Height = Math.Max(0.2f, sz.Height); // swatch height
196                 sz.Width += 3f / 96; // space on right side
197                 return sz;
198             }
199
200             public void Draw(Graphics g, AdvancedRect area, string seriesName)
201             {
202                 using (Pen p = series.Line.CreatePen())
203                     g.DrawLine(p,
204                         new PointF(area.TopLeft.X, area.Center.Y),
205                         new PointF(area.TopLeft.X + 0.2f, area.Center.Y)
206                     );
207                 if (series.SymbolStyle != SymbolStyle.NoSymbols)
208                 {
209                     using (Symbol sym = series.Symbol.CreateSymbol())
210                         sym.DrawCenteredAt(g, new PointF(area.TopLeft.X + 0.1f, area.Center.Y));
211                 }
212
213                 using (Font f = series.LegendFont.CreateFont())
214                 using (Brush br = series.LegendFont.CreateBrush())
215                 {
216                     SizeF sz = g.MeasureString(seriesName, f);
217                     g.DrawString(seriesName, f, br, area.TopLeft.X + 0.2f + (3f / 96), area.Center.Y - (sz.Height / 2));
218                 }
219             }
220         }
221
222         public override int TextExport(List<string> rows, string name)
223         {
224             rows.Add(String.Format("{0} X\t{0} Y\t", name, name));
225             for (int i = 0; i < Data.Count; i++)
226             {
227                 string x;
228                 if (Data.IsSetFromDateTime)
229                     x = String.Format("{0:g}", DateTime.FromOADate(Data.GetX(i)));
230                 else
231                     x = String.Format("{0}", Data.GetX(i));
232                 rows.Add(String.Format("{0}\t{1}\t", x, Data.GetY(i)));
233             }
234             return 2;
235         }
236
237         public PenDescription Line
238         {
239             get
240             {
241                 return line;
242             }
243         }
244
245         public PointCollection Data
246         {
247             get
248             {
249                 return data;
250             }
251         }
252
253         public SymbolDescription Symbol
254         {
255             get
256             {
257                 return symbol;
258             }
259         }
260
261         public bool StepLine
262         {
263             get
264             {
265                 return stepLine;
266             }
267             set
268             {
269                 stepLine = value;
270                 raiseSeriesChanged();
271             }
272         }
273
274         public SymbolStyle SymbolStyle
275         {
276             get
277             {
278                 return symbolStyle;
279             }
280             set
281             {
282                 symbolStyle = value;
283                 raiseSeriesChanged();
284             }
285         }
286
287         public bool AppearsOnLegend
288         {
289             get
290             {
291                 return appearsOnLegend;
292             }
293             set
294             {
295                 appearsOnLegend = value;
296                 raiseSeriesChanged();
297             }
298         }
299
300         public FontDescription LegendFont
301         {
302             get
303             {
304                 return legendFont;
305             }
306         }
307
308         public override double? MinY
309         {
310             get
311             {
312                 return data.MinY;
313             }
314         }
315
316         public override double? MaxY
317         {
318             get
319             {
320                 return data.MaxY;
321             }
322         }
323
324         public override double? MinY_gt_Zero
325         {
326             get
327             {
328                 return data.MinY_gt_Zero;
329             }
330         }
331
332         public override double? MinX
333         {
334             get
335             {
336                 return data.MinX;
337             }
338         }
339
340         public override double? MaxX
341         {
342             get
343             {
344                 return data.MaxX;
345             }
346         }
347
348         public override double? MinX_gt_Zero
349         {
350             get
351             {
352                 return data.MinX_gt_Zero;
353             }
354         }
355     }
356 }
Note: See TracBrowser for help on using the browser.