1 //-------------------------------------------------------------
2 // <copyright company=�Microsoft Corporation�>
3 //   Copyright � Microsoft Corporation. All Rights Reserved.
4 // </copyright>
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
8 //  File:		LineChart.cs
9 //
10 //  Namespace:	DataVisualization.Charting.ChartTypes
11 //
12 //	Classes:	LineChart, SplineChart
13 //
14 //  Purpose:	Provides 2D/3D drawing and hit testing
15 //              functionality for the Line and Spline charts.
16 //
17 //	Reviewed:	AG - August 6, 2002
18 //              AG - Microsoft 6, 2007
19 //
20 //===================================================================
21 
22 #region Used namespaces
23 
24 using System;
25 using System.Collections;
26 using System.Collections.Generic;
27 using System.Drawing;
28 using System.Drawing.Drawing2D;
29 
30 #if Microsoft_CONTROL
31     using System.Windows.Forms.DataVisualization.Charting.Utilities;
32 #else
33     using System.Web.UI.DataVisualization.Charting;
34     using System.Web.UI.DataVisualization.Charting.Utilities;
35 #endif
36 
37 #endregion
38 
39 #if Microsoft_CONTROL
40 	namespace System.Windows.Forms.DataVisualization.Charting.ChartTypes
41 #else
42 	namespace System.Web.UI.DataVisualization.Charting.ChartTypes
43 #endif
44 {
45 	/// <summary>
46     /// SplineChart class extends the LineChart class by
47     /// providing a different initial tension for the line.
48 	/// </summary>
49 	internal class SplineChart : LineChart
50 	{
51 		#region Constructor
52 
53 		/// <summary>
54 		/// Default constructor.
55 		/// </summary>
SplineChart()56 		public SplineChart()
57 		{
58 			// Set default line tension
59 			base.lineTension = 0.5f;
60 		}
61 
62 		#endregion
63 
64 		#region IChartType interface implementation
65 
66 		/// <summary>
67 		/// Chart type name
68 		/// </summary>
69 		public override string Name			{ get{ return ChartTypeNames.Spline;}}
70 
71 		/// <summary>
72 		/// Gets chart type image.
73 		/// </summary>
74 		/// <param name="registry">Chart types registry object.</param>
75 		/// <returns>Chart type image.</returns>
GetImage(ChartTypeRegistry registry)76 		override public System.Drawing.Image GetImage(ChartTypeRegistry registry)
77 		{
78 			return (System.Drawing.Image)registry.ResourceManager.GetObject(this.Name + "ChartType");
79 		}
80 
81 		#endregion
82 
83 		#region Helper methods
84 
85 		/// <summary>
86 		/// Checks if line tension is supported by the chart type.
87 		/// </summary>
88 		/// <returns>True if line tension is supported.</returns>
IsLineTensionSupported()89 		protected override bool IsLineTensionSupported()
90 		{
91 			return true;
92 		}
93 
94 		/// <summary>
95 		/// Fills a PointF array of data points positions.
96 		/// </summary>
97 		/// <param name="graph">Graphics object.</param>
98 		/// <param name="series">Point series.</param>
99 		/// <param name="indexedSeries">Indicate that point index should be used as X value.</param>
100 		/// <returns>Array of data points position.</returns>
GetPointsPosition( ChartGraphics graph, Series series, bool indexedSeries)101 		override protected PointF[] GetPointsPosition(
102 			ChartGraphics graph,
103 			Series series,
104 			bool indexedSeries)
105 		{
106 			// Check tension attribute in the series
107 			base.lineTension = GetDefaultTension();
108 			if(IsLineTensionSupported() && series.IsCustomPropertySet(CustomPropertyName.LineTension))
109 			{
110 				base.lineTension = CommonElements.ParseFloat(series[CustomPropertyName.LineTension]);
111 			}
112 
113 			// Call base LineChart class
114 			return base.GetPointsPosition(graph, series, indexedSeries);
115 		}
116 
117 		/// <summary>
118 		/// Gets default line tension.
119 		/// </summary>
120 		/// <returns>Default line tension.</returns>
GetDefaultTension()121 		override protected float GetDefaultTension()
122 		{
123 			return 0.5f;
124 		}
125 
126 		#endregion
127 	}
128 
129 	/// <summary>
130     /// LineChart class provides 2D/3D drawing and hit testing
131     /// functionality for the Line and Spline charts. The only
132     /// difference of the Spline chart is the default tension
133     /// of the line.
134     ///
135     /// PointChart base class provides functionality realted
136     /// to drawing labels and markers.
137 	/// </summary>
138 	internal class LineChart : PointChart
139 	{
140 		#region Fields and Constructor
141 
142 		/// <summary>
143 		/// Line tension
144 		/// </summary>
145 		protected	float	lineTension = 0f;
146 
147 		/// <summary>
148 		/// Index of the drawing center point. int.MaxValue if drawn from left->right or right->left.
149 		/// </summary>
150 		protected	int		centerPointIndex = int.MaxValue;
151 
152 		/// <summary>
153 		/// Inicates that border color attribute must be used to draw the line
154 		/// </summary>
155 		protected	bool	useBorderColor = false;
156 
157 		/// <summary>
158 		/// Inicates that line shadow should not be drawn
159 		/// </summary>
160 		protected	bool	disableShadow = false;
161 
162 		/// <summary>
163 		/// Inicates that only line shadow must be drawn
164 		/// </summary>
165 		protected	bool	drawShadowOnly = false;
166 
167 		// Pen used to draw the line chart
168 		private		Pen		_linePen = new Pen(Color.Black);
169 
170 		/// <summary>
171 		/// Horizontal axis minimum value
172 		/// </summary>
173 		protected	double	hAxisMin = 0.0;
174 
175 		/// <summary>
176 		/// Horizontal axis maximum value
177 		/// </summary>
178 		protected	double	hAxisMax = 0.0;
179 
180 		/// <summary>
181 		/// Vertical axis minimum value
182 		/// </summary>
183 		protected	double	vAxisMin = 0.0;
184 
185 		/// <summary>
186 		/// Vertical axis maximum value
187 		/// </summary>
188 		protected	double	vAxisMax = 0.0;
189 
190 		/// <summary>
191 		/// Clip region indicator
192 		/// </summary>
193 		protected	bool	clipRegionSet = false;
194 
195 		/// <summary>
196 		/// Indicates that several series are drawn at the same time. Stacked or Side-by-side.
197 		/// </summary>
198 		protected	bool	multiSeries = false;
199 
200 		/// <summary>
201 		/// Indicates which coordinates should be tested against the COP.
202 		/// </summary>
203 		protected	COPCoordinates	COPCoordinatesToCheck = COPCoordinates.X;
204 
205 		/// <summary>
206 		/// Number of data points loops required to draw chart.
207 		/// </summary>
208 		protected	int		allPointsLoopsNumber = 1;
209 
210 		/// <summary>
211 		/// Indicates that line markers are shown at data point.
212 		/// </summary>
213 		protected	bool	showPointLines = false;
214 
215 		/// <summary>
216 		/// Indicates that that lines outside the area should be still processed while drawing.
217 		/// </summary>
218 		protected	bool	drawOutsideLines = false;
219 
220 
221 		/// <summary>
222 		/// Indicates if base (point) chart type should be processed
223 		/// </summary>
224 		private		bool	_processBaseChart = false;
225 
226 		/// <summary>
227 		/// Default constructor
228 		/// </summary>
LineChart()229 		public LineChart() : base(false)
230 		{
231 			// Draw markers on the front edge
232 			middleMarker = false;
233 		}
234 
235 		#endregion
236 
237 		#region IChartType interface implementation
238 
239 		/// <summary>
240 		/// Chart type name
241 		/// </summary>
242 		public override string Name			{ get{ return ChartTypeNames.Line;}}
243 
244 		/// <summary>
245 		/// Gets chart type image.
246 		/// </summary>
247 		/// <param name="registry">Chart types registry object.</param>
248 		/// <returns>Chart type image.</returns>
GetImage(ChartTypeRegistry registry)249 		override public System.Drawing.Image GetImage(ChartTypeRegistry registry)
250 		{
251 			return (System.Drawing.Image)registry.ResourceManager.GetObject(this.Name + "ChartType");
252 		}
253 
254 		/// <summary>
255 		/// True if chart type is stacked
256 		/// </summary>
257 		public override bool Stacked		{ get{ return false;}}
258 
259 		/// <summary>
260 		/// True if chart type supports axeses
261 		/// </summary>
262 		public override bool RequireAxes	{ get{ return true;} }
263 
264 		/// <summary>
265 		/// True if chart type supports logarithmic axes
266 		/// </summary>
267 		public override bool SupportLogarithmicAxes	{ get{ return true;} }
268 
269 		/// <summary>
270 		/// True if chart type requires to switch the value (Y) axes position
271 		/// </summary>
272 		public override bool SwitchValueAxes	{ get{ return false;} }
273 
274 		/// <summary>
275 		/// True if chart series can be placed side-by-side.
276 		/// </summary>
277 		override public bool SideBySideSeries { get{ return false;} }
278 
279 		/// <summary>
280 		/// If the crossing value is auto Crossing value should be
281 		/// automatically set to zero for some chart
282 		/// types (Bar, column, area etc.)
283 		/// </summary>
284 		override public bool ZeroCrossing { get{ return true;} }
285 
286 		/// <summary>
287 		/// True if each data point of a chart must be represented in the legend
288 		/// </summary>
289 		public override bool DataPointsInLegend	{ get{ return false;} }
290 
291 		/// <summary>
292 		/// True if palette colors should be applied for each data paoint.
293 		/// Otherwise the color is applied to the series.
294 		/// </summary>
295 		public override bool ApplyPaletteColorsToPoints	{ get { return false; } }
296 
297 		/// <summary>
298 		/// How to draw series/points in legend:
299 		/// Filled rectangle, Line or Marker
300 		/// </summary>
301 		/// <param name="series">Legend item series.</param>
302 		/// <returns>Legend item style.</returns>
GetLegendImageStyle(Series series)303 		override public LegendImageStyle GetLegendImageStyle(Series series)
304 		{
305 			return LegendImageStyle.Line;
306 		}
307 
308 		/// <summary>
309 		/// Number of supported Y value(s) per point
310 		/// </summary>
311 		public override int YValuesPerPoint{ get { return 1; } }
312 
313 		#endregion
314 
315 		#region Painting and selection methods
316 
317 		/// <summary>
318 		/// Paint Line Chart.
319 		/// </summary>
320 		/// <param name="graph">The Chart Graphics object.</param>
321 		/// <param name="common">The Common elements object.</param>
322 		/// <param name="area">Chart area for this char.t</param>
323 		/// <param name="seriesToDraw">Chart series to draw.</param>
Paint( ChartGraphics graph, CommonElements common, ChartArea area, Series seriesToDraw )324 		public override void Paint( ChartGraphics graph, CommonElements common, ChartArea area, Series seriesToDraw )
325 		{
326 			// Save chart area reference
327 			this.Area = area;
328             this.Common = common;
329 			// Draw lines
330 			_processBaseChart = false;
331 			ProcessChartType( false, graph, common, area, seriesToDraw );
332 
333 			// Draw labels and markers using base class PointChart
334 			if(_processBaseChart)
335 			{
336 				base.ProcessChartType( false, graph, common, area, seriesToDraw );
337 			}
338 		}
339 
340 		/// <summary>
341 		/// Draws or perform the hit test for the line chart.
342 		/// </summary>
343 		/// <param name="selection">If True selection mode is active, otherwise paint mode is active.</param>
344 		/// <param name="graph">The Chart Graphics object.</param>
345 		/// <param name="common">The Common elements object.</param>
346 		/// <param name="area">Chart area for this chart.</param>
347 		/// <param name="seriesToDraw">Chart series to draw.</param>
ProcessChartType( bool selection, ChartGraphics graph, CommonElements common, ChartArea area, Series seriesToDraw )348 		protected override void ProcessChartType(
349 			bool selection,
350 			ChartGraphics graph,
351 			CommonElements common,
352 			ChartArea area,
353 			Series seriesToDraw )
354 		{
355             this.Common = common;
356             // Prosess 3D chart type
357 			if(area.Area3DStyle.Enable3D)
358 			{
359 				_processBaseChart = true;
360 				ProcessLineChartType3D( selection, graph, common, area, seriesToDraw );
361 				return;
362 			}
363 
364 
365 			// All data series from chart area which have Bar chart type
366 			List<string>	typeSeries = area.GetSeriesFromChartType(this.Name);
367 
368 			// Check if series are indexed
369             bool indexedSeries = ChartHelper.IndexedSeries(this.Common, typeSeries.ToArray());
370 
371 			//************************************************************
372 			//** Loop through all series
373 			//************************************************************
374 			foreach( Series ser in common.DataManager.Series )
375 			{
376 				// Process non empty series of the area with Line chart type
377 				if( String.Compare( ser.ChartTypeName, this.Name, true, System.Globalization.CultureInfo.CurrentCulture ) != 0
378 					|| ser.ChartArea != area.Name || !ser.IsVisible())
379 				{
380 					continue;
381 				}
382 
383                 // Check if only 1 specified series must be processed
384                 if (seriesToDraw != null && seriesToDraw.Name != ser.Name)
385                 {
386                     continue;
387                 }
388 
389 				// Set active horizontal/vertical axis
390 				HAxis = area.GetAxis(AxisName.X, ser.XAxisType, ser.XSubAxisName);
391 				VAxis = area.GetAxis(AxisName.Y, ser.YAxisType, ser.YSubAxisName);
392 				hAxisMin = HAxis.ViewMinimum;
393 				hAxisMax = HAxis.ViewMaximum;
394 				vAxisMin = VAxis.ViewMinimum;
395 				vAxisMax = VAxis.ViewMaximum;
396 
397                 float chartWidthPercentage = (graph.Common.ChartPicture.Width - 1) / 100F;
398                 float chartHeightPercentage = (graph.Common.ChartPicture.Height - 1) / 100F;
399 
400 				// Call Back Paint event
401 				if( !selection )
402 				{
403                     common.Chart.CallOnPrePaint(new ChartPaintEventArgs(ser, graph, common, area.PlotAreaPosition));
404 				}
405 
406                 // Check tension attribute in the series
407                 this.lineTension = GetDefaultTension();
408                 if (IsLineTensionSupported() && ser.IsCustomPropertySet(CustomPropertyName.LineTension))
409                 {
410                     this.lineTension = CommonElements.ParseFloat(ser[CustomPropertyName.LineTension]);
411                 }
412 
413 				// Fill the array of data points coordinates (absolute)
414 				bool		dataPointPosFilled = false;
415 				PointF[]	dataPointPos = null;
416 				if(this.lineTension == 0 && !common.ProcessModeRegions)
417 				{
418 					dataPointPos = new PointF[ser.Points.Count];
419 				}
420 				else
421 				{
422 					dataPointPosFilled = true;
423 					dataPointPos = GetPointsPosition(graph, ser, indexedSeries);
424 
425 					//*************************************************************************
426 					//** Solution for the "Out of Memory"  exception in the DrawCurve method
427 					//** All points in the array should be at least 0.1 pixel apart.
428 					//*************************************************************************
429 					if(this.lineTension != 0)
430 					{
431 						float	minDifference = 0.1f;
432 						for(int pointIndex = 1; pointIndex < dataPointPos.Length; pointIndex++)
433 						{
434 							if( Math.Abs(dataPointPos[pointIndex - 1].X - dataPointPos[pointIndex].X ) < minDifference )
435 							{
436 								if(dataPointPos[pointIndex].X > dataPointPos[pointIndex - 1].X)
437 								{
438 									dataPointPos[pointIndex].X = dataPointPos[pointIndex - 1].X + minDifference;
439 								}
440 								else
441 								{
442 									dataPointPos[pointIndex].X = dataPointPos[pointIndex - 1].X - minDifference;
443 								}
444 							}
445 							if( Math.Abs(dataPointPos[pointIndex - 1].Y - dataPointPos[pointIndex].Y ) < minDifference )
446 							{
447 								if(dataPointPos[pointIndex].Y > dataPointPos[pointIndex - 1].Y)
448 								{
449 									dataPointPos[pointIndex].Y = dataPointPos[pointIndex - 1].Y + minDifference;
450 								}
451 								else
452 								{
453 									dataPointPos[pointIndex].Y = dataPointPos[pointIndex - 1].Y - minDifference;
454 								}
455 							}
456 						}
457 					}
458 
459 				}
460 
461 				// Draw line if we have more than one data point
462 				if(dataPointPos.Length > 1)
463 				{
464 					// Draw each data point
465 					int index = 0;
466 					DataPoint	prevDataPoint = null;
467 					double	yValuePrev = 0.0;
468 					double	xValuePrev = 0.0;
469 					bool showLabelAsValue = ser.IsValueShownAsLabel;
470 					bool prevPointInArray = false;
471 					foreach( DataPoint point in ser.Points )
472 					{
473 						prevPointInArray = false;
474 
475 						// Reset pre-calculated point position
476 						point.positionRel = new PointF(float.NaN, float.NaN);
477 
478 						//************************************************************
479 						//** Check if point marker or label is visible
480 						//************************************************************
481 						if(!_processBaseChart)
482 						{
483 							string		pointMarkerImage = point.MarkerImage;
484 							MarkerStyle	pointMarkerStyle = point.MarkerStyle;
485 
486 							if( alwaysDrawMarkers ||
487 								pointMarkerStyle != MarkerStyle.None ||
488 								pointMarkerImage.Length > 0 ||
489 								showLabelAsValue ||
490 								point.IsValueShownAsLabel ||
491 								point.Label.Length > 0 )
492 							{
493 								_processBaseChart = true;
494 							}
495 						}
496 
497 						// Change Y value if line is out of plot area
498 						double yValue = GetYValue(common, area, ser, point, index, this.YValueIndex);
499 
500 						// Recalculates x position
501 						double	xValue = (indexedSeries) ? index + 1 : point.XValue;
502 
503 						// If not first point
504 						if(index != 0)
505 						{
506 							// Axes are logarithmic
507 							yValue = VAxis.GetLogValue( yValue );
508 							xValue = HAxis.GetLogValue( xValue );
509 
510 							// Check if line is completly out of the data scaleView
511 							if( (xValue <= hAxisMin && xValuePrev < hAxisMin) ||
512 								(xValue >= hAxisMax && xValuePrev > hAxisMax) ||
513 								(yValue <= vAxisMin && yValuePrev < vAxisMin) ||
514 								(yValue >= vAxisMax && yValuePrev > vAxisMax) )
515 							{
516 								if(!drawOutsideLines)
517 								{
518 									// Check if next point also outside of the scaleView and on the
519 									// same side as current point. If not line has to be processed
520 									// to correctly handle tooltips.
521 									// NOTE: Fixes issue #4961
522 									bool skipPoint = true;
523 									if( common.ProcessModeRegions &&
524 										(index + 1) < ser.Points.Count)
525 									{
526 										DataPoint nextPoint = ser.Points[index + 1];
527 
528 										// Recalculates x position
529 										double	xValueNext = (indexedSeries) ? index + 2 : nextPoint.XValue;
530 
531 										if( (xValue < hAxisMin && xValueNext > hAxisMin) ||
532 											(xValue > hAxisMax && xValueNext < hAxisMax) )
533 										{
534 											skipPoint = false;
535 										}
536 
537 
538 										// Change Y value if line is out of plot area
539 										if(skipPoint)
540 										{
541 											if( (yValue < vAxisMin && xValueNext > vAxisMin) ||
542 												(yValue > vAxisMax && xValueNext < vAxisMax) )
543 											{
544 												skipPoint = false;
545 											}
546 										}
547 									}
548 
549 									// Skip point
550 									if(skipPoint)
551 									{
552 										++index;
553 										prevDataPoint = point;
554 										yValuePrev = yValue;
555 										xValuePrev = xValue;
556 										continue;
557 									}
558 								}
559 							}
560 
561 							// Check if line is partialy in the data scaleView
562 							clipRegionSet = false;
563 							if(this.lineTension != 0.0 ||
564 								xValuePrev < hAxisMin || xValuePrev > hAxisMax ||
565 								xValue > hAxisMax || xValue < hAxisMin ||
566 								yValuePrev < vAxisMin || yValuePrev > vAxisMax ||
567 								yValue < vAxisMin || yValue > vAxisMax )
568 							{
569 								// Set clipping region for line drawing
570 								graph.SetClip( area.PlotAreaPosition.ToRectangleF() );
571 								clipRegionSet = true;
572 							}
573 
574 
575 							if(this.lineTension == 0 && !dataPointPosFilled)
576 							{
577 								float yPosition = 0f;
578 								float xPosition = 0f;
579 
580 								// Line reqires two points to draw
581 								// Check if previous point is in the array
582 								if(!prevPointInArray)
583 								{
584 									// Recalculates x/y position
585 									yPosition = (float)VAxis.GetLinearPosition( yValuePrev );
586 									xPosition = (float)HAxis.GetLinearPosition( xValuePrev );
587 
588 									// Add point position into array
589 									// IMPORTANT: Rounding was removed from this part of code because of
590 									// very bad drawing in Flash.
591 									dataPointPos[index - 1] = new PointF(
592 										xPosition * chartWidthPercentage,
593 										yPosition * chartHeightPercentage);
594 								}
595 
596 
597 								// Recalculates x/y position
598 								yPosition = (float)VAxis.GetLinearPosition( yValue );
599 								xPosition = (float)HAxis.GetLinearPosition( xValue );
600 
601 								// Add point position into array
602 								// IMPORTANT: Rounding was removed from this part of code because of
603 								// very bad drawing in Flash.
604 								dataPointPos[index] = new PointF(
605 									xPosition * chartWidthPercentage,
606 									yPosition * chartHeightPercentage);
607 
608 								prevPointInArray = true;
609 							}
610 
611 							// Remeber pre-calculated point position
612 							point.positionRel = graph.GetRelativePoint(dataPointPos[index]);
613 
614 							// Start Svg Selection mode
615 							graph.StartHotRegion( point );
616 
617 							if( index != 0 && prevDataPoint.IsEmpty )
618 							{
619 								// IsEmpty data point - second line
620 								DrawLine(
621 									graph,
622 									common,
623 									prevDataPoint,
624 									ser,
625 									dataPointPos,
626 									index,
627 									this.lineTension);
628 							}
629 							else
630 							{
631 								// Regular data point and empty point - first line
632 								DrawLine(
633 									graph,
634 									common,
635 									point,
636 									ser,
637 									dataPointPos,
638 									index,
639 									this.lineTension);
640 							}
641 
642     						// End Svg Selection mode
643 							graph.EndHotRegion( );
644 
645 							// Reset Clip Region
646 							if(clipRegionSet)
647 							{
648 								graph.ResetClip();
649 							}
650 
651 							// Remember previous point data
652 							prevDataPoint = point;
653 							yValuePrev = yValue;
654 							xValuePrev = xValue;
655 						}
656 						else
657 						{
658 							// Get Y values of the current and previous data points
659 							prevDataPoint = point;
660 							yValuePrev = GetYValue(common, area, ser, point, index, 0);
661 							xValuePrev = (indexedSeries) ? index + 1 : point.XValue;
662 							yValuePrev = VAxis.GetLogValue( yValuePrev );
663 							xValuePrev = HAxis.GetLogValue( xValuePrev );
664 
665 							// Remeber pre-calculated point position
666 							point.positionRel = new PointF(
667 								(float)HAxis.GetPosition( xValuePrev ),
668 								(float)VAxis.GetPosition( yValuePrev ) );
669 						}
670 
671 						// Process image map selection for the first point
672 						if(index == 0)
673 						{
674 							DrawLine(
675 								graph,
676 								common,
677 								point,
678 								ser,
679 								dataPointPos,
680 								index,
681 								this.lineTension);
682 						}
683 
684 						// Increase data point index
685 						++index;
686 					}
687 				}
688 				else if(dataPointPos.Length == 1 &&
689 					ser.Points.Count == 1)
690 				{
691 					//************************************************************
692 					//** Check if point marker or label is visible
693 					//************************************************************
694 					if(!_processBaseChart)
695 					{
696 						if( alwaysDrawMarkers ||
697 							ser.Points[0].MarkerStyle != MarkerStyle.None ||
698 							ser.Points[0].MarkerImage.Length > 0 ||
699 							ser.IsValueShownAsLabel ||
700 							ser.Points[0].IsValueShownAsLabel ||
701 							ser.Points[0].Label.Length > 0 )
702 						{
703 							_processBaseChart = true;
704 						}
705 					}
706 				}
707 
708 				// Reset points array
709 				dataPointPos = null;
710 
711 				// Call Paint event
712 				if( !selection )
713 				{
714                     common.Chart.CallOnPostPaint(new ChartPaintEventArgs(ser, graph, common, area.PlotAreaPosition));
715 				}
716 			}
717 		}
718 
719 		/// <summary>
720 		/// Calculate position and draw one chart line and/or shadow.
721 		/// </summary>
722 		/// <param name="graph">Graphics object.</param>
723 		/// <param name="common">The Common elements object.</param>
724 		/// <param name="point">Point to draw the line for.</param>
725 		/// <param name="series">Point series.</param>
726 		/// <param name="points">Array of oints coordinates.</param>
727 		/// <param name="pointIndex">Index of point to draw.</param>
728 		/// <param name="tension">Line tension.</param>
DrawLine( ChartGraphics graph, CommonElements common, DataPoint point, Series series, PointF[] points, int pointIndex, float tension)729 		virtual protected void DrawLine(
730 			ChartGraphics graph,
731 			CommonElements common,
732 			DataPoint point,
733 			Series series,
734 			PointF[] points,
735 			int pointIndex,
736 			float tension)
737 		{
738 			int	pointBorderWidth = point.BorderWidth;
739 
740 			// ****************************************************
741 			// Paint Mode
742 			// ****************************************************
743 			if( common.ProcessModePaint )
744 			{
745 				// Start drawing from the second point
746 				if(pointIndex > 0)
747 				{
748 					Color			color = (useBorderColor) ? point.BorderColor : point.Color;
749 					ChartDashStyle	dashStyle = point.BorderDashStyle;
750 
751 					// Draw line shadow
752 					if(!disableShadow && series.ShadowOffset != 0 && series.ShadowColor != Color.Empty)
753 					{
754 						if(color != Color.Empty && color != Color.Transparent && pointBorderWidth > 0 && dashStyle != ChartDashStyle.NotSet)
755 						{
756 							Pen shadowPen = new Pen((series.ShadowColor.A != 255) ? series.ShadowColor : Color.FromArgb((useBorderColor) ? point.BorderColor.A/2 : point.Color.A/2, series.ShadowColor), pointBorderWidth);
757 							shadowPen.DashStyle = graph.GetPenStyle( point.BorderDashStyle );
758 							shadowPen.StartCap = LineCap.Round;
759 							shadowPen.EndCap = LineCap.Round;
760 
761 							// Translate curve
762 							GraphicsState graphicsState = graph.Save();
763 							Matrix transform = graph.Transform.Clone();
764 							transform.Translate(series.ShadowOffset, series.ShadowOffset);
765 							graph.Transform = transform;
766 
767 							// Draw shadow
768 							if(this.lineTension == 0)
769 							{
770                                 try
771                                 {
772                                     graph.DrawLine(shadowPen, points[pointIndex - 1], points[pointIndex]);
773                                 }
774                                 catch (OverflowException)
775                                 {
776                                     this.DrawTruncatedLine(graph, shadowPen, points[pointIndex - 1], points[pointIndex]);
777                                 }
778 							}
779 							else
780 							{
781 								graph.DrawCurve(shadowPen, points, pointIndex - 1, 1, tension);
782 							}
783 							graph.Restore(graphicsState);
784 						}
785 					}
786 
787 					// If only shadow must be drawn - return
788 					if(drawShadowOnly)
789 					{
790 						return;
791 					}
792 
793                     //// IsEmpty data ` color and style
794                     // DT - removed code - line chart will have MS Office behavior for empty points -> broken line.
795                     //if( point.IsEmpty )
796                     //{
797                     //    if( point.Color == Color.IsEmpty)
798                     //    {
799                     //        color = Color.Black;
800                     //    }
801                     //    if( point.BorderDashStyle == ChartDashStyle.NotSet )
802                     //    {
803                     //        dashStyle = ChartDashStyle.Dash;
804                     //    }
805                     //}
806 
807 					// Draw data point line
808 					if(color != Color.Empty && pointBorderWidth > 0 && dashStyle != ChartDashStyle.NotSet)
809 					{
810 						if(_linePen.Color != color)
811 						{
812 							_linePen.Color = color;
813 						}
814 						if(_linePen.Width != pointBorderWidth)
815 						{
816 							_linePen.Width = pointBorderWidth;
817 						}
818 						if(_linePen.DashStyle != graph.GetPenStyle( dashStyle ))
819 						{
820 							_linePen.DashStyle = graph.GetPenStyle( dashStyle );
821 						}
822 
823 						// Set Rounded Cap
824 						if(_linePen.StartCap != LineCap.Round)
825 							_linePen.StartCap = LineCap.Round;
826 						if(_linePen.EndCap != LineCap.Round)
827 							_linePen.EndCap = LineCap.Round;
828 
829 						if(tension == 0)
830 						{
831                             // VSTS: 9698 - issue: the line start from X = 0 when GDI overflows (before we expected exception)
832                             if (IsLinePointsOverflow(points[pointIndex - 1]) || IsLinePointsOverflow(points[pointIndex]))
833                             {
834                                 this.DrawTruncatedLine(graph, _linePen, points[pointIndex - 1], points[pointIndex]);
835                             }
836                             else
837                             {
838                                 try
839                                 {
840                                     graph.DrawLine(_linePen, points[pointIndex - 1], points[pointIndex]);
841                                 }
842                                 catch (OverflowException)
843                                 {
844                                     this.DrawTruncatedLine(graph, _linePen, points[pointIndex - 1], points[pointIndex]);
845                                 }
846                             }
847 						}
848 						else
849 						{
850 							graph.DrawCurve(_linePen, points, pointIndex - 1, 1, tension);
851 						}
852 					}
853 				}
854 			}
855 
856 			//************************************************************
857 			// Hot Regions mode used for image maps, tool tips and
858 			// hit test function
859 			//************************************************************
860 			if( common.ProcessModeRegions )
861 			{
862 				int width = pointBorderWidth + 2;
863 
864 				// Create grapics path object dor the curve
865                 using (GraphicsPath path = new GraphicsPath())
866                 {
867 
868                     // If line tension is zero - it's a straight line
869                     if (this.lineTension == 0)
870                     {
871                         // Add half line segment prior to the data point
872                         if (pointIndex > 0)
873                         {
874                             PointF first = points[pointIndex - 1];
875                             PointF second = points[pointIndex];
876                             first.X = (first.X + second.X) / 2f;
877                             first.Y = (first.Y + second.Y) / 2f;
878 
879                             if (Math.Abs(first.X - second.X) > Math.Abs(first.Y - second.Y))
880                             {
881                                 path.AddLine(first.X, first.Y - width, second.X, second.Y - width);
882                                 path.AddLine(second.X, second.Y + width, first.X, first.Y + width);
883                                 path.CloseAllFigures();
884                             }
885                             else
886                             {
887                                 path.AddLine(first.X - width, first.Y, second.X - width, second.Y);
888                                 path.AddLine(second.X + width, second.Y, first.X + width, first.Y);
889                                 path.CloseAllFigures();
890 
891                             }
892                         }
893 
894                         // Add half line segment after the data point
895                         if (pointIndex + 1 < points.Length)
896                         {
897                             PointF first = points[pointIndex];
898                             PointF second = points[pointIndex + 1];
899                             second.X = (first.X + second.X) / 2f;
900                             second.Y = (first.Y + second.Y) / 2f;
901 
902                             // Set a marker in the path to separate from the first line segment
903                             if (pointIndex > 0)
904                             {
905                                 path.SetMarkers();
906                             }
907 
908                             if (Math.Abs(first.X - second.X) > Math.Abs(first.Y - second.Y))
909                             {
910                                 path.AddLine(first.X, first.Y - width, second.X, second.Y - width);
911                                 path.AddLine(second.X, second.Y + width, first.X, first.Y + width);
912                                 path.CloseAllFigures();
913                             }
914                             else
915                             {
916                                 path.AddLine(first.X - width, first.Y, second.X - width, second.Y);
917                                 path.AddLine(second.X + width, second.Y, first.X + width, first.Y);
918                                 path.CloseAllFigures();
919                             }
920                         }
921 
922                     }
923                     else if (pointIndex > 0)
924                     {
925                         try
926                         {
927                             path.AddCurve(points, pointIndex - 1, 1, this.lineTension);
928                             path.Widen(new Pen(point.Color, pointBorderWidth + 2));
929                             path.Flatten();
930                         }
931                         catch (OutOfMemoryException)
932                         {
933                             // GraphicsPath.Widen incorrectly throws OutOfMemoryException
934                             // catching here and reacting by not widening
935                         }
936                         catch (ArgumentException)
937                         {
938                         }
939                     }
940 
941                     // Path is empty
942                     if (path.PointCount == 0)
943                     {
944                         return;
945                     }
946 
947                     // Allocate array of floats
948                     PointF pointNew = PointF.Empty;
949                     float[] coord = new float[path.PointCount * 2];
950                     PointF[] pathPoints = path.PathPoints;
951                     for (int i = 0; i < path.PointCount; i++)
952                     {
953                         pointNew = graph.GetRelativePoint(pathPoints[i]);
954                         coord[2 * i] = pointNew.X;
955                         coord[2 * i + 1] = pointNew.Y;
956                     }
957 
958                     common.HotRegionsList.AddHotRegion(path, false, coord, point, series.Name, pointIndex);
959                 }
960 			}
961 		}
962 
963 
964         private const long maxGDIRange = 0x800000;
965         // VSTS: 9698 - issue: the line start from X = 0 when GDI overflows (before we expected exception)
IsLinePointsOverflow(PointF point)966         private bool IsLinePointsOverflow(PointF point)
967         {
968             return point.X <= -maxGDIRange || point.X >= maxGDIRange || point.Y <= -maxGDIRange || point.Y >= maxGDIRange;
969         }
970 
971 		/// <summary>
972 		/// During zooming there are scenarios when the line coordinates are extremly large and
973         /// originate outside of the chart pixel boundaries. This cause GDI+ line drawing methods
974         /// to throw stack overflow exceptions.
975         /// This method tries to change the coordinates into the chart boundaries and draw the line.
976 		/// </summary>
977         /// <param name="graph">Chart graphics.</param>
978 		/// <param name="pen">Pen object that determines the color, width, and style of the line.</param>
979 		/// <param name="pt1">PointF structure that represents the first point to connect.</param>
980 		/// <param name="pt2">PointF structure that represents the second point to connect.</param>
DrawTruncatedLine(ChartGraphics graph, Pen pen, PointF pt1, PointF pt2)981         private void DrawTruncatedLine(ChartGraphics graph, Pen pen, PointF pt1, PointF pt2)
982         {
983             PointF adjustedPoint1 = PointF.Empty;
984             PointF adjustedPoint2 = PointF.Empty;
985 
986             // Check line angle. Intersection with vertical or horizontal lines will be done based on the results
987             bool topBottomLine = (Math.Abs(pt2.Y - pt1.Y) > Math.Abs(pt2.X - pt1.X));
988             RectangleF rect = new RectangleF(0, 0, graph.Common.ChartPicture.Width, graph.Common.ChartPicture.Height);
989             if (topBottomLine)
990             {
991                 // Find the intersection point between the original line and Y = 0 and Y = Height lines
992                 adjustedPoint1 = rect.Contains(pt1) ? pt1 : GetIntersectionY(pt1, pt2, 0);
993                 adjustedPoint2 = rect.Contains(pt2) ? pt2 : GetIntersectionY(pt1, pt2, graph.Common.ChartPicture.Height);
994             }
995             else
996             {
997                 // Find the intersection point between the original line and X = 0 and X = Width lines
998                 adjustedPoint1 = rect.Contains(pt1) ? pt1 : GetIntersectionX(pt1, pt2, 0);
999                 adjustedPoint2 = rect.Contains(pt2) ? pt2 : GetIntersectionX(pt1, pt2, graph.Common.ChartPicture.Width);
1000             }
1001 
1002             // Draw Line
1003             graph.DrawLine(pen, adjustedPoint1, adjustedPoint2);
1004         }
1005 
1006         /// <summary>
1007         /// Gets intersection point coordinates between point line and and horizontal
1008         /// line specified by Y coordinate.
1009         /// </summary>
1010         /// <param name="firstPoint">First data point.</param>
1011         /// <param name="secondPoint">Second data point.</param>
1012         /// <param name="pointY">Y coordinate.</param>
1013         /// <returns>Intersection point coordinates.</returns>
GetIntersectionY(PointF firstPoint, PointF secondPoint, float pointY)1014         internal static PointF GetIntersectionY(PointF firstPoint, PointF secondPoint, float pointY)
1015         {
1016             PointF intersectionPoint = new PointF();
1017             intersectionPoint.Y = pointY;
1018             intersectionPoint.X = (pointY - firstPoint.Y) *
1019                 (secondPoint.X - firstPoint.X) /
1020                 (secondPoint.Y - firstPoint.Y) +
1021                 firstPoint.X;
1022             return intersectionPoint;
1023         }
1024 
1025         /// <summary>
1026         /// Gets intersection point coordinates between point line and and vertical
1027         /// line specified by X coordinate.
1028         /// </summary>
1029         /// <param name="firstPoint">First data point.</param>
1030         /// <param name="secondPoint">Second data point.</param>
1031         /// <param name="pointX">X coordinate.</param>
1032         /// <returns>Intersection point coordinates.</returns>
GetIntersectionX(PointF firstPoint, PointF secondPoint, float pointX)1033         internal static PointF GetIntersectionX(PointF firstPoint, PointF secondPoint, float pointX)
1034         {
1035             PointF intersectionPoint = new PointF();
1036             intersectionPoint.X = pointX;
1037             intersectionPoint.Y = (pointX - firstPoint.X) *
1038                 (secondPoint.Y - firstPoint.Y) /
1039                 (secondPoint.X - firstPoint.X) +
1040                 firstPoint.Y;
1041             return intersectionPoint;
1042         }
1043 
1044 		/// <summary>
1045 		/// Draw chart line.
1046 		/// </summary>
1047 		/// <param name="graph">Graphics object.</param>
1048 		/// <param name="point">Point to draw the line for.</param>
1049 		/// <param name="series">Point series.</param>
1050 		/// <param name="firstPoint">First line point.</param>
1051 		/// <param name="secondPoint">Seconf line point.</param>
DrawLine( ChartGraphics graph, DataPoint point, Series series, PointF firstPoint, PointF secondPoint)1052 		protected void DrawLine(
1053 			ChartGraphics graph,
1054 			DataPoint point,
1055 			Series series,
1056 			PointF firstPoint,
1057 			PointF secondPoint)
1058 		{
1059 			graph.DrawLineRel( point.Color, point.BorderWidth, point.BorderDashStyle, firstPoint, secondPoint, series.ShadowColor, series.ShadowOffset );
1060 		}
1061 
1062 		/// <summary>
1063 		/// Checks if line tension is supported by the chart type.
1064 		/// </summary>
1065 		/// <returns>True if line tension is supported.</returns>
IsLineTensionSupported()1066 		protected virtual bool IsLineTensionSupported()
1067 		{
1068 			return false;
1069 		}
1070 
1071 		#endregion
1072 
1073 		#region Position helper methods
1074 
1075 		/// <summary>
1076 		/// Gets default line tension.
1077 		/// </summary>
1078 		/// <returns>Default line tension.</returns>
GetDefaultTension()1079 		virtual protected float GetDefaultTension()
1080 		{
1081 			return 0f;
1082 		}
1083 
1084 		/// <summary>
1085 		/// Gets label position depending on the prev/next point values.
1086 		/// This method will reduce label overlapping with the chart itself (line).
1087 		/// </summary>
1088 		/// <param name="series">Data series.</param>
1089 		/// <param name="pointIndex">Point index.</param>
1090 		/// <returns>Return automaticly detected label position.</returns>
GetAutoLabelPosition(Series series, int pointIndex)1091 		override protected LabelAlignmentStyles GetAutoLabelPosition(Series series, int pointIndex)
1092 		{
1093 			int pointsCount = series.Points.Count;	// Number of data points
1094 			double previous;						// Y Value from the previous data point
1095 			double next;							// Y Value from the next data point
1096 
1097 			// There is only one data point
1098 			if( pointsCount == 1 )
1099 			{
1100 				return LabelAlignmentStyles.Top;
1101 			}
1102 
1103 			// Y Value from the current data point
1104 			double current = GetYValue(Common, Area, series, series.Points[pointIndex], pointIndex, 0);
1105 
1106 			// The data point is between two data points
1107 			if( pointIndex < pointsCount - 1 && pointIndex > 0 )
1108 			{
1109 				// Y Value from the previous data point
1110 				previous = GetYValue(Common, Area, series, series.Points[pointIndex-1], pointIndex-1, 0);
1111 
1112 				// Y Value from the next data point
1113 				next = GetYValue(Common, Area, series, series.Points[pointIndex+1], pointIndex+1, 0);
1114 
1115 				// Put the label below lines
1116 				if( previous > current && next > current )
1117 				{
1118 					return LabelAlignmentStyles.Bottom;
1119 				}
1120 			}
1121 
1122 			// This is the last data point
1123 			if( pointIndex == pointsCount - 1 )
1124 			{
1125 				// Y Value from the previous data point
1126 				previous = GetYValue(Common, Area, series, series.Points[pointIndex-1], pointIndex-1, 0);
1127 
1128 				// Put the label below line
1129 				if( previous > current )
1130 				{
1131 					return LabelAlignmentStyles.Bottom;
1132 				}
1133 			}
1134 
1135 			// This is the first data point
1136 			if( pointIndex == 0 )
1137 			{
1138 				// Y Value from the next data point
1139 				next = GetYValue(Common, Area, series, series.Points[pointIndex + 1], pointIndex + 1, 0);
1140 
1141 				// Put the label below line
1142 				if( next > current )
1143 				{
1144 					return LabelAlignmentStyles.Bottom;
1145 				}
1146 			}
1147 
1148 			return LabelAlignmentStyles.Top;
1149 		}
1150 
1151 		/// <summary>
1152 		/// Fills a PointF array of data points absolute pixel positions.
1153 		/// </summary>
1154 		/// <param name="graph">Graphics object.</param>
1155 		/// <param name="series">Point series.</param>
1156 		/// <param name="indexedSeries">Indicate that point index should be used as X value.</param>
1157 		/// <returns>Array of data points position.</returns>
GetPointsPosition(ChartGraphics graph, Series series, bool indexedSeries)1158 		virtual protected PointF[] GetPointsPosition(ChartGraphics graph, Series series, bool indexedSeries)
1159 		{
1160 			PointF[]	pointPos = new PointF[series.Points.Count];
1161 			int index = 0;
1162 			foreach( DataPoint point in series.Points )
1163 			{
1164 				// Change Y value if line is out of plot area
1165 				double yValue = GetYValue(Common, Area, series, point, index, this.YValueIndex);
1166 
1167 				// Recalculates y position
1168 				double yPosition = VAxis.GetPosition( yValue );
1169 
1170 				// Recalculates x position
1171 				double xPosition = HAxis.GetPosition( point.XValue );
1172 				if( indexedSeries )
1173 				{
1174 					xPosition = HAxis.GetPosition( index + 1 );
1175 				}
1176 
1177 				// Add point position into array
1178 				// IMPORTANT: Rounding was removed from this part of code because of
1179 				// very bad drawing in Flash.
1180 				pointPos[index] = new PointF(
1181                     (float)xPosition * (graph.Common.ChartPicture.Width - 1) / 100F,
1182                     (float)yPosition * (graph.Common.ChartPicture.Height - 1) / 100F);
1183 
1184 				index++;
1185 			}
1186 
1187 			return pointPos;
1188 		}
1189 
1190 		#endregion
1191 
1192 		#region 3D Drawing and selection methods
1193 
1194 		/// <summary>
1195 		/// Draws or perform the hit test for the line chart in 3D.
1196 		/// </summary>
1197 		/// <param name="selection">If True selection mode is active, otherwise paint mode is active.</param>
1198 		/// <param name="graph">The Chart Graphics object.</param>
1199 		/// <param name="common">The Common elements object.</param>
1200 		/// <param name="area">Chart area for this chart.</param>
1201 		/// <param name="seriesToDraw">Chart series to draw.</param>
ProcessLineChartType3D( bool selection, ChartGraphics graph, CommonElements common, ChartArea area, Series seriesToDraw )1202 		protected void ProcessLineChartType3D(
1203 			bool selection,
1204 			ChartGraphics graph,
1205 			CommonElements common,
1206 			ChartArea area,
1207 			Series seriesToDraw )
1208 		{
1209 
1210 			// Reset graphics fields
1211 			graph.frontLinePen = null;
1212 			graph.frontLinePoint1 = PointF.Empty;
1213 			graph.frontLinePoint2 = PointF.Empty;
1214 
1215 			// Get list of series to draw
1216 			List<string> typeSeries = null;
1217 			if( (area.Area3DStyle.IsClustered && this.SideBySideSeries) ||
1218 				this.Stacked)
1219 			{
1220 				// Draw all series of the same chart type
1221 				typeSeries = area.GetSeriesFromChartType(Name);
1222 			}
1223 			else
1224 			{
1225 				// Draw just one chart series
1226 				typeSeries = new List<string>();
1227 				typeSeries.Add(seriesToDraw.Name);
1228 			}
1229 
1230 			//***************************************************************
1231 			//** Check that data points XValues. Must be sorted or set to 0.
1232 			//***************************************************************
1233 			foreach(string seriesName in typeSeries)
1234 			{
1235 				// Get series object
1236 				Series	currentSeries = common.DataManager.Series[seriesName];
1237 
1238 				// Do not check indexed series
1239 				if(currentSeries.IsXValueIndexed)
1240 				{
1241 					continue;
1242 				}
1243 
1244 				// Loop through all data points in the series
1245 				bool	allZeros = true;
1246 				int		order = int.MaxValue;	// 0 - Ascending; 1 - Descending;
1247 				double	prevValue = double.NaN;
1248 				foreach(DataPoint dp in currentSeries.Points)
1249 				{
1250 					// Check if X values were set (or all zeros)
1251 					if(allZeros && dp.XValue == 0.0)
1252 					{
1253 						continue;
1254 					}
1255 					allZeros = false;
1256 
1257 					// Check X values order
1258 					bool	validOrder = true;
1259 					if(!double.IsNaN(prevValue) && dp.XValue != prevValue)
1260 					{
1261 						// Determine sorting order
1262 						if(order == int.MaxValue)
1263 						{
1264 							order = (dp.XValue > prevValue) ? 0 : 1;	// 0 - Ascending; 1 - Descending;
1265 						}
1266 
1267 						// Compare current X value with previous
1268 						if(dp.XValue > prevValue && order == 1)
1269 						{
1270 							validOrder = false;
1271 						}
1272 						if(dp.XValue < prevValue && order == 0)
1273 						{
1274 							validOrder = false;
1275 						}
1276 					}
1277 
1278 					// Throw error exception
1279 					if(!validOrder)
1280 					{
1281                         throw (new InvalidOperationException(SR.Exception3DChartPointsXValuesUnsorted));
1282 					}
1283 
1284 					// Remember previous value
1285 					prevValue = dp.XValue;
1286 				}
1287 			}
1288 
1289 			//************************************************************
1290 			//** Get order of data points drawing
1291 			//************************************************************
1292 			ArrayList	dataPointDrawingOrder = area.GetDataPointDrawingOrder(
1293 				typeSeries,
1294 				this,
1295 				selection,
1296 				this.COPCoordinatesToCheck,
1297 				null,
1298 				0,
1299 				false);
1300 
1301 
1302 			//************************************************************
1303 			//** Get line tension attribute
1304 			//************************************************************
1305 			this.lineTension = GetDefaultTension();
1306 			if(dataPointDrawingOrder.Count > 0)
1307 			{
1308 				Series firstSeries = firstSeries = ((DataPoint3D)dataPointDrawingOrder[0]).dataPoint.series;
1309 				if(IsLineTensionSupported() && firstSeries.IsCustomPropertySet(CustomPropertyName.LineTension))
1310 				{
1311 					this.lineTension = CommonElements.ParseFloat(firstSeries[CustomPropertyName.LineTension]);
1312 				}
1313 			}
1314 
1315 			//************************************************************
1316 			//** Check if second ALL points loop is required
1317 			//************************************************************
1318 			allPointsLoopsNumber = GetPointLoopNumber(selection, dataPointDrawingOrder);
1319 
1320 			//************************************************************
1321 			//** Loop through all data poins (one or two times)
1322 			//************************************************************
1323 			for(int pointsLoop = 0; pointsLoop < allPointsLoopsNumber; pointsLoop++)
1324 			{
1325 				int		index = 0;
1326 				this.centerPointIndex = int.MaxValue;
1327 				foreach(object obj in dataPointDrawingOrder)
1328 				{
1329 					// Get point & series
1330 					DataPoint3D	pointEx = (DataPoint3D) obj;
1331 					DataPoint	point = pointEx.dataPoint;
1332 					Series		ser = point.series;
1333 
1334 					// Set active horizontal/vertical axis
1335 					HAxis = area.GetAxis(AxisName.X, ser.XAxisType, ser.XSubAxisName);
1336 					VAxis = area.GetAxis(AxisName.Y, ser.YAxisType, ser.YSubAxisName);
1337 					hAxisMin = HAxis.ViewMinimum;
1338 					hAxisMax = HAxis.ViewMaximum;
1339 					vAxisMin = VAxis.ViewMinimum;
1340 					vAxisMax = VAxis.ViewMaximum;
1341 
1342 					// First point is not drawn as a 3D line
1343 					if(pointEx.index > 1)
1344 					{
1345 						//************************************************************
1346 						//** Get previous data point using the point index in the series
1347 						//************************************************************
1348 						int	pointArrayIndex = index;
1349 						DataPoint3D	prevDataPointEx = ChartGraphics.FindPointByIndex(
1350 							dataPointDrawingOrder,
1351 							pointEx.index - 1,
1352 							(this.multiSeries) ? pointEx : null,
1353 							ref pointArrayIndex);
1354 
1355 						//************************************************************
1356 						//** Painting mode
1357 						//************************************************************
1358 						GraphicsPath	rectPath = null;
1359 
1360 						// Get Y values of the current and previous data points
1361 						double	yValue = GetYValue(common, area, ser, pointEx.dataPoint, pointEx.index - 1, 0);
1362 						double	yValuePrev = GetYValue(common, area, ser, prevDataPointEx.dataPoint, prevDataPointEx.index - 1, 0);
1363 						double	xValue = (pointEx.indexedSeries) ? pointEx.index : pointEx.dataPoint.XValue;
1364 						double	xValuePrev = (prevDataPointEx.indexedSeries) ? prevDataPointEx.index : prevDataPointEx.dataPoint.XValue;
1365 
1366 						// Axes are logarithmic
1367 						yValue = VAxis.GetLogValue( yValue );
1368 						yValuePrev = VAxis.GetLogValue( yValuePrev );
1369 
1370 						xValue = HAxis.GetLogValue( xValue );
1371 						xValuePrev = HAxis.GetLogValue( xValuePrev );
1372 
1373 						//************************************************************
1374 						//** Draw line
1375 						//************************************************************
1376 						DataPoint3D		pointAttr = (prevDataPointEx.dataPoint.IsEmpty) ? prevDataPointEx : pointEx;
1377 						if(pointAttr.dataPoint.Color != Color.Empty)
1378 						{
1379 							// Detect if we need to get graphical path of drawn object
1380 							DrawingOperationTypes	drawingOperationType = DrawingOperationTypes.DrawElement;
1381 
1382 							if( common.ProcessModeRegions )
1383 							{
1384 								drawingOperationType |= DrawingOperationTypes.CalcElementPath;
1385 							}
1386 
1387 							// Check if point markers lines should be drawn
1388 							this.showPointLines = false;
1389 							if(pointAttr.dataPoint.IsCustomPropertySet(CustomPropertyName.ShowMarkerLines))
1390 							{
1391 								if(String.Compare(pointAttr.dataPoint[CustomPropertyName.ShowMarkerLines], "TRUE", StringComparison.OrdinalIgnoreCase) == 0)
1392 								{
1393 									this.showPointLines = true;
1394 								}
1395 							}
1396 							else
1397 							{
1398 								if(pointAttr.dataPoint.series.IsCustomPropertySet(CustomPropertyName.ShowMarkerLines))
1399 								{
1400                                     if (String.Compare(pointAttr.dataPoint.series[CustomPropertyName.ShowMarkerLines], "TRUE", StringComparison.OrdinalIgnoreCase) == 0)
1401 									{
1402 										this.showPointLines = true;
1403 									}
1404 								}
1405 							}
1406 
1407 							// Start Svg Selection mode
1408 							graph.StartHotRegion( point );
1409 
1410 							// Draw line surface
1411 							area.IterationCounter = 0;
1412 							rectPath = Draw3DSurface(
1413 								area,
1414 								graph,
1415 								area.matrix3D,
1416 								area.Area3DStyle.LightStyle,
1417 								prevDataPointEx,
1418 								pointAttr.zPosition,
1419 								pointAttr.depth,
1420 								dataPointDrawingOrder,
1421 								index,
1422 								pointsLoop,
1423 								lineTension,
1424 								drawingOperationType,
1425 								0f, 0f,
1426 								new PointF(float.NaN, float.NaN),
1427 								new PointF(float.NaN, float.NaN),
1428 								false);
1429 
1430 							// End Svg Selection mode
1431 							graph.EndHotRegion( );
1432 						}
1433 
1434 						//************************************************************
1435 						// Hot Regions mode used for image maps, tool tips and
1436 						// hit test function
1437 						//************************************************************
1438 						if( common.ProcessModeRegions && rectPath != null)
1439 						{
1440 							common.HotRegionsList.AddHotRegion(
1441 								rectPath,
1442 								false,
1443 								graph,
1444 								point,
1445 								ser.Name,
1446 								pointEx.index - 1 );
1447 						}
1448                         if (rectPath != null)
1449                         {
1450                             rectPath.Dispose();
1451                         }
1452 					}
1453 
1454 					// Increase point index
1455 					++index;
1456 				}
1457 			}
1458 		}
1459 
1460 		/// <summary>
1461 		/// Draws a 3D surface connecting the two specified points in 2D space.
1462 		/// Used to draw Line based charts.
1463 		/// </summary>
1464 		/// <param name="area">Chart area reference.</param>
1465 		/// <param name="graph">Chart graphics.</param>
1466 		/// <param name="matrix">Coordinates transformation matrix.</param>
1467 		/// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
1468 		/// <param name="prevDataPointEx">Previous data point object.</param>
1469 		/// <param name="positionZ">Z position of the back side of the 3D surface.</param>
1470 		/// <param name="depth">Depth of the 3D surface.</param>
1471 		/// <param name="points">Array of points.</param>
1472 		/// <param name="pointIndex">Index of point to draw.</param>
1473 		/// <param name="pointLoopIndex">Index of points loop.</param>
1474 		/// <param name="tension">Line tension.</param>
1475 		/// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
1476 		/// <param name="topDarkening">Darkenning scale for top surface. 0 - None.</param>
1477 		/// <param name="bottomDarkening">Darkenning scale for bottom surface. 0 - None.</param>
1478 		/// <param name="thirdPointPosition">Position where the third point is actually located or float.NaN if same as in "firstPoint".</param>
1479 		/// <param name="fourthPointPosition">Position where the fourth point is actually located or float.NaN if same as in "secondPoint".</param>
1480 		/// <param name="clippedSegment">Indicates that drawn segment is 3D clipped. Only top/bottom should be drawn.</param>
1481 		/// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
Draw3DSurface( ChartArea area, ChartGraphics graph, Matrix3D matrix, LightStyle lightStyle, DataPoint3D prevDataPointEx, float positionZ, float depth, ArrayList points, int pointIndex, int pointLoopIndex, float tension, DrawingOperationTypes operationType, float topDarkening, float bottomDarkening, PointF thirdPointPosition, PointF fourthPointPosition, bool clippedSegment)1482 		protected virtual GraphicsPath Draw3DSurface(
1483 			ChartArea area,
1484 			ChartGraphics graph,
1485 			Matrix3D matrix,
1486 			LightStyle lightStyle,
1487 			DataPoint3D prevDataPointEx,
1488 			float positionZ,
1489 			float depth,
1490 			ArrayList points,
1491 			int pointIndex,
1492 			int pointLoopIndex,
1493 			float tension,
1494 			DrawingOperationTypes operationType,
1495 			float topDarkening,
1496 			float bottomDarkening,
1497 			PointF thirdPointPosition,
1498 			PointF fourthPointPosition,
1499 			bool clippedSegment)
1500 		{
1501 			// Check if points are drawn from sides to center (do only once)
1502 			if(centerPointIndex == int.MaxValue)
1503 			{
1504 				centerPointIndex = GetCenterPointIndex(points);
1505 			}
1506 
1507 			//************************************************************
1508 			//** Find line first & second points
1509 			//************************************************************
1510 			DataPoint3D	secondPoint = (DataPoint3D)points[pointIndex];
1511 			int pointArrayIndex = pointIndex;
1512 			DataPoint3D firstPoint = ChartGraphics.FindPointByIndex(
1513 				points,
1514 				secondPoint.index - 1,
1515 				(this.multiSeries) ? secondPoint : null,
1516 				ref pointArrayIndex);
1517 
1518 
1519 			// Fint point with line properties
1520 			DataPoint3D		pointAttr = secondPoint;
1521 			if(prevDataPointEx.dataPoint.IsEmpty)
1522 			{
1523 				pointAttr = prevDataPointEx;
1524 			}
1525 			else if(firstPoint.index > secondPoint.index)
1526 			{
1527 				pointAttr = firstPoint;
1528 			}
1529 
1530 			// Adjust point visual properties
1531 			Color			color = (useBorderColor) ? pointAttr.dataPoint.BorderColor : pointAttr.dataPoint.Color;
1532 			ChartDashStyle	dashStyle = pointAttr.dataPoint.BorderDashStyle;
1533 			if( pointAttr.dataPoint.IsEmpty && pointAttr.dataPoint.Color == Color.Empty)
1534 			{
1535 				color = Color.Gray;
1536 			}
1537 			if( pointAttr.dataPoint.IsEmpty && pointAttr.dataPoint.BorderDashStyle == ChartDashStyle.NotSet )
1538 			{
1539 				dashStyle = ChartDashStyle.Solid;
1540 			}
1541 
1542 			// Draw point using 2 points
1543 			return graph.Draw3DSurface(
1544 				area,
1545 				matrix,
1546 				lightStyle,
1547 				SurfaceNames.Top,
1548 				positionZ,
1549 				depth,
1550 				color,
1551 				pointAttr.dataPoint.BorderColor,
1552 				pointAttr.dataPoint.BorderWidth,
1553 				dashStyle,
1554 				firstPoint,
1555 				secondPoint,
1556 				points,
1557 				pointIndex,
1558 				tension,
1559 				operationType,
1560 				LineSegmentType.Single,
1561 				(this.showPointLines) ? true : false,
1562 				false,
1563                 area.ReverseSeriesOrder,
1564 				this.multiSeries,
1565 				0,
1566 				true);
1567 		}
1568 
1569 		/// <summary>
1570 		/// Gets index of center point.
1571 		/// </summary>
1572 		/// <param name="points">Points list.</param>
1573 		/// <returns>Index of center point or int.MaxValue.</returns>
GetCenterPointIndex(ArrayList points)1574 		protected int GetCenterPointIndex(ArrayList points)
1575 		{
1576 			for(int pointIndex = 1; pointIndex < points.Count; pointIndex++)
1577 			{
1578 				DataPoint3D	firstPoint = (DataPoint3D)points[pointIndex - 1];
1579 				DataPoint3D	secondPoint = (DataPoint3D)points[pointIndex];
1580 				if(Math.Abs(secondPoint.index - firstPoint.index) != 1)
1581 				{
1582 					return pointIndex - 1;
1583 				}
1584 			}
1585 			return int.MaxValue;
1586 		}
1587 
1588 		/// <summary>
1589 		/// Returns how many loops through all data points is required (1 or 2)
1590 		/// </summary>
1591 		/// <param name="selection">Selection indicator.</param>
1592 		/// <param name="pointsArray">Points array list.</param>
1593 		/// <returns>Number of loops (1 or 2).</returns>
GetPointLoopNumber(bool selection, ArrayList pointsArray)1594 		virtual protected int GetPointLoopNumber(bool selection, ArrayList pointsArray)
1595 		{
1596 			return 1;
1597 		}
1598 
1599 		/// <summary>
1600 		/// Clips the top (left and right) points of the segment to plotting area.
1601 		/// Used in area and range charts.
1602 		/// </summary>
1603 		/// <param name="resultPath">Segment area path.</param>
1604 		/// <param name="firstPoint">First data point.</param>
1605 		/// <param name="secondPoint">Second data point.</param>
1606 		/// <param name="reversed">Points are in reversed order.</param>
1607 		/// <param name="area">Chart area reference.</param>
1608 		/// <param name="graph">Chart graphics.</param>
1609 		/// <param name="matrix">Coordinates transformation matrix.</param>
1610 		/// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
1611 		/// <param name="prevDataPointEx">Previous data point object.</param>
1612 		/// <param name="positionZ">Z position of the back side of the 3D surface.</param>
1613 		/// <param name="depth">Depth of the 3D surface.</param>
1614 		/// <param name="points">Array of points.</param>
1615 		/// <param name="pointIndex">Index of point to draw.</param>
1616 		/// <param name="pointLoopIndex">Index of points loop.</param>
1617 		/// <param name="tension">Line tension.</param>
1618 		/// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
1619 		/// <param name="surfaceSegmentType">Define surface segment type if it consists of several segments.</param>
1620 		/// <param name="topDarkening">Darkenning scale for top surface. 0 - None.</param>
1621 		/// <param name="bottomDarkening">Darkenning scale for bottom surface. 0 - None.</param>
1622 		/// <returns>Returns element shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
ClipTopPoints( GraphicsPath resultPath, ref DataPoint3D firstPoint, ref DataPoint3D secondPoint, bool reversed, ChartArea area, ChartGraphics graph, Matrix3D matrix, LightStyle lightStyle, DataPoint3D prevDataPointEx, float positionZ, float depth, ArrayList points, int pointIndex, int pointLoopIndex, float tension, DrawingOperationTypes operationType, LineSegmentType surfaceSegmentType, float topDarkening, float bottomDarkening)1623 		protected bool ClipTopPoints(
1624 			GraphicsPath resultPath,
1625 			ref DataPoint3D firstPoint,
1626 			ref DataPoint3D secondPoint,
1627 			bool reversed,
1628 			ChartArea area,
1629 			ChartGraphics graph,
1630 			Matrix3D matrix,
1631 			LightStyle lightStyle,
1632 			DataPoint3D prevDataPointEx,
1633 			float positionZ,
1634 			float depth,
1635 			ArrayList points,
1636 			int pointIndex,
1637 			int pointLoopIndex,
1638 			float tension,
1639 			DrawingOperationTypes operationType,
1640 			LineSegmentType surfaceSegmentType,
1641 			float topDarkening,
1642 			float bottomDarkening)
1643 		{
1644 			// Do not allow recursion to go too deep
1645 			++area.IterationCounter;
1646 			if(area.IterationCounter > 20)
1647 			{
1648 				area.IterationCounter = 0;
1649 				return true;
1650 			}
1651 
1652 			//****************************************************************
1653 			//** Check point values
1654 			//****************************************************************
1655 			if( double.IsNaN(firstPoint.xPosition) ||
1656 				double.IsNaN(firstPoint.yPosition) ||
1657 				double.IsNaN(secondPoint.xPosition) ||
1658 				double.IsNaN(secondPoint.yPosition) )
1659 			{
1660 				return true;
1661 			}
1662 
1663 			//****************************************************************
1664 			//** Round plot are position and point coordinates
1665 			//****************************************************************
1666 			int decimals = 3;
1667 			decimal plotAreaPositionX = Math.Round((decimal)area.PlotAreaPosition.X, decimals);
1668 			decimal plotAreaPositionY = Math.Round((decimal)area.PlotAreaPosition.Y, decimals);
1669 			decimal plotAreaPositionRight = Math.Round((decimal)area.PlotAreaPosition.Right, decimals);
1670 			decimal plotAreaPositionBottom = Math.Round((decimal)area.PlotAreaPosition.Bottom, decimals);
1671 
1672 			// Make area a little bit bigger
1673 			plotAreaPositionX -= 0.001M;
1674 			plotAreaPositionY -= 0.001M;
1675 			plotAreaPositionRight += 0.001M;
1676 			plotAreaPositionBottom += 0.001M;
1677 
1678 			// Round top points coordinates
1679 			firstPoint.xPosition = Math.Round(firstPoint.xPosition, decimals);
1680 			firstPoint.yPosition = Math.Round(firstPoint.yPosition, decimals);
1681 			secondPoint.xPosition = Math.Round(secondPoint.xPosition, decimals);
1682 			secondPoint.yPosition = Math.Round(secondPoint.yPosition, decimals);
1683 
1684 
1685 			//****************************************************************
1686 			//** Clip area data points inside the plotting area
1687 			//****************************************************************
1688 
1689 			// Chech data points X values
1690 			if((decimal)firstPoint.xPosition < plotAreaPositionX ||
1691 				(decimal)firstPoint.xPosition > plotAreaPositionRight ||
1692 				(decimal)secondPoint.xPosition < plotAreaPositionX ||
1693 				(decimal)secondPoint.xPosition > plotAreaPositionRight )
1694 			{
1695 				// Check if surface completly out of the plot area
1696 				if((decimal)firstPoint.xPosition < plotAreaPositionX &&
1697 					(decimal)secondPoint.xPosition < plotAreaPositionX)
1698 				{
1699 					return true;
1700 				}
1701 				// Check if surface completly out of the plot area
1702 				if((decimal)firstPoint.xPosition > plotAreaPositionRight &&
1703 					(decimal)secondPoint.xPosition > plotAreaPositionRight)
1704 				{
1705 					return true;
1706 				}
1707 
1708 				// Only part of the surface is outside - fix X value and adjust Y value
1709 				if((decimal)firstPoint.xPosition < plotAreaPositionX)
1710 				{
1711 					firstPoint.yPosition = ((double)plotAreaPositionX - secondPoint.xPosition) /
1712 						(firstPoint.xPosition - secondPoint.xPosition) *
1713 						(firstPoint.yPosition - secondPoint.yPosition) +
1714 						secondPoint.yPosition;
1715 					firstPoint.xPosition = (double)plotAreaPositionX;
1716 				}
1717 				else if((decimal)firstPoint.xPosition > plotAreaPositionRight)
1718 				{
1719 					firstPoint.yPosition = ((double)plotAreaPositionRight - secondPoint.xPosition) /
1720 						(firstPoint.xPosition - secondPoint.xPosition) *
1721 						(firstPoint.yPosition - secondPoint.yPosition) +
1722 						secondPoint.yPosition;
1723 					firstPoint.xPosition = (double)plotAreaPositionRight;
1724 				}
1725 				if((decimal)secondPoint.xPosition < plotAreaPositionX)
1726 				{
1727 					secondPoint.yPosition = ((double)plotAreaPositionX - secondPoint.xPosition) /
1728 						(firstPoint.xPosition - secondPoint.xPosition) *
1729 						(firstPoint.yPosition - secondPoint.yPosition) +
1730 						secondPoint.yPosition;
1731 					secondPoint.xPosition = (double)plotAreaPositionX;
1732 				}
1733 				else if((decimal)secondPoint.xPosition > plotAreaPositionRight)
1734 				{
1735 					secondPoint.yPosition = ((double)plotAreaPositionRight - secondPoint.xPosition) /
1736 						(firstPoint.xPosition - secondPoint.xPosition) *
1737 						(firstPoint.yPosition - secondPoint.yPosition) +
1738 						secondPoint.yPosition;
1739 					secondPoint.xPosition = (double)plotAreaPositionRight;
1740 				}
1741 			}
1742 
1743 			// Chech data points Y values
1744 			if((decimal)firstPoint.yPosition < plotAreaPositionY ||
1745 				(decimal)firstPoint.yPosition > plotAreaPositionBottom ||
1746 				(decimal)secondPoint.yPosition < plotAreaPositionY ||
1747 				(decimal)secondPoint.yPosition > plotAreaPositionBottom )
1748 			{
1749 				// Remember previous y positions
1750 				double prevFirstPointY = firstPoint.yPosition;
1751 				double prevSecondPointY = secondPoint.yPosition;
1752 
1753 				// Check if whole line is outside plotting region
1754 				bool	surfaceCompletlyOutside = false;
1755 				bool	outsideBottom = false;
1756 				if((decimal)firstPoint.yPosition < plotAreaPositionY &&
1757 					(decimal)secondPoint.yPosition < plotAreaPositionY)
1758 				{
1759 					surfaceCompletlyOutside = true;
1760 					firstPoint.yPosition = (double)plotAreaPositionY;
1761 					secondPoint.yPosition = (double)plotAreaPositionY;
1762 				}
1763 				if((decimal)firstPoint.yPosition > plotAreaPositionBottom &&
1764 					(decimal)secondPoint.yPosition > plotAreaPositionBottom)
1765 				{
1766 					surfaceCompletlyOutside = true;
1767 					outsideBottom = true;
1768 					firstPoint.yPosition = (double)plotAreaPositionBottom;
1769 					secondPoint.yPosition = (double)plotAreaPositionBottom;
1770 				}
1771 
1772 				// Draw just one surface
1773 				if(surfaceCompletlyOutside)
1774 				{
1775 					resultPath =  Draw3DSurface( firstPoint, secondPoint, reversed,
1776 						area, graph, matrix, lightStyle, prevDataPointEx,
1777 						positionZ, depth, points, pointIndex, pointLoopIndex,
1778 						tension, operationType, surfaceSegmentType,
1779 						0.5f, 0f,
1780 						new PointF(float.NaN, float.NaN),
1781 						new PointF(float.NaN, float.NaN),
1782 						outsideBottom,
1783 						false, true);
1784 
1785 					// Restore previous y positions
1786 					firstPoint.yPosition = prevFirstPointY;
1787 					secondPoint.yPosition = prevSecondPointY;
1788 
1789 					return true;
1790 				}
1791 
1792 				// Get intersection point
1793 				DataPoint3D	intersectionPoint = new DataPoint3D();
1794 				intersectionPoint.yPosition = (double)plotAreaPositionY;
1795 				if((decimal)firstPoint.yPosition > plotAreaPositionBottom ||
1796 					(decimal)secondPoint.yPosition > plotAreaPositionBottom )
1797 				{
1798 					intersectionPoint.yPosition = (double)plotAreaPositionBottom;
1799 				}
1800 				intersectionPoint.xPosition = (intersectionPoint.yPosition - secondPoint.yPosition) *
1801 					(firstPoint.xPosition - secondPoint.xPosition) /
1802 					(firstPoint.yPosition - secondPoint.yPosition) +
1803 					secondPoint.xPosition;
1804 
1805 				if(double.IsNaN(intersectionPoint.xPosition) ||
1806 					double.IsInfinity(intersectionPoint.xPosition) ||
1807 					double.IsNaN(intersectionPoint.yPosition) ||
1808 					double.IsInfinity(intersectionPoint.yPosition) )
1809 				{
1810 					return true;
1811 				}
1812 
1813 				// Check if there are 2 intersection points (3 segments)
1814 				int		segmentNumber = 2;
1815 				DataPoint3D	intersectionPoint2 = null;
1816 				if( ((decimal)firstPoint.yPosition < plotAreaPositionY &&
1817 					(decimal)secondPoint.yPosition > plotAreaPositionBottom) ||
1818 					((decimal)firstPoint.yPosition > plotAreaPositionBottom &&
1819 					(decimal)secondPoint.yPosition < plotAreaPositionY))
1820 				{
1821 					segmentNumber = 3;
1822 					intersectionPoint2 = new DataPoint3D();
1823 					if((decimal)intersectionPoint.yPosition == plotAreaPositionY)
1824 					{
1825 						intersectionPoint2.yPosition = (double)plotAreaPositionBottom;
1826 					}
1827 					else
1828 					{
1829 						intersectionPoint2.yPosition = (double)plotAreaPositionY;
1830 					}
1831 					intersectionPoint2.xPosition = (intersectionPoint2.yPosition - secondPoint.yPosition) *
1832 						(firstPoint.xPosition - secondPoint.xPosition) /
1833 						(firstPoint.yPosition - secondPoint.yPosition) +
1834 						secondPoint.xPosition;
1835 
1836 					if(double.IsNaN(intersectionPoint2.xPosition) ||
1837 						double.IsInfinity(intersectionPoint2.xPosition) ||
1838 						double.IsNaN(intersectionPoint2.yPosition) ||
1839 						double.IsInfinity(intersectionPoint2.yPosition) )
1840 					{
1841 						return true;
1842 					}
1843 
1844 					// Switch intersection points
1845 					if((decimal)firstPoint.yPosition > plotAreaPositionBottom)
1846 					{
1847 						DataPoint3D tempPoint = new DataPoint3D();
1848 						tempPoint.xPosition = intersectionPoint.xPosition;
1849 						tempPoint.yPosition = intersectionPoint.yPosition;
1850 						intersectionPoint.xPosition = intersectionPoint2.xPosition;
1851 						intersectionPoint.yPosition = intersectionPoint2.yPosition;
1852 						intersectionPoint2.xPosition = tempPoint.xPosition;
1853 						intersectionPoint2.yPosition = tempPoint.yPosition;
1854 					}
1855 				}
1856 
1857 
1858 				// Adjust points Y values
1859 				bool	firstSegmentVisible = true;
1860 				bool	firstSegmentOutsideBottom = false;
1861 				bool	secondSegmentOutsideBottom = false;
1862 				if((decimal)firstPoint.yPosition < plotAreaPositionY)
1863 				{
1864 					firstSegmentVisible = false;
1865 					firstPoint.yPosition = (double)plotAreaPositionY;
1866 				}
1867 				else if((decimal)firstPoint.yPosition > plotAreaPositionBottom)
1868 				{
1869 					firstSegmentOutsideBottom = true;
1870 					firstSegmentVisible = false;
1871 					firstPoint.yPosition = (double)plotAreaPositionBottom;
1872 				}
1873 				if((decimal)secondPoint.yPosition < plotAreaPositionY)
1874 				{
1875 					secondPoint.yPosition = (double)plotAreaPositionY;
1876 				}
1877 				else if((decimal)secondPoint.yPosition > plotAreaPositionBottom)
1878 				{
1879 					secondSegmentOutsideBottom = true;
1880 					secondPoint.yPosition = (double)plotAreaPositionBottom;
1881 				}
1882 
1883 				// Draw surfaces in 2 or 3 segments
1884 				for(int segmentIndex = 0; segmentIndex < 3; segmentIndex++)
1885 				{
1886 					GraphicsPath segmentPath = null;
1887 					if(segmentIndex == 0 && !reversed ||
1888 						segmentIndex == 2 && reversed)
1889 					{
1890 						// Draw first segment
1891 						if(intersectionPoint2 == null)
1892 						{
1893 							intersectionPoint2 = intersectionPoint;
1894 						}
1895 						intersectionPoint2.dataPoint = secondPoint.dataPoint;
1896 						intersectionPoint2.index = secondPoint.index;
1897 						intersectionPoint2.xCenterVal = secondPoint.xCenterVal;
1898 
1899 						segmentPath =  Draw3DSurface( firstPoint, intersectionPoint2, reversed,
1900 							area, graph, matrix, lightStyle, prevDataPointEx,
1901 							positionZ, depth, points, pointIndex, pointLoopIndex,
1902 							tension, operationType,
1903 							(surfaceSegmentType == LineSegmentType.Middle) ? LineSegmentType.Middle : LineSegmentType.First,
1904 							(firstSegmentVisible && segmentNumber != 3) ? 0f : 0.5f, 0f,
1905 							new PointF(float.NaN, float.NaN),
1906 							new PointF((float)intersectionPoint2.xPosition, float.NaN),
1907 							firstSegmentOutsideBottom,
1908 							false, true);
1909 
1910 					}
1911 
1912 					if(segmentIndex == 1 && intersectionPoint2 != null && segmentNumber == 3)
1913 					{
1914 						// Draw middle segment
1915 						intersectionPoint2.dataPoint = secondPoint.dataPoint;
1916 						intersectionPoint2.index = secondPoint.index;
1917 						intersectionPoint2.xCenterVal = secondPoint.xCenterVal;
1918 
1919 						intersectionPoint.xCenterVal = firstPoint.xCenterVal;
1920 						intersectionPoint.index = firstPoint.index;
1921 						intersectionPoint.dataPoint = firstPoint.dataPoint;
1922 
1923 						segmentPath =  Draw3DSurface( intersectionPoint, intersectionPoint2, reversed,
1924 							area, graph, matrix, lightStyle, prevDataPointEx,
1925 							positionZ, depth, points, pointIndex, pointLoopIndex,
1926 							tension, operationType, LineSegmentType.Middle,
1927 							topDarkening, bottomDarkening,
1928 							new PointF((float)intersectionPoint.xPosition, float.NaN),
1929 							new PointF((float)intersectionPoint2.xPosition, float.NaN),
1930 							false,
1931 							false, true);
1932 
1933 					}
1934 
1935 					if(segmentIndex == 2 && !reversed ||
1936 						segmentIndex == 0 && reversed)
1937 					{
1938 						// Draw second segment
1939 						intersectionPoint.dataPoint = firstPoint.dataPoint;
1940 						intersectionPoint.index = firstPoint.index;
1941 						intersectionPoint.xCenterVal = firstPoint.xCenterVal;
1942 
1943 						segmentPath =  Draw3DSurface( intersectionPoint, secondPoint, reversed,
1944 							area, graph, matrix, lightStyle, prevDataPointEx,
1945 							positionZ, depth, points, pointIndex, pointLoopIndex,
1946 							tension, operationType,
1947 							(surfaceSegmentType == LineSegmentType.Middle) ? LineSegmentType.Middle : LineSegmentType.Last,
1948 							(!firstSegmentVisible && segmentNumber != 3) ? 0f : 0.5f, 0f,
1949 							new PointF((float)intersectionPoint.xPosition, float.NaN),
1950 							new PointF(float.NaN, float.NaN),
1951 							secondSegmentOutsideBottom,
1952 							false, true);
1953 
1954 					}
1955 
1956 					// Add segment path
1957 					if(resultPath != null && segmentPath != null && segmentPath.PointCount > 0)
1958 					{
1959 						resultPath.AddPath(segmentPath, true);
1960 					}
1961 				}
1962 
1963 				// Restore previous y positions
1964 				firstPoint.yPosition = prevFirstPointY;
1965 				secondPoint.yPosition = prevSecondPointY;
1966 
1967 				return true;
1968 			}
1969 			return false;
1970 		}
1971 
1972 		/// <summary>
1973 		/// Clips the bottom (left and right) points of the segment to plotting area.
1974 		/// Used in area and range charts.
1975 		/// </summary>
1976 		/// <param name="resultPath"></param>
1977 		/// <param name="firstPoint">First data point.</param>
1978 		/// <param name="secondPoint">Second data point.</param>
1979 		/// <param name="thirdPoint">Coordinates of the bottom left point.</param>
1980 		/// <param name="fourthPoint">Coordinates of the bottom right point.</param>
1981 		/// <param name="reversed">Points are in reversed order.</param>
1982 		/// <param name="area">Chart area reference.</param>
1983 		/// <param name="graph">Chart graphics.</param>
1984 		/// <param name="matrix">Coordinates transformation matrix.</param>
1985 		/// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
1986 		/// <param name="prevDataPointEx">Previous data point object.</param>
1987 		/// <param name="positionZ">Z position of the back side of the 3D surface.</param>
1988 		/// <param name="depth">Depth of the 3D surface.</param>
1989 		/// <param name="points">Array of points.</param>
1990 		/// <param name="pointIndex">Index of point to draw.</param>
1991 		/// <param name="pointLoopIndex">Index of points loop.</param>
1992 		/// <param name="tension">Line tension.</param>
1993 		/// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
1994 		/// <param name="surfaceSegmentType">Define surface segment type if it consists of several segments.</param>
1995 		/// <param name="topDarkening">Darkenning scale for top surface. 0 - None.</param>
1996 		/// <param name="bottomDarkening">Darkenning scale for bottom surface. 0 - None.</param>
1997 		/// <returns>Returns element shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
ClipBottomPoints( GraphicsPath resultPath, ref DataPoint3D firstPoint, ref DataPoint3D secondPoint, ref PointF thirdPoint, ref PointF fourthPoint, bool reversed, ChartArea area, ChartGraphics graph, Matrix3D matrix, LightStyle lightStyle, DataPoint3D prevDataPointEx, float positionZ, float depth, ArrayList points, int pointIndex, int pointLoopIndex, float tension, DrawingOperationTypes operationType, LineSegmentType surfaceSegmentType, float topDarkening, float bottomDarkening)1998 		protected bool ClipBottomPoints(
1999 			GraphicsPath resultPath,
2000 			ref DataPoint3D firstPoint,
2001 			ref DataPoint3D secondPoint,
2002 			ref PointF thirdPoint,
2003 			ref PointF fourthPoint,
2004 			bool reversed,
2005 			ChartArea area,
2006 			ChartGraphics graph,
2007 			Matrix3D matrix,
2008 			LightStyle lightStyle,
2009 			DataPoint3D prevDataPointEx,
2010 			float positionZ,
2011 			float depth,
2012 			ArrayList points,
2013 			int pointIndex,
2014 			int pointLoopIndex,
2015 			float tension,
2016 			DrawingOperationTypes operationType,
2017 			LineSegmentType surfaceSegmentType,
2018 			float topDarkening,
2019 			float bottomDarkening)
2020 		{
2021 			// Do not allow recursion to go too deep
2022 			++area.IterationCounter;
2023 			if(area.IterationCounter > 20)
2024 			{
2025 				area.IterationCounter = 0;
2026 				return true;
2027 			}
2028 
2029 			//****************************************************************
2030 			//** Round plot are position and point coordinates
2031 			//****************************************************************
2032 			int decimals = 3;
2033 			decimal plotAreaPositionX = Math.Round((decimal)area.PlotAreaPosition.X, decimals);
2034 			decimal plotAreaPositionY = Math.Round((decimal)area.PlotAreaPosition.Y, decimals);
2035 			decimal plotAreaPositionRight = Math.Round((decimal)area.PlotAreaPosition.Right, decimals);
2036 			decimal plotAreaPositionBottom = Math.Round((decimal)area.PlotAreaPosition.Bottom, decimals);
2037 
2038 			// Make area a little bit bigger
2039 			plotAreaPositionX -= 0.001M;
2040 			plotAreaPositionY -= 0.001M;
2041 			plotAreaPositionRight += 0.001M;
2042 			plotAreaPositionBottom += 0.001M;
2043 
2044 
2045 
2046 			// Round top points coordinates
2047 			firstPoint.xPosition = Math.Round(firstPoint.xPosition, decimals);
2048 			firstPoint.yPosition = Math.Round(firstPoint.yPosition, decimals);
2049 			secondPoint.xPosition = Math.Round(secondPoint.xPosition, decimals);
2050 			secondPoint.yPosition = Math.Round(secondPoint.yPosition, decimals);
2051 
2052 			thirdPoint.X = (float)Math.Round(thirdPoint.X, decimals);
2053 			thirdPoint.Y = (float)Math.Round(thirdPoint.Y, decimals);
2054 
2055 			fourthPoint.X = (float)Math.Round(fourthPoint.X, decimals);
2056 			fourthPoint.Y = (float)Math.Round(fourthPoint.Y, decimals);
2057 
2058 			//****************************************************************
2059 			//** Clip area data points inside the plotting area
2060 			//****************************************************************
2061 
2062 			// Chech data points Y values
2063 			if((decimal)thirdPoint.Y < plotAreaPositionY ||
2064 				(decimal)thirdPoint.Y > plotAreaPositionBottom ||
2065 				(decimal)fourthPoint.Y < plotAreaPositionY ||
2066 				(decimal)fourthPoint.Y > plotAreaPositionBottom )
2067 			{
2068 				// Remember previous y positions
2069 				PointF prevThirdPoint = new PointF(thirdPoint.X, thirdPoint.Y);
2070 				PointF prevFourthPoint = new PointF(fourthPoint.X, fourthPoint.Y);
2071 
2072 				// Check if whole line is outside plotting region
2073 				bool	surfaceCompletlyOutside = false;
2074 				bool	outsideTop = false;
2075 				if((decimal)thirdPoint.Y < plotAreaPositionY &&
2076 					(decimal)fourthPoint.Y < plotAreaPositionY)
2077 				{
2078 					outsideTop = true;
2079 					surfaceCompletlyOutside = true;
2080 					thirdPoint.Y = area.PlotAreaPosition.Y;
2081 					fourthPoint.Y = area.PlotAreaPosition.Y;
2082 				}
2083 				if((decimal)thirdPoint.Y > plotAreaPositionBottom &&
2084 					(decimal)fourthPoint.Y > plotAreaPositionBottom)
2085 				{
2086 					surfaceCompletlyOutside = true;
2087 					thirdPoint.Y = area.PlotAreaPosition.Bottom;
2088 					fourthPoint.Y = area.PlotAreaPosition.Bottom;
2089 				}
2090 
2091 				// Draw just one surface
2092 				if(surfaceCompletlyOutside)
2093 				{
2094 					resultPath =  Draw3DSurface( firstPoint, secondPoint, reversed,
2095 						area, graph, matrix, lightStyle, prevDataPointEx,
2096 						positionZ, depth, points, pointIndex, pointLoopIndex,
2097 						tension, operationType, surfaceSegmentType,
2098 						topDarkening, 0.5f,
2099 						new PointF(thirdPoint.X, thirdPoint.Y),
2100 						new PointF(fourthPoint.X, fourthPoint.Y),
2101 						outsideTop,
2102 						false, false);
2103 
2104 					// Restore previous x\y positions
2105 					thirdPoint = new PointF(prevThirdPoint.X, prevThirdPoint.Y);
2106 					fourthPoint = new PointF(prevFourthPoint.X, prevFourthPoint.Y);
2107 
2108 					return true;
2109 				}
2110 
2111 				// Get intersection point
2112 				DataPoint3D	intersectionPoint = new DataPoint3D();
2113 				bool		firstIntersectionOnBottom = false;
2114 				intersectionPoint.yPosition = (double)plotAreaPositionY;
2115 				if((decimal)thirdPoint.Y > plotAreaPositionBottom ||
2116 					(decimal)fourthPoint.Y > plotAreaPositionBottom )
2117 				{
2118 					intersectionPoint.yPosition = (double)area.PlotAreaPosition.Bottom;
2119 					firstIntersectionOnBottom = true;
2120 				}
2121 				intersectionPoint.xPosition = (intersectionPoint.yPosition - fourthPoint.Y) *
2122 					(thirdPoint.X - fourthPoint.X) /
2123 					(thirdPoint.Y - fourthPoint.Y) +
2124 					fourthPoint.X;
2125 
2126 				// Intersection point must be between first and second points
2127 				intersectionPoint.yPosition = (intersectionPoint.xPosition - secondPoint.xPosition) /
2128 					(firstPoint.xPosition - secondPoint.xPosition) *
2129 					(firstPoint.yPosition - secondPoint.yPosition) +
2130 					secondPoint.yPosition;
2131 
2132 				if(double.IsNaN(intersectionPoint.xPosition) ||
2133 					double.IsInfinity(intersectionPoint.xPosition) ||
2134 					double.IsNaN(intersectionPoint.yPosition) ||
2135 					double.IsInfinity(intersectionPoint.yPosition) )
2136 				{
2137 					return true;
2138 				}
2139 
2140 				// Check if there are 2 intersection points (3 segments)
2141 				int		segmentNumber = 2;
2142 				DataPoint3D	intersectionPoint2 = null;
2143 				bool	switchPoints = false;
2144 				if( ((decimal)thirdPoint.Y < plotAreaPositionY &&
2145 					(decimal)fourthPoint.Y > plotAreaPositionBottom) ||
2146 					((decimal)thirdPoint.Y > plotAreaPositionBottom &&
2147 					(decimal)fourthPoint.Y < plotAreaPositionY))
2148 				{
2149 					segmentNumber = 3;
2150 					intersectionPoint2 = new DataPoint3D();
2151 					if(!firstIntersectionOnBottom)
2152 					{
2153 						intersectionPoint2.yPosition = (double)area.PlotAreaPosition.Bottom;
2154 					}
2155 					else
2156 					{
2157 						intersectionPoint2.yPosition = (double)area.PlotAreaPosition.Y;
2158 					}
2159 					intersectionPoint2.xPosition = (intersectionPoint2.yPosition - fourthPoint.Y) *
2160 						(thirdPoint.X - fourthPoint.X) /
2161 						(thirdPoint.Y - fourthPoint.Y) +
2162 						fourthPoint.X;
2163 
2164 					intersectionPoint2.yPosition = (intersectionPoint2.xPosition - secondPoint.xPosition) /
2165 						(firstPoint.xPosition - secondPoint.xPosition) *
2166 						(firstPoint.yPosition - secondPoint.yPosition) +
2167 						secondPoint.yPosition;
2168 
2169 					if(double.IsNaN(intersectionPoint2.xPosition) ||
2170 						double.IsInfinity(intersectionPoint2.xPosition) ||
2171 						double.IsNaN(intersectionPoint2.yPosition) ||
2172 						double.IsInfinity(intersectionPoint2.yPosition) )
2173 					{
2174 						return true;
2175 					}
2176 
2177 
2178 					// Switch intersection points
2179 					//if(firstPoint.yPosition > plotAreaPositionBottom)
2180 					if((decimal)thirdPoint.Y > plotAreaPositionBottom)
2181 					{
2182 						switchPoints = true;
2183 						/*
2184 						DataPoint3D tempPoint = new DataPoint3D();
2185 						tempPoint.xPosition = intersectionPoint.xPosition;
2186 						tempPoint.yPosition = intersectionPoint.yPosition;
2187 						intersectionPoint.xPosition = intersectionPoint2.xPosition;
2188 						intersectionPoint.yPosition = intersectionPoint2.yPosition;
2189 						intersectionPoint2.xPosition = tempPoint.xPosition;
2190 						intersectionPoint2.yPosition = tempPoint.yPosition;
2191 						*/
2192 					}
2193 				}
2194 
2195 
2196 				// Adjust points Y values
2197 				bool	firstSegmentVisible = true;
2198 				float	bottomDarken = bottomDarkening;
2199 				bool	firstSegmentOutsideTop = false;
2200 				bool	secondSegmentOutsideTop = false;
2201 				if((decimal)thirdPoint.Y < plotAreaPositionY)
2202 				{
2203 					firstSegmentOutsideTop = true;
2204 					firstSegmentVisible = false;
2205 					thirdPoint.Y = area.PlotAreaPosition.Y;
2206 					bottomDarken = 0.5f;
2207 				}
2208 				else if((decimal)thirdPoint.Y > plotAreaPositionBottom)
2209 				{
2210 					firstSegmentVisible = false;
2211 					thirdPoint.Y = area.PlotAreaPosition.Bottom;
2212 					if(firstPoint.yPosition >= thirdPoint.Y)
2213 					{
2214 						bottomDarken = 0.5f;
2215 					}
2216 				}
2217 				if((decimal)fourthPoint.Y < plotAreaPositionY)
2218 				{
2219 					secondSegmentOutsideTop = true;
2220 					fourthPoint.Y = area.PlotAreaPosition.Y;
2221 					bottomDarken = 0.5f;
2222 				}
2223 				else if((decimal)fourthPoint.Y > plotAreaPositionBottom)
2224 				{
2225 					fourthPoint.Y = area.PlotAreaPosition.Bottom;
2226 					if(fourthPoint.Y <= secondPoint.yPosition)
2227 					{
2228 						bottomDarken = 0.5f;
2229 					}
2230 				}
2231 
2232 				// Draw surfaces in 2 or 3 segments
2233 				for(int segmentIndex = 0; segmentIndex < 3; segmentIndex++)
2234 				{
2235 					GraphicsPath segmentPath = null;
2236 					if(segmentIndex == 0 && !reversed ||
2237 						segmentIndex == 2 && reversed)
2238 					{
2239 						// Draw first segment
2240 						if(intersectionPoint2 == null)
2241 						{
2242 							intersectionPoint2 = intersectionPoint;
2243 						}
2244 
2245 						if(switchPoints)
2246 						{
2247 							DataPoint3D tempPoint = new DataPoint3D();
2248 							tempPoint.xPosition = intersectionPoint.xPosition;
2249 							tempPoint.yPosition = intersectionPoint.yPosition;
2250 							intersectionPoint.xPosition = intersectionPoint2.xPosition;
2251 							intersectionPoint.yPosition = intersectionPoint2.yPosition;
2252 							intersectionPoint2.xPosition = tempPoint.xPosition;
2253 							intersectionPoint2.yPosition = tempPoint.yPosition;
2254 						}
2255 
2256 
2257 						intersectionPoint2.dataPoint = secondPoint.dataPoint;
2258 						intersectionPoint2.index = secondPoint.index;
2259 						intersectionPoint2.xCenterVal = secondPoint.xCenterVal;
2260 
2261 						segmentPath =  Draw3DSurface( firstPoint, intersectionPoint2, reversed,
2262 							area, graph, matrix, lightStyle, prevDataPointEx,
2263 							positionZ, depth, points, pointIndex, pointLoopIndex,
2264 							tension, operationType,
2265 							(surfaceSegmentType == LineSegmentType.Middle) ? LineSegmentType.Middle : LineSegmentType.First,
2266 							topDarkening, bottomDarken,
2267 							new PointF(float.NaN, thirdPoint.Y),
2268 							new PointF((float)intersectionPoint2.xPosition, (!firstSegmentVisible || segmentNumber == 3) ? thirdPoint.Y : fourthPoint.Y),
2269 							firstSegmentOutsideTop,
2270 							false, false);
2271 
2272 						if(switchPoints)
2273 						{
2274 							DataPoint3D tempPoint = new DataPoint3D();
2275 							tempPoint.xPosition = intersectionPoint.xPosition;
2276 							tempPoint.yPosition = intersectionPoint.yPosition;
2277 							intersectionPoint.xPosition = intersectionPoint2.xPosition;
2278 							intersectionPoint.yPosition = intersectionPoint2.yPosition;
2279 							intersectionPoint2.xPosition = tempPoint.xPosition;
2280 							intersectionPoint2.yPosition = tempPoint.yPosition;
2281 						}
2282 
2283 					}
2284 
2285 					if(segmentIndex == 1 && intersectionPoint2 != null && segmentNumber == 3)
2286 					{
2287 						if(!switchPoints)
2288 						{
2289 							DataPoint3D tempPoint = new DataPoint3D();
2290 							tempPoint.xPosition = intersectionPoint.xPosition;
2291 							tempPoint.yPosition = intersectionPoint.yPosition;
2292 							intersectionPoint.xPosition = intersectionPoint2.xPosition;
2293 							intersectionPoint.yPosition = intersectionPoint2.yPosition;
2294 							intersectionPoint2.xPosition = tempPoint.xPosition;
2295 							intersectionPoint2.yPosition = tempPoint.yPosition;
2296 						}
2297 
2298 						// Draw middle segment
2299 						intersectionPoint2.dataPoint = secondPoint.dataPoint;
2300 						intersectionPoint2.index = secondPoint.index;
2301 						intersectionPoint2.xCenterVal = secondPoint.xCenterVal;
2302 
2303 						intersectionPoint.xCenterVal = firstPoint.xCenterVal;
2304 						intersectionPoint.index = firstPoint.index;
2305 						intersectionPoint.dataPoint = firstPoint.dataPoint;
2306 
2307 						segmentPath =  Draw3DSurface( intersectionPoint, intersectionPoint2, reversed,
2308 							area, graph, matrix, lightStyle, prevDataPointEx,
2309 							positionZ, depth, points, pointIndex, pointLoopIndex,
2310 							tension, operationType, LineSegmentType.Middle,
2311 							topDarkening, bottomDarkening,
2312 							new PointF((float)intersectionPoint.xPosition, thirdPoint.Y),
2313 							new PointF((float)intersectionPoint2.xPosition, fourthPoint.Y),
2314 							false,
2315 							false, false);
2316 
2317 						if(!switchPoints)
2318 						{
2319 							DataPoint3D tempPoint = new DataPoint3D();
2320 							tempPoint.xPosition = intersectionPoint.xPosition;
2321 							tempPoint.yPosition = intersectionPoint.yPosition;
2322 							intersectionPoint.xPosition = intersectionPoint2.xPosition;
2323 							intersectionPoint.yPosition = intersectionPoint2.yPosition;
2324 							intersectionPoint2.xPosition = tempPoint.xPosition;
2325 							intersectionPoint2.yPosition = tempPoint.yPosition;
2326 						}
2327 
2328 					}
2329 
2330 					if(segmentIndex == 2 && !reversed ||
2331 						segmentIndex == 0 && reversed)
2332 					{
2333 						if(switchPoints)
2334 						{
2335 							DataPoint3D tempPoint = new DataPoint3D();
2336 							tempPoint.xPosition = intersectionPoint.xPosition;
2337 							tempPoint.yPosition = intersectionPoint.yPosition;
2338 							intersectionPoint.xPosition = intersectionPoint2.xPosition;
2339 							intersectionPoint.yPosition = intersectionPoint2.yPosition;
2340 							intersectionPoint2.xPosition = tempPoint.xPosition;
2341 							intersectionPoint2.yPosition = tempPoint.yPosition;
2342 						}
2343 
2344 						// Draw second segment
2345 						intersectionPoint.dataPoint = firstPoint.dataPoint;
2346 						intersectionPoint.index = firstPoint.index;
2347 						intersectionPoint.xCenterVal = firstPoint.xCenterVal;
2348 
2349 						float thirdPointNewY = (!firstSegmentVisible || segmentNumber == 3) ? thirdPoint.Y : fourthPoint.Y;
2350 						if(segmentNumber == 3)
2351 						{
2352 							thirdPointNewY = (secondSegmentOutsideTop) ? thirdPoint.Y : fourthPoint.Y;
2353 						}
2354 
2355 						segmentPath =  Draw3DSurface( intersectionPoint, secondPoint, reversed,
2356 							area, graph, matrix, lightStyle, prevDataPointEx,
2357 							positionZ, depth, points, pointIndex, pointLoopIndex,
2358 							tension, operationType,
2359 							(surfaceSegmentType == LineSegmentType.Middle) ? LineSegmentType.Middle : LineSegmentType.Last,
2360 							topDarkening, bottomDarken,
2361 							new PointF((float)intersectionPoint.xPosition, thirdPointNewY),
2362 							new PointF(float.NaN, fourthPoint.Y),
2363 							secondSegmentOutsideTop,
2364 							false, false);
2365 
2366 						if(switchPoints)
2367 						{
2368 							DataPoint3D tempPoint = new DataPoint3D();
2369 							tempPoint.xPosition = intersectionPoint.xPosition;
2370 							tempPoint.yPosition = intersectionPoint.yPosition;
2371 							intersectionPoint.xPosition = intersectionPoint2.xPosition;
2372 							intersectionPoint.yPosition = intersectionPoint2.yPosition;
2373 							intersectionPoint2.xPosition = tempPoint.xPosition;
2374 							intersectionPoint2.yPosition = tempPoint.yPosition;
2375 						}
2376 
2377 					}
2378 
2379 					// Add segment path
2380 					if(resultPath != null && segmentPath != null && segmentPath.PointCount > 0)
2381 					{
2382 						resultPath.AddPath(segmentPath, true);
2383 					}
2384 				}
2385 
2386 				// Restore previous x\y positions
2387 				thirdPoint = new PointF(prevThirdPoint.X, prevThirdPoint.Y);
2388 				fourthPoint = new PointF(prevFourthPoint.X, prevFourthPoint.Y);
2389 				return true;
2390 			}
2391 			return false;
2392 		}
2393 
2394 		/// <summary>
2395 		/// Draws a 3D surface connecting the two specified points in 2D space.
2396 		/// Used to draw Line based charts.
2397 		/// </summary>
2398 		/// <param name="firstPoint">First data point.</param>
2399 		/// <param name="secondPoint">Second data point.</param>
2400 		/// <param name="reversed">Points are in reversed order.</param>
2401 		/// <param name="area">Chart area reference.</param>
2402 		/// <param name="graph">Chart graphics.</param>
2403 		/// <param name="matrix">Coordinates transformation matrix.</param>
2404 		/// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
2405 		/// <param name="prevDataPointEx">Previous data point object.</param>
2406 		/// <param name="positionZ">Z position of the back side of the 3D surface.</param>
2407 		/// <param name="depth">Depth of the 3D surface.</param>
2408 		/// <param name="points">Array of points.</param>
2409 		/// <param name="pointIndex">Index of point to draw.</param>
2410 		/// <param name="pointLoopIndex">Index of points loop.</param>
2411 		/// <param name="tension">Line tension.</param>
2412 		/// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
2413 		/// <param name="surfaceSegmentType">Define surface segment type if it consists of several segments.</param>
2414 		/// <param name="topDarkening">Darkenning scale for top surface. 0 - None.</param>
2415 		/// <param name="bottomDarkening">Darkenning scale for bottom surface. 0 - None.</param>
2416 		/// <param name="thirdPointPosition">Position where the third point is actually located or float.NaN if same as in "firstPoint".</param>
2417 		/// <param name="fourthPointPosition">Position where the fourth point is actually located or float.NaN if same as in "secondPoint".</param>
2418 		/// <param name="clippedSegment">Indicates that drawn segment is 3D clipped. Only top/bottom should be drawn.</param>
2419 		/// <param name="clipOnTop">Indicates that top segment line should be clipped to the pkot area.</param>
2420 		/// <param name="clipOnBottom">Indicates that bottom segment line should be clipped to the pkot area.</param>
2421 		/// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
Draw3DSurface( DataPoint3D firstPoint, DataPoint3D secondPoint, bool reversed, ChartArea area, ChartGraphics graph, Matrix3D matrix, LightStyle lightStyle, DataPoint3D prevDataPointEx, float positionZ, float depth, ArrayList points, int pointIndex, int pointLoopIndex, float tension, DrawingOperationTypes operationType, LineSegmentType surfaceSegmentType, float topDarkening, float bottomDarkening, PointF thirdPointPosition, PointF fourthPointPosition, bool clippedSegment, bool clipOnTop, bool clipOnBottom)2422 		protected virtual GraphicsPath Draw3DSurface(
2423 			DataPoint3D firstPoint,
2424 			DataPoint3D secondPoint,
2425 			bool reversed,
2426 			ChartArea area,
2427 			ChartGraphics graph,
2428 			Matrix3D matrix,
2429 			LightStyle lightStyle,
2430 			DataPoint3D prevDataPointEx,
2431 			float positionZ,
2432 			float depth,
2433 			ArrayList points,
2434 			int pointIndex,
2435 			int pointLoopIndex,
2436 			float tension,
2437 			DrawingOperationTypes operationType,
2438 			LineSegmentType surfaceSegmentType,
2439 			float topDarkening,
2440 			float bottomDarkening,
2441 			PointF thirdPointPosition,
2442 			PointF fourthPointPosition,
2443 			bool clippedSegment,
2444 			bool clipOnTop,
2445 			bool clipOnBottom)
2446 		{
2447 			// Implemented in area and range chart
2448 			return null;
2449 		}
2450 		#endregion
2451 
2452         #region IDisposable overrides
2453         /// <summary>
2454         /// Releases unmanaged and - optionally - managed resources
2455         /// </summary>
2456         /// <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)2457         protected override void Dispose(bool disposing)
2458         {
2459             if (disposing)
2460             {
2461                 // Dispose managed resources
2462                 if (this._linePen != null)
2463                 {
2464                     this._linePen.Dispose();
2465                     this._linePen = null;
2466                 }
2467             }
2468             base.Dispose(disposing);
2469         }
2470         #endregion
2471     }
2472 }
2473