1 //-------------------------------------------------------------
2 // <copyright company=�Microsoft Corporation�>
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>
FastLineChart()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>
GetLegendImageStyle(Series series)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>
GetImage(ChartTypeRegistry registry)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>
Paint( ChartGraphics graph, CommonElements common, ChartArea area, Series seriesToDraw )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>
DrawLine( Series series, DataPoint point, DataPoint pointMin, DataPoint pointMax, int pointIndex, Pen pen, float firstPointX, float firstPointY, float secondPointX, float secondPointY )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>
GetYValue( CommonElements common, ChartArea area, Series series, DataPoint point, int pointIndex, int yValueIndex)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>
AddSmartLabelMarkerPositions(CommonElements common, ChartArea area, Series series, ArrayList list)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>
Dispose(bool disposing)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>
Dispose()731         public void Dispose()
732         {
733             Dispose(true);
734             GC.SuppressFinalize(this);
735         }
736         #endregion
737     }
738 }
739