Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Web.DataVisualization / Common / ChartTypes / FastLineChart.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:               FastLineChart.cs
9 //
10 //  Namespace:  DataVisualization.Charting.ChartTypes
11 //
12 //      Classes:        FastLineChart
13 //
14 //  Purpose:    When performance is critical, the FastLine chart 
15 //              type is a good alternative to the Line chart. FastLine 
16 //              charts significantly reduce the drawing time of a 
17 //              series that contains a very large number of data points.
18 //              
19 //              To make the FastLine chart a high performance chart, 
20 //              some charting features have been omitted. The features 
21 //              omitted include the ability to control Point level 
22 //              visual properties, the ability to draw markers, the 
23 //              use of data point labels, shadows, and the use of 
24 //              chart animation.
25 //
26 //              FastLine chart performance was improved by limiting 
27 //              visual appearance features and by introducing data 
28 //              point compacting algorithm. When chart contains 
29 //              thousands of data points, it is common to have tens 
30 //              or hundreds points displayed in the area comparable 
31 //              to a single pixel. FastLine algorithm accumulates 
32 //              point information and only draw points if they extend 
33 //              outside currently filled pixels.
34 //              
35 //      Reviewed:       AG - Microsoft 6, 2007
36 //
37 //===================================================================
38
39 #region Used namespaces
40
41 using System;
42 using System.Collections;
43 using System.Drawing;
44 using System.Drawing.Drawing2D;
45 using System.Globalization;
46
47 #if Microsoft_CONTROL
48 using System.Windows.Forms.DataVisualization.Charting.Utilities;
49 #else
50 using System.Web.UI.DataVisualization.Charting;
51
52 using System.Web.UI.DataVisualization.Charting.Utilities;
53 #endif
54 #endregion
55
56 #if Microsoft_CONTROL
57         namespace System.Windows.Forms.DataVisualization.Charting.ChartTypes
58 #else
59 namespace System.Web.UI.DataVisualization.Charting.ChartTypes
60 #endif
61 {
62         /// <summary>
63     /// FastLineChart class implements a simplified line chart drawing 
64     /// algorithm which is optimized for the performance.
65         /// </summary>
66     internal class FastLineChart : IChartType
67         {
68                 #region Fields and Constructor
69
70                 /// <summary>
71                 /// Indicates that chart is drawn in 3D area
72                 /// </summary>
73                 internal bool                           chartArea3DEnabled = false;
74                 
75                 /// <summary>
76                 /// Current chart graphics
77                 /// </summary>
78         internal ChartGraphics Graph { get; set; }
79
80                 /// <summary>
81                 /// Z coordinate of the 3D series
82                 /// </summary>
83         internal float seriesZCoordinate = 0f;
84
85                 /// <summary>
86                 /// 3D transformation matrix
87                 /// </summary>
88         internal Matrix3D matrix3D = null;
89
90                 /// <summary>
91                 /// Reference to common chart elements
92                 /// </summary>
93         internal CommonElements Common { get; set; }
94
95                 /// <summary>
96                 /// Default constructor
97                 /// </summary>
98                 public FastLineChart()
99                 {
100                 }
101
102                 #endregion
103
104                 #region IChartType interface implementation
105
106                 /// <summary>
107                 /// Chart type name
108                 /// </summary>
109                 virtual public string Name                      { get{ return ChartTypeNames.FastLine;}}
110
111                 /// <summary>
112                 /// True if chart type is stacked
113                 /// </summary>
114                 virtual public bool Stacked             { get{ return false;}}
115
116
117                 /// <summary>
118                 /// True if stacked chart type supports groups
119                 /// </summary>
120                 virtual public bool SupportStackedGroups        { get { return false; } }
121
122
123                 /// <summary>
124                 /// True if stacked chart type should draw separately positive and 
125                 /// negative data points ( Bar and column Stacked types ).
126                 /// </summary>
127                 public bool StackSign           { get{ return false;}}
128
129                 /// <summary>
130                 /// True if chart type supports axeses
131                 /// </summary>
132                 virtual public bool RequireAxes { get{ return true;} }
133
134                 /// <summary>
135                 /// Chart type with two y values used for scale ( bubble chart type )
136                 /// </summary>
137                 virtual public bool SecondYScale{ get{ return false;} }
138
139                 /// <summary>
140                 /// True if chart type requires circular chart area.
141                 /// </summary>
142                 public bool CircularChartArea   { get{ return false;} }
143
144                 /// <summary>
145                 /// True if chart type supports logarithmic axes
146                 /// </summary>
147                 virtual public bool SupportLogarithmicAxes      { get{ return true;} }
148
149                 /// <summary>
150                 /// True if chart type requires to switch the value (Y) axes position
151                 /// </summary>
152                 virtual public bool SwitchValueAxes     { get{ return false;} }
153
154                 /// <summary>
155                 /// True if chart series can be placed side-by-side.
156                 /// </summary>
157                 virtual public bool SideBySideSeries { get{ return false;} }
158
159                 /// <summary>
160                 /// True if each data point of a chart must be represented in the legend
161                 /// </summary>
162                 virtual public bool DataPointsInLegend  { get{ return false;} }
163
164                 /// <summary>
165                 /// If the crossing value is auto Crossing value should be 
166                 /// automatically set to zero for some chart 
167                 /// types (Bar, column, area etc.)
168                 /// </summary>
169                 virtual public bool ZeroCrossing { get{ return false;} }
170
171                 /// <summary>
172                 /// True if palette colors should be applied for each data paoint.
173                 /// Otherwise the color is applied to the series.
174                 /// </summary>
175                 virtual public bool ApplyPaletteColorsToPoints  { get { return false; } }
176
177                 /// <summary>
178                 /// Indicates that extra Y values are connected to the scale of the Y axis
179                 /// </summary>
180                 virtual public bool ExtraYValuesConnectedToYAxis{ get { return false; } }
181                 
182                 /// <summary>
183                 /// Indicates that it's a hundredred percent chart.
184                 /// Axis scale from 0 to 100 percent should be used.
185                 /// </summary>
186                 virtual public bool HundredPercent{ get{return false;} }
187
188                 /// <summary>
189                 /// Indicates that it's a hundredred percent chart.
190                 /// Axis scale from 0 to 100 percent should be used.
191                 /// </summary>
192                 virtual public bool HundredPercentSupportNegative{ get{return false;} }
193
194                 /// <summary>
195                 /// How to draw series/points in legend:
196                 /// Filled rectangle, Line or Marker
197                 /// </summary>
198                 /// <param name="series">Legend item series.</param>
199                 /// <returns>Legend item style.</returns>
200                 virtual public LegendImageStyle GetLegendImageStyle(Series series)
201                 {
202                         return LegendImageStyle.Line;
203                 }
204
205                 /// <summary>
206                 /// Number of supported Y value(s) per point 
207                 /// </summary>
208                 virtual public int YValuesPerPoint      { get { return 1; } }
209
210                 /// <summary>
211                 /// Gets chart type image.
212                 /// </summary>
213                 /// <param name="registry">Chart types registry object.</param>
214                 /// <returns>Chart type image.</returns>
215         virtual public System.Drawing.Image GetImage(ChartTypeRegistry registry)
216                 {
217                         return (System.Drawing.Image)registry.ResourceManager.GetObject(this.Name + "ChartType");
218                 }
219
220                 #endregion
221
222                 #region Painting
223
224                 /// <summary>
225                 /// Paint FastLine Chart.
226                 /// </summary>
227                 /// <param name="graph">The Chart Graphics object.</param>
228                 /// <param name="common">The Common elements object.</param>
229                 /// <param name="area">Chart area for this chart.</param>
230                 /// <param name="seriesToDraw">Chart series to draw.</param>
231                 virtual public void Paint( 
232                         ChartGraphics graph, 
233                         CommonElements common, 
234                         ChartArea area, 
235                         Series seriesToDraw )
236                 {       
237                         this.Common = common;
238                         this.Graph = graph;
239                         bool    clipRegionSet = false;
240                         if(area.Area3DStyle.Enable3D)
241                         {
242                                 // Initialize variables
243                                 this.chartArea3DEnabled = true;
244                                 matrix3D = area.matrix3D;
245                         }
246                         else
247                         {
248                                 this.chartArea3DEnabled = false;
249                         }
250                         
251                         //************************************************************
252                         //** Loop through all series
253                         //************************************************************
254                         foreach( Series series in common.DataManager.Series )
255                         {
256                                 // Process non empty series of the area with FastLine chart type
257                                 if( String.Compare( series.ChartTypeName, this.Name, true, System.Globalization.CultureInfo.CurrentCulture ) != 0 
258                                         || series.ChartArea != area.Name || 
259                                         !series.IsVisible())
260                                 {
261                                         continue;
262                                 }
263
264                                 // Get 3D series depth and Z position
265                                 if(this.chartArea3DEnabled)
266                                 {
267                                         float seriesDepth;
268                                         area.GetSeriesZPositionAndDepth(series, out seriesDepth, out seriesZCoordinate);
269                                         this.seriesZCoordinate += seriesDepth/2.0f;
270                                 }
271
272                                 // Set active horizontal/vertical axis
273                                 Axis hAxis = area.GetAxis(AxisName.X, series.XAxisType, (area.Area3DStyle.Enable3D) ? string.Empty : series.XSubAxisName);
274                                 Axis vAxis = area.GetAxis(AxisName.Y, series.YAxisType, (area.Area3DStyle.Enable3D) ? string.Empty : series.YSubAxisName);
275                                 double hAxisMin = hAxis.ViewMinimum;
276                                 double hAxisMax = hAxis.ViewMaximum;
277                                 double vAxisMin = vAxis.ViewMinimum;
278                                 double vAxisMax = vAxis.ViewMaximum;
279
280                                 // Get "PermittedPixelError" attribute
281                                 float   permittedPixelError = 1.0f;
282                 if (series.IsCustomPropertySet(CustomPropertyName.PermittedPixelError))
283                 {
284                     string attrValue = series[CustomPropertyName.PermittedPixelError];
285
286                     float pixelError;
287                     bool parseSucceed = float.TryParse(attrValue, NumberStyles.Any, CultureInfo.CurrentCulture, out pixelError);
288
289                     if (parseSucceed)
290                     {
291                         permittedPixelError = pixelError;
292                     }
293                     else
294                     {
295                         throw (new InvalidOperationException(SR.ExceptionCustomAttributeValueInvalid2("PermittedPixelError")));
296                     }
297
298                     // "PermittedPixelError" attribute value should be in range from zero to 1
299                     if (permittedPixelError < 0f || permittedPixelError > 1f)
300                     {
301                         throw (new InvalidOperationException(SR.ExceptionCustomAttributeIsNotInRange0to1("PermittedPixelError")));
302                     }
303                 }
304
305                                 // Get pixel size in axes coordinates
306                                 SizeF pixelSize = graph.GetRelativeSize(new SizeF(permittedPixelError, permittedPixelError));
307                                 SizeF axesMin = graph.GetRelativeSize(new SizeF((float)hAxisMin, (float)vAxisMin));
308                                 double axesValuesPixelSizeX = Math.Abs(hAxis.PositionToValue(axesMin.Width + pixelSize.Width, false) - hAxis.PositionToValue(axesMin.Width, false));
309
310                                 // Create line pen
311                                 Pen     linePen = new Pen(series.Color, series.BorderWidth);
312                                 linePen.DashStyle = graph.GetPenStyle( series.BorderDashStyle );
313                                 linePen.StartCap = LineCap.Round;
314                                 linePen.EndCap = LineCap.Round;
315
316                                 // Create empty line pen
317                                 Pen     emptyLinePen = new Pen(series.EmptyPointStyle.Color, series.EmptyPointStyle.BorderWidth);
318                                 emptyLinePen.DashStyle = graph.GetPenStyle( series.EmptyPointStyle.BorderDashStyle );
319                                 emptyLinePen.StartCap = LineCap.Round;
320                                 emptyLinePen.EndCap = LineCap.Round;
321
322                                 // Check if series is indexed
323                                 bool indexedSeries = ChartHelper.IndexedSeries(this.Common, series.Name );
324
325                                 // Loop through all ponts in the series
326                                 int             index = 0;
327                                 double  yValueRangeMin = double.NaN;
328                                 double  yValueRangeMax = double.NaN;
329                                 DataPoint pointRangeMin = null;
330                                 DataPoint pointRangeMax = null;
331                                 double  xValue = 0;
332                                 double  yValue = 0;
333                                 double  xValuePrev = 0;
334                                 double  yValuePrev = 0;
335                                 DataPoint prevDataPoint = null;
336                                 PointF  lastVerticalSegmentPoint = PointF.Empty;
337                                 PointF  prevPoint = PointF.Empty;
338                                 PointF  currentPoint = PointF.Empty;
339                                 bool    prevPointInAxesCoordinates = false;
340                                 bool    verticalLineDetected = false;
341                                 bool    prevPointIsEmpty = false;
342                                 bool    currentPointIsEmpty = false;
343                 bool    firstNonEmptyPoint = false;
344                                 double  xPixelConverter = (graph.Common.ChartPicture.Width - 1.0) / 100.0;
345                                 double  yPixelConverter = (graph.Common.ChartPicture.Height - 1.0) / 100.0;
346                                 foreach( DataPoint point in series.Points )
347                                 {
348                                         // Get point X and Y values
349                                         xValue = (indexedSeries) ? index + 1 : point.XValue;
350                                         xValue = hAxis.GetLogValue(xValue);
351                                         yValue = vAxis.GetLogValue(point.YValues[0]);
352                                         currentPointIsEmpty = point.IsEmpty;
353
354                     // NOTE: Fixes issue #7094
355                     // If current point is non-empty but the previous one was, 
356                     // use empty point style properties to draw it.
357                     if (prevPointIsEmpty && !currentPointIsEmpty && !firstNonEmptyPoint)
358                     {
359                         firstNonEmptyPoint = true;
360                         currentPointIsEmpty = true;
361                     }
362                     else
363                     {
364                         firstNonEmptyPoint = false;
365                     }
366
367                                         // Check if line is completly out of the data scaleView
368                                         if( !verticalLineDetected &&
369                                                 ((xValue < hAxisMin && xValuePrev < hAxisMin) ||
370                                                 (xValue > hAxisMax && xValuePrev > hAxisMax) ||
371                                                 (yValue < vAxisMin && yValuePrev < vAxisMin) ||
372                                                 (yValue > vAxisMax && yValuePrev > vAxisMax) ))
373                                         {
374                                                 xValuePrev = xValue;
375                                                 yValuePrev = yValue;
376                                                 prevPointInAxesCoordinates = true;
377                                                 ++index;
378                                                 continue;
379                                         }
380                                         else if(!clipRegionSet)
381                                         {
382                                                 // Check if line is partialy in the data scaleView
383                                                 if(xValuePrev < hAxisMin || xValuePrev > hAxisMax || 
384                                                         xValue > hAxisMax || xValue < hAxisMin ||
385                                                         yValuePrev < vAxisMin || yValuePrev > vAxisMax ||
386                                                         yValue < vAxisMin || yValue > vAxisMax )
387                                                 {
388                                                         // Set clipping region for line drawing 
389                                                         graph.SetClip( area.PlotAreaPosition.ToRectangleF() );
390                                                         clipRegionSet = true;
391                                                 }
392                                         }
393
394                                         // Check if point may be skipped
395                                         if(index > 0 &&
396                                                 currentPointIsEmpty == prevPointIsEmpty)
397                                         {
398                                                 // Check if points X value in acceptable error boundary
399                                                 if( Math.Abs(xValue - xValuePrev) < axesValuesPixelSizeX)
400                                                 {
401                                                         if(!verticalLineDetected)
402                                                         {
403                                                                 verticalLineDetected = true;
404                                                                 if(yValue > yValuePrev)
405                                                                 {
406                                                                         yValueRangeMax = yValue;
407                                                                         yValueRangeMin = yValuePrev;
408                                                                         pointRangeMax = point;
409                                                                         pointRangeMin = prevDataPoint;
410                                                                 }
411                                                                 else
412                                                                 {
413                                                                         yValueRangeMax = yValuePrev;
414                                                                         yValueRangeMin = yValue;
415                                                                         pointRangeMax = prevDataPoint;
416                                                                         pointRangeMin = point;
417                                                                 }
418
419                                                                 // NOTE: Prev. version code - A.G.
420 //                                                              yValueRangeMin = Math.Min(yValue, yValuePrev);
421 //                                                              yValueRangeMax = Math.Max(yValue, yValuePrev);
422                                                         }
423                                                         else
424                                                         {
425                                                                 if(yValue > yValueRangeMax)
426                                                                 {
427                                                                         yValueRangeMax = yValue;
428                                                                         pointRangeMax = point;
429                                                                 }
430
431                                                                 else if(yValue < yValueRangeMin)
432                                                                 {
433                                                                         yValueRangeMin = yValue;
434                                                                         pointRangeMin = point;
435                                                                 }
436
437                                                                 // NOTE: Prev. version code - A.G.
438 //                                                              yValueRangeMin = Math.Min(yValue, yValueRangeMin);
439 //                                                              yValueRangeMax = Math.Max(yValue, yValueRangeMax);
440                                                         }
441
442                                                         // Remember last point
443                                                         prevDataPoint = point;
444
445                                                         // Remember last vertical range point
446                                                         // Note! Point is in axes coordinate.
447                                                         lastVerticalSegmentPoint.Y = (float)yValue;
448
449                                                         // Increase counter and proceed to next data point
450                                                         ++index;
451                                                         continue;
452                                                 }
453                                         }
454
455                                         // Get point pixel position
456                                         currentPoint.X = (float)
457                                                 (hAxis.GetLinearPosition( xValue ) * xPixelConverter);
458                                         currentPoint.Y = (float)
459                                                 (vAxis.GetLinearPosition( yValue ) * yPixelConverter); 
460
461                                         // Check if previous point must be converted from axes values to pixels
462                                         if(prevPointInAxesCoordinates)
463                                         {
464                                                 prevPoint.X = (float)
465                                                         (hAxis.GetLinearPosition( xValuePrev ) * xPixelConverter);
466                                                 prevPoint.Y = (float)
467                                                         (vAxis.GetLinearPosition( yValuePrev ) * yPixelConverter); 
468                                         }
469
470                                         // Draw accumulated vertical line (with minimal X values differences)
471                                         if(verticalLineDetected)
472                                         {
473                                                 // Convert Y coordinates to pixels
474                                                 yValueRangeMin = (vAxis.GetLinearPosition( yValueRangeMin ) * yPixelConverter); 
475                                                 yValueRangeMax = (vAxis.GetLinearPosition( yValueRangeMax ) * yPixelConverter); 
476
477                                                 // Draw accumulated vertical line
478                                                 DrawLine(
479                                                         series,
480                                                         prevDataPoint,
481                                                         pointRangeMin,
482                                                         pointRangeMax,
483                                                         index,
484                                                         (prevPointIsEmpty) ? emptyLinePen : linePen, 
485                                                         prevPoint.X, 
486                                                         (float)yValueRangeMin, 
487                                                         prevPoint.X,
488                                                         (float)yValueRangeMax);
489                                                 
490                                                 // Reset vertical line detected flag
491                                                 verticalLineDetected = false;
492
493                                                 // Convert last point of the vertical line segment to pixel coordinates
494                                                 prevPoint.Y = (float)
495                                                         (vAxis.GetLinearPosition( lastVerticalSegmentPoint.Y ) * yPixelConverter); 
496                                         }
497                                         
498                                         // Draw line from previous to current point
499                                         if(index > 0)
500                                         {
501                                                 DrawLine(
502                                                         series,
503                                                         point,
504                                                         pointRangeMin,
505                                                         pointRangeMax,
506                                                         index,
507                                                         (currentPointIsEmpty) ? emptyLinePen : linePen,  
508                                                         prevPoint.X, 
509                                                         prevPoint.Y, 
510                                                         currentPoint.X,
511                                                         currentPoint.Y);
512                                         }
513
514                                         // Remember last point coordinates
515                                         xValuePrev = xValue;
516                                         yValuePrev = yValue;
517                                         prevDataPoint = point;
518                                         prevPoint = currentPoint;
519                                         prevPointInAxesCoordinates = false;
520                                         prevPointIsEmpty = currentPointIsEmpty;
521                                         ++index;
522                                 }
523
524                                 // Draw last accumulated line segment
525                                 if(verticalLineDetected)
526                                 {
527                                         // Check if previous point must be converted from axes values to pixels
528                                         if(prevPointInAxesCoordinates)
529                                         {
530                                                 prevPoint.X = (float)
531                                                         (hAxis.GetLinearPosition( xValuePrev ) * xPixelConverter);
532                                                 prevPoint.Y = (float)
533                                                         (vAxis.GetLinearPosition( yValuePrev ) * yPixelConverter); 
534                                         }
535
536                                         // Convert Y coordinates to pixels
537                                         yValueRangeMin = (vAxis.GetLinearPosition( yValueRangeMin ) * yPixelConverter); 
538                                         yValueRangeMax = (vAxis.GetLinearPosition( yValueRangeMax ) * yPixelConverter); 
539
540                                         // Draw accumulated vertical line
541                                         DrawLine(
542                                                 series,
543                                                 prevDataPoint,
544                                                 pointRangeMin,
545                                                 pointRangeMax,
546                                                 index - 1,
547                                                 (prevPointIsEmpty) ? emptyLinePen : linePen, 
548                                                 prevPoint.X, 
549                                                 (float)yValueRangeMin, 
550                                                 prevPoint.X,
551                                                 (float)yValueRangeMax);
552                                                 
553                                         verticalLineDetected = false;
554                                         yValueRangeMin = double.NaN;
555                                         yValueRangeMax = double.NaN;
556                                         pointRangeMin = null;
557                                         pointRangeMax = null;
558                                 }
559
560                         }
561
562                         // Reset Clip Region
563                         if(clipRegionSet)
564                         {
565                                 graph.ResetClip();
566                         }
567         
568                 }
569
570                 /// <summary>
571                 /// Draws a line connecting two PointF structures.
572                 /// </summary>
573                 /// <param name="series">Chart series.</param>
574                 /// <param name="point">Series last data point in the group.</param>
575                 /// <param name="pointMin">Series minimum Y value data point in the group.</param>
576                 /// <param name="pointMax">Series maximum Y value data point in the group.</param>
577                 /// <param name="pointIndex">Point index.</param>
578                 /// <param name="pen">Pen object that determines the color, width, and style of the line.</param>
579                 /// <param name="firstPointX">First point X coordinate.</param>
580                 /// <param name="firstPointY">First point Y coordinate</param>
581                 /// <param name="secondPointX">Second point X coordinate.</param>
582                 /// <param name="secondPointY">Second point Y coordinate</param>
583                 public virtual void DrawLine(
584                         Series series,
585                         DataPoint point,
586                         DataPoint pointMin,
587                         DataPoint pointMax,
588                         int pointIndex,
589                         Pen pen,
590                         float firstPointX,
591                         float firstPointY,
592                         float secondPointX,
593                         float secondPointY
594                         )
595                 {
596                         // Transform 3D coordinates
597                         if(chartArea3DEnabled)
598                         {
599                                 Point3D [] points = new Point3D[2];
600
601                                 // All coordinates has to be transformed in relative coordinate system
602                                 // NOTE: Fixes issue #5496
603                                 PointF firstPoint = Graph.GetRelativePoint(new PointF(firstPointX, firstPointY));
604                                 PointF secondPoint = Graph.GetRelativePoint(new PointF(secondPointX, secondPointY));
605
606                                 points[0] = new Point3D(firstPoint.X, firstPoint.Y, seriesZCoordinate);
607                                 points[1] = new Point3D(secondPoint.X, secondPoint.Y, seriesZCoordinate);
608                                 matrix3D.TransformPoints( points );
609
610                                 // All coordinates has to be transformed back to pixels
611                                 // NOTE: Fixes issue #5496
612                                 points[0].PointF = Graph.GetAbsolutePoint(points[0].PointF);
613                                 points[1].PointF = Graph.GetAbsolutePoint(points[1].PointF);
614
615                                 firstPointX = points[0].X;
616                                 firstPointY = points[0].Y;
617                                 secondPointX = points[1].X;
618                                 secondPointY = points[1].Y;
619                         }
620
621                         // Draw line
622                         Graph.DrawLine(pen, firstPointX, firstPointY, secondPointX,secondPointY);
623
624                         // Process selection regions
625                         if( this.Common.ProcessModeRegions )
626                         {
627                                 // Create grapics path object for the line
628                 using (GraphicsPath path = new GraphicsPath())
629                 {
630                     float width = pen.Width + 2;
631
632                     if (Math.Abs(firstPointX - secondPointX) > Math.Abs(firstPointY - secondPointY))
633                     {
634                         path.AddLine(firstPointX, firstPointY - width, secondPointX, secondPointY - width);
635                         path.AddLine(secondPointX, secondPointY + width, firstPointX, firstPointY + width);
636                         path.CloseAllFigures();
637                     }
638                     else
639                     {
640                         path.AddLine(firstPointX - width, firstPointY, secondPointX - width, secondPointY);
641                         path.AddLine(secondPointX + width, secondPointY, firstPointX + width, firstPointY);
642                         path.CloseAllFigures();
643                     }
644
645                     // Calculate bounding rectangle
646                     RectangleF pathBounds = path.GetBounds();
647
648                     // If one side of the bounding rectangle is less than 2 pixels
649                     // use rectangle region shape to optimize used coordinates space
650                     if (pathBounds.Width <= 2.0 || pathBounds.Height <= 2.0)
651                     {
652                         // Add hot region path as rectangle
653                         pathBounds.Inflate(pen.Width, pen.Width);
654                         this.Common.HotRegionsList.AddHotRegion(
655                             Graph.GetRelativeRectangle(pathBounds),
656                             point,
657                             point.series.Name,
658                             pointIndex);
659                     }
660                     else
661                     {
662                         // Add hot region path as polygon
663                         this.Common.HotRegionsList.AddHotRegion(
664                             path,
665                             false,
666                             Graph,
667                             point,
668                             point.series.Name,
669                             pointIndex);
670                     }
671                 }
672                         }
673                 }
674
675                 #endregion
676
677                 #region Y values related methods
678
679                 /// <summary>
680                 /// Helper function, which returns the Y value of the point.
681                 /// </summary>
682                 /// <param name="common">Chart common elements.</param>
683                 /// <param name="area">Chart area the series belongs to.</param>
684                 /// <param name="series">Sereis of the point.</param>
685                 /// <param name="point">Point object.</param>
686                 /// <param name="pointIndex">Index of the point.</param>
687                 /// <param name="yValueIndex">Index of the Y value to get.</param>
688                 /// <returns>Y value of the point.</returns>
689                 virtual public double GetYValue(
690                         CommonElements common, 
691                         ChartArea area, 
692                         Series series, 
693                         DataPoint point, 
694                         int pointIndex, 
695                         int yValueIndex)
696                 {
697                         return point.YValues[yValueIndex];
698                 }
699
700                 #endregion
701
702                 #region SmartLabelStyle methods
703
704                 /// <summary>
705                 /// Adds markers position to the list. Used to check SmartLabelStyle overlapping.
706                 /// </summary>
707                 /// <param name="common">Common chart elements.</param>
708                 /// <param name="area">Chart area.</param>
709                 /// <param name="series">Series values to be used.</param>
710                 /// <param name="list">List to add to.</param>
711                 public void AddSmartLabelMarkerPositions(CommonElements common, ChartArea area, Series series, ArrayList list)          
712                 {
713                         // Fast Line chart type do not support labels
714                 }
715
716                 #endregion
717
718         #region IDisposable interface implementation
719         /// <summary>
720         /// Releases unmanaged and - optionally - managed resources
721         /// </summary>
722         /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
723         protected virtual void Dispose(bool disposing)
724         {
725             //Nothing to dispose at the base class. 
726         }
727
728         /// <summary>
729         /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
730         /// </summary>
731         public void Dispose()
732         {
733             Dispose(true);
734             GC.SuppressFinalize(this);
735         }
736         #endregion
737     }
738 }