Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Web.DataVisualization / Common / ChartTypes / StepLineChart.cs
1 //-------------------------------------------------------------
2 // <copyright company=\92Microsoft Corporation\92>
3 //   Copyright © Microsoft Corporation. All Rights Reserved.
4 // </copyright>
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
8 //  File:               StepLineChart.cs
9 //
10 //  Namespace:  DataVisualization.Charting.ChartTypes
11 //
12 //      Classes:        StepLineChart
13 //
14 //  Purpose:    Step Line chart uses two line segments (horizontal 
15 //              and vertical) to connect data points. Markers and 
16 //              labels drawing code is inherited from the Line chart.
17 //
18 //      Reviewed:       AG - Aug 6, 2002
19 //              AG - Microsoft 7, 2007
20 //
21 //===================================================================
22
23 #region Used namespaces
24
25 using System;
26 using System.Resources;
27 using System.Reflection;
28 using System.Collections;
29 using System.Drawing;
30 using System.Drawing.Drawing2D;
31
32 #if Microsoft_CONTROL
33         using System.Windows.Forms.DataVisualization.Charting.Data;
34         using System.Windows.Forms.DataVisualization.Charting.ChartTypes;
35         using System.Windows.Forms.DataVisualization.Charting.Utilities;
36         using System.Windows.Forms.DataVisualization.Charting.Borders3D;
37         using System.Windows.Forms.DataVisualization.Charting;
38 #else
39 using System.Web.UI.DataVisualization.Charting;
40
41         using System.Web.UI.DataVisualization.Charting.ChartTypes;
42         using System.Web.UI.DataVisualization.Charting.Data;
43         using System.Web.UI.DataVisualization.Charting.Utilities;
44 #endif
45
46 #endregion
47
48 #if Microsoft_CONTROL
49         namespace System.Windows.Forms.DataVisualization.Charting.ChartTypes
50 #else
51         namespace System.Web.UI.DataVisualization.Charting.ChartTypes
52 #endif
53 {
54         /// <summary>
55     /// StepLine class extends its base class LineChart by changing how two 
56     /// neighbouring data points are connected with a line. Step Line chart 
57     /// uses two line segments (horizontal and vertical) to connect data 
58     /// points. Markers and labels drawing code is inherited from the Line chart.
59         /// </summary>
60         internal class StepLineChart : LineChart
61         {
62                 #region Constructor
63
64                 /// <summary>
65                 /// StepLineChart class constructor.
66                 /// </summary>
67                 public StepLineChart()
68                 {
69                 }
70
71                 #endregion
72
73                 #region IChartType interface implementation
74
75                 /// <summary>
76                 /// Chart type name
77                 /// </summary>
78                 public override string Name                     { get{ return ChartTypeNames.StepLine;}}
79
80                 /// <summary>
81                 /// Gets chart type image.
82                 /// </summary>
83                 /// <param name="registry">Chart types registry object.</param>
84                 /// <returns>Chart type image.</returns>
85                 override public System.Drawing.Image GetImage(ChartTypeRegistry registry)
86                 {
87                         return (System.Drawing.Image)registry.ResourceManager.GetObject(this.Name + "ChartType");
88                 }
89
90                 #endregion
91
92                 #region Line drawing and selecting methods
93
94                 /// <summary>
95                 /// Draw chart line using horisontal and vertical lines.
96                 /// </summary>
97                 /// <param name="graph">Graphics object.</param>
98                 /// <param name="common">The Common elements object</param>
99                 /// <param name="point">Point to draw the line for.</param>
100                 /// <param name="series">Point series.</param>
101                 /// <param name="points">Array of points coordinates.</param>
102                 /// <param name="pointIndex">Index of point to draw.</param>
103                 /// <param name="tension">Line tension</param>
104                 override protected void DrawLine(
105                         ChartGraphics graph,  
106                         CommonElements common, 
107                         DataPoint point, 
108                         Series series, 
109                         PointF[] points, 
110                         int pointIndex, 
111                         float tension)
112                 {
113                         // Start drawing from the second point
114                         if(pointIndex <= 0)
115                         {
116                                 return;
117                         }
118
119                         // Darw two lines
120                         PointF  point1 = points[pointIndex - 1];
121                         PointF  point2 = new PointF(points[pointIndex].X, points[pointIndex - 1].Y);
122                         PointF  point3 = points[pointIndex];
123                         graph.DrawLineRel( point.Color, point.BorderWidth, point.BorderDashStyle, graph.GetRelativePoint(point1), graph.GetRelativePoint(point2), series.ShadowColor, series.ShadowOffset );
124                         graph.DrawLineRel( point.Color, point.BorderWidth, point.BorderDashStyle, graph.GetRelativePoint(point2), graph.GetRelativePoint(point3), series.ShadowColor, series.ShadowOffset );
125
126                         if( common.ProcessModeRegions )
127                         {
128                                 // Create grapics path object for the line
129                                 // Split line into 2 segments.
130                                 GraphicsPath    path = new GraphicsPath();
131                                 try
132                                 {
133                                         path.AddLine(point2, point3);
134                     if (!point2.Equals(point3))
135                     {
136                         path.Widen(new Pen(point.Color, point.BorderWidth + 2));
137                     }
138                                 }
139                 catch (OutOfMemoryException)
140                 {
141                     // GraphicsPath.Widen incorrectly throws OutOfMemoryException
142                     // catching here and reacting by not widening
143                 }
144                 catch (ArgumentException)
145                 {
146                 }
147
148                                 // Allocate array of floats
149                                 PointF  pointNew = PointF.Empty;
150                                 float[] coord = new float[path.PointCount * 2];
151                                 PointF[] pathPoints = path.PathPoints;
152                                 for( int i = 0; i < path.PointCount; i++ )
153                                 {
154                                         pointNew = graph.GetRelativePoint( pathPoints[i] );
155                                         coord[2*i] = pointNew.X;
156                                         coord[2*i + 1] = pointNew.Y;
157                                 }
158
159                                 common.HotRegionsList.AddHotRegion( 
160                                         path, 
161                                         false, 
162                                         coord, 
163                                         point, 
164                                         series.Name, 
165                                         pointIndex );
166                 path.Dispose();
167                                 // Create grapics path object for the line
168                                 path = new GraphicsPath();
169                                 try
170                                 {
171                                         path.AddLine(point1, point2);
172                                         path.Widen(new Pen(point.Color, point.BorderWidth + 2));
173                                 }
174                 catch (OutOfMemoryException)
175                 {
176                     // GraphicsPath.Widen incorrectly throws OutOfMemoryException
177                     // catching here and reacting by not widening
178                 }
179                 catch (ArgumentException)
180                 {
181                 }
182
183                                 // Allocate array of floats
184                                 coord = new float[path.PointCount * 2];
185                                 pathPoints = path.PathPoints;
186                                 for( int i = 0; i < path.PointCount; i++ )
187                                 {
188                                         pointNew = graph.GetRelativePoint( pathPoints[i] );
189                                         coord[2*i] = pointNew.X;
190                                         coord[2*i + 1] = pointNew.Y;
191                                 }
192
193                                 common.HotRegionsList.AddHotRegion( 
194                                         path, 
195                                         false, 
196                                         coord, 
197                                         series.Points[pointIndex - 1],
198                                         series.Name, 
199                                         pointIndex - 1);
200                 path.Dispose();
201                         }
202                 }
203                 
204                 #endregion
205
206                 #region 3D Line drawing and selection
207
208                 /// <summary>
209                 /// Draws a 3D surface connecting the two specified points in 2D space.
210                 /// Used to draw Line based charts.
211                 /// </summary>
212                 /// <param name="area">Chart area reference.</param>
213                 /// <param name="graph">Chart graphics.</param>
214                 /// <param name="matrix">Coordinates transformation matrix.</param>
215                 /// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
216                 /// <param name="prevDataPointEx">Previous data point object.</param>
217                 /// <param name="positionZ">Z position of the back side of the 3D surface.</param>
218                 /// <param name="depth">Depth of the 3D surface.</param>
219                 /// <param name="points">Array of points.</param>
220                 /// <param name="pointIndex">Index of point to draw.</param>
221                 /// <param name="pointLoopIndex">Index of points loop.</param>
222                 /// <param name="tension">Line tension.</param>
223                 /// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
224                 /// <param name="topDarkening">Darkenning scale for top surface. 0 - None.</param>
225                 /// <param name="bottomDarkening">Darkenning scale for bottom surface. 0 - None.</param>
226                 /// <param name="thirdPointPosition">Position where the third point is actually located or float.NaN if same as in "firstPoint".</param>
227                 /// <param name="fourthPointPosition">Position where the fourth point is actually located or float.NaN if same as in "secondPoint".</param>
228                 /// <param name="clippedSegment">Indicates that drawn segment is 3D clipped. Only top/bottom should be drawn.</param>
229                 /// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
230                 protected override GraphicsPath Draw3DSurface( 
231                         ChartArea area,
232                         ChartGraphics graph, 
233                         Matrix3D matrix,
234                         LightStyle lightStyle,
235                         DataPoint3D prevDataPointEx,
236                         float positionZ, 
237                         float depth, 
238                         ArrayList points,
239                         int pointIndex, 
240                         int pointLoopIndex,
241                         float tension,
242                         DrawingOperationTypes operationType,
243                         float topDarkening,
244                         float bottomDarkening,
245                         PointF thirdPointPosition,
246                         PointF fourthPointPosition,
247                         bool clippedSegment)
248                 {
249                         // Create graphics path for selection
250                         GraphicsPath    resultPath = ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
251                                 ? new GraphicsPath() : null;
252
253                         // Check if points are drawn from sides to center (do only once)
254                         if(centerPointIndex == int.MaxValue)
255                         {
256                                 centerPointIndex = GetCenterPointIndex(points);
257                         }
258
259                         //************************************************************
260                         //** Find line first & second points
261                         //************************************************************
262                         DataPoint3D     secondPoint = (DataPoint3D)points[pointIndex];
263                         int pointArrayIndex = pointIndex;
264                         DataPoint3D firstPoint = ChartGraphics.FindPointByIndex(
265                                 points, 
266                                 secondPoint.index - 1, 
267                                 (this.multiSeries) ? secondPoint : null, 
268                                 ref pointArrayIndex);
269
270                         // Fint point with line properties
271                         DataPoint3D             pointAttr = secondPoint;
272                         if(prevDataPointEx.dataPoint.IsEmpty)
273                         {
274                                 pointAttr = prevDataPointEx;
275                         }
276                         else if(firstPoint.index > secondPoint.index)
277                         {
278                                 pointAttr = firstPoint;
279                         }
280
281                         // Adjust point visual properties 
282                         Color                   color = (useBorderColor) ? pointAttr.dataPoint.BorderColor : pointAttr.dataPoint.Color;
283                         ChartDashStyle  dashStyle = pointAttr.dataPoint.BorderDashStyle;
284                         if( pointAttr.dataPoint.IsEmpty && pointAttr.dataPoint.Color == Color.Empty)
285                         {
286                                 color = Color.Gray;
287                         }
288                         if( pointAttr.dataPoint.IsEmpty && pointAttr.dataPoint.BorderDashStyle == ChartDashStyle.NotSet )
289                         {
290                                 dashStyle = ChartDashStyle.Solid;
291                         }
292
293                         //************************************************************
294                         //** Create "middle" point
295                         //************************************************************
296                         DataPoint3D     middlePoint = new DataPoint3D();
297                         middlePoint.xPosition = secondPoint.xPosition;
298                         middlePoint.yPosition = firstPoint.yPosition;
299
300                         // Check if reversed drawing order required
301                         bool originalDrawOrder = true;
302                         if((pointIndex + 1) < points.Count)
303                         {
304                                 DataPoint3D p = (DataPoint3D)points[pointIndex + 1];
305                                 if(p.index == firstPoint.index)
306                                 {
307                                         originalDrawOrder = false;
308                                 }
309                         }
310
311                         // Check in which order vertical & horizontal lines segments should be drawn
312                         if(centerPointIndex != int.MaxValue)
313                         {
314                                 if(pointIndex >= centerPointIndex)
315                                 {
316                                         originalDrawOrder = false;
317                                 }
318                         }
319
320                         // Draw two segments of the step line
321                         GraphicsPath resultPathLine1, resultPathLine2;
322                         if(originalDrawOrder)
323                         {
324                                 // Draw first line
325                                 middlePoint.dataPoint = secondPoint.dataPoint;
326                                 resultPathLine1 = graph.Draw3DSurface( 
327                                         area, matrix, lightStyle, SurfaceNames.Top, positionZ, depth, color, 
328                                         pointAttr.dataPoint.BorderColor, pointAttr.dataPoint.BorderWidth, dashStyle, 
329                                         firstPoint, middlePoint, 
330                                         points, pointIndex, 0f, operationType, LineSegmentType.First, 
331                                         (this.showPointLines) ? true : false, false,
332                     area.ReverseSeriesOrder,
333                                         this.multiSeries, 0, true);
334
335                                 // No second draw of the prev. front line required
336                                 graph.frontLinePen = null;
337
338                                 // Draw second line
339                                 middlePoint.dataPoint = firstPoint.dataPoint;
340                                 resultPathLine2 = graph.Draw3DSurface( 
341                                         area, matrix, lightStyle, SurfaceNames.Top, positionZ, depth, color, 
342                                         pointAttr.dataPoint.BorderColor, pointAttr.dataPoint.BorderWidth, dashStyle, 
343                                         middlePoint, secondPoint, 
344                                         points, pointIndex, 0f, operationType, LineSegmentType.Last, 
345                                         (this.showPointLines) ? true : false, false,
346                     area.ReverseSeriesOrder,
347                                         this.multiSeries, 0, true);
348
349                                 // No second draw of the prev. front line required
350                                 graph.frontLinePen = null;
351                         }
352                         else
353                         {
354                                 // Draw second line
355                                 middlePoint.dataPoint = firstPoint.dataPoint;
356                                 resultPathLine2 = graph.Draw3DSurface( 
357                                         area, matrix, lightStyle, SurfaceNames.Top, positionZ, depth, color, 
358                                         pointAttr.dataPoint.BorderColor, pointAttr.dataPoint.BorderWidth, dashStyle, 
359                                         middlePoint, secondPoint, 
360                                         points, pointIndex, 0f, operationType, LineSegmentType.Last, 
361                                         (this.showPointLines) ? true : false, false,
362                     area.ReverseSeriesOrder,
363                                         this.multiSeries, 0, true);
364                                 
365                                 // No second draw of the prev. front line required
366                                 graph.frontLinePen = null;
367                                 
368                                 // Draw first line
369                                 middlePoint.dataPoint = secondPoint.dataPoint;
370                                 resultPathLine1 = graph.Draw3DSurface( 
371                                         area, matrix, lightStyle, SurfaceNames.Top, positionZ, depth, color, 
372                                         pointAttr.dataPoint.BorderColor, pointAttr.dataPoint.BorderWidth, dashStyle, 
373                                         firstPoint, middlePoint, 
374                                         points, pointIndex, 0f, operationType, LineSegmentType.First, 
375                                         (this.showPointLines) ? true : false, false,
376                     area.ReverseSeriesOrder,
377                                         this.multiSeries, 0, true);
378
379                                 // No second draw of the prev. front line required
380                                 graph.frontLinePen = null;
381                         }
382
383                         if(resultPath != null)
384                         {
385                                 if( area.Common.ProcessModeRegions)
386                                 {
387                                         if(resultPathLine1 != null && resultPathLine1.PointCount > 0)
388                                         {
389                                                 area.Common.HotRegionsList.AddHotRegion( 
390                                                         resultPathLine1, 
391                                                         false, 
392                                                         graph, 
393                                                         prevDataPointEx.dataPoint, 
394                                                         prevDataPointEx.dataPoint.series.Name, 
395                                                         prevDataPointEx.index - 1 );
396                                         }
397                                 }
398
399                                 if(resultPathLine2 != null && resultPathLine2.PointCount > 0)
400                                 {
401                                         resultPath.AddPath(resultPathLine2, true);
402                                 }
403                         }
404                         return resultPath;
405                 }
406
407                 #endregion
408         }
409 }