Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Web.DataVisualization / Common / ChartTypes / RangeChart.cs
1 //-------------------------------------------------------------
2 // <copyright company=\92Microsoft Corporation\92>
3 //   Copyright © Microsoft Corporation. All Rights Reserved.
4 // </copyright>
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
8 //  File:               RangeChart.cs
9 //
10 //  Namespace:  DataVisualization.Charting.ChartTypes
11 //
12 //      Classes:        RangeChart, SplineRangeChart
13 //
14 //  Purpose:    Provides 2D/3D drawing and hit testing functionality 
15 //              for the Range and SplineRange charts. The Range chart 
16 //              displays a range of data by plotting two Y values per 
17 //              data point, with each Y value being drawn as a line 
18 //              chart. The range between the Y values can then be 
19 //              filled with color. Spline Range chart changes the 
20 //              default tension of the lines between data points.
21 //              
22 //      Reviewed:       AG - Aug 6, 2002
23 //              AG - Microsoft 6, 2007
24 //
25 //===================================================================
26
27 #region Used namespaces
28
29 using System;
30 using System.Resources;
31 using System.Reflection;
32 using System.Collections;
33 using System.Drawing;
34 using System.Drawing.Drawing2D;
35
36 #if Microsoft_CONTROL
37         using System.Windows.Forms.DataVisualization.Charting;
38         using System.Windows.Forms.DataVisualization.Charting.Data;
39         using System.Windows.Forms.DataVisualization.Charting.ChartTypes;
40         using System.Windows.Forms.DataVisualization.Charting.Utilities;
41         using System.Windows.Forms.DataVisualization.Charting.Borders3D;
42
43 #else
44 using System.Web.UI.DataVisualization.Charting;
45
46         using System.Web.UI.DataVisualization.Charting.ChartTypes;
47         using System.Web.UI.DataVisualization.Charting.Data;
48         using System.Web.UI.DataVisualization.Charting.Utilities;
49 #endif
50
51 #endregion
52
53 #if Microsoft_CONTROL
54         namespace System.Windows.Forms.DataVisualization.Charting.ChartTypes
55 #else
56         namespace System.Web.UI.DataVisualization.Charting.ChartTypes
57 #endif
58 {
59         /// <summary>
60     /// SplineRangeChart class extends the RangeChart class by 
61     /// providing a different initial tension for the lines.
62     /// </summary>
63         internal class SplineRangeChart : RangeChart
64         {
65                 #region Constructor
66
67                 /// <summary>
68                 /// Default constructor.
69                 /// </summary>
70                 public SplineRangeChart()
71                 {
72                         // Set default line tension
73                         base.lineTension = 0.5f;
74                 }
75
76                 #endregion
77
78                 #region IChartType interface implementation
79
80                 /// <summary>
81                 /// Chart type name
82                 /// </summary>
83                 public override string Name                     { get{ return ChartTypeNames.SplineRange;}}
84
85                 /// <summary>
86                 /// Gets chart type image.
87                 /// </summary>
88                 /// <param name="registry">Chart types registry object.</param>
89                 /// <returns>Chart type image.</returns>
90                 override public System.Drawing.Image GetImage(ChartTypeRegistry registry)
91                 {
92                         return (System.Drawing.Image)registry.ResourceManager.GetObject(this.Name + "ChartType");
93                 }
94
95                 #endregion
96
97                 #region Default tension method
98
99                 /// <summary>
100                 /// Gets default line tension.
101                 /// </summary>
102                 /// <returns>Default line tension.</returns>
103                 override protected float GetDefaultTension()
104                 {
105                         return 0.5f;
106                 }
107
108                 /// <summary>
109                 /// Checks if line tension is supported by the chart type.
110                 /// </summary>
111                 /// <returns>True if line tension is supported.</returns>
112                 protected override bool IsLineTensionSupported()
113                 {
114                         return true;
115                 }
116
117                 #endregion
118         }
119
120         /// <summary>
121     /// RangeChart class provides 2D/3D drawing and hit testing 
122     /// functionality for the Range and SplineRange charts. The 
123     /// only difference of the SplineRange chart is the default 
124     /// tension of the line.
125     /// 
126     /// SplineChart base class provides most of the functionality 
127     /// like drawing lines, labels and markers.
128     /// </summary>
129         internal class RangeChart : SplineChart
130         {
131                 #region Fields
132
133                 /// <summary>
134                 /// Fields used to fill area with gradient 
135                 /// </summary>
136         internal bool gradientFill = false;
137
138                 /// <summary>
139                 /// Shape of the low values
140                 /// </summary>
141         internal GraphicsPath areaBottomPath = new GraphicsPath();
142
143                 /// <summary>
144                 /// Coordinates of the area path
145                 /// </summary>
146                 protected       GraphicsPath    areaPath = null;
147
148                 /// <summary>
149                 /// Reference to the current series object
150                 /// </summary>
151         private Series _series = null;
152
153                 /// <summary>
154                 /// Array of low line values
155                 /// </summary>
156         internal PointF[] lowPoints = null;
157
158                 /// <summary>
159                 /// Check if series are indexed based
160                 /// </summary>
161         internal bool indexedBasedX = false;
162
163                 /// <summary>
164                 /// Secondary Y coordinate that should be used for bottom line of the range (left point)
165                 /// </summary>
166                 private         float                   _thirdPointY2Value = float.NaN;
167
168                 /// <summary>
169                 /// Secondary Y coordinate that should be used for bottom line of the range (right point)
170                 /// </summary>
171                 private         float                   _fourthPointY2Value = float.NaN;
172
173                 #endregion
174
175                 #region Constructor
176
177                 /// <summary>
178                 /// Default constructor.
179                 /// </summary>
180                 public RangeChart()
181                 {
182                         this.drawOutsideLines = true;
183                 }
184
185                 #endregion 
186
187                 #region IChartType interface implementation
188
189                 /// <summary>
190                 /// Chart type name
191                 /// </summary>
192                 public override string Name                     { get{ return ChartTypeNames.Range;}}
193
194                 /// <summary>
195                 /// Number of supported Y value(s) per point 
196                 /// </summary>
197                 override public int YValuesPerPoint     { get { return 2; } }
198
199                 /// <summary>
200                 /// Indicates that extra Y values are connected to the scale of the Y axis
201                 /// </summary>
202                 override public bool ExtraYValuesConnectedToYAxis{ get { return true; } }
203
204                 /// <summary>
205                 /// How to draw series/points in legend:
206                 /// Filled rectangle, Line or Marker
207                 /// </summary>
208                 /// <param name="series">Legend item series.</param>
209                 /// <returns>Legend item style.</returns>
210                 override public LegendImageStyle GetLegendImageStyle(Series series)
211                 {
212                         return LegendImageStyle.Rectangle;
213                 }
214
215                 /// <summary>
216                 /// Gets chart type image.
217                 /// </summary>
218                 /// <param name="registry">Chart types registry object.</param>
219                 /// <returns>Chart type image.</returns>
220                 override public System.Drawing.Image GetImage(ChartTypeRegistry registry)
221                 {
222                         return (System.Drawing.Image)registry.ResourceManager.GetObject(this.Name + "ChartType");
223                 }
224
225                 #endregion
226
227                 #region Default tension method
228
229                 /// <summary>
230                 /// Gets default line tension.
231                 /// </summary>
232                 /// <returns>Line tension.</returns>
233                 override protected float GetDefaultTension()
234                 {
235                         return 0.0f;
236                 }
237
238                 /// <summary>
239                 /// Checks if line tension is supported by the chart type.
240                 /// </summary>
241                 /// <returns>True if line tension is supported.</returns>
242                 protected override bool IsLineTensionSupported()
243                 {
244                         return false;
245                 }
246
247                 #endregion
248
249                 #region Painting and Selection related methods
250
251                 /// <summary>
252                 /// Fills last series area with gradient.
253                 /// If gradient is used as a back color of the series it must be drawn 
254                 /// at the same time.
255                 /// </summary>
256                 /// <param name="graph">The Chart Graphics object.</param>
257                 private void FillLastSeriesGradient(ChartGraphics graph)
258                 {
259                         // Add last line in the path
260                         if(areaPath != null)
261                         {
262                                 areaPath.AddLine(
263                                         areaPath.GetLastPoint().X, 
264                                         areaPath.GetLastPoint().Y, 
265                                         areaPath.GetLastPoint().X, 
266                                         areaBottomPath.GetLastPoint().Y);
267                         }
268                         
269                         // Fill whole area with gradient
270                         if(gradientFill && areaPath != null)
271                         {
272                                 // Set clip region
273                                 graph.SetClip( Area.PlotAreaPosition.ToRectangleF() );
274
275                                 // Create new path from high/low lines 
276                 using (GraphicsPath gradientPath = new GraphicsPath())
277                 {
278                     gradientPath.AddPath(areaPath, true);
279                     areaBottomPath.Reverse();
280                     gradientPath.AddPath(areaBottomPath, true);
281
282                     // Create brush
283                     using (Brush areaGradientBrush = graph.GetGradientBrush(gradientPath.GetBounds(), this._series.Color, this._series.BackSecondaryColor, this._series.BackGradientStyle))
284                     {
285                         // Fill area with gradient
286                         graph.FillPath(areaGradientBrush, gradientPath);
287                         gradientFill = false;
288                     }
289                 }
290
291                                 // Reset clip region
292                                 graph.ResetClip();
293                         }
294                         if(areaPath != null)
295                         {
296                                 areaPath.Dispose();
297                                 areaPath = null;
298                         }
299
300                         // Reset bottom area path
301                         areaBottomPath.Reset();
302                 }
303
304                 /// <summary>
305                 /// This method recalculates position of the end points of lines. This method 
306                 /// is used from Paint or Select method.
307                 /// </summary>
308                 /// <param name="selection">If True selection mode is active, otherwise paint mode is active.</param>
309                 /// <param name="graph">The Chart Graphics object.</param>
310                 /// <param name="common">The Common elements object.</param>
311                 /// <param name="area">Chart area for this chart.</param>
312                 /// <param name="seriesToDraw">Chart series to draw.</param>
313                 protected override void ProcessChartType( 
314                         bool selection, 
315                         ChartGraphics graph, 
316                         CommonElements common, 
317                         ChartArea area, 
318                         Series seriesToDraw )
319                 {
320                         gradientFill = false;
321                         lowPoints = null;
322
323                         // Check if series is indexed based
324             indexedBasedX = ChartHelper.IndexedSeries(common, area.GetSeriesFromChartType(this.Name).ToArray());
325
326                         // Call base class
327                         base.ProcessChartType(selection, graph, common, area, seriesToDraw);
328
329                         // Fill gradient fro the previous series
330                         FillLastSeriesGradient(graph);
331                 }
332
333                 /// <summary>
334                 /// Override line drawing method to fill the range and draw lines.
335                 /// </summary>
336                 /// <param name="graph">Graphics object.</param>
337                 /// <param name="common">The Common elements object.</param>
338                 /// <param name="point">Point to draw the line for.</param>
339                 /// <param name="series">Point series.</param>
340                 /// <param name="points">Array of oints coordinates.</param>
341                 /// <param name="pointIndex">Index of point to draw.</param>
342                 /// <param name="tension">Line tension.</param>
343                 override protected void DrawLine(
344                         ChartGraphics graph,  
345                         CommonElements common, 
346                         DataPoint point, 
347                         Series series, 
348                         PointF[] points, 
349                         int pointIndex, 
350                         float tension)
351                 {
352                         // Two Y values required
353                         if(point.YValues.Length < 2)
354                         {
355                                 throw(new InvalidOperationException( SR.ExceptionChartTypeRequiresYValues( this.Name, "2" )));
356                         }
357
358                         // Start drawing from the second point
359                         if(pointIndex <= 0)
360                         {
361                                 return;
362                         }
363
364                         // Do nothing for the low values line
365                         if(this.YValueIndex == 1)
366                         {
367                                 return;
368                         }
369
370                         // Check if its a beginning of a new series
371                         if(this._series != null)
372                         {
373                                 if(this._series.Name != series.Name)
374                                 {
375                                         // Fill gradient from the previous series
376                                         FillLastSeriesGradient(graph);
377                                         this._series = series;
378                                         lowPoints = null;
379                                         areaBottomPath.Reset();
380                                 }
381                         }
382                         else
383                         {
384                                 this._series = series;
385                         }
386
387                         // Fill array of lower points of the range
388                         if(lowPoints == null)
389                         {
390                                 this.YValueIndex = 1;
391                                 lowPoints = GetPointsPosition(graph, series, indexedBasedX);
392                                 this.YValueIndex = 0;
393                         }
394
395                         // Calculate points position
396                         PointF  highPoint1 = points[pointIndex-1];
397                         PointF  highPoint2 = points[pointIndex];
398                         PointF  lowPoint1 = lowPoints[pointIndex-1];
399                         PointF  lowPoint2 = lowPoints[pointIndex];
400                         
401                         // Create area brush
402                         Brush   areaBrush = null;
403                         if( point.BackHatchStyle != ChartHatchStyle.None )
404                         {
405                                 areaBrush = graph.GetHatchBrush( point.BackHatchStyle, point.Color, point.BackSecondaryColor);
406                         }
407                         else if( point.BackGradientStyle != GradientStyle.None )
408                         {
409                                 this.gradientFill = true;
410                                 this._series = point.series;
411                         }
412                         else if( point.BackImage.Length > 0 && point.BackImageWrapMode != ChartImageWrapMode.Unscaled && point.BackImageWrapMode != ChartImageWrapMode.Scaled)
413                         {
414                                 areaBrush = graph.GetTextureBrush(point.BackImage, point.BackImageTransparentColor, point.BackImageWrapMode, point.Color );
415                         }
416                         else
417                         {
418                                 areaBrush = new SolidBrush(point.Color);
419                         }
420
421                         // Calculate data point area segment path
422                         GraphicsPath path = new GraphicsPath();
423                         path.AddLine(highPoint1.X, lowPoint1.Y, highPoint1.X, highPoint1.Y);
424                         if(this.lineTension == 0)
425                         {
426                                 path.AddLine(points[pointIndex-1], points[pointIndex]);
427                         }
428                         else
429                         {
430                                 path.AddCurve(points, pointIndex-1, 1, this.lineTension);
431                         }
432
433                         path.AddLine(highPoint2.X, highPoint2.Y, highPoint2.X, lowPoint2.Y);
434
435                         // Because of SVG Rendering order of points in the close shape 
436                         // has to be respected.
437                         if( graph.ActiveRenderingType == RenderingType.Svg )
438                         {
439                 using (GraphicsPath pathReverse = new GraphicsPath())
440                 {
441                     // Add curve to the new graphics path
442                     if (this.lineTension == 0)
443                     {
444                         path.AddLine(lowPoints[pointIndex - 1], lowPoints[pointIndex]);
445                     }
446                     else
447                     {
448                         pathReverse.AddCurve(lowPoints, pointIndex - 1, 1, this.lineTension);
449
450                         // Convert to polygon
451                         pathReverse.Flatten();
452
453                         // Reversed points order in the aray
454                         PointF[] pointsReversed = pathReverse.PathPoints;
455                         PointF[] pointF = new PointF[pointsReversed.Length];
456                         int pntIndex = pointsReversed.Length - 1;
457                         foreach (PointF pp in pointsReversed)
458                         {
459                             pointF[pntIndex] = pp;
460                             pntIndex--;
461                         }
462
463                         // Path can not have polygon width two points
464                         if (pointF.Length == 2)
465                         {
466                             PointF[] newPointF = new PointF[3];
467                             newPointF[0] = pointF[0];
468                             newPointF[1] = pointF[1];
469                             newPointF[2] = pointF[1];
470                             pointF = newPointF;
471                         }
472
473                         // Add Polygon to the path
474                         path.AddPolygon(pointF);
475                     }
476                 }
477                         }
478                         else
479                         {
480                                 if(this.lineTension == 0)
481                                 {
482                                         path.AddLine(lowPoints[pointIndex-1], lowPoints[pointIndex]);
483                                 }
484                                 else
485                                 {
486                                         path.AddCurve(lowPoints, pointIndex-1, 1, this.lineTension);
487                                 }
488
489                         }
490
491                         // Check if bottom line is partialy in the data scaleView
492                         if(!clipRegionSet)
493                         {
494                                 double  xValue = (indexedSeries) ? pointIndex + 1 : series.Points[pointIndex].XValue;
495                                 double  xPrevValue = (indexedSeries) ? pointIndex : series.Points[pointIndex - 1].XValue;
496                                 if(xPrevValue < hAxisMin || xPrevValue > hAxisMax || 
497                                         xValue > hAxisMax || xValue < hAxisMin ||
498                                         series.Points[pointIndex-1].YValues[1] < vAxisMin || series.Points[pointIndex-1].YValues[1] > vAxisMax ||
499                                         series.Points[pointIndex].YValues[1] < vAxisMin || series.Points[pointIndex].YValues[1] > vAxisMax )
500                                 {
501                                         // Set clipping region for bottom line drawing 
502                                         graph.SetClip( Area.PlotAreaPosition.ToRectangleF() );
503                                         clipRegionSet = true;
504                                 }
505                         }
506
507                         // Draw shadow
508                         if(series.ShadowColor != Color.Empty && series.ShadowOffset != 0)
509                         {
510                                 if(point.Color != Color.Empty && point.Color != Color.Transparent)
511                                 {
512                                         // Translate drawing matrix
513                                         Matrix translateMatrix = graph.Transform.Clone();
514                                         translateMatrix.Translate(series.ShadowOffset, series.ShadowOffset);
515                                         Matrix oldMatrix = graph.Transform;
516                                         graph.Transform = translateMatrix;
517
518                                         Region  shadowRegion = new Region(path);
519                     using (Brush shadowBrush = new SolidBrush((series.ShadowColor.A != 255) ? series.ShadowColor : Color.FromArgb(point.Color.A / 2, series.ShadowColor)))
520                     {
521                         Region clipRegion = null;
522                         if (!graph.IsClipEmpty && !graph.Clip.IsInfinite(graph.Graphics))
523                         {
524                             clipRegion = graph.Clip;
525                             clipRegion.Translate(series.ShadowOffset + 1, series.ShadowOffset + 1);
526                             graph.Clip = clipRegion;
527
528                         }
529
530                         // Fill region
531                         graph.FillRegion(shadowBrush, shadowRegion);
532
533                         // Draw leftmost and rightmost vertical lines
534                         using (Pen areaLinePen = new Pen(shadowBrush, 1))
535                         {
536                             if (pointIndex == 0)
537                             {
538                                 graph.DrawLine(areaLinePen, highPoint1.X, lowPoint1.Y, highPoint1.X, highPoint1.Y);
539                             }
540                             if (pointIndex == series.Points.Count - 1)
541                             {
542                                 graph.DrawLine(areaLinePen, highPoint2.X, highPoint2.Y, highPoint2.X, lowPoint2.Y);
543                             }
544                         }
545
546                         // Restore graphics parameters
547                         graph.Transform = oldMatrix;
548
549                         // Draw high and low line shadows
550                         this.drawShadowOnly = true;
551                         base.DrawLine(graph, common, point, series, points, pointIndex, tension);
552                         this.YValueIndex = 1;
553                         base.DrawLine(graph, common, point, series, lowPoints, pointIndex, tension);
554                         this.YValueIndex = 0;
555                         this.drawShadowOnly = false;
556
557                         // Restore clip region
558                         if (clipRegion != null)
559                         {
560                             clipRegion = graph.Clip;
561                             clipRegion.Translate(-(series.ShadowOffset + 1), -(series.ShadowOffset + 1));
562                             graph.Clip = clipRegion;
563                         }
564                     }
565                 }
566             }
567
568                         // Draw area
569                         if(!gradientFill)
570                         {
571                                 // Turn off anti aliasing and fill area
572                                 SmoothingMode oldMode = graph.SmoothingMode;
573                                 graph.SmoothingMode = SmoothingMode.None;
574                                 path.CloseAllFigures();
575                                 graph.FillPath(areaBrush, path);
576                                 graph.SmoothingMode = oldMode;
577
578                                 // Draw top and bottom lines, because anti aliasing is not working for the FillPath method
579                                 if(graph.SmoothingMode != SmoothingMode.None)
580                                 {
581                                         Pen areaLinePen = new Pen(areaBrush, 1);
582
583                                         // This code is introduce because of problem 
584                                         // with Svg and Hatch Color
585                     HatchBrush hatchBrush = areaBrush as HatchBrush;
586                                         if( hatchBrush != null )
587                                         {
588                                                 areaLinePen.Color = hatchBrush.ForegroundColor;
589                                         }
590
591                                         if(pointIndex == 0)
592                                         {
593                                                 graph.DrawLine(areaLinePen, highPoint1.X, lowPoint1.Y, highPoint1.X, highPoint1.Y);
594                                         }
595                                         if(pointIndex == series.Points.Count - 1)
596                                         {
597                                                 graph.DrawLine(areaLinePen, highPoint2.X, highPoint2.Y, highPoint2.X, lowPoint2.Y);
598                                         }
599                                         
600                                         if(this.lineTension == 0)
601                                         {
602                                                 graph.DrawLine(areaLinePen, points[pointIndex - 1], points[pointIndex]);
603                                         }
604                                         else
605                                         {
606                                                 graph.DrawCurve(areaLinePen, points, pointIndex-1, 1, this.lineTension);
607                                         }
608
609                                         if(this.lineTension == 0)
610                                         {
611                                                 graph.DrawLine(areaLinePen, lowPoints[pointIndex - 1], lowPoints[pointIndex]);
612                                         }
613                                         else
614                                         {
615                                                 graph.DrawCurve(areaLinePen, lowPoints, pointIndex-1, 1, this.lineTension);
616                                         }
617                                 }
618                         }
619                         
620                         // Add first line
621                         if(areaPath == null)
622                         {
623                                 areaPath = new GraphicsPath();
624                                 areaPath.AddLine(highPoint1.X, lowPoint1.Y, highPoint1.X, highPoint1.Y);
625                         }
626
627                         // Add line to the gradient path
628                         if(this.lineTension == 0)
629                         {
630                                 areaPath.AddLine(points[pointIndex-1], points[pointIndex]);
631                         }
632                         else
633                         {
634                                 areaPath.AddCurve(points, pointIndex-1, 1, this.lineTension);
635                         }
636
637                         if(this.lineTension == 0)
638                         {
639                                 areaBottomPath.AddLine(lowPoints[pointIndex-1], lowPoints[pointIndex]);
640                         }
641                         else
642                         {
643                                 areaBottomPath.AddCurve(lowPoints, pointIndex-1, 1, this.lineTension);
644                         }
645
646                         // Draw range High and Low border lines
647                         if((point.BorderWidth > 0 && 
648                                 point.BorderDashStyle != ChartDashStyle.NotSet && 
649                                 point.BorderColor != Color.Empty) || 
650                                 areaBrush is SolidBrush)
651                         {
652                                 this.useBorderColor = true;
653                                 this.disableShadow = true;
654                                 base.DrawLine(graph, common, point, series, points, pointIndex, tension);
655                                 this.YValueIndex = 1;
656                                 base.DrawLine(graph, common, point, series, lowPoints, pointIndex, tension);
657                                 this.YValueIndex = 0;
658                                 this.useBorderColor = false;
659                                 this.disableShadow = false;
660                         }
661
662                         if( common.ProcessModeRegions )
663                         {
664                                 //**************************************************************
665                                 //** Add area for the inside of the area
666                                 //**************************************************************
667
668                                 path.AddLine(highPoint1.X, lowPoint1.Y, highPoint1.X, highPoint1.Y);
669                                 if(this.lineTension == 0)
670                                 {
671                                         path.AddLine(points[pointIndex-1], points[pointIndex]);
672                                 }
673                                 else
674                                 {
675                                         path.AddCurve(points, pointIndex-1, 1, this.lineTension);
676                                 }
677                                 path.AddLine(highPoint2.X, highPoint2.Y, highPoint2.X, lowPoint2.Y);
678                                 if(this.lineTension == 0)
679                                 {
680                                         path.AddLine(lowPoints[pointIndex-1], lowPoints[pointIndex]);
681                                 }
682                                 else
683                                 {
684                                         path.AddCurve(lowPoints, pointIndex-1, 1, this.lineTension);
685                                 }
686
687                                 // Create grapics path object dor the curved area
688                                 GraphicsPath mapAreaPath = new GraphicsPath();
689                                 mapAreaPath.AddLine(highPoint1.X, lowPoint1.Y, highPoint1.X, highPoint1.Y);
690                                 if(this.lineTension == 0)
691                                 {
692                                         mapAreaPath.AddLine(points[pointIndex - 1], points[pointIndex]);
693                                 }
694                                 else
695                                 {
696                                         mapAreaPath.AddCurve(points, pointIndex-1, 1, this.lineTension);
697                                         mapAreaPath.Flatten();
698                                 }
699                                 mapAreaPath.AddLine(highPoint2.X, highPoint2.Y, highPoint2.X, lowPoint2.Y);
700                                 if(this.lineTension == 0)
701                                 {
702                                         mapAreaPath.AddLine(lowPoints[pointIndex - 1], lowPoints[pointIndex]);
703                                 }
704                                 else
705                                 {
706                                         mapAreaPath.AddCurve(lowPoints, pointIndex-1, 1, this.lineTension);
707                                         mapAreaPath.Flatten();
708                                 }
709
710                                 // Allocate array of floats
711                                 PointF  pointNew = PointF.Empty;
712                                 float[] coord = new float[mapAreaPath.PointCount * 2];
713                                 PointF[] pathPoints = mapAreaPath.PathPoints;
714                                 for( int i = 0; i < mapAreaPath.PointCount; i++ )
715                                 {
716                                         pointNew = graph.GetRelativePoint( pathPoints[i] );
717                                         coord[2*i] = pointNew.X;
718                                         coord[2*i + 1] = pointNew.Y;
719                                 }
720
721                                 common.HotRegionsList.AddHotRegion(
722                                         mapAreaPath, 
723                                         false, 
724                                         coord, 
725                                         point, 
726                                         series.Name,
727                                         pointIndex );
728                         
729                         }
730             //Clean up
731             if (areaBrush != null)
732                 areaBrush.Dispose();
733             if (path != null)
734             {
735                 path.Dispose();
736                 path = null;
737             }
738
739                 }
740
741                 
742                 #endregion
743
744                 #region 3D painting and selection methods
745
746                 /// <summary>
747                 /// Draws a 3D surface connecting the two specified points in 2D space.
748                 /// Used to draw Line based charts.
749                 /// </summary>
750                 /// <param name="area">Chart area reference.</param>
751                 /// <param name="graph">Chart graphics.</param>
752                 /// <param name="matrix">Coordinates transformation matrix.</param>
753                 /// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
754                 /// <param name="prevDataPointEx">Previous data point object.</param>
755                 /// <param name="positionZ">Z position of the back side of the 3D surface.</param>
756                 /// <param name="depth">Depth of the 3D surface.</param>
757                 /// <param name="points">Array of points.</param>
758                 /// <param name="pointIndex">Index of point to draw.</param>
759                 /// <param name="pointLoopIndex">Index of points loop.</param>
760                 /// <param name="tension">Line tension.</param>
761                 /// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
762                 /// <param name="topDarkening">Darkenning scale for top surface. 0 - None.</param>
763                 /// <param name="bottomDarkening">Darkenning scale for bottom surface. 0 - None.</param>
764                 /// <param name="thirdPointPosition">Position where the third point is actually located or float.NaN if same as in "firstPoint".</param>
765                 /// <param name="fourthPointPosition">Position where the fourth point is actually located or float.NaN if same as in "secondPoint".</param>
766                 /// <param name="clippedSegment">Indicates that drawn segment is 3D clipped. Only top/bottom should be drawn.</param>
767                 /// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
768                 protected override GraphicsPath Draw3DSurface( 
769                         ChartArea area,
770                         ChartGraphics graph, 
771                         Matrix3D matrix,
772                         LightStyle lightStyle,
773                         DataPoint3D prevDataPointEx,
774                         float positionZ, 
775                         float depth, 
776                         ArrayList points,
777                         int pointIndex, 
778                         int pointLoopIndex,
779                         float tension,
780                         DrawingOperationTypes operationType,
781                         float topDarkening,
782                         float bottomDarkening,
783                         PointF thirdPointPosition,
784                         PointF fourthPointPosition,
785                         bool clippedSegment)
786                 {
787                         // Create graphics path for selection
788                         GraphicsPath    resultPath = ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
789                                 ? new GraphicsPath() : null;
790
791
792                         //****************************************************************
793                         //** Find line first and second points.
794                         //****************************************************************
795
796                         // Check if points are drawn from sides to center (do only once)
797                         if(centerPointIndex == int.MaxValue)
798                         {
799                                 centerPointIndex = GetCenterPointIndex(points);
800                         }
801
802                         //************************************************************
803                         //** Find line first & second points
804                         //************************************************************
805                         DataPoint3D     secondPoint = (DataPoint3D)points[pointIndex];
806                         int pointArrayIndex = pointIndex;
807                         DataPoint3D firstPoint = ChartGraphics.FindPointByIndex(
808                                 points, 
809                                 secondPoint.index - 1, 
810                                 (this.multiSeries) ? secondPoint : null, 
811                                 ref pointArrayIndex);
812
813                         //****************************************************************
814                         //** Switch first and second points.
815                         //****************************************************************
816                         bool reversed = false;
817                         if(firstPoint.index > secondPoint.index)
818                         {
819                                 DataPoint3D     tempPoint = firstPoint;
820                                 firstPoint = secondPoint;
821                                 secondPoint = tempPoint;
822                                 reversed = true;
823                         }
824
825
826                         // Points can be drawn from sides to the center.
827                         // In this case can't use index in the list to find first point.
828                         // Use point series and real point index to find the first point.
829                         // Get required point index
830                         if(matrix.Perspective != 0 && centerPointIndex != int.MaxValue)
831                         {
832                                 pointArrayIndex = pointIndex;
833                                 if( pointIndex != (centerPointIndex + 1))
834                                 {
835                                         firstPoint = ChartGraphics.FindPointByIndex(points, secondPoint.index - 1, (this.multiSeries) ? secondPoint : null, ref pointArrayIndex);
836                                 }
837                                 else
838                                 {
839                     if (!area.ReverseSeriesOrder)
840                                         {
841                                                 secondPoint = ChartGraphics.FindPointByIndex(points, firstPoint.index + 1, (this.multiSeries) ? secondPoint : null, ref pointArrayIndex);
842                                         }
843                                         else
844                                         {
845                                                 firstPoint = secondPoint;
846                                                 secondPoint = ChartGraphics.FindPointByIndex(points, secondPoint.index - 1, (this.multiSeries) ? secondPoint : null, ref pointArrayIndex);
847                                         }
848                                 }
849                         }
850
851                         // Check if points are not null
852                         if(firstPoint == null || secondPoint == null)
853                         {
854                                 return resultPath;
855                         }
856
857
858             // Area point is drawn as one segment
859                         return Draw3DSurface( firstPoint, secondPoint, reversed,
860                                 area, graph, matrix, lightStyle, prevDataPointEx,
861                                 positionZ, depth, points, pointIndex, pointLoopIndex,
862                                 tension, operationType, LineSegmentType.Single,
863                                 topDarkening, bottomDarkening,
864                                 thirdPointPosition, fourthPointPosition,
865                                 clippedSegment,
866                                 true, true);
867                 }
868
869                 /// <summary>
870                 /// Draws a 3D surface connecting the two specified points in 2D space.
871                 /// Used to draw Line based charts.
872                 /// </summary>
873                 /// <param name="firstPoint">First data point.</param>
874                 /// <param name="secondPoint">Second data point.</param>
875                 /// <param name="reversed">Points are in reversed order.</param>
876                 /// <param name="area">Chart area reference.</param>
877                 /// <param name="graph">Chart graphics.</param>
878                 /// <param name="matrix">Coordinates transformation matrix.</param>
879                 /// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
880                 /// <param name="prevDataPointEx">Previous data point object.</param>
881                 /// <param name="positionZ">Z position of the back side of the 3D surface.</param>
882                 /// <param name="depth">Depth of the 3D surface.</param>
883                 /// <param name="points">Array of points.</param>
884                 /// <param name="pointIndex">Index of point to draw.</param>
885                 /// <param name="pointLoopIndex">Index of points loop.</param>
886                 /// <param name="tension">Line tension.</param>
887                 /// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
888                 /// <param name="surfaceSegmentType">Define surface segment type if it consists of several segments.</param>
889                 /// <param name="topDarkening">Darkenning scale for top surface. 0 - None.</param>
890                 /// <param name="bottomDarkening">Darkenning scale for bottom surface. 0 - None.</param>
891                 /// <param name="thirdPointPosition">Position where the third point is actually located or float.NaN if same as in "firstPoint".</param>
892                 /// <param name="fourthPointPosition">Position where the fourth point is actually located or float.NaN if same as in "secondPoint".</param>
893                 /// <param name="clippedSegment">Indicates that drawn segment is 3D clipped. Only top/bottom should be drawn.</param>
894                 /// <param name="clipOnTop">Indicates that top segment line should be clipped to the pkot area.</param>
895                 /// <param name="clipOnBottom">Indicates that bottom segment line should be clipped to the pkot area.</param>
896                 /// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
897                 protected override GraphicsPath Draw3DSurface( 
898                         DataPoint3D firstPoint, 
899                         DataPoint3D secondPoint, 
900                         bool reversed,
901                         ChartArea area,
902                         ChartGraphics graph, 
903                         Matrix3D matrix,
904                         LightStyle lightStyle,
905                         DataPoint3D prevDataPointEx,
906                         float positionZ, 
907                         float depth, 
908                         ArrayList points,
909                         int pointIndex, 
910                         int pointLoopIndex,
911                         float tension,
912                         DrawingOperationTypes operationType,
913                         LineSegmentType surfaceSegmentType,
914                         float topDarkening,
915                         float bottomDarkening,
916                         PointF thirdPointPosition,
917                         PointF fourthPointPosition,
918                         bool clippedSegment,
919                         bool clipOnTop,
920                         bool clipOnBottom)
921                 {
922                         // Create graphics path for selection
923                         GraphicsPath    resultPath = ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
924                                 ? new GraphicsPath() : null;
925
926                         // Fint point with line properties
927                         DataPoint3D             pointAttr = secondPoint;
928                         if(prevDataPointEx.dataPoint.IsEmpty)
929                         {
930                                 pointAttr = prevDataPointEx;
931                         }
932                         else if(firstPoint.index > secondPoint.index)
933                         {
934                                 pointAttr = firstPoint;
935                         }
936
937                         //****************************************************************
938                         //** Adjust point visual properties.
939                         //****************************************************************
940                         Color                   color = (useBorderColor) ? pointAttr.dataPoint.BorderColor : pointAttr.dataPoint.Color;
941                         ChartDashStyle  dashStyle = pointAttr.dataPoint.BorderDashStyle;
942                         if( pointAttr.dataPoint.IsEmpty && pointAttr.dataPoint.Color == Color.Empty)
943                         {
944                                 color = Color.Gray;
945                         }
946                         if( pointAttr.dataPoint.IsEmpty && pointAttr.dataPoint.BorderDashStyle == ChartDashStyle.NotSet )
947                         {
948                                 dashStyle = ChartDashStyle.Solid;
949                         }
950
951                         //****************************************************************
952                         //** Get axis position
953                         //****************************************************************
954                         float   axisPosition = (float)VAxis.GetPosition(this.VAxis.Crossing);
955
956
957                         //****************************************************************
958                         //** Calculate position of top/bootom points.
959                         //****************************************************************
960                         PointF  thirdPoint, fourthPoint; 
961                         GetBottomPointsPosition(
962                                 Common, 
963                                 area, 
964                                 axisPosition, 
965                                 ref firstPoint, 
966                                 ref secondPoint, 
967                                 out thirdPoint, 
968                                 out fourthPoint);
969
970                         // Check if point's position provided as parameter
971                         if(!float.IsNaN(thirdPointPosition.Y))
972                         {
973                                 thirdPoint.Y = thirdPointPosition.Y;
974                         }
975                         if(!float.IsNaN(fourthPointPosition.Y))
976                         {
977                                 fourthPoint.Y = fourthPointPosition.Y;
978                         }
979
980
981                         //****************************************************************
982                         //** Check if top/bottom lines of range segment intersect.
983                         //****************************************************************
984             double smallDouble = 0.0001;
985             if (Math.Abs(firstPoint.yPosition - (double)thirdPoint.Y) > smallDouble && 
986                 Math.Abs(secondPoint.yPosition - (double)fourthPoint.Y) > smallDouble &&
987                 ((firstPoint.yPosition > thirdPoint.Y && secondPoint.yPosition < fourthPoint.Y) ||
988                 (firstPoint.yPosition < thirdPoint.Y && secondPoint.yPosition > fourthPoint.Y)))
989                         {
990                                 // This feature is not supported in 3D SplineRange chart type
991                                 if(tension != 0f)
992                                 {
993                     throw (new InvalidOperationException(SR.Exception3DSplineY1ValueIsLessThenY2));
994                                 }
995
996                                 // Find intersection point
997                                 PointF  intersectionCoordinates = ChartGraphics.GetLinesIntersection(
998                                         (float)firstPoint.xPosition, (float)firstPoint.yPosition,
999                                         (float)secondPoint.xPosition, (float)secondPoint.yPosition,
1000                                         thirdPoint.X, thirdPoint.Y,
1001                                         fourthPoint.X, fourthPoint.Y);
1002                                 DataPoint3D     intersectionPoint = new DataPoint3D();
1003                                 intersectionPoint.xPosition = intersectionCoordinates.X;
1004                                 intersectionPoint.yPosition = intersectionCoordinates.Y;
1005
1006                                 // Check if intersection point is valid
1007                                 bool splitDraw = true;
1008                                 if( double.IsNaN(intersectionCoordinates.X) ||
1009                                         double.IsNaN(intersectionCoordinates.Y) )
1010                                 {
1011                                         splitDraw = false;
1012                                 }
1013                                 else
1014                                 {
1015                                         if( (decimal)intersectionCoordinates.X == (decimal)firstPoint.xPosition && 
1016                                                 (decimal)intersectionCoordinates.Y == (decimal)firstPoint.yPosition )
1017                                         {
1018                                                 splitDraw = false;
1019                                         }
1020                                         if( (decimal)intersectionCoordinates.X == (decimal)secondPoint.xPosition && 
1021                                                 (decimal)intersectionCoordinates.Y == (decimal)secondPoint.yPosition )
1022                                         {
1023                                                 splitDraw = false;
1024                                         }
1025                                 }
1026
1027                                 if(splitDraw)
1028                                 {
1029                                         // Check if reversed drawing order required
1030                                         reversed = false;
1031                                         if((pointIndex + 1) < points.Count)
1032                                         {
1033                                                 DataPoint3D p = (DataPoint3D)points[pointIndex + 1];
1034                                                 if(p.index == firstPoint.index)
1035                                                 {
1036                                                         reversed = true;
1037                                                 }
1038                                         }
1039
1040                                         // Draw two segments
1041                                         for(int segmentIndex = 0; segmentIndex <= 1; segmentIndex++)
1042                                         {
1043                                                 GraphicsPath segmentPath = null;
1044                                                 if(segmentIndex == 0 && !reversed ||
1045                                                         segmentIndex == 1 && reversed)
1046                                                 {
1047                                                         // Set coordinates of bottom points
1048                                                         _fourthPointY2Value = (float)intersectionPoint.yPosition;
1049
1050                                                         // Draw first segment
1051                                                         intersectionPoint.dataPoint = secondPoint.dataPoint;
1052                                                         intersectionPoint.index = secondPoint.index;
1053                                                         segmentPath =  Draw3DSurface( firstPoint, intersectionPoint, reversed,
1054                                                                 area, graph, matrix, lightStyle, prevDataPointEx,
1055                                                                 positionZ, depth, points, pointIndex, pointLoopIndex,
1056                                                                 tension, operationType, surfaceSegmentType,
1057                                                                 topDarkening, bottomDarkening,
1058                                                                 new PointF(float.NaN, float.NaN),
1059                                                                 new PointF(float.NaN, float.NaN),
1060                                                                 clippedSegment,
1061                                                                 true, true);
1062                                                 }
1063
1064                                                 if(segmentIndex == 1 && !reversed ||
1065                                                         segmentIndex == 0 && reversed)
1066                                                 {
1067                                                         // Set coordinates of bottom points
1068                                                         _thirdPointY2Value = (float)intersectionPoint.yPosition;
1069
1070                                                         // Draw second segment
1071                                                         intersectionPoint.dataPoint = firstPoint.dataPoint;
1072                                                         intersectionPoint.index = firstPoint.index;
1073                                                         segmentPath =  Draw3DSurface( intersectionPoint, secondPoint, reversed,
1074                                                                 area, graph, matrix, lightStyle, prevDataPointEx,
1075                                                                 positionZ, depth, points, pointIndex, pointLoopIndex,
1076                                                                 tension, operationType, surfaceSegmentType,
1077                                                                 topDarkening, bottomDarkening,
1078                                                                 new PointF(float.NaN, float.NaN),
1079                                                                 new PointF(float.NaN, float.NaN),
1080                                                                 clippedSegment,
1081                                                                 true, true);
1082                                                 }
1083
1084                                                 // Add segment path
1085                                                 if(resultPath != null && segmentPath != null && segmentPath.PointCount > 0)
1086                                                 {
1087                                                         resultPath.AddPath(segmentPath, true);
1088                                                 }
1089
1090                                                 // Reset bottom line "forced" Y coordinates
1091                                                 _thirdPointY2Value = float.NaN;
1092                                                 _fourthPointY2Value = float.NaN;
1093
1094                                         }
1095
1096                                         return resultPath;
1097                                 }
1098                         }
1099
1100             
1101                         //****************************************************************
1102                         //** Detect visibility of the bounding rectangle.
1103                         //****************************************************************
1104                         float minX = (float)Math.Min(firstPoint.xPosition, secondPoint.xPosition);
1105                         float minY = (float)Math.Min(firstPoint.yPosition, secondPoint.yPosition);
1106                         minY = (float)Math.Min(minY, axisPosition);
1107                         float maxX = (float)Math.Max(firstPoint.xPosition, secondPoint.xPosition);
1108                         float maxY = (float)Math.Max(firstPoint.yPosition, secondPoint.yPosition);
1109                         maxY = (float)Math.Max(maxY, axisPosition);
1110                         RectangleF position = new RectangleF(minX, minY, maxX - minX, maxY - minY);
1111                         SurfaceNames visibleSurfaces = graph.GetVisibleSurfaces(position,positionZ,depth,matrix);
1112
1113                         // Check if area point is drawn upside down.
1114                         bool upSideDown = false;
1115                         if(firstPoint.yPosition >= thirdPoint.Y && secondPoint.yPosition >= fourthPoint.Y)
1116                         {
1117                                 upSideDown = true;
1118
1119                                 // Switch visibility between Top & Bottom surfaces
1120                                 bool topVisible = ( (visibleSurfaces & SurfaceNames.Top) == SurfaceNames.Top );
1121                                 bool bottomVisible = ( (visibleSurfaces & SurfaceNames.Bottom) == SurfaceNames.Bottom );
1122                                 visibleSurfaces ^= SurfaceNames.Bottom;
1123                                 visibleSurfaces ^= SurfaceNames.Top;
1124                                 if(topVisible)
1125                                 {
1126                                         visibleSurfaces |= SurfaceNames.Bottom;
1127                                 }
1128                                 if(bottomVisible)
1129                                 {
1130                                         visibleSurfaces |= SurfaceNames.Top;
1131                                 }
1132                         }
1133
1134                         // Check Top/Bottom surface visibility
1135                         GetTopSurfaceVisibility(area, firstPoint, secondPoint, upSideDown, positionZ, depth, matrix, ref visibleSurfaces);
1136
1137                         // Top and bottom surfaces are always visible for spline range
1138                         bool bottomFirst = true;
1139                         if(tension != 0f)
1140                         {
1141                                 if( (visibleSurfaces & SurfaceNames.Bottom) == SurfaceNames.Bottom )
1142                                 {
1143                                         bottomFirst = false;
1144                                 }
1145                                 if( (visibleSurfaces & SurfaceNames.Bottom) == 0 &&
1146                                         (visibleSurfaces & SurfaceNames.Top) == 0 )
1147                                 {
1148                                         bottomFirst = false;
1149                                 }
1150
1151
1152                                 visibleSurfaces |= SurfaceNames.Bottom;
1153                                 visibleSurfaces |= SurfaceNames.Top;
1154                         }
1155
1156                         // Round double values to 5 decimals
1157                         firstPoint.xPosition = Math.Round(firstPoint.xPosition, 5);
1158                         firstPoint.yPosition = Math.Round(firstPoint.yPosition, 5);
1159                         secondPoint.xPosition = Math.Round(secondPoint.xPosition, 5);
1160                         secondPoint.yPosition = Math.Round(secondPoint.yPosition, 5);
1161
1162                         //****************************************************************
1163                         //** Clip area first and second data points inside 
1164                         //** the plotting area.
1165                         //****************************************************************
1166                         if(ClipTopPoints(
1167                                 resultPath,
1168                                 ref firstPoint, 
1169                                 ref secondPoint, 
1170                                 reversed,
1171                                 area,
1172                                 graph, 
1173                                 matrix,
1174                                 lightStyle,
1175                                 prevDataPointEx,
1176                                 positionZ, 
1177                                 depth, 
1178                                 points,
1179                                 pointIndex, 
1180                                 pointLoopIndex,
1181                                 tension,
1182                                 operationType,
1183                                 surfaceSegmentType,
1184                                 topDarkening,
1185                                 bottomDarkening
1186                                 ) == true)
1187                         {
1188                                 return resultPath;
1189                         }
1190
1191                         //****************************************************************
1192                         //** Clip area third and fourth data points inside 
1193                         //** the plotting area.
1194                         //****************************************************************
1195                         if(ClipBottomPoints(
1196                                 resultPath,
1197                                 ref firstPoint, 
1198                                 ref secondPoint, 
1199                                 ref thirdPoint,
1200                                 ref fourthPoint,
1201                                 reversed,
1202                                 area,
1203                                 graph, 
1204                                 matrix,
1205                                 lightStyle,
1206                                 prevDataPointEx,
1207                                 positionZ, 
1208                                 depth, 
1209                                 points,
1210                                 pointIndex, 
1211                                 pointLoopIndex,
1212                                 tension,
1213                                 operationType,
1214                                 surfaceSegmentType,
1215                                 topDarkening,
1216                                 bottomDarkening
1217                                 ) == true)
1218                         {
1219                                 return resultPath;
1220                         }
1221
1222                         //****************************************************************
1223                         //** Draw elements of area chart in 2 layers (back & front)
1224                         //****************************************************************
1225                         for(int elemLayer = 1; elemLayer <= 2; elemLayer++)
1226                         {
1227                                 // Loop through all surfaces
1228                                 SurfaceNames[]  surfacesOrder = null;
1229                                 if(bottomFirst)
1230                                         surfacesOrder = new SurfaceNames[] {SurfaceNames.Back, SurfaceNames.Bottom, SurfaceNames.Top, SurfaceNames.Left, SurfaceNames.Right, SurfaceNames.Front};
1231                                 else
1232                                         surfacesOrder = new SurfaceNames[] {SurfaceNames.Back, SurfaceNames.Top, SurfaceNames.Bottom, SurfaceNames.Left, SurfaceNames.Right, SurfaceNames.Front};
1233
1234                                 LineSegmentType lineSegmentType = LineSegmentType.Middle;
1235                                 foreach(SurfaceNames    currentSurface in surfacesOrder)
1236                                 {
1237                                         // Check id surface should be drawn
1238                     if (ChartGraphics.ShouldDrawLineChartSurface(area, area.ReverseSeriesOrder, currentSurface, visibleSurfaces, color, 
1239                                                 points, firstPoint, secondPoint, this.multiSeries, ref lineSegmentType) != elemLayer)
1240                                         {
1241                                                 continue;
1242                                         }
1243
1244                                         // Draw only borders of the invisible elements on the back layer
1245                                         Color   surfaceColor = color;
1246                                         Color   surfaceBorderColor = pointAttr.dataPoint.BorderColor;
1247                                         if(elemLayer == 1)
1248                                         {
1249                                                 // Draw only if point color is semi-transparent
1250                                                 if(surfaceColor.A == 255)
1251                                                 {
1252                                                         continue;
1253                                                 }
1254
1255                                                 // Define drawing colors
1256                                                 surfaceColor = Color.Transparent;
1257                                                 if(surfaceBorderColor == Color.Empty)
1258                                                 {
1259                                                         // If border color is emty use color slightly darker than main back color
1260                                                         surfaceBorderColor = ChartGraphics.GetGradientColor( color, Color.Black, 0.2 );
1261                                                 }
1262                                         }
1263
1264                                         // Draw surfaces
1265                                         GraphicsPath surfacePath = null;
1266                                         switch(currentSurface)
1267                                         {
1268                                                 case(SurfaceNames.Top):
1269                                                         surfacePath = graph.Draw3DSurface( area, matrix, lightStyle, currentSurface, positionZ,  depth, 
1270                                                                 surfaceColor, surfaceBorderColor, pointAttr.dataPoint.BorderWidth, dashStyle, 
1271                                                                 firstPoint, secondPoint,  points, pointIndex,
1272                                                                 tension, operationType, LineSegmentType.Middle,
1273                                 (this.showPointLines) ? true : false, false, area.ReverseSeriesOrder, this.multiSeries, 0, true);
1274                                                         break;
1275                                                 case(SurfaceNames.Bottom):
1276                                                 {
1277                                                         // Calculate coordinates
1278                                                         DataPoint3D     dp1 = new DataPoint3D();
1279                                                         dp1.dataPoint = firstPoint.dataPoint;
1280                                                         dp1.index = firstPoint.index;
1281                                                         dp1.xPosition = firstPoint.xPosition;
1282                                                         dp1.yPosition = thirdPoint.Y;
1283                                                         DataPoint3D     dp2 = new DataPoint3D();
1284                                                         dp2.dataPoint = secondPoint.dataPoint;
1285                                                         dp2.index = secondPoint.index;
1286                                                         dp2.xPosition = secondPoint.xPosition;
1287                                                         dp2.yPosition = fourthPoint.Y;
1288
1289                                                         // Draw surface
1290                                                         surfacePath = graph.Draw3DSurface( area, matrix, lightStyle, currentSurface, positionZ, depth, 
1291                                                                 surfaceColor, surfaceBorderColor, pointAttr.dataPoint.BorderWidth, dashStyle, 
1292                                                                 dp1, dp2, points, pointIndex,
1293                                                                 tension, operationType, LineSegmentType.Middle,
1294                                 (this.showPointLines) ? true : false, false, area.ReverseSeriesOrder, this.multiSeries, 1, true);
1295                                                         break;
1296                                                 }
1297
1298                                                 case(SurfaceNames.Left):
1299                                                 {
1300                                                         if(surfaceSegmentType == LineSegmentType.Single ||
1301                                 (!area.ReverseSeriesOrder && surfaceSegmentType == LineSegmentType.First) ||
1302                                 (area.ReverseSeriesOrder && surfaceSegmentType == LineSegmentType.Last))
1303                                                         {
1304                                                                 
1305                                                                 // Calculate coordinates
1306                                                                 DataPoint3D     leftMostPoint = (firstPoint.xPosition <= secondPoint.xPosition) ? firstPoint : secondPoint;
1307                                                                 DataPoint3D     dp1 = new DataPoint3D();
1308                                                                 dp1.xPosition = leftMostPoint.xPosition;
1309                                                                 dp1.yPosition = (firstPoint.xPosition <= secondPoint.xPosition) ? thirdPoint.Y : fourthPoint.Y;
1310                                                                 DataPoint3D     dp2 = new DataPoint3D();
1311                                                                 dp2.xPosition = leftMostPoint.xPosition;;
1312                                                                 dp2.yPosition = leftMostPoint.yPosition;
1313
1314                                                                 // Draw surface
1315                                                                 surfacePath = graph.Draw3DSurface( area, matrix, lightStyle, currentSurface, positionZ, depth, 
1316                                                                         surfaceColor, surfaceBorderColor, pointAttr.dataPoint.BorderWidth, dashStyle, 
1317                                                                         dp1, dp2, points, pointIndex,
1318                                     0f, operationType, LineSegmentType.Single, false, true, area.ReverseSeriesOrder, this.multiSeries, 0, true);
1319                                                                         
1320                                                         }
1321                                                         break;
1322                                                 }
1323                                                 case(SurfaceNames.Right):
1324                                                 {
1325                                                         if(surfaceSegmentType == LineSegmentType.Single ||
1326                                 (!area.ReverseSeriesOrder && surfaceSegmentType == LineSegmentType.Last) ||
1327                                 (area.ReverseSeriesOrder && surfaceSegmentType == LineSegmentType.First))
1328
1329                                                         {
1330                                                                 // Calculate coordinates
1331                                                                 DataPoint3D     rightMostPoint = (secondPoint.xPosition >= firstPoint.xPosition) ? secondPoint : firstPoint;
1332                                                                 DataPoint3D     dp1 = new DataPoint3D();
1333                                                                 dp1.xPosition = rightMostPoint.xPosition;
1334                                                                 dp1.yPosition = (secondPoint.xPosition >= firstPoint.xPosition) ? fourthPoint.Y : thirdPoint.Y;
1335                                                                 DataPoint3D     dp2 = new DataPoint3D();
1336                                                                 dp2.xPosition = rightMostPoint.xPosition;
1337                                                                 dp2.yPosition = rightMostPoint.yPosition;
1338
1339                                                                 // Draw surface
1340                                                                 surfacePath = graph.Draw3DSurface( area, matrix, lightStyle, currentSurface, positionZ, depth, 
1341                                                                         surfaceColor, surfaceBorderColor, pointAttr.dataPoint.BorderWidth, dashStyle, 
1342                                                                         dp1, dp2, points, pointIndex,
1343                                     0f, operationType, LineSegmentType.Single, false, true, area.ReverseSeriesOrder, this.multiSeries, 0, true);
1344                                                         }
1345
1346                                                         break;
1347                                                 }
1348                                                 case(SurfaceNames.Back):
1349                                                 {
1350                                                         // Calculate coordinates
1351                                                         DataPoint3D     dp1 = new DataPoint3D();
1352                                                         dp1.dataPoint = firstPoint.dataPoint;
1353                                                         dp1.index = firstPoint.index;
1354                                                         dp1.xPosition = firstPoint.xPosition;
1355                                                         dp1.yPosition = thirdPoint.Y;
1356                                                         DataPoint3D     dp2 = new DataPoint3D();
1357                                                         dp2.dataPoint = secondPoint.dataPoint;
1358                                                         dp2.index = secondPoint.index;
1359                                                         dp2.xPosition = secondPoint.xPosition;
1360                                                         dp2.yPosition = fourthPoint.Y;
1361
1362                                                         // Draw surface
1363                                                         surfacePath = Draw3DSplinePolygon( graph, area, positionZ,
1364                                                                 surfaceColor, surfaceBorderColor, pointAttr.dataPoint.BorderWidth, 
1365                                                                 firstPoint, secondPoint, dp2, dp1, points, 
1366                                                                 tension, operationType, lineSegmentType, 
1367                                                                 (this.showPointLines) ? true : false);
1368
1369                                                         break;
1370                                                 }
1371                                                 case(SurfaceNames.Front):
1372                                                 {
1373                                                         
1374                                                         // Calculate coordinates
1375                                                         DataPoint3D     dp1 = new DataPoint3D();
1376                                                         dp1.dataPoint = firstPoint.dataPoint;
1377                                                         dp1.index = firstPoint.index;
1378                                                         dp1.xPosition = firstPoint.xPosition;
1379                                                         dp1.yPosition = thirdPoint.Y;
1380                                                         DataPoint3D     dp2 = new DataPoint3D();
1381                                                         dp2.dataPoint = secondPoint.dataPoint;
1382                                                         dp2.index = secondPoint.index;
1383                                                         dp2.xPosition = secondPoint.xPosition;
1384                                                         dp2.yPosition = fourthPoint.Y;
1385
1386                                                         // Change segment type for the reversed series order
1387                             if (area.ReverseSeriesOrder)
1388                                                         {
1389                                                                 if(lineSegmentType == LineSegmentType.First)
1390                                                                 {
1391                                                                         lineSegmentType = LineSegmentType.Last;
1392                                                                 }
1393                                                                 else if(lineSegmentType == LineSegmentType.Last)
1394                                                                 {
1395                                                                         lineSegmentType = LineSegmentType.First;
1396                                                                 }
1397                                                         }
1398
1399                                                         if(surfaceSegmentType != LineSegmentType.Single)
1400                                                         {
1401                                                                 if( surfaceSegmentType == LineSegmentType.Middle ||
1402                                                                         ( surfaceSegmentType == LineSegmentType.First && lineSegmentType != LineSegmentType.First) ||
1403                                                                         ( surfaceSegmentType == LineSegmentType.Last && lineSegmentType != LineSegmentType.Last) )
1404                                                                 {
1405                                                                         lineSegmentType = LineSegmentType.Middle;
1406                                                                 }
1407                                                                 if(reversed) 
1408                                                                 {
1409                                                                         if(lineSegmentType == LineSegmentType.First)
1410                                                                         {
1411                                                                                 lineSegmentType = LineSegmentType.Last;
1412                                                                         }
1413                                                                         else if(lineSegmentType == LineSegmentType.Last)
1414                                                                         {
1415                                                                                 lineSegmentType = LineSegmentType.First;
1416                                                                         }
1417                                                                 }
1418                                                         }
1419
1420                                                         // Draw surface
1421                                                         surfacePath = Draw3DSplinePolygon( graph, area, positionZ + depth,
1422                                                                 surfaceColor, surfaceBorderColor, pointAttr.dataPoint.BorderWidth, 
1423                                                                 firstPoint, secondPoint, dp2, dp1, points, 
1424                                                                 tension, operationType, lineSegmentType, 
1425                                                                 (this.showPointLines) ? true : false);
1426                                                                 
1427                                                         break;
1428                                                 }
1429                                         }
1430
1431                                         // Add path of the fully visible surfaces to the result surface
1432                                         if(elemLayer == 2 && resultPath != null && surfacePath != null && surfacePath.PointCount > 0)
1433                                         {
1434                                                 resultPath.CloseFigure();
1435                                                 resultPath.AddPath(surfacePath, true);
1436                                         }
1437
1438                                 }
1439                         }
1440
1441                         return resultPath;
1442                 }
1443
1444                 /// <summary>
1445                 /// Gets visibility of the top surface.
1446                 /// </summary>
1447                 /// <param name="area">Chart area object.</param>
1448                 /// <param name="firstPoint">First data point of the line.</param>
1449                 /// <param name="secondPoint">Second data point of the line.</param>
1450                 /// <param name="upSideDown">Indicates that Y values of the data points are below axis line.</param>
1451                 /// <param name="positionZ">Z coordinate of the back side of the cube.</param>
1452                 /// <param name="depth">Cube depth.</param>
1453                 /// <param name="matrix">Coordinate transformation matrix.</param>
1454                 /// <param name="visibleSurfaces">Surface visibility reference. Initialized with bounary cube visibility.</param>
1455                 protected virtual void GetTopSurfaceVisibility(
1456                         ChartArea area,
1457                         DataPoint3D firstPoint, 
1458                         DataPoint3D secondPoint, 
1459                         bool upSideDown,
1460                         float positionZ, 
1461                         float depth, 
1462                         Matrix3D matrix,
1463                         ref SurfaceNames visibleSurfaces)
1464                 {
1465                         //***********************************************************************
1466                         //** Check Top surface visibility
1467                         //***********************************************************************
1468                         // If Top surface visibility in bounding rectangle - do not gurantee angled linde visibility
1469                         if( (visibleSurfaces & SurfaceNames.Top) == SurfaceNames.Top)
1470                         {
1471                                 visibleSurfaces ^= SurfaceNames.Top;
1472                         }
1473
1474                         // Create top surface coordinates in 3D space
1475                         Point3D[] cubePoints = new Point3D[3];
1476             if (!area.ReverseSeriesOrder)
1477                         {
1478                                 if(!upSideDown && firstPoint.xPosition < secondPoint.xPosition ||
1479                                         upSideDown && firstPoint.xPosition > secondPoint.xPosition)
1480                                 {
1481                                         cubePoints[0] = new Point3D( (float)firstPoint.xPosition, (float)firstPoint.yPosition, positionZ + depth );
1482                                         cubePoints[1] = new Point3D( (float)firstPoint.xPosition, (float)firstPoint.yPosition, positionZ );
1483                                         cubePoints[2] = new Point3D( (float)secondPoint.xPosition, (float)secondPoint.yPosition, positionZ );
1484                                 }
1485                                 else
1486                                 {
1487                                         cubePoints[0] = new Point3D( (float)secondPoint.xPosition, (float)secondPoint.yPosition, positionZ + depth );
1488                                         cubePoints[1] = new Point3D( (float)secondPoint.xPosition, (float)secondPoint.yPosition, positionZ );
1489                                         cubePoints[2] = new Point3D( (float)firstPoint.xPosition, (float)firstPoint.yPosition, positionZ );
1490                                 }
1491                         }
1492                         else
1493                         {
1494                                 if(!upSideDown && secondPoint.xPosition < firstPoint.xPosition ||
1495                                         upSideDown && secondPoint.xPosition > firstPoint.xPosition)
1496                                 {
1497                                         cubePoints[0] = new Point3D( (float)secondPoint.xPosition, (float)secondPoint.yPosition, positionZ + depth );
1498                                         cubePoints[1] = new Point3D( (float)secondPoint.xPosition, (float)secondPoint.yPosition, positionZ );
1499                                         cubePoints[2] = new Point3D( (float)firstPoint.xPosition, (float)firstPoint.yPosition, positionZ );
1500                                 }
1501                                 else
1502                                 {
1503                                         cubePoints[0] = new Point3D( (float)firstPoint.xPosition, (float)firstPoint.yPosition, positionZ + depth );
1504                                         cubePoints[1] = new Point3D( (float)firstPoint.xPosition, (float)firstPoint.yPosition, positionZ );
1505                                         cubePoints[2] = new Point3D( (float)secondPoint.xPosition, (float)secondPoint.yPosition, positionZ );
1506                                 }
1507                         }
1508
1509                         // Tranform coordinates 
1510                         matrix.TransformPoints( cubePoints );
1511
1512                         // Check the top side visibility
1513                         if(ChartGraphics.IsSurfaceVisible(cubePoints[0],cubePoints[1],cubePoints[2]))
1514                         {
1515                                 visibleSurfaces |= SurfaceNames.Top;
1516                         }
1517
1518
1519                         //***********************************************************************
1520                         //** Check Bottom surface visibility
1521                         //***********************************************************************
1522
1523                         // Get bottom surface points
1524                         PointF  thirdPoint, fourthPoint; 
1525                         GetBottomPointsPosition(area.Common, area, 0, ref firstPoint, ref secondPoint, out thirdPoint, out fourthPoint);
1526
1527
1528                         // If Bottom surface visibility in bounding rectangle - do not gurantee angled linde visibility
1529                         if( (visibleSurfaces & SurfaceNames.Bottom) == SurfaceNames.Bottom)
1530                         {
1531                                 visibleSurfaces ^= SurfaceNames.Bottom;
1532                         }
1533
1534                         // Create top surface coordinates in 3D space
1535                         cubePoints = new Point3D[3];
1536             if (!area.ReverseSeriesOrder)
1537                         {
1538                                 if(!upSideDown && firstPoint.xPosition < secondPoint.xPosition ||
1539                                         upSideDown && firstPoint.xPosition > secondPoint.xPosition)
1540                                 {
1541                                         cubePoints[0] = new Point3D( (float)firstPoint.xPosition, (float)thirdPoint.Y, positionZ + depth );
1542                                         cubePoints[1] = new Point3D( (float)firstPoint.xPosition, (float)thirdPoint.Y, positionZ );
1543                                         cubePoints[2] = new Point3D( (float)secondPoint.xPosition, (float)fourthPoint.Y, positionZ );
1544                                 }
1545                                 else
1546                                 {
1547                                         cubePoints[0] = new Point3D( (float)secondPoint.xPosition, (float)fourthPoint.Y, positionZ + depth );
1548                                         cubePoints[1] = new Point3D( (float)secondPoint.xPosition, (float)fourthPoint.Y, positionZ );
1549                                         cubePoints[2] = new Point3D( (float)firstPoint.xPosition, (float)thirdPoint.Y, positionZ );
1550                                 }
1551                         }
1552                         else
1553                         {
1554                                 if(!upSideDown && secondPoint.xPosition < firstPoint.xPosition ||
1555                                         upSideDown && secondPoint.xPosition > firstPoint.xPosition)
1556                                 {
1557                                         cubePoints[0] = new Point3D( (float)secondPoint.xPosition, (float)fourthPoint.Y, positionZ + depth );
1558                                         cubePoints[1] = new Point3D( (float)secondPoint.xPosition, (float)fourthPoint.Y, positionZ );
1559                                         cubePoints[2] = new Point3D( (float)firstPoint.xPosition, (float)thirdPoint.Y, positionZ );
1560                                 }
1561                                 else
1562                                 {
1563                                         cubePoints[0] = new Point3D( (float)firstPoint.xPosition, (float)thirdPoint.Y, positionZ + depth );
1564                                         cubePoints[1] = new Point3D( (float)firstPoint.xPosition, (float)thirdPoint.Y, positionZ );
1565                                         cubePoints[2] = new Point3D( (float)secondPoint.xPosition, (float)fourthPoint.Y, positionZ );
1566                                 }
1567                         }
1568
1569                         // Tranform coordinates 
1570                         matrix.TransformPoints( cubePoints );
1571
1572                         // Check the top side visibility
1573                         if(ChartGraphics.IsSurfaceVisible(cubePoints[2],cubePoints[1],cubePoints[0]))
1574                         {
1575                                 visibleSurfaces |= SurfaceNames.Bottom;
1576                         }
1577
1578                 }
1579
1580                 /// <summary>
1581                 /// Gets position ob the bottom points in area chart.
1582                 /// </summary>
1583                 /// <param name="common">Chart common elements.</param>
1584                 /// <param name="area">Chart area the series belongs to.</param>
1585                 /// <param name="axisPosition">Axis position.</param>
1586                 /// <param name="firstPoint">First top point coordinates.</param>
1587                 /// <param name="secondPoint">Second top point coordinates.</param>
1588                 /// <param name="thirdPoint">Returns third bottom point coordinates.</param>
1589                 /// <param name="fourthPoint">Returns fourth bottom point coordinates.</param>
1590                 protected virtual void GetBottomPointsPosition(
1591                         CommonElements common, 
1592                         ChartArea area, 
1593                         float axisPosition, 
1594                         ref DataPoint3D firstPoint, 
1595                         ref DataPoint3D secondPoint, 
1596                         out PointF thirdPoint, 
1597                         out PointF fourthPoint)
1598                 {
1599                         // Set active vertical axis
1600                         Axis    vAxis = area.GetAxis(AxisName.Y, firstPoint.dataPoint.series.YAxisType, firstPoint.dataPoint.series.YSubAxisName);
1601
1602                         // Initialize points using second Y value
1603                         float secondYValue = (float)vAxis.GetPosition(firstPoint.dataPoint.YValues[1]);
1604                         thirdPoint = new PointF((float)firstPoint.xPosition, secondYValue);
1605                         secondYValue = (float)vAxis.GetPosition(secondPoint.dataPoint.YValues[1]);
1606                         fourthPoint = new PointF((float)secondPoint.xPosition, secondYValue);
1607
1608                         // Check if "forced" Y values where set
1609                         if(!float.IsNaN(_thirdPointY2Value))
1610                         {
1611                                 thirdPoint.Y = _thirdPointY2Value;
1612                                 
1613                         }
1614                         if(!float.IsNaN(_fourthPointY2Value))
1615                         {
1616                                 fourthPoint.Y = _fourthPointY2Value;
1617                         }
1618                 }
1619
1620                 /// <summary>
1621                 /// Draws a 3D polygon defined by 4 points in 2D space.
1622                 /// Top and Bottom lines are drawn as splines of specified tension.
1623                 /// </summary>
1624                 /// <param name="area">Chart area reference.</param>
1625                 /// <param name="graph">Chart graphics.</param>
1626                 /// <param name="positionZ">Z position of the back side of the 3D surface.</param>
1627                 /// <param name="backColor">Color of rectangle</param>
1628                 /// <param name="borderColor">Border Color</param>
1629                 /// <param name="borderWidth">Border Width</param>
1630                 /// <param name="firstPoint">First point.</param>
1631                 /// <param name="secondPoint">Second point.</param>
1632                 /// <param name="thirdPoint">Third point.</param>
1633                 /// <param name="fourthPoint">Fourth point.</param>
1634                 /// <param name="points">Array of points.</param>
1635                 /// <param name="tension">Line tension.</param>
1636                 /// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
1637                 /// <param name="lineSegmentType">AxisName of line segment. Used for step lines and splines.</param>
1638                 /// <param name="forceThinBorder">Thin border will be drawn on all segments.</param>
1639                 /// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
1640                 internal GraphicsPath Draw3DSplinePolygon( 
1641                         ChartGraphics graph, 
1642                         ChartArea area,
1643                         float positionZ, 
1644                         Color backColor, 
1645                         Color borderColor, 
1646                         int borderWidth, 
1647                         DataPoint3D     firstPoint,
1648                         DataPoint3D     secondPoint, 
1649                         DataPoint3D     thirdPoint,
1650                         DataPoint3D     fourthPoint, 
1651                         ArrayList points,
1652                         float tension,
1653                         DrawingOperationTypes operationType,
1654                         LineSegmentType lineSegmentType,
1655                         bool forceThinBorder)
1656                 {
1657                         // Check tension parameter
1658                         if(tension == 0f)
1659                         {
1660                                 SurfaceNames    thinBorderSides = 0;
1661                                 if(forceThinBorder)
1662                                 {
1663                                         thinBorderSides = SurfaceNames.Left | SurfaceNames.Right;
1664                                 }
1665
1666                                 return graph.Draw3DPolygon( area, area.matrix3D, SurfaceNames.Front, positionZ, 
1667                                         backColor, borderColor, borderWidth, 
1668                                         firstPoint, secondPoint, thirdPoint, fourthPoint, 
1669                                         operationType, lineSegmentType, thinBorderSides);
1670                         }
1671
1672                         // Create graphics path for selection
1673                         bool    drawElements = ((operationType & DrawingOperationTypes.DrawElement) == DrawingOperationTypes.DrawElement);
1674                         GraphicsPath    resultPath = new GraphicsPath();
1675
1676                         //**********************************************************************
1677                         //** Prepare, transform polygon coordinates
1678                         //**********************************************************************
1679
1680                         // Get top line path
1681                         GraphicsPath    topLine = graph.GetSplineFlattenPath(
1682                                 area, positionZ, 
1683                                 firstPoint, secondPoint, points, tension, false, true, 0);
1684
1685                         // Get bottom line path
1686                         GraphicsPath    bottomLine = graph.GetSplineFlattenPath(
1687                                 area, positionZ, 
1688                                 thirdPoint, fourthPoint, points, tension, false, true, 1);
1689
1690                         // Add paths to the result path
1691                         resultPath.AddPath(topLine, true);
1692                         resultPath.AddPath(bottomLine, true);
1693                         resultPath.CloseAllFigures();
1694
1695
1696                         //**********************************************************************
1697                         //** Define drawing colors
1698                         //**********************************************************************
1699
1700                         // Define 3 points polygon
1701                         Point3D [] points3D = new Point3D[3];
1702                         points3D[0] = new Point3D((float)firstPoint.xPosition, (float)firstPoint.yPosition, positionZ);
1703                         points3D[1] = new Point3D((float)secondPoint.xPosition, (float)secondPoint.yPosition, positionZ);
1704                         points3D[2] = new Point3D((float)thirdPoint.xPosition, (float)thirdPoint.yPosition, positionZ);
1705
1706                         // Transform coordinates
1707                         area.matrix3D.TransformPoints( points3D );
1708
1709                         // Get colors
1710                         bool topIsVisible = ChartGraphics.IsSurfaceVisible( points3D[0], points3D[1], points3D[2]);
1711             Color polygonColor = area.matrix3D.GetPolygonLight(points3D, backColor, topIsVisible, area.Area3DStyle.Rotation, SurfaceNames.Front, area.ReverseSeriesOrder);
1712                         Color   surfaceBorderColor = borderColor;
1713                         if(surfaceBorderColor == Color.Empty)
1714                         {
1715                                 // If border color is emty use color slightly darker than main back color
1716                                 surfaceBorderColor = ChartGraphics.GetGradientColor( backColor, Color.Black, 0.2 );
1717                         }
1718
1719                         //**********************************************************************
1720                         //** Draw elements if required.
1721                         //**********************************************************************
1722                         Pen thickBorderPen = null;
1723                         if(drawElements)
1724                         {
1725                                 // Remember SmoothingMode and turn off anti aliasing
1726                                 SmoothingMode oldSmoothingMode = graph.SmoothingMode;
1727                                 graph.SmoothingMode = SmoothingMode.Default;
1728
1729                                 // Draw the polygon
1730                 using (Brush brush = new SolidBrush(polygonColor))
1731                 {
1732                     graph.FillPath(brush, resultPath);
1733                 }
1734
1735                                 // Return old smoothing mode
1736                                 graph.SmoothingMode = oldSmoothingMode;
1737                         
1738                                 // Draw thin polygon border of darker color around the whole polygon
1739                                 if(forceThinBorder)
1740                                 {
1741                                         graph.DrawPath(new Pen(surfaceBorderColor, 1), resultPath);
1742                                 }
1743                                 else if(polygonColor.A == 255)
1744                                 {
1745                                         graph.DrawPath(new Pen(polygonColor, 1), resultPath);
1746                                 }
1747
1748                                 // Create thick border line pen
1749                                 thickBorderPen = new Pen(surfaceBorderColor, borderWidth);
1750                                 thickBorderPen.StartCap = LineCap.Round;
1751                                 thickBorderPen.EndCap = LineCap.Round;
1752
1753                                 // Draw thick Top & Bottom lines
1754                                 graph.DrawPath(thickBorderPen, topLine);
1755                                 graph.DrawPath(thickBorderPen, bottomLine);
1756
1757                                 // Draw thick Right & Left lines on first & last segments of the line
1758                                 if(lineSegmentType == LineSegmentType.First)
1759                                 {
1760                                         graph.DrawLine(thickBorderPen, topLine.PathPoints[0], bottomLine.GetLastPoint());
1761                                 
1762                                 }
1763                                 else if(lineSegmentType == LineSegmentType.Last)
1764                                 {
1765                                         graph.DrawLine(thickBorderPen, topLine.GetLastPoint(), bottomLine.PathPoints[0]);                                       
1766                                 }
1767                         }
1768
1769
1770                         // Calculate path for selection
1771                         if(resultPath != null && thickBorderPen != null)
1772                         {
1773                                 // Widen result path
1774                                 try
1775                                 {
1776                                         resultPath.Widen(thickBorderPen);
1777                                 }
1778                 catch (OutOfMemoryException)
1779                 {
1780                     // GraphicsPath.Widen incorrectly throws OutOfMemoryException
1781                     // catching here and reacting by not widening
1782                 }
1783                 catch (ArgumentException)
1784                 {
1785                 }
1786                         }
1787
1788                         return resultPath;
1789                 }
1790
1791         #endregion
1792
1793         #region IDisposable overrides
1794         /// <summary>
1795         /// Releases unmanaged and - optionally - managed resources
1796         /// </summary>
1797         /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
1798         protected override void Dispose(bool disposing)
1799         {
1800             if (disposing)
1801             {
1802                 // Dispose managed resources
1803                 if (this.areaBottomPath != null)
1804                 {
1805                     this.areaBottomPath.Dispose();
1806                     this.areaBottomPath = null;
1807                 }
1808                 if (this.areaPath != null)
1809                 {
1810                     this.areaPath.Dispose();
1811                     this.areaPath = null;
1812                 }
1813             }
1814             base.Dispose(disposing);
1815         }
1816         #endregion
1817
1818         }
1819 }
1820