1 //-------------------------------------------------------------
2 // <copyright company=�Microsoft Corporation�>
3 //   Copyright � Microsoft Corporation. All Rights Reserved.
4 // </copyright>
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
8 //  File:		ChartGraphics3D.cs
9 //
10 //  Namespace:	System.Web.UI.WebControls[Windows.Forms].Charting
11 //
12 //	Classes:	ChartGraphics3D, Point3D
13 //
14 //  Purpose:	ChartGraphics3D class is 3D chart rendering engine.
15 //              All chart 3D shapes are drawn in specific order so
16 //              that correct Z order of all shapes is achieved. 3D
17 //              graphics engine do not support shapes intersection.
18 //              3D shapes are transformed into one or more 2D shapes
19 //              and then drawn with 2D chart graphics engine.
20 //
21 //	Reviewed:	AG - Microsoft 16, 2007
22 //
23 //===================================================================
24 
25 #region Used namespaces
26 
27 using System;
28 using System.Drawing;
29 using System.Drawing.Drawing2D;
30 using System.Drawing.Text;
31 using System.Drawing.Imaging;
32 using System.ComponentModel;
33 using System.Collections;
34 using System.Diagnostics.CodeAnalysis;
35 
36 #endregion
37 
38 #if Microsoft_CONTROL
39 	namespace System.Windows.Forms.DataVisualization.Charting
40 #else
41 namespace System.Web.UI.DataVisualization.Charting
42 
43 #endif
44 {
45 	#region 3D enumerations
46 
47 	/// <summary>
48 	/// 3D cube surfaces names.
49 	/// </summary>
50 	[Flags]
51     internal enum SurfaceNames
52 	{
53 		/// <summary>
54 		/// Front.
55 		/// </summary>
56 		Front = 1,
57 		/// <summary>
58 		/// Back.
59 		/// </summary>
60 		Back = 2,
61 		/// <summary>
62 		/// Left.
63 		/// </summary>
64 		Left = 4,
65 		/// <summary>
66 		/// Right.
67 		/// </summary>
68 		Right = 8,
69 		/// <summary>
70 		/// Top.
71 		/// </summary>
72 		Top = 16,
73 		/// <summary>
74 		/// Bottom.
75 		/// </summary>
76 		Bottom = 32
77 	}
78 
79 	/// <summary>
80 	/// This enumeration defines all significant points in a pie
81 	/// slice. Only these points should be transformed for pie
82 	/// chart using Matrix object.
83 	/// </summary>
84     internal enum PiePoints
85 	{
86 		/// <summary>
87 		/// Angle 180 Top point on the arc
88 		/// </summary>
89 		Top180,
90 
91 		/// <summary>
92 		/// Angle 180 Bottom point on the arc
93 		/// </summary>
94 		Bottom180,
95 
96 		/// <summary>
97 		/// Angle 0 Top point on the arc
98 		/// </summary>
99 		Top0,
100 
101 		/// <summary>
102 		/// Angle 0 Bottom point on the arc
103 		/// </summary>
104 		Bottom0,
105 
106 		/// <summary>
107 		/// Top Start Angle point on the arc
108 		/// </summary>
109 		TopStart,
110 
111 		/// <summary>
112 		/// Top End Angle point on the arc
113 		/// </summary>
114 		TopEnd,
115 
116 		/// <summary>
117 		/// Bottom Start Angle point on the arc
118 		/// </summary>
119 		BottomStart,
120 
121 		/// <summary>
122 		/// Bottom End Angle point on the arc
123 		/// </summary>
124 		BottomEnd,
125 
126 		/// <summary>
127 		/// Center Top
128 		/// </summary>
129 		TopCenter,
130 
131 		/// <summary>
132 		/// Center Bottom
133 		/// </summary>
134 		BottomCenter,
135 
136 		/// <summary>
137 		/// Top Label Line
138 		/// </summary>
139 		TopLabelLine,
140 
141 		/// <summary>
142 		/// Top Label Line Out
143 		/// </summary>
144 		TopLabelLineout,
145 
146 		/// <summary>
147 		/// Top Label Center
148 		/// </summary>
149 		TopLabelCenter,
150 
151 		/// <summary>
152 		/// Top Rectangle Top Left Point
153 		/// </summary>
154 		TopRectTopLeftPoint,
155 
156 		/// <summary>
157 		/// Top Rectangle Right Bottom Point
158 		/// </summary>
159 		TopRectBottomRightPoint,
160 
161 		/// <summary>
162 		/// Bottom Rectangle Top Left Point
163 		/// </summary>
164 		BottomRectTopLeftPoint,
165 
166 		/// <summary>
167 		/// Bottom Rectangle Right Bottom Point
168 		/// </summary>
169 		BottomRectBottomRightPoint,
170 
171 		/// <summary>
172 		/// Angle 180 Top point on the Doughnut arc
173 		/// </summary>
174 		DoughnutTop180,
175 
176 		/// <summary>
177 		/// Angle 180 Bottom point on the Doughnut arc
178 		/// </summary>
179 		DoughnutBottom180,
180 
181 		/// <summary>
182 		/// Angle 0 Top point on the Doughnut arc
183 		/// </summary>
184 		DoughnutTop0,
185 
186 		/// <summary>
187 		/// Angle 0 Bottom point on the Doughnut arc
188 		/// </summary>
189 		DoughnutBottom0,
190 
191 		/// <summary>
192 		/// Top Start Angle point on the Doughnut arc
193 		/// </summary>
194 		DoughnutTopStart,
195 
196 		/// <summary>
197 		/// Top End Angle point on the Doughnut arc
198 		/// </summary>
199 		DoughnutTopEnd,
200 
201 		/// <summary>
202 		/// Bottom Start Angle point on the Doughnut arc
203 		/// </summary>
204 		DoughnutBottomStart,
205 
206 		/// <summary>
207 		/// Bottom End Angle point on the Doughnut arc
208 		/// </summary>
209 		DoughnutBottomEnd,
210 
211 		/// <summary>
212 		/// Doughnut Top Rectangle Top Left Point
213 		/// </summary>
214 		DoughnutTopRectTopLeftPoint,
215 
216 		/// <summary>
217 		/// Doughnut Top Rectangle Right Bottom Point
218 		/// </summary>
219 		DoughnutTopRectBottomRightPoint,
220 
221 		/// <summary>
222 		/// Doughnut Bottom Rectangle Top Left Point
223 		/// </summary>
224 		DoughnutBottomRectTopLeftPoint,
225 
226 		/// <summary>
227 		/// Doughnut Bottom Rectangle Right Bottom Point
228 		/// </summary>
229 		DoughnutBottomRectBottomRightPoint,
230 	}
231 
232 
233 	/// <summary>
234 	/// AxisName of drawing operation.
235 	/// </summary>
236 	[Flags]
237     internal enum DrawingOperationTypes
238 	{
239 		/// <summary>
240 		/// Draw element.
241 		/// </summary>
242 		DrawElement = 1,
243 
244 		/// <summary>
245 		/// Calculate element path. (for selection or tooltips)
246 		/// </summary>
247 		CalcElementPath = 2,
248 	}
249 
250 	/// <summary>
251 	/// AxisName of line segment.
252 	/// </summary>
253     internal enum LineSegmentType
254 	{
255 		/// <summary>
256 		/// Only one segment exists.
257 		/// </summary>
258 		Single,
259 
260 		/// <summary>
261 		/// First segment.
262 		/// </summary>
263 		First,
264 
265 		/// <summary>
266 		/// Middle segment.
267 		/// </summary>
268 		Middle,
269 
270 		/// <summary>
271 		/// Last segment.
272 		/// </summary>
273 		Last
274 	}
275 
276 	#endregion
277 
278 	/// <summary>
279 	/// The ChartGraphics class is 3D chart rendering engine. All chart
280 	/// 3D shapes are drawn in specific order so that correct Z order
281 	/// of all shapes is achieved. 3D graphics engine do not support
282 	/// shapes intersection. 3D shapes are transformed into one or
283 	/// more 2D shapes and then drawn with 2D chart graphics engine.
284 	/// </summary>
285     public partial class ChartGraphics
286 	{
287 		#region Fields
288 
289 		/// <summary>
290 		/// Helper field used to store the index of cylinder left/bottom side coordinate.
291 		/// </summary>
292 		private		int _oppLeftBottomPoint = -1;
293 
294 		/// <summary>
295 		/// Helper field used to store the index of cylinder right/top side coordinate.
296 		/// </summary>
297 		private		int _oppRigthTopPoint = -1;
298 
299 		/// <summary>
300 		/// Point of the front line from the previous line segment.
301 		/// </summary>
302 		internal	PointF	frontLinePoint1 = PointF.Empty;
303 
304 		/// <summary>
305 		/// Point of the front line from the previous line segment.
306 		/// </summary>
307 		internal	PointF	frontLinePoint2 = PointF.Empty;
308 
309 		/// <summary>
310 		/// Previous line segment pen.
311 		/// </summary>
312 		internal	Pen		frontLinePen = null;
313 
314 		#endregion
315 
316 		#region 3D Line drawing methods
317 
318         /// <summary>
319         /// Draws grid line in 3D space (on two area scene walls)
320         /// </summary>
321         /// <param name="area">Chart area.</param>
322         /// <param name="color">Line color.</param>
323         /// <param name="width">Line width.</param>
324         /// <param name="style">Line style.</param>
325         /// <param name="point1">First line point.</param>
326         /// <param name="point2">Second line point.</param>
327         /// <param name="horizontal">Indicates that grid line is horizontal</param>
328         /// <param name="common">Common Elements</param>
329         /// <param name="obj">Selected object</param>
Draw3DGridLine( ChartArea area, Color color, int width, ChartDashStyle style, PointF point1, PointF point2, bool horizontal, CommonElements common, object obj )330 		internal void Draw3DGridLine(
331 			ChartArea area,
332 			Color color,
333 			int width,
334 			ChartDashStyle style,
335 			PointF point1,
336 			PointF point2,
337 			bool horizontal,
338 			CommonElements common,
339 			object obj
340 			)
341 		{
342 			float	zPositon = area.IsMainSceneWallOnFront() ? area.areaSceneDepth : 0f;
343 
344             ChartElementType chartElementType = obj is StripLine ? ChartElementType.StripLines : ChartElementType.Gridlines;
345 
346 			// Draw strip line on the back/front wall
347 			((ChartGraphics)this).Draw3DLine(
348 				area.matrix3D,
349 				color, width, style,
350 				new Point3D(point1.X, point1.Y, zPositon),
351 				new Point3D(point2.X, point2.Y, zPositon),
352 				common,
353 				obj,
354                 chartElementType
355 				);
356 
357 			if(horizontal)
358 			{
359 				// Draw strip line on the side wall (left or right)
360 				if(area.IsSideSceneWallOnLeft())
361 				{
362 					point1.X = Math.Min(point1.X, point2.X);
363 				}
364 				else
365 				{
366 					point1.X = Math.Max(point1.X, point2.X);
367 				}
368 
369 				((ChartGraphics)this).Draw3DLine(
370 					area.matrix3D,
371 					color, width, style,
372 					new Point3D(point1.X, point1.Y, 0f),
373 					new Point3D(point1.X, point1.Y, area.areaSceneDepth),
374 					common,
375 					obj,
376 					chartElementType
377 					);
378 
379 			}
380 			else if(area.IsBottomSceneWallVisible())
381 			{
382 				// Draw strip line on the bottom wall (if visible)
383 				point1.Y = Math.Max(point1.Y, point2.Y);
384 
385 				((ChartGraphics)this).Draw3DLine(
386 					area.matrix3D,
387 					color, width, style,
388 					new Point3D(point1.X, point1.Y, 0f),
389 					new Point3D(point1.X, point1.Y, area.areaSceneDepth),
390 					common,
391 					obj,
392                     chartElementType
393 					);
394 			}
395 		}
396 
397 		/// <summary>
398 		/// Draws a line connecting the two specified points.
399 		/// </summary>
400 		/// <param name="matrix">Coordinates transformation matrix.</param>
401 		/// <param name="color">Line color.</param>
402 		/// <param name="width">Line width.</param>
403 		/// <param name="style">Line style.</param>
404 		/// <param name="firstPoint">A Point that represents the first point to connect.</param>
405 		/// <param name="secondPoint">A Point that represents the second point to connect.</param>
406 		/// <param name="common">Common elements</param>
407 		/// <param name="obj">Selected object</param>
408 		/// <param name="type">Selected chart element</param>
Draw3DLine( Matrix3D matrix, Color color, int width, ChartDashStyle style, Point3D firstPoint, Point3D secondPoint, CommonElements common, object obj, ChartElementType type )409 		internal void Draw3DLine(
410 			Matrix3D matrix,
411 			Color color,
412 			int width,
413 			ChartDashStyle style,
414 			Point3D firstPoint,
415 			Point3D secondPoint,
416 			CommonElements common,
417 			object obj,
418 			ChartElementType type
419 			)
420 		{
421 
422 			// Transform coordinates
423 			Point3D [] points = new Point3D[] {firstPoint, secondPoint};
424 			matrix.TransformPoints( points );
425 
426 			// Selection mode
427             if (common.ProcessModeRegions && type != ChartElementType.Nothing)
428 			{
429                 using (GraphicsPath path = new GraphicsPath())
430                 {
431 
432                     if (Math.Abs(points[0].X - points[1].X) > Math.Abs(points[0].Y - points[1].Y))
433                     {
434                         path.AddLine(points[0].X, points[0].Y - 1, points[1].X, points[1].Y - 1);
435                         path.AddLine(points[1].X, points[1].Y + 1, points[0].X, points[0].Y + 1);
436                         path.CloseAllFigures();
437                     }
438                     else
439                     {
440                         path.AddLine(points[0].X - 1, points[0].Y, points[1].X - 1, points[1].Y);
441                         path.AddLine(points[1].X + 1, points[1].Y, points[0].X + 1, points[0].Y);
442                         path.CloseAllFigures();
443 
444                     }
445                     common.HotRegionsList.AddHotRegion(path, true, type, obj);
446                 }
447 			}
448 
449 			if( common.ProcessModePaint )
450 			{
451 				// Draw 2D line in 3D space
452 				((ChartGraphics)this).DrawLineRel(color, width, style, points[0].PointF, points[1].PointF);
453 			}
454 
455 		}
456 
457 		#endregion
458 
459 		#region 3D Pie Drawing methods and enumerations
460 
461 
462 		/// <summary>
463 		/// This method draw and fill four point polygons which
464 		/// represents sides of a pie slice.
465 		/// </summary>
466 		/// <param name="area">Chart Area</param>
467 		/// <param name="inclination">X angle rotation</param>
468 		/// <param name="startAngle">Start Angle of a pie slice</param>
469 		/// <param name="sweepAngle">Sweep angle of a pie slice</param>
470 		/// <param name="points">Significant points of a pie slice</param>
471 		/// <param name="brush">Brush used for fill</param>
472 		/// <param name="pen">Pen used for drawing</param>
473 		/// <param name="doughnut">Chart AxisName is Doughnut</param>
FillPieSides( ChartArea area, float inclination, float startAngle, float sweepAngle, PointF [] points, SolidBrush brush, Pen pen, bool doughnut )474 		internal void FillPieSides(
475 			ChartArea area,
476             float inclination,
477 			float startAngle,
478 			float sweepAngle,
479 			PointF [] points,
480 			SolidBrush brush,
481 			Pen pen,
482 			bool doughnut
483 			)
484 		{
485 
486 			// Create a graphics path
487 			GraphicsPath path = new GraphicsPath();
488 
489 			// Significant Points for Side polygons
490 			PointF topCenter = points[(int)PiePoints.TopCenter];
491 			PointF bottomCenter = points[(int)PiePoints.BottomCenter];
492 			PointF topStart = points[(int)PiePoints.TopStart];
493 			PointF bottomStart = points[(int)PiePoints.BottomStart];
494 			PointF topEnd = points[(int)PiePoints.TopEnd];
495 			PointF bottomEnd = points[(int)PiePoints.BottomEnd];
496 
497 			// For Doughnut
498 			PointF topDoughnutStart = PointF.Empty;
499 			PointF bottomDoughnutStart = PointF.Empty;
500 			PointF topDoughnutEnd = PointF.Empty;
501 			PointF bottomDoughnutEnd = PointF.Empty;
502 
503 			if( doughnut )
504 			{
505 				// For Doughnut
506 				topDoughnutStart = points[(int)PiePoints.DoughnutTopStart];
507 				bottomDoughnutStart = points[(int)PiePoints.DoughnutBottomStart];
508 				topDoughnutEnd = points[(int)PiePoints.DoughnutTopEnd];
509 				bottomDoughnutEnd = points[(int)PiePoints.DoughnutBottomEnd];
510 			}
511 
512 			bool startSide = false;
513 			bool endSide = false;
514 			float endAngle = startAngle + sweepAngle;
515 
516 			// If X angle is negative different side of pie slice is visible.
517             if (inclination > 0)
518 			{
519 				// Enable start or/and the end side
520 				if( startAngle > -90 && startAngle < 90 || startAngle > 270 && startAngle < 450 )
521 				{
522 					startSide = true;
523 				}
524 				if( endAngle >= -180 && endAngle < -90 || endAngle > 90 && endAngle < 270 || endAngle > 450 && endAngle <= 540 )
525 				{
526 					endSide = true;
527 				}
528 			}
529 			else
530 			{
531 				// Enable start or/and the end side
532 				if( startAngle >= -180 && startAngle < -90 || startAngle > 90 && startAngle < 270 || startAngle > 450 && startAngle <= 540 )
533 				{
534 					startSide = true;
535 
536 				}
537 				if( endAngle > -90 && endAngle < 90 || endAngle > 270 && endAngle < 450 )
538 				{
539 					endSide = true;
540 				}
541 			}
542 
543 			if( startSide )
544 			{
545 				// *****************************************
546 				// Draw Start Angle side
547 				// *****************************************
548                 using (path = new GraphicsPath())
549                 {
550 
551                     if (doughnut)
552                     {
553                         // Add Line between The Doughnut Arc and Arc
554                         path.AddLine(topDoughnutStart, topStart);
555 
556                         // Add Line between The Top Start and Bottom Start
557                         path.AddLine(topStart, bottomStart);
558 
559                         // Add Line between The Bottom Start and Doughnut Start
560                         path.AddLine(bottomStart, bottomDoughnutStart);
561 
562                         // Add Line between The Bottom Doughnut Start and The Top Doughnut Start
563                         path.AddLine(bottomDoughnutStart, topDoughnutStart);
564                     }
565                     else
566                     {
567                         // Add Line between The Center and Arc
568                         path.AddLine(topCenter, topStart);
569 
570                         // Add Line between The Top Start and Bottom Start
571                         path.AddLine(topStart, bottomStart);
572 
573                         // Add Line between The Bottom Start and The Center Bottom
574                         path.AddLine(bottomStart, bottomCenter);
575 
576                         // Add Line between The Bottom Center and The Top Center
577                         path.AddLine(bottomCenter, topCenter);
578                     }
579                     // Get surface colors
580                     Color frontLightColor, leftLightColor, topLightColor, backLightColor, rightLightColor, bottomLightColor;
581                     area.matrix3D.GetLight(brush.Color, out frontLightColor, out backLightColor, out leftLightColor, out rightLightColor, out topLightColor, out bottomLightColor);
582 
583                     Color lightColor;
584                     if (area.Area3DStyle.Inclination < 0)
585                     {
586                         lightColor = bottomLightColor;
587                     }
588                     else
589                     {
590                         lightColor = topLightColor;
591                     }
592 
593                     // Draw Path
594                     using (Brush lightBrush = new SolidBrush(lightColor))
595                     {
596                         FillPath(lightBrush, path);
597                     }
598 
599                     DrawGraphicsPath(pen, path);
600                 }
601 			}
602 
603 			if( endSide )
604 			{
605 				// *****************************************
606 				// Draw End Angle side
607 				// *****************************************
608                 using (path = new GraphicsPath())
609                 {
610                     if (doughnut)
611                     {
612                         // Add Line between The Doughnut Arc and Arc
613                         path.AddLine(topDoughnutEnd, topEnd);
614 
615                         // Add Line between The Top End and Bottom End
616                         path.AddLine(topEnd, bottomEnd);
617 
618                         // Add Line between The Bottom End and Doughnut End
619                         path.AddLine(bottomEnd, bottomDoughnutEnd);
620 
621                         // Add Line between The Bottom Doughnut End and The Top Doughnut End
622                         path.AddLine(bottomDoughnutEnd, topDoughnutEnd);
623                     }
624                     else
625                     {
626                         // Add Line between The Center and Arc
627                         path.AddLine(topCenter, topEnd);
628 
629                         // Add Line between The Top End and Bottom End
630                         path.AddLine(topEnd, bottomEnd);
631 
632                         // Add Line between The Bottom End and The Center Bottom
633                         path.AddLine(bottomEnd, bottomCenter);
634 
635                         // Add Line between The Bottom Center and The Top Center
636                         path.AddLine(bottomCenter, topCenter);
637 
638                     }
639 
640                     // Get surface colors
641                     Color frontLightColor, leftLightColor, topLightColor, backLightColor, rightLightColor, bottomLightColor;
642                     area.matrix3D.GetLight(brush.Color, out frontLightColor, out backLightColor, out leftLightColor, out rightLightColor, out topLightColor, out bottomLightColor);
643 
644                     Color lightColor;
645                     if (area.Area3DStyle.Inclination < 0)
646                     {
647                         lightColor = bottomLightColor;
648                     }
649                     else
650                     {
651                         lightColor = topLightColor;
652                     }
653 
654                     // Draw Path
655                     using (Brush lightBrush = new SolidBrush(lightColor))
656                     {
657                         FillPath(lightBrush, path);
658                     }
659 
660                     DrawGraphicsPath(pen, path);
661                 }
662 			}
663 		}
664 
665 		/// <summary>
666 		/// This method Draw and fill pie curves.
667 		/// </summary>
668 		/// <param name="area">Chart area used for drawing</param>
669 		/// <param name="point">Data Point</param>
670 		/// <param name="brush">Graphic Brush used for drawing</param>
671 		/// <param name="pen">Graphic Pen used for drawing</param>
672 		/// <param name="topFirstRectPoint">Rotated bounded rectangle points</param>
673 		/// <param name="topSecondRectPoint">Rotated bounded rectangle points</param>
674 		/// <param name="bottomFirstRectPoint">Rotated bounded rectangle points</param>
675 		/// <param name="bottomSecondRectPoint">Rotated bounded rectangle points</param>
676 		/// <param name="topFirstPoint">Significant pie points</param>
677 		/// <param name="topSecondPoint">Significant pie points</param>
678 		/// <param name="bottomFirstPoint">Significant pie points</param>
679 		/// <param name="bottomSecondPoint">Significant pie points</param>
680 		/// <param name="startAngle">Start pie angle</param>
681 		/// <param name="sweepAngle">End pie angle</param>
682 		/// <param name="pointIndex">Data Point Index</param>
FillPieCurve( ChartArea area, DataPoint point, Brush brush, Pen pen, PointF topFirstRectPoint, PointF topSecondRectPoint, PointF bottomFirstRectPoint, PointF bottomSecondRectPoint, PointF topFirstPoint, PointF topSecondPoint, PointF bottomFirstPoint, PointF bottomSecondPoint, float startAngle, float sweepAngle, int pointIndex )683 		internal void FillPieCurve(
684 			ChartArea area,
685 			DataPoint point,
686 			Brush brush,
687 			Pen pen,
688 			PointF topFirstRectPoint,
689 			PointF topSecondRectPoint,
690 			PointF bottomFirstRectPoint,
691 			PointF bottomSecondRectPoint,
692 			PointF topFirstPoint,
693 			PointF topSecondPoint,
694 			PointF bottomFirstPoint,
695 			PointF bottomSecondPoint,
696 			float startAngle,
697 			float sweepAngle,
698 			int pointIndex
699 			)
700 		{
701 			// Common Elements
702 			CommonElements common = area.Common;
703 
704 			// Create a graphics path
705             using (GraphicsPath path = new GraphicsPath())
706             {
707 
708                 // It is enough to transform only two points from
709                 // rectangle. This code will create RectangleF from
710                 // top left and bottom right points.
711                 RectangleF pieTopRectangle = new RectangleF();
712                 pieTopRectangle.X = topFirstRectPoint.X;
713                 pieTopRectangle.Y = topFirstRectPoint.Y;
714                 pieTopRectangle.Height = topSecondRectPoint.Y - topFirstRectPoint.Y;
715                 pieTopRectangle.Width = topSecondRectPoint.X - topFirstRectPoint.X;
716 
717                 RectangleF pieBottomRectangle = new RectangleF();
718                 pieBottomRectangle.X = bottomFirstRectPoint.X;
719                 pieBottomRectangle.Y = bottomFirstRectPoint.Y;
720                 pieBottomRectangle.Height = bottomSecondRectPoint.Y - bottomFirstRectPoint.Y;
721                 pieBottomRectangle.Width = bottomSecondRectPoint.X - bottomFirstRectPoint.X;
722 
723                 // Angle correction algorithm. After rotation AddArc method should used
724                 // different transformed angles. This method transforms angles.
725                 double angleCorrection = pieTopRectangle.Height / pieTopRectangle.Width;
726 
727                 float endAngle;
728                 endAngle = AngleCorrection(startAngle + sweepAngle, angleCorrection);
729                 startAngle = AngleCorrection(startAngle, angleCorrection);
730 
731                 sweepAngle = endAngle - startAngle;
732 
733                 // Add Line between first points
734                 path.AddLine(topFirstPoint, bottomFirstPoint);
735 
736                 if (pieBottomRectangle.Height <= 0)
737                 {
738                     // If x angle is 0 this arc will be line in projection.
739                     path.AddLine(bottomFirstPoint.X, bottomFirstPoint.Y, bottomSecondPoint.X, bottomSecondPoint.Y);
740                 }
741                 else
742                 {
743                     // Add Arc
744                     path.AddArc(pieBottomRectangle.X, pieBottomRectangle.Y, pieBottomRectangle.Width, pieBottomRectangle.Height, startAngle, sweepAngle);
745                 }
746 
747                 // Add Line between second points
748                 path.AddLine(bottomSecondPoint, topSecondPoint);
749 
750                 if (pieTopRectangle.Height <= 0)
751                 {
752                     // If x angle is 0 this arc will be line in projection.
753                     path.AddLine(topFirstPoint.X, topFirstPoint.Y, topSecondPoint.X, topSecondPoint.Y);
754                 }
755                 else
756                 {
757                     path.AddArc(pieTopRectangle.X, pieTopRectangle.Y, pieTopRectangle.Width, pieTopRectangle.Height, startAngle + sweepAngle, -sweepAngle);
758                 }
759 
760                 if (common.ProcessModePaint)
761                 {
762                     // Drawing Mode
763                     FillPath(brush, path);
764 
765                     if (point.BorderColor != Color.Empty &&
766                         point.BorderWidth > 0 &&
767                         point.BorderDashStyle != ChartDashStyle.NotSet)
768                     {
769                         DrawGraphicsPath(pen, path);
770                     }
771 
772                 }
773                 if (common.ProcessModeRegions)
774                 {
775 
776 
777                     // Check if processing collected data point
778                     if (point.IsCustomPropertySet("_COLLECTED_DATA_POINT"))
779                     {
780                         // Add point to the map area
781                         common.HotRegionsList.AddHotRegion(
782                             (ChartGraphics)this,
783                             path,
784                             false,
785                             point.ReplaceKeywords(point.ToolTip),
786 #if Microsoft_CONTROL
787 						string.Empty,
788 						string.Empty,
789 						string.Empty,
790 #else // Microsoft_CONTROL
791  point.ReplaceKeywords(point.Url),
792                             point.ReplaceKeywords(point.MapAreaAttributes),
793                             point.ReplaceKeywords(point.PostBackValue),
794 #endif // Microsoft_CONTROL
795  point,
796                             ChartElementType.DataPoint);
797 
798                         return;
799                     }
800 
801 
802 
803                     common.HotRegionsList.AddHotRegion(
804                         path,
805                         false,
806                         (ChartGraphics)this,
807                         point,
808                         point.series.Name,
809                         pointIndex);
810                 }
811             }
812 		}
813 
814 		/// <summary>
815 		/// This method draws projection of 3D pie slice.
816 		/// </summary>
817 		/// <param name="area">Chart area used for drawing</param>
818 		/// <param name="point">Data point which creates this pie slice</param>
819 		/// <param name="brush">Graphic Brush used for drawing</param>
820 		/// <param name="pen">Graphic Pen used for drawing</param>
821 		/// <param name="firstRectPoint">The first point of transformed bounding rectangle</param>
822 		/// <param name="firstPoint">The first arc point of pie slice</param>
823 		/// <param name="secondRectPoint">The second point of transformed bounding rectangle</param>
824 		/// <param name="secondPoint">The second arc point of pie slice</param>
825 		/// <param name="center">The center point of pie slice</param>
826 		/// <param name="startAngle">Start angle of pie slice</param>
827 		/// <param name="sweepAngle">The end angle of pie slice</param>
828 		/// <param name="fill">Fill pie slice with brush</param>
829 		/// <param name="pointIndex"></param>
FillPieSlice( ChartArea area, DataPoint point, SolidBrush brush, Pen pen, PointF firstRectPoint, PointF firstPoint, PointF secondRectPoint, PointF secondPoint, PointF center, float startAngle, float sweepAngle, bool fill, int pointIndex )830 		internal void FillPieSlice( ChartArea area, DataPoint point, SolidBrush brush, Pen pen, PointF firstRectPoint, PointF firstPoint, PointF secondRectPoint, PointF secondPoint, PointF center, float startAngle, float sweepAngle, bool fill, int pointIndex )
831 		{
832 			// Common elements
833 			CommonElements common = area.Common;
834 
835 			// Create a graphics path
836             using (GraphicsPath path = new GraphicsPath())
837             {
838 
839                 // It is enough to transform only two points from
840                 // rectangle. This code will create RectangleF from
841                 // top left and bottom right points.
842                 RectangleF pieRectangle = new RectangleF();
843                 pieRectangle.X = firstRectPoint.X;
844                 pieRectangle.Y = firstRectPoint.Y;
845                 pieRectangle.Height = secondRectPoint.Y - firstRectPoint.Y;
846                 pieRectangle.Width = secondRectPoint.X - firstRectPoint.X;
847 
848                 // Angle correction algorithm. After rotation AddArc method should used
849                 // different transformed angles. This method transforms angles.
850                 double angleCorrection = pieRectangle.Height / pieRectangle.Width;
851 
852                 float endAngle;
853                 endAngle = AngleCorrection(startAngle + sweepAngle, angleCorrection);
854                 startAngle = AngleCorrection(startAngle, angleCorrection);
855 
856                 sweepAngle = endAngle - startAngle;
857 
858                 // Add Line between The Center and Arc
859                 path.AddLine(center, firstPoint);
860 
861                 // Add Arc
862                 if (pieRectangle.Height > 0)
863                 {
864                     // If x angle is 0 this arc will be line in projection.
865                     path.AddArc(pieRectangle.X, pieRectangle.Y, pieRectangle.Width, pieRectangle.Height, startAngle, sweepAngle);
866                 }
867 
868                 // Add Line between the end of the arc and the centre.
869                 path.AddLine(secondPoint, center);
870 
871                 if (common.ProcessModePaint)
872                 {
873                     // Get surface colors
874                     Color frontLightColor, leftLightColor, topLightColor, backLightColor, rightLightColor, bottomLightColor;
875                     area.matrix3D.GetLight(brush.Color, out frontLightColor, out backLightColor, out leftLightColor, out rightLightColor, out topLightColor, out bottomLightColor);
876 
877                     Pen newPen = (Pen)pen.Clone();
878 
879                     if (area.Area3DStyle.LightStyle == LightStyle.Realistic && point.BorderColor == Color.Empty)
880                     {
881                         newPen.Color = frontLightColor;
882                     }
883 
884                     // Drawing Mode
885                     if (fill)
886                     {
887                         using (Brush lightBrush = new SolidBrush(frontLightColor))
888                         {
889                             FillPath(lightBrush, path);
890                         }
891                     }
892 
893                     if (point.BorderColor != Color.Empty &&
894                         point.BorderWidth > 0 &&
895                         point.BorderDashStyle != ChartDashStyle.NotSet)
896                     {
897                         DrawGraphicsPath(newPen, path);
898                     }
899                 }
900 
901                 if (common.ProcessModeRegions && fill)
902                 {
903 
904 
905                     // Check if processing collected data point
906                     if (point.IsCustomPropertySet("_COLLECTED_DATA_POINT"))
907                     {
908                         // Add point to the map area
909                         common.HotRegionsList.AddHotRegion(
910                             (ChartGraphics)this,
911                             path,
912                             false,
913                             point.ReplaceKeywords(point.ToolTip),
914 #if Microsoft_CONTROL
915 						string.Empty,
916 						string.Empty,
917 						string.Empty,
918 #else // Microsoft_CONTROL
919                             point.ReplaceKeywords(point.Url),
920                             point.ReplaceKeywords(point.MapAreaAttributes),
921                             point.ReplaceKeywords(point.PostBackValue),
922 #endif // Microsoft_CONTROL
923                             point,
924                             ChartElementType.DataPoint);
925 
926                         return;
927                     }
928 
929 
930 
931                     common.HotRegionsList.AddHotRegion(path, false, (ChartGraphics)this, point, point.series.Name, pointIndex);
932                 }
933             }
934 		}
935 
936 		/// <summary>
937 		/// This method draws projection of 3D pie slice.
938 		/// </summary>
939 		/// <param name="area">Chart area used for drawing</param>
940 		/// <param name="point">Data point which creates this Doughnut slice</param>
941 		/// <param name="brush">Graphic Brush used for drawing</param>
942 		/// <param name="pen">Graphic Pen used for drawing</param>
943 		/// <param name="firstRectPoint">The first point of transformed bounding rectangle</param>
944 		/// <param name="firstPoint">The first arc point of Doughnut slice</param>
945 		/// <param name="secondRectPoint">The second point of transformed bounding rectangle</param>
946 		/// <param name="secondPoint">The second arc point of Doughnut slice</param>
947 		/// <param name="threePoint">The three point of Doughnut slice</param>
948 		/// <param name="fourPoint">The four point of Doughnut slice</param>
949 		/// <param name="startAngle">Start angle of Doughnut slice</param>
950 		/// <param name="sweepAngle">The end angle of Doughnut slice</param>
951 		/// <param name="fill">Fill Doughnut slice with brush</param>
952 		/// <param name="doughnutRadius">Radius for doughnut chart</param>
953 		/// <param name="pointIndex">Data Point Index</param>
FillDoughnutSlice( ChartArea area, DataPoint point, SolidBrush brush, Pen pen, PointF firstRectPoint, PointF firstPoint, PointF secondRectPoint, PointF secondPoint, PointF threePoint, PointF fourPoint, float startAngle, float sweepAngle, bool fill, float doughnutRadius, int pointIndex )954 		internal void FillDoughnutSlice( ChartArea area, DataPoint point, SolidBrush brush, Pen pen, PointF firstRectPoint, PointF firstPoint, PointF secondRectPoint, PointF secondPoint, PointF threePoint, PointF fourPoint, float startAngle, float sweepAngle, bool fill, float doughnutRadius, int pointIndex )
955 		{
956 			// Common Elements
957 			CommonElements common = area.Common;
958 
959 			doughnutRadius = 1F - doughnutRadius / 100F;
960 
961 			// Create a graphics path
962             using (GraphicsPath path = new GraphicsPath())
963             {
964 
965                 // It is enough to transform only two points from
966                 // rectangle. This code will create RectangleF from
967                 // top left and bottom right points.
968                 RectangleF pieRectangle = new RectangleF();
969                 pieRectangle.X = firstRectPoint.X;
970                 pieRectangle.Y = firstRectPoint.Y;
971                 pieRectangle.Height = secondRectPoint.Y - firstRectPoint.Y;
972                 pieRectangle.Width = secondRectPoint.X - firstRectPoint.X;
973 
974                 RectangleF pieDoughnutRectangle = new RectangleF();
975                 pieDoughnutRectangle.X = pieRectangle.X + pieRectangle.Width * (1F - doughnutRadius) / 2F;
976                 pieDoughnutRectangle.Y = pieRectangle.Y + pieRectangle.Height * (1F - doughnutRadius) / 2F;
977                 pieDoughnutRectangle.Height = pieRectangle.Height * doughnutRadius;
978                 pieDoughnutRectangle.Width = pieRectangle.Width * doughnutRadius;
979 
980                 // Angle correction algorithm. After rotation AddArc method should used
981                 // different transformed angles. This method transforms angles.
982                 double angleCorrection = pieRectangle.Height / pieRectangle.Width;
983 
984                 float endAngle;
985                 endAngle = AngleCorrection(startAngle + sweepAngle, angleCorrection);
986                 startAngle = AngleCorrection(startAngle, angleCorrection);
987 
988                 sweepAngle = endAngle - startAngle;
989 
990                 // Add Line between The Doughnut Arc and Arc
991                 path.AddLine(fourPoint, firstPoint);
992 
993                 // Add Arc
994                 if (pieRectangle.Height > 0)
995                 {
996                     // If x angle is 0 this arc will be line in projection.
997                     path.AddArc(pieRectangle.X, pieRectangle.Y, pieRectangle.Width, pieRectangle.Height, startAngle, sweepAngle);
998                 }
999 
1000                 // Add Line between the end of the arc and The Doughnut Arc.
1001                 path.AddLine(secondPoint, threePoint);
1002 
1003                 // Add Doughnut Arc
1004                 if (pieDoughnutRectangle.Height > 0)
1005                 {
1006                     path.AddArc(pieDoughnutRectangle.X, pieDoughnutRectangle.Y, pieDoughnutRectangle.Width, pieDoughnutRectangle.Height, startAngle + sweepAngle, -sweepAngle);
1007                 }
1008 
1009                 if (common.ProcessModePaint)
1010                 {
1011                     // Get surface colors
1012                     Color frontLightColor, leftLightColor, topLightColor, backLightColor, rightLightColor, bottomLightColor;
1013                     area.matrix3D.GetLight(brush.Color, out frontLightColor, out backLightColor, out leftLightColor, out rightLightColor, out topLightColor, out bottomLightColor);
1014 
1015                     Pen newPen = (Pen)pen.Clone();
1016 
1017                     if (area.Area3DStyle.LightStyle == LightStyle.Realistic && point.BorderColor == Color.Empty)
1018                     {
1019                         newPen.Color = frontLightColor;
1020                     }
1021 
1022                     // Drawing Mode
1023                     if (fill)
1024                     {
1025                         using (Brush lightBrush = new SolidBrush(frontLightColor))
1026                         {
1027                             FillPath(lightBrush, path);
1028                         }
1029                     }
1030 
1031                     if (point.BorderColor != Color.Empty &&
1032                         point.BorderWidth > 0 &&
1033                         point.BorderDashStyle != ChartDashStyle.NotSet)
1034                     {
1035                         DrawGraphicsPath(newPen, path);
1036                     }
1037                 }
1038 
1039                 if (common.ProcessModeRegions && fill)
1040                 {
1041 
1042 
1043                     // Check if processing collected data point
1044                     if (point.IsCustomPropertySet("_COLLECTED_DATA_POINT"))
1045                     {
1046                         // Add point to the map area
1047                         common.HotRegionsList.AddHotRegion(
1048                             (ChartGraphics)this,
1049                             path,
1050                             false,
1051                             point.ReplaceKeywords(point.ToolTip),
1052 #if Microsoft_CONTROL
1053 						string.Empty,
1054 						string.Empty,
1055 						string.Empty,
1056 #else // Microsoft_CONTROL
1057                             point.ReplaceKeywords(point.Url),
1058                             point.ReplaceKeywords(point.MapAreaAttributes),
1059                             point.ReplaceKeywords(point.PostBackValue),
1060 #endif // Microsoft_CONTROL
1061                             point,
1062                             ChartElementType.DataPoint);
1063 
1064                         return;
1065                     }
1066 
1067 
1068 
1069                     // Add points to the map area
1070                     common.HotRegionsList.AddHotRegion(
1071                         path,
1072                         false,
1073                         (ChartGraphics)this,
1074                         point,
1075                         point.series.Name,
1076                         pointIndex);
1077                 }
1078             }
1079 		}
1080 
1081 		/// <summary>
1082 		/// Draw Graphics Path. This method is introduced because of
1083 		/// bug in DrawPath method when Pen Width is bigger then 1.
1084 		/// </summary>
1085 		/// <param name="pen">Pen</param>
1086 		/// <param name="path">Graphics Path</param>
DrawGraphicsPath( Pen pen, GraphicsPath path )1087 		private void DrawGraphicsPath( Pen pen, GraphicsPath path )
1088 		{
1089 			// Normal case. Very fast Drawing.
1090 			if( pen.Width < 2 )
1091 			{
1092 				DrawPath( pen, path );
1093 			}
1094 			else
1095 			{
1096 
1097 				// Converts each curve in this path into a sequence
1098 				// of connected line segments. Slow Drawing.
1099 				path.Flatten();
1100 
1101 				// Set Pen cap
1102 				pen.EndCap = LineCap.Round;
1103 				pen.StartCap = LineCap.Round;
1104 
1105 				PointF [] pathPoints;
1106 
1107 				pathPoints = path.PathPoints;
1108 
1109 				// Draw any segment as a line.
1110 				for( int point = 0; point < path.PathPoints.Length - 1; point++ )
1111 				{
1112 					PointF [] points;
1113 
1114 					points = new PointF[2];
1115 					points[0] = pathPoints[point];
1116 					points[1] = pathPoints[point+1];
1117 
1118 					DrawLine( pen, points[0], points[1] );
1119 				}
1120 			}
1121 		}
1122 
1123 		/// <summary>
1124 		/// Angle correction algorithm. After rotation different
1125 		/// transformed angle should be used. This method transforms angles.
1126 		/// </summary>
1127 		/// <param name="angle">Not transformed angle</param>
1128 		/// <param name="correction">Correction of bounding rectangle (change between width and height)</param>
1129 		/// <returns>Transformed angle</returns>
AngleCorrection( float angle, double correction )1130 		private float AngleCorrection( float angle, double correction )
1131 		{
1132 			// Make all angles to be between -90 and 90.
1133 			if( angle > -90 && angle < 90 )
1134 			{
1135 				angle = (float)(Math.Atan( Math.Tan( ( angle ) * Math.PI / 180 ) * correction ) * 180 / Math.PI);
1136 			}
1137 			else if( angle > -270 && angle < -90 )
1138 			{
1139 				angle = angle + 180;
1140 				angle = (float)(Math.Atan( Math.Tan( ( angle ) * Math.PI / 180 ) * correction ) * 180 / Math.PI);
1141 				angle = angle - 180;
1142 			}
1143 			else if( angle > 90 && angle < 270 )
1144 			{
1145 				angle = angle - 180;
1146 				angle = (float)(Math.Atan( Math.Tan( ( angle ) * Math.PI / 180 ) * correction ) * 180 / Math.PI);
1147 				angle = angle + 180;
1148 			}
1149 			else if( angle > 270 && angle < 450 )
1150 			{
1151 				angle = angle - 360;
1152 				angle = (float)(Math.Atan( Math.Tan( ( angle ) * Math.PI / 180 ) * correction ) * 180 / Math.PI);
1153 				angle = angle + 360;
1154 			}
1155 			else if( angle > 450 )
1156 			{
1157 				angle = angle - 540;
1158 				angle = (float)(Math.Atan( Math.Tan( ( angle ) * Math.PI / 180 ) * correction ) * 180 / Math.PI);
1159 				angle = angle + 540;
1160 			}
1161 			return angle;
1162 		}
1163 
1164 		#endregion
1165 
1166 		#region 3D Surface drawing methods (used in Line charts)
1167 
1168 		/// <summary>
1169 		/// Draws a 3D polygon defined by 4 points in 2D space.
1170 		/// </summary>
1171 		/// <param name="area">Chart area reference.</param>
1172 		/// <param name="matrix">Coordinates transformation matrix.</param>
1173 		/// <param name="surfaceName">Name of the surface to draw.</param>
1174 		/// <param name="positionZ">Z position of the back side of the 3D surface.</param>
1175 		/// <param name="backColor">Color of rectangle</param>
1176 		/// <param name="borderColor">Border Color</param>
1177 		/// <param name="borderWidth">Border Width</param>
1178 		/// <param name="firstPoint">First point.</param>
1179 		/// <param name="secondPoint">Second point.</param>
1180 		/// <param name="thirdPoint">Third point.</param>
1181 		/// <param name="fourthPoint">Fourth point.</param>
1182 		/// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
1183 		/// <param name="lineSegmentType">AxisName of line segment. Used for step lines and splines.</param>
1184 		/// <param name="thinBorders">Thin border will be drawn on specified sides.</param>
1185 		/// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
Draw3DPolygon( ChartArea area, Matrix3D matrix, SurfaceNames surfaceName, float positionZ, Color backColor, Color borderColor, int borderWidth, DataPoint3D firstPoint, DataPoint3D secondPoint, DataPoint3D thirdPoint, DataPoint3D fourthPoint, DrawingOperationTypes operationType, LineSegmentType lineSegmentType, SurfaceNames thinBorders)1186 		internal GraphicsPath Draw3DPolygon(
1187 			ChartArea area,
1188 			Matrix3D matrix,
1189 			SurfaceNames surfaceName,
1190 			float positionZ,
1191 			Color backColor,
1192 			Color borderColor,
1193 			int borderWidth,
1194 			DataPoint3D	firstPoint,
1195 			DataPoint3D	secondPoint,
1196 			DataPoint3D	thirdPoint,
1197 			DataPoint3D	fourthPoint,
1198 			DrawingOperationTypes operationType,
1199 			LineSegmentType lineSegmentType,
1200 			SurfaceNames thinBorders)
1201 		{
1202 			// Create graphics path for selection
1203 			bool	drawElements = ((operationType & DrawingOperationTypes.DrawElement) == DrawingOperationTypes.DrawElement);
1204 			GraphicsPath	resultPath = ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
1205 				? new GraphicsPath() : null;
1206 
1207 			//**********************************************************************
1208 			//** Prepare, transform polygon coordinates
1209 			//**********************************************************************
1210 
1211 			// Define 4 points polygon
1212 			Point3D [] points3D = new Point3D[4];
1213 			points3D[0] = new Point3D((float)firstPoint.xPosition, (float)firstPoint.yPosition, positionZ);
1214 			points3D[1] = new Point3D((float)secondPoint.xPosition, (float)secondPoint.yPosition, positionZ);
1215 			points3D[2] = new Point3D((float)thirdPoint.xPosition, (float)thirdPoint.yPosition, positionZ);
1216 			points3D[3] = new Point3D((float)fourthPoint.xPosition, (float)fourthPoint.yPosition, positionZ);
1217 
1218 			// Transform coordinates
1219 			matrix.TransformPoints( points3D );
1220 
1221 			// Get absolute coordinates and create array of PointF
1222 			PointF[]	polygonPoints = new PointF[4];
1223 			polygonPoints[0] = GetAbsolutePoint(points3D[0].PointF);
1224 			polygonPoints[1] = GetAbsolutePoint(points3D[1].PointF);
1225 			polygonPoints[2] = GetAbsolutePoint(points3D[2].PointF);
1226 			polygonPoints[3] = GetAbsolutePoint(points3D[3].PointF);
1227 
1228 
1229 			//**********************************************************************
1230 			//** Define drawing colors
1231 			//**********************************************************************
1232 			bool topIsVisible = IsSurfaceVisible( points3D[0], points3D[1], points3D[2]);
1233 			Color polygonColor = matrix.GetPolygonLight( points3D, backColor, topIsVisible, area.Area3DStyle.Rotation, surfaceName, area.ReverseSeriesOrder );
1234 			Color	surfaceBorderColor = borderColor;
1235 			if(surfaceBorderColor == Color.Empty)
1236 			{
1237 				// If border color is emty use color slightly darker than main back color
1238 				surfaceBorderColor = ChartGraphics.GetGradientColor( backColor, Color.Black, 0.2 );
1239 			}
1240 
1241 			//**********************************************************************
1242 			//** Draw elements if required.
1243 			//**********************************************************************
1244 			Pen thickBorderPen = null;
1245 			if(drawElements)
1246 			{
1247 				// Remember SmoothingMode and turn off anti aliasing
1248 				SmoothingMode oldSmoothingMode = SmoothingMode;
1249 				SmoothingMode = SmoothingMode.Default;
1250 
1251 				// Draw the polygon
1252                 using (Brush brush = new SolidBrush(polygonColor))
1253                 {
1254 				    FillPolygon(brush, polygonPoints);
1255                 }
1256 
1257 				// Return old smoothing mode
1258 				SmoothingMode = oldSmoothingMode;
1259 
1260 				// Draw thin polygon border of darker color around the whole polygon
1261 				if(thinBorders != 0)
1262 				{
1263 					Pen thinLinePen = new Pen(surfaceBorderColor, 1);
1264 					if( (thinBorders & SurfaceNames.Left) != 0 )
1265 						DrawLine(thinLinePen, polygonPoints[3], polygonPoints[0]);
1266 					if( (thinBorders & SurfaceNames.Right) != 0 )
1267 						DrawLine(thinLinePen, polygonPoints[1], polygonPoints[2]);
1268 					if( (thinBorders & SurfaceNames.Top) != 0 )
1269 						DrawLine(thinLinePen, polygonPoints[0], polygonPoints[1]);
1270 					if( (thinBorders & SurfaceNames.Bottom) != 0 )
1271 						DrawLine(thinLinePen, polygonPoints[2], polygonPoints[3]);
1272 				}
1273 				else if(polygonColor.A == 255)
1274 				{
1275 					DrawPolygon(new Pen(polygonColor, 1), polygonPoints);
1276 				}
1277 
1278 				// Create thick border line pen
1279 				thickBorderPen = new Pen(surfaceBorderColor, borderWidth);
1280 				thickBorderPen.StartCap = LineCap.Round;
1281 				thickBorderPen.EndCap = LineCap.Round;
1282 
1283 				// Draw thick Top & Bottom lines
1284 				DrawLine(thickBorderPen, polygonPoints[0], polygonPoints[1]);
1285 				DrawLine(thickBorderPen, polygonPoints[2], polygonPoints[3]);
1286 
1287 				// Draw thick Right & Left lines on first & last segments of the line
1288 				if(lineSegmentType == LineSegmentType.First)
1289 				{
1290 					DrawLine(thickBorderPen, polygonPoints[3], polygonPoints[0]);
1291 				}
1292 				else if(lineSegmentType == LineSegmentType.Last)
1293 				{
1294 					DrawLine(thickBorderPen, polygonPoints[1], polygonPoints[2]);
1295 				}
1296 			}
1297 
1298 			//**********************************************************************
1299 			//** Redraw front line of the previuos line segment.
1300 			//**********************************************************************
1301 			if(area.Area3DStyle.Perspective == 0)
1302 			{
1303 				if(frontLinePoint1 != PointF.Empty && frontLinePen != null)
1304 				{
1305 					if( (frontLinePoint1.X == polygonPoints[0].X &&
1306 						frontLinePoint1.Y == polygonPoints[0].Y ||
1307 						frontLinePoint2.X == polygonPoints[1].X &&
1308 						frontLinePoint2.Y == polygonPoints[1].Y ) ||
1309 
1310 						(frontLinePoint1.X == polygonPoints[1].X &&
1311 						frontLinePoint1.Y == polygonPoints[1].Y ||
1312 						frontLinePoint2.X == polygonPoints[0].X &&
1313 						frontLinePoint2.Y == polygonPoints[0].Y ) ||
1314 
1315 						(frontLinePoint1.X == polygonPoints[3].X &&
1316 						frontLinePoint1.Y == polygonPoints[3].Y ||
1317 						frontLinePoint2.X == polygonPoints[2].X &&
1318 						frontLinePoint2.Y == polygonPoints[2].Y) ||
1319 
1320 						(frontLinePoint1.X == polygonPoints[2].X &&
1321 						frontLinePoint1.Y == polygonPoints[2].Y ||
1322 						frontLinePoint2.X == polygonPoints[3].X &&
1323 						frontLinePoint2.Y == polygonPoints[3].Y) )
1324 					{
1325 						// Do not draw the line if it will be overlapped with current
1326 					}
1327 					else
1328 					{
1329 						// Draw line !!!!
1330 						DrawLine(
1331 							frontLinePen,
1332 							(float)Math.Round(frontLinePoint1.X),
1333 							(float)Math.Round(frontLinePoint1.Y),
1334 							(float)Math.Round(frontLinePoint2.X),
1335 							(float)Math.Round(frontLinePoint2.Y) );
1336 					}
1337 
1338 					// Reset line properties
1339 					frontLinePen = null;
1340 					frontLinePoint1 = PointF.Empty;
1341 					frontLinePoint2 = PointF.Empty;
1342 				}
1343 
1344 				//**********************************************************************
1345 				//** Check if front line should be redrawn whith the next segment.
1346 				//**********************************************************************
1347 				if(drawElements)
1348 				{
1349 					// Add top line
1350 					frontLinePen = thickBorderPen;
1351 					frontLinePoint1 = polygonPoints[0];
1352 					frontLinePoint2 = polygonPoints[1];
1353 				}
1354 			}
1355 
1356 			// Calculate path for selection
1357 			if(resultPath != null)
1358 			{
1359 				// Add polygon to the path
1360 				resultPath.AddPolygon(polygonPoints);
1361 			}
1362 
1363 			return resultPath;
1364 		}
1365 
1366 		/// <summary>
1367 		/// Helper method which returns the splines flatten path.
1368 		/// </summary>
1369 		/// <param name="area">Chart area reference.</param>
1370 		/// <param name="positionZ">Z position of the back side of the 3D surface.</param>
1371 		/// <param name="firstPoint">First point.</param>
1372 		/// <param name="secondPoint">Second point.</param>
1373 		/// <param name="points">Array of points.</param>
1374 		/// <param name="tension">Line tension.</param>
1375 		/// <param name="flatten">Flatten result path.</param>
1376 		/// <param name="translateCoordinates">Indicates that points coordinates should be translated.</param>
1377 		/// <param name="yValueIndex">Index of the Y value to use.</param>
1378 		/// <returns>Spline path.</returns>
GetSplineFlattenPath( ChartArea area, float positionZ, DataPoint3D firstPoint, DataPoint3D secondPoint, ArrayList points, float tension, bool flatten, bool translateCoordinates, int yValueIndex)1379 		internal GraphicsPath GetSplineFlattenPath(
1380 			ChartArea area,
1381 			float positionZ,
1382 			DataPoint3D	firstPoint,
1383 			DataPoint3D	secondPoint,
1384 			ArrayList points,
1385 			float tension,
1386 			bool flatten,
1387 			bool translateCoordinates,
1388 			int yValueIndex)
1389 		{
1390 			// Find first spline point index
1391 			int firtsSplinePointIndex = (firstPoint.index < secondPoint.index) ? firstPoint.index : secondPoint.index;
1392 			--firtsSplinePointIndex;
1393 			if(firtsSplinePointIndex >= (points.Count - 2) )
1394 			{
1395 				--firtsSplinePointIndex;
1396 			}
1397 			if(firtsSplinePointIndex < 1)
1398 			{
1399 				firtsSplinePointIndex = 1;
1400 			}
1401 
1402 			// Find four points which are required to draw the spline
1403 			int	pointArrayIndex = int.MinValue;
1404 			DataPoint3D [] splineDataPoints = new DataPoint3D[4];
1405 			splineDataPoints[0] = FindPointByIndex(points, firtsSplinePointIndex, null, ref pointArrayIndex);
1406 			splineDataPoints[1] = FindPointByIndex(points, firtsSplinePointIndex + 1, null, ref pointArrayIndex);
1407 			splineDataPoints[2] = FindPointByIndex(points, firtsSplinePointIndex + 2, null, ref pointArrayIndex);
1408 			splineDataPoints[3] = FindPointByIndex(points, firtsSplinePointIndex + 3, null, ref pointArrayIndex);
1409 
1410 			// Get offset of spline segment in array
1411 			int splineSegmentOffset = 0;
1412 			while(splineSegmentOffset < 4)
1413 			{
1414 				if(splineDataPoints[splineSegmentOffset].index == firstPoint.index ||
1415 					splineDataPoints[splineSegmentOffset].index == secondPoint.index)
1416 				{
1417 					break;
1418 				}
1419 				++splineSegmentOffset;
1420 			}
1421 
1422 			// Get number of found points
1423 			int nonNullPoints = 2;
1424 			if(splineDataPoints[2] != null)
1425 				++nonNullPoints;
1426 			if(splineDataPoints[3] != null)
1427 				++nonNullPoints;
1428 
1429 
1430 			// Get coordinates and create array of PointF for the front spline
1431 			PointF[]	polygonPointsFront = new PointF[nonNullPoints];
1432 			if(yValueIndex == 0)
1433 			{
1434 				polygonPointsFront[0] = new PointF((float)splineDataPoints[0].xPosition, (float)splineDataPoints[0].yPosition);
1435 				polygonPointsFront[1] = new PointF((float)splineDataPoints[1].xPosition, (float)splineDataPoints[1].yPosition);
1436 				if(nonNullPoints > 2)
1437 					polygonPointsFront[2] = new PointF((float)splineDataPoints[2].xPosition, (float)splineDataPoints[2].yPosition);
1438 				if(nonNullPoints > 3)
1439 					polygonPointsFront[3] = new PointF((float)splineDataPoints[3].xPosition, (float)splineDataPoints[3].yPosition);
1440 			}
1441 			else
1442 			{
1443 				// Set active vertical axis
1444 				Axis	vAxis = (firstPoint.dataPoint.series.YAxisType == AxisType.Primary) ? area.AxisY : area.AxisY2;
1445 
1446 				float secondYValue = (float)vAxis.GetPosition(splineDataPoints[0].dataPoint.YValues[yValueIndex]);
1447 				polygonPointsFront[0] = new PointF((float)splineDataPoints[0].xPosition, secondYValue);
1448 				secondYValue = (float)vAxis.GetPosition(splineDataPoints[1].dataPoint.YValues[yValueIndex]);
1449 				polygonPointsFront[1] = new PointF((float)splineDataPoints[1].xPosition, secondYValue);
1450 				if(nonNullPoints > 2)
1451 				{
1452 					secondYValue = (float)vAxis.GetPosition(splineDataPoints[2].dataPoint.YValues[yValueIndex]);
1453 					polygonPointsFront[2] = new PointF((float)splineDataPoints[2].xPosition, secondYValue);
1454 				}
1455 				if(nonNullPoints > 3)
1456 				{
1457 					secondYValue = (float)vAxis.GetPosition(splineDataPoints[3].dataPoint.YValues[yValueIndex]);
1458 					polygonPointsFront[3] = new PointF((float)splineDataPoints[3].xPosition, secondYValue);
1459 				}
1460 			}
1461 
1462 			// Translate points coordinates in 3D space and get absolute coordinate
1463 			if(translateCoordinates)
1464 			{
1465 				// Prepare array of points
1466 				Point3D[]	points3D = new Point3D[nonNullPoints];
1467 				for(int index = 0; index < nonNullPoints; index++)
1468 				{
1469 					points3D[index] = new Point3D(polygonPointsFront[index].X, polygonPointsFront[index].Y, positionZ);
1470 				}
1471 
1472 				// Make coordinates transformation
1473 				area.matrix3D.TransformPoints( points3D );
1474 
1475 				// Get absolute values
1476 				for(int index = 0; index < nonNullPoints; index++)
1477 				{
1478 					polygonPointsFront[index] = GetAbsolutePoint(points3D[index].PointF);
1479 				}
1480 
1481 			}
1482 
1483 			// Create graphics path for the front spline surface and flatten it.
1484 			GraphicsPath	splineSurfacePath = new GraphicsPath();
1485 			splineSurfacePath.AddCurve(polygonPointsFront, splineSegmentOffset, 1, tension);
1486 			if(flatten)
1487 			{
1488 				splineSurfacePath.Flatten();
1489 			}
1490 
1491 			// IsReversed points order
1492 			if(firstPoint.index > secondPoint.index)
1493 			{
1494 				splineSurfacePath.Reverse();
1495 			}
1496 
1497 			return splineSurfacePath;
1498 		}
1499 
1500 		/// <summary>
1501 		/// Draws a 3D spline surface connecting the two specified points in 2D space.
1502 		/// Used to draw Spline based charts.
1503 		/// </summary>
1504 		/// <param name="area">Chart area reference.</param>
1505 		/// <param name="matrix">Coordinates transformation matrix.</param>
1506 		/// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
1507 		/// <param name="surfaceName">Name of the surface to draw.</param>
1508 		/// <param name="positionZ">Z position of the back side of the 3D surface.</param>
1509 		/// <param name="depth">Depth of the 3D surface.</param>
1510 		/// <param name="backColor">Color of rectangle</param>
1511 		/// <param name="borderColor">Border Color</param>
1512 		/// <param name="borderWidth">Border Width</param>
1513 		/// <param name="borderDashStyle">Border Style</param>
1514 		/// <param name="firstPoint">First point.</param>
1515 		/// <param name="secondPoint">Second point.</param>
1516 		/// <param name="points">Array of points.</param>
1517 		/// <param name="pointIndex">Index of point to draw.</param>
1518 		/// <param name="tension">Line tension.</param>
1519 		/// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
1520 		/// <param name="forceThinBorder">Thin border will be drawn on all segments.</param>
1521 		/// <param name="forceThickBorder">Thick border will be drawn on all segments.</param>
1522 		/// <param name="reversedSeriesOrder">Series are drawn in reversed order.</param>
1523 		/// <param name="multiSeries">Multiple series are drawn at the same time.</param>
1524 		/// <param name="yValueIndex">Index of the Y value to use.</param>
1525 		/// <param name="clipInsideArea">Surface should be clipped inside plotting area.</param>
1526 		/// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
Draw3DSplineSurface( ChartArea area, Matrix3D matrix, LightStyle lightStyle, SurfaceNames surfaceName, float positionZ, float depth, Color backColor, Color borderColor, int borderWidth, ChartDashStyle borderDashStyle, DataPoint3D firstPoint, DataPoint3D secondPoint, ArrayList points, int pointIndex, float tension, DrawingOperationTypes operationType, bool forceThinBorder, bool forceThickBorder, bool reversedSeriesOrder, bool multiSeries, int yValueIndex, bool clipInsideArea)1527 		internal GraphicsPath Draw3DSplineSurface(
1528 			ChartArea area,
1529 			Matrix3D matrix,
1530 			LightStyle lightStyle,
1531 			SurfaceNames surfaceName,
1532 			float positionZ,
1533 			float depth,
1534 			Color backColor,
1535 			Color borderColor,
1536 			int borderWidth,
1537 			ChartDashStyle borderDashStyle,
1538 			DataPoint3D	firstPoint,
1539 			DataPoint3D	secondPoint,
1540 			ArrayList points,
1541 			int pointIndex,
1542 			float tension,
1543 			DrawingOperationTypes operationType,
1544 			bool forceThinBorder,
1545 			bool forceThickBorder,
1546 			bool reversedSeriesOrder,
1547 			bool multiSeries,
1548 			int yValueIndex,
1549 			bool clipInsideArea)
1550 		{
1551 			// If zero tension is specified - draw a Line Surface
1552 			if(tension == 0f)
1553 			{
1554 				return Draw3DSurface(
1555 					area,
1556 					matrix,
1557 					lightStyle,
1558 					surfaceName,
1559 					positionZ,
1560 					depth,
1561 					backColor,
1562 					borderColor,
1563 					borderWidth,
1564 					borderDashStyle,
1565 					firstPoint,
1566 					secondPoint,
1567 					points,
1568 					pointIndex,
1569 					tension,
1570 					operationType,
1571 					LineSegmentType.Single,
1572 					forceThinBorder,
1573 					forceThickBorder,
1574 					reversedSeriesOrder,
1575 					multiSeries,
1576 					yValueIndex,
1577 					clipInsideArea);
1578 			}
1579 
1580 			// Create graphics path for selection
1581 			GraphicsPath	resultPath = ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
1582 				? new GraphicsPath() : null;
1583 
1584 			// Get spline flatten path
1585 			GraphicsPath splineSurfacePath = GetSplineFlattenPath(
1586 				area, positionZ,
1587 				firstPoint, secondPoint, points, tension, true, false, yValueIndex);
1588 
1589 			// Check if reversed drawing order required
1590 			bool	reversed = false;
1591 			if((pointIndex + 1) < points.Count)
1592 			{
1593 				DataPoint3D p = (DataPoint3D)points[pointIndex + 1];
1594 				if(p.index == firstPoint.index)
1595 				{
1596 					reversed = true;
1597 				}
1598 			}
1599 
1600 			if(reversed)
1601 			{
1602 				splineSurfacePath.Reverse();
1603 			}
1604 
1605 			// Loop through all segment lines the spline consists off
1606 			PointF[] splinePathPoints = splineSurfacePath.PathPoints;
1607 			DataPoint3D	dp1 = new DataPoint3D();
1608 			DataPoint3D	dp2 = new DataPoint3D();
1609 			LineSegmentType	lineSegmentType = LineSegmentType.Middle;
1610 			for(int pIndex = 1; pIndex < splinePathPoints.Length; pIndex++)
1611 			{
1612 				bool forceSegmentThinBorder = false;
1613 				bool forceSegmentThickBorder = false;
1614 
1615 				// Calculate surface coordinates
1616 				if(!reversed)
1617 				{
1618 					dp1.index = firstPoint.index;
1619 					dp1.dataPoint = firstPoint.dataPoint;
1620 					dp1.xPosition = splinePathPoints[pIndex - 1].X;
1621 					dp1.yPosition = splinePathPoints[pIndex - 1].Y;
1622 
1623 					dp2.index = secondPoint.index;
1624 					dp2.index = secondPoint.index;
1625 					dp2.xPosition = splinePathPoints[pIndex].X;
1626 					dp2.yPosition = splinePathPoints[pIndex].Y;
1627 				}
1628 				else
1629 				{
1630 					dp2.index = firstPoint.index;
1631 					dp2.dataPoint = firstPoint.dataPoint;
1632 					dp2.xPosition = splinePathPoints[pIndex - 1].X;
1633 					dp2.yPosition = splinePathPoints[pIndex - 1].Y;
1634 
1635 					dp1.index = secondPoint.index;
1636 					dp1.dataPoint = secondPoint.dataPoint;
1637 					dp1.xPosition = splinePathPoints[pIndex].X;
1638 					dp1.yPosition = splinePathPoints[pIndex].Y;
1639 				}
1640 
1641 				// Get sefment type
1642 				lineSegmentType = LineSegmentType.Middle;
1643 				if(pIndex == 1)
1644 				{
1645 					if(!reversed)
1646 						lineSegmentType = LineSegmentType.First;
1647 					else
1648 						lineSegmentType = LineSegmentType.Last;
1649 
1650 					forceSegmentThinBorder = forceThinBorder;
1651 					forceSegmentThickBorder = forceThickBorder;
1652 				}
1653 				else if(pIndex == splinePathPoints.Length - 1)
1654 				{
1655 					if(!reversed)
1656 						lineSegmentType = LineSegmentType.Last;
1657 					else
1658 						lineSegmentType = LineSegmentType.First;
1659 
1660 					forceSegmentThinBorder = forceThinBorder;
1661 					forceSegmentThickBorder = forceThickBorder;
1662 				}
1663 
1664 				// Draw flat surface
1665 				GraphicsPath	segmentResultPath = Draw3DSurface(
1666 					area,
1667 					matrix,
1668 					lightStyle,
1669 					surfaceName,
1670 					positionZ,
1671 					depth,
1672 					backColor,
1673 					borderColor,
1674 					borderWidth,
1675 					borderDashStyle,
1676 					dp1,
1677 					dp2,
1678 					points,
1679 					pointIndex,
1680 					0f,
1681 					operationType,
1682 					lineSegmentType,
1683 					forceSegmentThinBorder,
1684 					forceSegmentThickBorder,
1685 					reversedSeriesOrder,
1686 					multiSeries,
1687 					yValueIndex,
1688 					clipInsideArea);
1689 
1690 				// Add selection path
1691 				if(resultPath != null && segmentResultPath != null && segmentResultPath.PointCount > 0)
1692 				{
1693 					resultPath.AddPath(segmentResultPath, true);
1694 				}
1695 
1696 			}
1697 
1698 			return resultPath;
1699 		}
1700 
1701 
1702 		/// <summary>
1703 		/// Draws a 3D surface connecting the two specified points in 2D space.
1704 		/// Used to draw Line based charts.
1705 		/// </summary>
1706 		/// <param name="area">Chart area reference.</param>
1707 		/// <param name="matrix">Coordinates transformation matrix.</param>
1708 		/// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
1709 		/// <param name="surfaceName">Name of the surface to draw.</param>
1710 		/// <param name="positionZ">Z position of the back side of the 3D surface.</param>
1711 		/// <param name="depth">Depth of the 3D surface.</param>
1712 		/// <param name="backColor">Color of rectangle</param>
1713 		/// <param name="borderColor">Border Color</param>
1714 		/// <param name="borderWidth">Border Width</param>
1715 		/// <param name="borderDashStyle">Border Style</param>
1716 		/// <param name="firstPoint">First point.</param>
1717 		/// <param name="secondPoint">Second point.</param>
1718 		/// <param name="points">Array of points.</param>
1719 		/// <param name="pointIndex">Index of point to draw.</param>
1720 		/// <param name="tension">Line tension.</param>
1721 		/// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
1722 		/// <param name="lineSegmentType">AxisName of line segment. Used for step lines and splines.</param>
1723 		/// <param name="forceThinBorder">Thin border will be drawn on all segments.</param>
1724 		/// <param name="forceThickBorder">Thick border will be drawn on all segments.</param>
1725 		/// <param name="reversedSeriesOrder">Series are drawn in reversed order.</param>
1726 		/// <param name="multiSeries">Multiple series are drawn at the same time.</param>
1727 		/// <param name="yValueIndex">Index of the Y value to use.</param>
1728 		/// <param name="clipInsideArea">Surface should be clipped inside plotting area.</param>
1729 		/// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
Draw3DSurface( ChartArea area, Matrix3D matrix, LightStyle lightStyle, SurfaceNames surfaceName, float positionZ, float depth, Color backColor, Color borderColor, int borderWidth, ChartDashStyle borderDashStyle, DataPoint3D firstPoint, DataPoint3D secondPoint, ArrayList points, int pointIndex, float tension, DrawingOperationTypes operationType, LineSegmentType lineSegmentType, bool forceThinBorder, bool forceThickBorder, bool reversedSeriesOrder, bool multiSeries, int yValueIndex, bool clipInsideArea)1730 		internal GraphicsPath Draw3DSurface(
1731 			ChartArea area,
1732 			Matrix3D matrix,
1733 			LightStyle lightStyle,
1734 			SurfaceNames surfaceName,
1735 			float positionZ,
1736 			float depth,
1737 			Color backColor,
1738 			Color borderColor,
1739 			int borderWidth,
1740 			ChartDashStyle borderDashStyle,
1741 			DataPoint3D	firstPoint,
1742 			DataPoint3D	secondPoint,
1743 			ArrayList points,
1744 			int pointIndex,
1745 			float tension,
1746 			DrawingOperationTypes operationType,
1747 			LineSegmentType lineSegmentType,
1748 			bool forceThinBorder,
1749 			bool forceThickBorder,
1750 			bool reversedSeriesOrder,
1751 			bool multiSeries,
1752 			int yValueIndex,
1753 			bool clipInsideArea)
1754 		{
1755 			// If non-zero tension is specified - draw a Spline Surface
1756 			if(tension != 0f)
1757 			{
1758 				return Draw3DSplineSurface(
1759 					area,
1760 					matrix,
1761 					lightStyle,
1762 					surfaceName,
1763 					positionZ,
1764 					depth,
1765 					backColor,
1766 					borderColor,
1767 					borderWidth,
1768 					borderDashStyle,
1769 					firstPoint,
1770 					secondPoint,
1771 					points,
1772 					pointIndex,
1773 					tension,
1774 					operationType,
1775 					forceThinBorder,
1776 					forceThickBorder,
1777 					reversedSeriesOrder,
1778 					multiSeries,
1779 					yValueIndex,
1780 					clipInsideArea);
1781 			}
1782 
1783 			//**********************************************************************
1784 			//** Create graphics path for selection
1785 			//**********************************************************************
1786 			bool	drawElements = ((operationType & DrawingOperationTypes.DrawElement) == DrawingOperationTypes.DrawElement);
1787 			GraphicsPath	resultPath = ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
1788 				? new GraphicsPath() : null;
1789 
1790 			//**********************************************************************
1791 			//** Check surface coordinates
1792 			//**********************************************************************
1793 			if((decimal)firstPoint.xPosition == (decimal)secondPoint.xPosition &&
1794 				(decimal)firstPoint.yPosition == (decimal)secondPoint.yPosition)
1795 			{
1796 				return resultPath;
1797 			}
1798 
1799 			//**********************************************************************
1800 			//** Clip surface
1801 			//**********************************************************************
1802 
1803 			// Check if line between the first and second points intersects with
1804 			// plotting area top or bottom boundary
1805 			if(clipInsideArea)
1806 			{
1807 				//****************************************************************
1808 				//** Round plot are position and point coordinates
1809 				//****************************************************************
1810 				int decimals = 3;
1811 				decimal plotAreaPositionX = Math.Round((decimal)area.PlotAreaPosition.X, decimals);
1812 				decimal plotAreaPositionY = Math.Round((decimal)area.PlotAreaPosition.Y, decimals);
1813 				decimal plotAreaPositionRight = Math.Round((decimal)area.PlotAreaPosition.Right, decimals);
1814 				decimal plotAreaPositionBottom = Math.Round((decimal)area.PlotAreaPosition.Bottom, decimals);
1815 
1816 				// Make area a little bit bigger
1817 				plotAreaPositionX -= 0.001M;
1818 				plotAreaPositionY -= 0.001M;
1819 				plotAreaPositionRight += 0.001M;
1820 				plotAreaPositionBottom += 0.001M;
1821 
1822 				// Chech data points X values
1823 				if((decimal)firstPoint.xPosition < plotAreaPositionX ||
1824 					(decimal)firstPoint.xPosition > plotAreaPositionRight ||
1825 					(decimal)secondPoint.xPosition < plotAreaPositionX ||
1826 					(decimal)secondPoint.xPosition > plotAreaPositionRight )
1827 				{
1828 					// Check if surface completly out of the plot area
1829 					if((decimal)firstPoint.xPosition < plotAreaPositionX &&
1830 						(decimal)secondPoint.xPosition < plotAreaPositionX)
1831 					{
1832 						return resultPath;
1833 					}
1834 					// Check if surface completly out of the plot area
1835 					if((decimal)firstPoint.xPosition > plotAreaPositionRight &&
1836 						(decimal)secondPoint.xPosition > plotAreaPositionRight)
1837 					{
1838 						return resultPath;
1839 					}
1840 
1841 					// Only part of the surface is outside - fix X value and adjust Y value
1842 					if((decimal)firstPoint.xPosition < plotAreaPositionX)
1843 					{
1844 						firstPoint.yPosition = ((double)plotAreaPositionX - secondPoint.xPosition) /
1845 							(firstPoint.xPosition - secondPoint.xPosition) *
1846 							(firstPoint.yPosition - secondPoint.yPosition) +
1847 							secondPoint.yPosition;
1848 						firstPoint.xPosition = (double)plotAreaPositionX;
1849 					}
1850 					else if((decimal)firstPoint.xPosition > plotAreaPositionRight)
1851 					{
1852 						firstPoint.yPosition = ((double)plotAreaPositionRight - secondPoint.xPosition) /
1853 							(firstPoint.xPosition - secondPoint.xPosition) *
1854 							(firstPoint.yPosition - secondPoint.yPosition) +
1855 							secondPoint.yPosition;
1856 						firstPoint.xPosition = (double)plotAreaPositionRight;
1857 					}
1858 					if((decimal)secondPoint.xPosition < plotAreaPositionX)
1859 					{
1860 						secondPoint.yPosition = ((double)plotAreaPositionX - secondPoint.xPosition) /
1861 							(firstPoint.xPosition - secondPoint.xPosition) *
1862 							(firstPoint.yPosition - secondPoint.yPosition) +
1863 							secondPoint.yPosition;
1864 						secondPoint.xPosition = (double)plotAreaPositionX;
1865 					}
1866 					else if((decimal)secondPoint.xPosition > plotAreaPositionRight)
1867 					{
1868 						secondPoint.yPosition = ((double)plotAreaPositionRight - secondPoint.xPosition) /
1869 							(firstPoint.xPosition - secondPoint.xPosition) *
1870 							(firstPoint.yPosition - secondPoint.yPosition) +
1871 							secondPoint.yPosition;
1872 						secondPoint.xPosition = (double)plotAreaPositionRight;
1873 					}
1874 				}
1875 
1876 				// Chech data points Y values
1877 				if((decimal)firstPoint.yPosition < plotAreaPositionY ||
1878 					(decimal)firstPoint.yPosition > plotAreaPositionBottom ||
1879 					(decimal)secondPoint.yPosition < plotAreaPositionY ||
1880 					(decimal)secondPoint.yPosition > plotAreaPositionBottom )
1881 				{
1882 					// Remember previous y positions
1883 					double prevFirstPointY = firstPoint.yPosition;
1884 					double prevSecondPointY = secondPoint.yPosition;
1885 
1886 					// Check if whole line is outside plotting region
1887 					bool	surfaceCompletlyOutside = false;
1888 					if((decimal)firstPoint.yPosition < plotAreaPositionY &&
1889 						(decimal)secondPoint.yPosition < plotAreaPositionY)
1890 					{
1891 						surfaceCompletlyOutside = true;
1892 						firstPoint.yPosition = (double)plotAreaPositionY;
1893 						secondPoint.yPosition = (double)plotAreaPositionY;
1894 					}
1895 					if((decimal)firstPoint.yPosition > plotAreaPositionBottom &&
1896 						(decimal)secondPoint.yPosition > plotAreaPositionBottom)
1897 					{
1898 						surfaceCompletlyOutside = true;
1899 						firstPoint.yPosition = (double)plotAreaPositionBottom;
1900 						secondPoint.yPosition = (double)plotAreaPositionBottom;
1901 					}
1902 
1903 					// Calculate color used to draw "cut" surfaces
1904 					Color	cutSurfaceBackColor = ChartGraphics.GetGradientColor(backColor, Color.Black, 0.5);
1905 					Color	cutSurfaceBorderColor = ChartGraphics.GetGradientColor(borderColor, Color.Black, 0.5);
1906 
1907 					// Draw just one surface
1908 					if(surfaceCompletlyOutside)
1909 					{
1910 						resultPath = this.Draw3DSurface(
1911 							area, matrix, lightStyle, surfaceName, positionZ,  depth,
1912 							cutSurfaceBackColor, cutSurfaceBorderColor, borderWidth, borderDashStyle,
1913 							firstPoint, secondPoint,
1914 							points, pointIndex, tension, operationType, lineSegmentType,
1915 							forceThinBorder, forceThickBorder, reversedSeriesOrder,
1916 							multiSeries, yValueIndex, clipInsideArea);
1917 
1918 						// Restore previous y positions
1919 						firstPoint.yPosition = prevFirstPointY;
1920 						secondPoint.yPosition = prevSecondPointY;
1921 
1922 						return resultPath;
1923 					}
1924 
1925 					// Get intersection point
1926 					DataPoint3D	intersectionPoint = new DataPoint3D();
1927 					intersectionPoint.yPosition = (double)plotAreaPositionY;
1928 					if((decimal)firstPoint.yPosition > plotAreaPositionBottom ||
1929 						(decimal)secondPoint.yPosition > plotAreaPositionBottom )
1930 					{
1931 						intersectionPoint.yPosition = (double)plotAreaPositionBottom;
1932 					}
1933 					intersectionPoint.xPosition = (intersectionPoint.yPosition - secondPoint.yPosition) *
1934 						(firstPoint.xPosition - secondPoint.xPosition) /
1935 						(firstPoint.yPosition - secondPoint.yPosition) +
1936 						secondPoint.xPosition;
1937 
1938 					// Check if there are 2 intersection points (3 segments)
1939 					int		segmentNumber = 2;
1940 					DataPoint3D	intersectionPoint2 = null;
1941 					if( ((decimal)firstPoint.yPosition < plotAreaPositionY &&
1942 						(decimal)secondPoint.yPosition > plotAreaPositionBottom) ||
1943 						((decimal)firstPoint.yPosition > plotAreaPositionBottom &&
1944 						(decimal)secondPoint.yPosition < plotAreaPositionY))
1945 					{
1946 						segmentNumber = 3;
1947 						intersectionPoint2 = new DataPoint3D();
1948 						if((decimal)intersectionPoint.yPosition == plotAreaPositionY)
1949 						{
1950 							intersectionPoint2.yPosition = (double)plotAreaPositionBottom;
1951 						}
1952 						else
1953 						{
1954 							intersectionPoint2.yPosition = (double)plotAreaPositionY;
1955 						}
1956 						intersectionPoint2.xPosition = (intersectionPoint2.yPosition - secondPoint.yPosition) *
1957 							(firstPoint.xPosition - secondPoint.xPosition) /
1958 							(firstPoint.yPosition - secondPoint.yPosition) +
1959 							secondPoint.xPosition;
1960 
1961 						// Switch intersection points
1962 						if((decimal)firstPoint.yPosition > plotAreaPositionBottom)
1963 						{
1964 							DataPoint3D tempPoint = new DataPoint3D();
1965 							tempPoint.xPosition = intersectionPoint.xPosition;
1966 							tempPoint.yPosition = intersectionPoint.yPosition;
1967 							intersectionPoint.xPosition = intersectionPoint2.xPosition;
1968 							intersectionPoint.yPosition = intersectionPoint2.yPosition;
1969 							intersectionPoint2.xPosition = tempPoint.xPosition;
1970 							intersectionPoint2.yPosition = tempPoint.yPosition;
1971 						}
1972 					}
1973 
1974 
1975 					// Adjust points Y values
1976 					bool	firstSegmentVisible = true;
1977 					if((decimal)firstPoint.yPosition < plotAreaPositionY)
1978 					{
1979 						firstSegmentVisible = false;
1980 						firstPoint.yPosition = (double)plotAreaPositionY;
1981 					}
1982 					else if((decimal)firstPoint.yPosition > plotAreaPositionBottom)
1983 					{
1984 						firstSegmentVisible = false;
1985 						firstPoint.yPosition = (double)plotAreaPositionBottom;
1986 					}
1987 					if((decimal)secondPoint.yPosition < plotAreaPositionY)
1988 					{
1989 						secondPoint.yPosition = (double)plotAreaPositionY;
1990 					}
1991 					else if((decimal)secondPoint.yPosition > plotAreaPositionBottom)
1992 					{
1993 						secondPoint.yPosition = (double)plotAreaPositionBottom;
1994 					}
1995 
1996 					// Check if reversed drawing order required
1997 					bool	reversed = false;
1998 					if((pointIndex + 1) < points.Count)
1999 					{
2000 						DataPoint3D p = (DataPoint3D)points[pointIndex + 1];
2001 						if(p.index == firstPoint.index)
2002 						{
2003 							reversed = true;
2004 						}
2005 					}
2006 
2007 					// Draw surfaces in 2 or 3 segments
2008 					for(int segmentIndex = 0; segmentIndex < 3; segmentIndex++)
2009 					{
2010 						GraphicsPath segmentPath = null;
2011 						if(segmentIndex == 0 && !reversed ||
2012 							segmentIndex == 2 && reversed)
2013 						{
2014 							// Draw first segment
2015 							if(intersectionPoint2 == null)
2016 							{
2017 								intersectionPoint2 = intersectionPoint;
2018 							}
2019 							intersectionPoint2.dataPoint = secondPoint.dataPoint;
2020 							intersectionPoint2.index = secondPoint.index;
2021 
2022 							segmentPath = this.Draw3DSurface(
2023 								area, matrix, lightStyle, surfaceName, positionZ,  depth,
2024 								(firstSegmentVisible && segmentNumber != 3) ? backColor : cutSurfaceBackColor,
2025 								(firstSegmentVisible && segmentNumber != 3) ? borderColor : cutSurfaceBorderColor,
2026 								borderWidth, borderDashStyle,
2027 								firstPoint, intersectionPoint2,
2028 								points, pointIndex, tension, operationType, lineSegmentType,
2029 								forceThinBorder, forceThickBorder, reversedSeriesOrder,
2030 								multiSeries, yValueIndex, clipInsideArea);
2031 						}
2032 
2033 						if(segmentIndex == 1 && intersectionPoint2 != null && segmentNumber == 3)
2034 						{
2035 							// Draw middle segment
2036 							intersectionPoint2.dataPoint = secondPoint.dataPoint;
2037 							intersectionPoint2.index = secondPoint.index;
2038 
2039 							segmentPath = this.Draw3DSurface(
2040 								area, matrix, lightStyle, surfaceName, positionZ,  depth,
2041 								backColor,
2042 								borderColor,
2043 								borderWidth, borderDashStyle,
2044 								intersectionPoint, intersectionPoint2,
2045 								points, pointIndex, tension, operationType, lineSegmentType,
2046 								forceThinBorder, forceThickBorder, reversedSeriesOrder,
2047 								multiSeries, yValueIndex, clipInsideArea);
2048 						}
2049 
2050 						if(segmentIndex == 2 && !reversed ||
2051 							segmentIndex == 0 && reversed)
2052 						{
2053 							// Draw second segment
2054 							intersectionPoint.dataPoint = firstPoint.dataPoint;
2055 							intersectionPoint.index = firstPoint.index;
2056 
2057 							segmentPath = this.Draw3DSurface(
2058 								area, matrix, lightStyle, surfaceName, positionZ,  depth,
2059 								(!firstSegmentVisible && segmentNumber != 3) ? backColor : cutSurfaceBackColor,
2060 								(!firstSegmentVisible && segmentNumber != 3) ? borderColor : cutSurfaceBorderColor,
2061 								borderWidth, borderDashStyle,
2062 								intersectionPoint, secondPoint,
2063 								points, pointIndex, tension, operationType, lineSegmentType,
2064 								forceThinBorder, forceThickBorder, reversedSeriesOrder,
2065 								multiSeries, yValueIndex, clipInsideArea);
2066 						}
2067 
2068 						// Add segment path
2069 						if(resultPath != null && segmentPath != null && segmentPath.PointCount > 0)
2070 						{
2071 							resultPath.SetMarkers();
2072 							resultPath.AddPath(segmentPath, true);
2073 						}
2074 					}
2075 
2076 					// Restore previous y positions
2077 					firstPoint.yPosition = prevFirstPointY;
2078 					secondPoint.yPosition = prevSecondPointY;
2079 
2080 					return resultPath;
2081 				}
2082 			}
2083 
2084 			//**********************************************************************
2085 			//** Prepare, transform polygon coordinates
2086 			//**********************************************************************
2087 
2088 			// Define 4 points polygon
2089 			Point3D [] points3D = new Point3D[4];
2090 			points3D[0] = new Point3D((float)firstPoint.xPosition, (float)firstPoint.yPosition, positionZ + depth);
2091 			points3D[1] = new Point3D((float)secondPoint.xPosition, (float)secondPoint.yPosition, positionZ + depth);
2092 			points3D[2] = new Point3D((float)secondPoint.xPosition, (float)secondPoint.yPosition, positionZ);
2093 			points3D[3] = new Point3D((float)firstPoint.xPosition, (float)firstPoint.yPosition, positionZ);
2094 
2095 			// Transform coordinates
2096 			matrix.TransformPoints( points3D );
2097 
2098 			// Get absolute coordinates and create array of PointF
2099 			PointF[]	polygonPoints = new PointF[4];
2100 			polygonPoints[0] = GetAbsolutePoint(points3D[0].PointF);
2101 			polygonPoints[1] = GetAbsolutePoint(points3D[1].PointF);
2102 			polygonPoints[2] = GetAbsolutePoint(points3D[2].PointF);
2103 			polygonPoints[3] = GetAbsolutePoint(points3D[3].PointF);
2104 
2105 			//**********************************************************************
2106 			//** Define drawing colors
2107 			//**********************************************************************
2108 			bool topIsVisible = IsSurfaceVisible( points3D[0], points3D[1], points3D[2]);
2109 			Color polygonColor = matrix.GetPolygonLight( points3D, backColor, topIsVisible, area.Area3DStyle.Rotation, surfaceName, area.ReverseSeriesOrder );
2110 			Color	surfaceBorderColor = borderColor;
2111 			if(surfaceBorderColor == Color.Empty)
2112 			{
2113 				// If border color is emty use color slightly darker than main back color
2114 				surfaceBorderColor = ChartGraphics.GetGradientColor( backColor, Color.Black, 0.2 );
2115 			}
2116 
2117 			//**********************************************************************
2118 			//** Draw elements if required.
2119 			//**********************************************************************
2120 			Pen thinBorderPen = new Pen(surfaceBorderColor, 1);
2121 			if(drawElements)
2122 			{
2123 				// Draw the polygon
2124 				if(backColor != Color.Transparent)
2125 				{
2126 					// Remember SmoothingMode and turn off anti aliasing
2127 					SmoothingMode oldSmoothingMode = SmoothingMode;
2128 					SmoothingMode = SmoothingMode.Default;
2129 
2130 					// Draw the polygon
2131                     using (Brush brush = new SolidBrush(polygonColor))
2132                     {
2133 					    FillPolygon(brush, polygonPoints);
2134                     }
2135 
2136 					// Return old smoothing mode
2137 					SmoothingMode = oldSmoothingMode;
2138 				}
2139 
2140 				// Draw thin polygon border of darker color
2141 				if(forceThinBorder || forceThickBorder)
2142 				{
2143 					if(forceThickBorder)
2144 					{
2145 						Pen linePen = new Pen(surfaceBorderColor, borderWidth);
2146 						linePen.StartCap = LineCap.Round;
2147 						linePen.EndCap = LineCap.Round;
2148 
2149 						DrawLine(linePen, polygonPoints[0], polygonPoints[1]);
2150 						DrawLine(linePen, polygonPoints[2], polygonPoints[3]);
2151 						DrawLine(linePen, polygonPoints[3], polygonPoints[0]);
2152 						DrawLine(linePen, polygonPoints[1], polygonPoints[2]);
2153 					}
2154 					else
2155 					{
2156 						// Front & Back lines
2157 						DrawLine(thinBorderPen, polygonPoints[0], polygonPoints[1]);
2158 						DrawLine(thinBorderPen, polygonPoints[2], polygonPoints[3]);
2159 						if(lineSegmentType == LineSegmentType.First)
2160 						{
2161 							// Left line
2162 							DrawLine(thinBorderPen, polygonPoints[3], polygonPoints[0]);
2163 						}
2164 						else if(lineSegmentType == LineSegmentType.Last)
2165 						{
2166 							// Right Line
2167 							DrawLine(thinBorderPen, polygonPoints[1], polygonPoints[2]);
2168 						}
2169 						else
2170 						{
2171 							// Left & Right lines
2172 							DrawLine(thinBorderPen, polygonPoints[3], polygonPoints[0]);
2173 							DrawLine(thinBorderPen, polygonPoints[1], polygonPoints[2]);
2174 						}
2175 					}
2176 
2177 				}
2178 				else
2179 				{
2180 					// Draw thin polygon border of same color (solves anti-aliasing issues)
2181 					if(polygonColor.A == 255)
2182 					{
2183 						DrawPolygon(new Pen(polygonColor, 1), polygonPoints);
2184 					}
2185 
2186 					// Draw thin Front & Back lines
2187 					DrawLine(thinBorderPen, polygonPoints[0], polygonPoints[1]);
2188 					DrawLine(thinBorderPen, polygonPoints[2], polygonPoints[3]);
2189 				}
2190 			}
2191 
2192 			//**********************************************************************
2193 			//** Draw thick border line on visible sides
2194 			//**********************************************************************
2195 			Pen	thickBorderPen = null;
2196 			if(borderWidth > 1 && !forceThickBorder)
2197 			{
2198 				// Create thick border line pen
2199 				thickBorderPen = new Pen(surfaceBorderColor, borderWidth);
2200 				thickBorderPen.StartCap = LineCap.Round;
2201 				thickBorderPen.EndCap = LineCap.Round;
2202 
2203 				//****************************************************************
2204 				//** Switch first and second points.
2205 				//****************************************************************
2206 				if(firstPoint.index > secondPoint.index)
2207 				{
2208 					DataPoint3D	tempPoint = firstPoint;
2209 					firstPoint = secondPoint;
2210 					secondPoint = tempPoint;
2211 				}
2212 
2213 				//**********************************************************************
2214 				//** Check if there are visible (non-empty) lines to the left & right
2215 				//** of the current line.
2216 				//**********************************************************************
2217 
2218 				// Get visibility of bounding rectangle
2219 				float minX = (float)Math.Min(points3D[0].X, points3D[1].X);
2220 				float minY = (float)Math.Min(points3D[0].Y, points3D[1].Y);
2221 				float maxX = (float)Math.Max(points3D[0].X, points3D[1].X);
2222 				float maxY = (float)Math.Max(points3D[0].Y, points3D[1].Y);
2223 				RectangleF position = new RectangleF(minX, minY, maxX - minX, maxY - minY);
2224 				SurfaceNames visibleSurfaces = GetVisibleSurfaces(position,positionZ,depth,matrix);
2225 
2226 				// Check left line visibility
2227 				bool	thickBorderOnLeft = false;
2228 				bool	thickBorderOnRight = false;
2229 
2230 				if(lineSegmentType != LineSegmentType.Middle)
2231 				{
2232 					LineSegmentType	tempLineSegmentType = LineSegmentType.Single;
2233 
2234 					// Check left line visibility
2235 					thickBorderOnLeft = (ChartGraphics.ShouldDrawLineChartSurface(
2236 						area,
2237 						reversedSeriesOrder,
2238 						SurfaceNames.Left,
2239 						visibleSurfaces,
2240 						polygonColor,
2241 						points,
2242 						firstPoint,
2243 						secondPoint,
2244 						multiSeries,
2245 						ref tempLineSegmentType) == 2);
2246 
2247 
2248 					// Check right line visibility
2249 					thickBorderOnRight = (ChartGraphics.ShouldDrawLineChartSurface(
2250 						area,
2251 						reversedSeriesOrder,
2252 						SurfaceNames.Right,
2253 						visibleSurfaces,
2254 						polygonColor,
2255 						points,
2256 						firstPoint,
2257 						secondPoint,
2258 						multiSeries,
2259 						ref tempLineSegmentType) == 2);
2260 				}
2261 
2262 				// Switch left & right border if series is reversed
2263 				if(reversedSeriesOrder)
2264 				{
2265 					bool tempVal = thickBorderOnLeft;
2266 					thickBorderOnLeft = thickBorderOnRight;
2267 					thickBorderOnRight = tempVal;
2268 				}
2269 
2270 				// Draw thick border for single segment lines only
2271 				// or for the first & last segment
2272 				if(lineSegmentType != LineSegmentType.First && lineSegmentType != LineSegmentType.Single)
2273 				{
2274 					thickBorderOnLeft = false;
2275 				}
2276 				if(lineSegmentType != LineSegmentType.Last && lineSegmentType != LineSegmentType.Single)
2277 				{
2278 					thickBorderOnRight = false;
2279 				}
2280 
2281 				//**********************************************************************
2282 				//** Draw border on the front side of line surface (only when visible)
2283 				//**********************************************************************
2284 				if( matrix.Perspective != 0 ||
2285                     (matrix.AngleX != 90 && matrix.AngleX != -90 &&
2286 					matrix.AngleY != 90 && matrix.AngleY != -90 &&
2287 					matrix.AngleY != 180 && matrix.AngleY != -180))
2288 				{
2289 					// Draw thick line on the front side of the line surface
2290 					if(drawElements)
2291 					{
2292 						DrawLine(
2293 							thickBorderPen,
2294 							(float)Math.Round(polygonPoints[0].X),
2295 							(float)Math.Round(polygonPoints[0].Y),
2296 							(float)Math.Round(polygonPoints[1].X),
2297 							(float)Math.Round(polygonPoints[1].Y) );
2298 					}
2299 
2300 					// Calculate path for selection
2301 					if(resultPath != null)
2302 					{
2303 						// Add front line to the path
2304 						resultPath.AddLine(
2305 							(float)Math.Round(polygonPoints[0].X),
2306 							(float)Math.Round(polygonPoints[0].Y),
2307 							(float)Math.Round(polygonPoints[1].X),
2308 							(float)Math.Round(polygonPoints[1].Y));
2309 					}
2310 				}
2311 
2312 
2313 				//**********************************************************************
2314 				//** Draw border on the left side of line surface (only when visible)
2315 				//**********************************************************************
2316 
2317 				// Use flat end for Right & Left border
2318 				thickBorderPen.EndCap = LineCap.Flat;
2319 
2320 				// Draw border on the left side
2321                 if (matrix.Perspective != 0 || (matrix.AngleX != 90 && matrix.AngleX != -90))
2322 				{
2323 					if(thickBorderOnLeft)
2324 					{
2325 						if(drawElements)
2326 						{
2327 							DrawLine(
2328 								thickBorderPen,
2329 								(float)Math.Round(polygonPoints[3].X),
2330 								(float)Math.Round(polygonPoints[3].Y),
2331 								(float)Math.Round(polygonPoints[0].X),
2332 								(float)Math.Round(polygonPoints[0].Y) );
2333 						}
2334 
2335 						// Calculate path for selection
2336 						if(resultPath != null)
2337 						{
2338 							// Add left line to the path
2339 							resultPath.AddLine(
2340 								(float)Math.Round(polygonPoints[3].X),
2341 								(float)Math.Round(polygonPoints[3].Y),
2342 								(float)Math.Round(polygonPoints[0].X),
2343 								(float)Math.Round(polygonPoints[0].Y));
2344 						}
2345 					}
2346 				}
2347 
2348 				//**********************************************************************
2349 				//** Draw border on the right side of the line surface
2350 				//**********************************************************************
2351                 if (matrix.Perspective != 0 || (matrix.AngleX != 90 && matrix.AngleX != -90))
2352 				{
2353 					if(thickBorderOnRight)
2354 					{
2355 						if(drawElements)
2356 						{
2357 							DrawLine(
2358 								thickBorderPen,
2359 								(float)Math.Round(polygonPoints[1].X),
2360 								(float)Math.Round(polygonPoints[1].Y),
2361 								(float)Math.Round(polygonPoints[2].X),
2362 								(float)Math.Round(polygonPoints[2].Y) );
2363 						}
2364 
2365 						// Calculate path for selection
2366 						if(resultPath != null)
2367 						{
2368 							// Add right line to the path
2369 							resultPath.AddLine(
2370 								(float)Math.Round(polygonPoints[1].X),
2371 								(float)Math.Round(polygonPoints[1].Y),
2372 								(float)Math.Round(polygonPoints[2].X),
2373 								(float)Math.Round(polygonPoints[2].Y));
2374 						}
2375 					}
2376 				}
2377 			}
2378 
2379 			//**********************************************************************
2380 			// Redraw front line of the previuos line segment.
2381 			// Solves 3D visibility problem between wide border line and line surface.
2382 			//**********************************************************************
2383 			if( area.Area3DStyle.Perspective == 0 )
2384 			{
2385 				if(frontLinePoint1 != PointF.Empty && frontLinePen != null)
2386 				{
2387 					// Draw line
2388 					DrawLine(
2389 						frontLinePen,
2390 						(float)Math.Round(frontLinePoint1.X),
2391 						(float)Math.Round(frontLinePoint1.Y),
2392 						(float)Math.Round(frontLinePoint2.X),
2393 						(float)Math.Round(frontLinePoint2.Y) );
2394 
2395 					// Reset line properties
2396 					frontLinePen = null;
2397 					frontLinePoint1 = PointF.Empty;
2398 					frontLinePoint2 = PointF.Empty;
2399 				}
2400 
2401 				//**********************************************************************
2402 				//** Check if front line should be redrawn whith the next segment.
2403 				//**********************************************************************
2404 				if(drawElements)
2405 				{
2406 					frontLinePen = (borderWidth > 1) ? thickBorderPen : thinBorderPen;
2407 					frontLinePoint1 = polygonPoints[0];
2408 					frontLinePoint2 = polygonPoints[1];
2409 				}
2410 			}
2411 
2412 			//**********************************************************************
2413 			//** Calculate path for selection
2414 			//**********************************************************************
2415 			if(resultPath != null)
2416 			{
2417 				// Widen all the lines currently in the path
2418 				if(thickBorderPen != null)
2419 				{
2420 					try
2421 					{
2422 						resultPath.Widen(thickBorderPen);
2423 					}
2424                     catch (OutOfMemoryException)
2425                     {
2426                         // GraphicsPath.Widen incorrectly throws OutOfMemoryException
2427                         // catching here and reacting by not widening
2428                     }
2429                     catch (ArgumentException)
2430                     {
2431                     }
2432 				}
2433 
2434 				// Add polygon to the path
2435 				resultPath.AddPolygon(polygonPoints);
2436 			}
2437 
2438 			return resultPath;
2439 		}
2440 
2441 
2442 
2443 		/// <summary>
2444 		/// Helper method, which indicates if area chart surface should be drawn or not.
2445 		/// </summary>
2446 		/// <param name="area">Chart area object.</param>
2447 		/// <param name="reversedSeriesOrder">Series are drawn in reversed order.</param>
2448 		/// <param name="surfaceName">Surface name.</param>
2449 		/// <param name="boundaryRectVisibleSurfaces">Visible surfaces of the bounding rectangle.</param>
2450 		/// <param name="color">Point back color.</param>
2451 		/// <param name="points">Array of all points.</param>
2452 		/// <param name="firstPoint">First point.</param>
2453 		/// <param name="secondPoint">Second point.</param>
2454 		/// <param name="multiSeries">Indicates that multiple series are painted at the same time (stacked or side-by-side).</param>
2455 		/// <param name="lineSegmentType">Returns line segment type.</param>
2456 		/// <returns>Function retrns 0, 1 or 2. 0 - Do not draw surface, 1 - draw on the back, 2 - draw in front.</returns>
ShouldDrawLineChartSurface( ChartArea area, bool reversedSeriesOrder, SurfaceNames surfaceName, SurfaceNames boundaryRectVisibleSurfaces, Color color, ArrayList points, DataPoint3D firstPoint, DataPoint3D secondPoint, bool multiSeries, ref LineSegmentType lineSegmentType)2457 		static internal int ShouldDrawLineChartSurface(
2458 			ChartArea area,
2459 			bool reversedSeriesOrder,
2460 			SurfaceNames surfaceName,
2461 			SurfaceNames boundaryRectVisibleSurfaces,
2462 			Color color,
2463 			ArrayList points,
2464 			DataPoint3D firstPoint,
2465 			DataPoint3D secondPoint,
2466 			bool multiSeries,
2467 			ref LineSegmentType lineSegmentType)
2468 		{
2469 			int	result = 0;
2470 			Series series = firstPoint.dataPoint.series;
2471 
2472 			// Set active horizontal/vertical axis
2473 			Axis hAxis = (series.XAxisType == AxisType.Primary) ? area.AxisX : area.AxisX2;
2474 			double hAxisMin = hAxis.ViewMinimum;
2475 			double hAxisMax = hAxis.ViewMaximum;
2476 
2477 			//****************************************************************
2478 			//** Check if data point and it's neigbours have non-transparent
2479 			//** colors.
2480 			//****************************************************************
2481 
2482             // Check if point main color has transparency
2483 			bool transparent = color.A != 255;
2484 
2485 			// Check if points on the left and right side exsit and are transparent
2486 			bool leftPointVisible = false;
2487 			bool rightPointVisible = false;
2488 			if( surfaceName == SurfaceNames.Left )
2489 			{
2490 				// Find Left point
2491 				DataPoint3D leftPoint = null, leftPointAttr = null;
2492 				int pointArrayIndex = int.MinValue;
2493 				if(!reversedSeriesOrder)
2494 				{
2495 					leftPoint = ChartGraphics.FindPointByIndex(points, Math.Min(firstPoint.index, secondPoint.index) - 1, (multiSeries) ? secondPoint : null, ref pointArrayIndex);
2496 					leftPointAttr = ChartGraphics.FindPointByIndex(points, Math.Min(firstPoint.index, secondPoint.index), (multiSeries) ? secondPoint : null, ref pointArrayIndex);
2497 				}
2498 				else
2499 				{
2500 					leftPoint = ChartGraphics.FindPointByIndex(points, Math.Max(firstPoint.index, secondPoint.index) + 1, (multiSeries) ? secondPoint : null, ref pointArrayIndex);
2501 					leftPointAttr = leftPoint;
2502 				}
2503 				if(leftPoint != null)
2504 				{
2505 					if(leftPointAttr.dataPoint.IsEmpty)
2506 					{
2507 						if(leftPointAttr.dataPoint.series.EmptyPointStyle.Color == color ||
2508 							leftPointAttr.dataPoint.series.EmptyPointStyle.Color.A == 255)
2509 						{
2510 							leftPointVisible = true;
2511 						}
2512 					}
2513 					else
2514 					{
2515 						if(leftPointAttr.dataPoint.Color == color ||
2516 							leftPointAttr.dataPoint.Color.A == 255)
2517 						{
2518 							leftPointVisible = true;
2519 						}
2520 					}
2521 
2522 					// Check if found point is outside the scaleView
2523 					double	xValue = (leftPoint.indexedSeries) ? leftPoint.index : leftPoint.dataPoint.XValue;
2524 					if(xValue > hAxisMax || xValue < hAxisMin)
2525 					{
2526 						DataPoint3D currentPoint = null;
2527 						if(reversedSeriesOrder)
2528 						{
2529 							currentPoint = (firstPoint.index > secondPoint.index) ? firstPoint : secondPoint;
2530 						}
2531 						else
2532 						{
2533 							currentPoint = (firstPoint.index < secondPoint.index) ? firstPoint : secondPoint;
2534 						}
2535 						double	currentXValue = (currentPoint.indexedSeries) ? currentPoint.index : currentPoint.dataPoint.XValue;
2536 						if(currentXValue > hAxisMax || currentXValue < hAxisMin)
2537 						{
2538 							leftPointVisible = false;
2539 						}
2540 					}
2541 				}
2542 			}
2543 
2544 			// Find Right point
2545 			if( surfaceName == SurfaceNames.Right )
2546 			{
2547 				DataPoint3D rightPoint = null, rightPointAttr = null;
2548 				int pointArrayIndex = int.MinValue;
2549 				if(!reversedSeriesOrder)
2550 				{
2551 					rightPoint = ChartGraphics.FindPointByIndex(points, Math.Max(firstPoint.index, secondPoint.index) + 1, (multiSeries) ? secondPoint : null, ref pointArrayIndex);
2552 					rightPointAttr = rightPoint;
2553 				}
2554 				else
2555 				{
2556 					rightPoint = ChartGraphics.FindPointByIndex(points, Math.Min(firstPoint.index, secondPoint.index) - 1, (multiSeries) ? secondPoint : null, ref pointArrayIndex);
2557 					rightPointAttr = ChartGraphics.FindPointByIndex(points, Math.Min(firstPoint.index, secondPoint.index), (multiSeries) ? secondPoint : null, ref pointArrayIndex);
2558 				}
2559 				if(rightPoint != null)
2560 				{
2561 					if(rightPointAttr.dataPoint.IsEmpty)
2562 					{
2563 						if(rightPointAttr.dataPoint.series.EmptyPointStyle.Color == color ||
2564 							rightPointAttr.dataPoint.series.EmptyPointStyle.Color.A == 255)
2565 						{
2566 							rightPointVisible = true;
2567 						}
2568 					}
2569 					else
2570 					{
2571 						if(rightPointAttr.dataPoint.Color == color ||
2572 							rightPointAttr.dataPoint.Color.A == 255)
2573 						{
2574 							rightPointVisible = true;
2575 						}
2576 					}
2577 
2578 					// Check if found point is outside the scaleView
2579 					double	xValue = (rightPoint.indexedSeries) ? rightPoint.index : rightPoint.dataPoint.XValue;
2580 					if(xValue > hAxisMax || xValue < hAxisMin)
2581 					{
2582 						DataPoint3D currentPoint = null;
2583 						if(reversedSeriesOrder)
2584 						{
2585 							currentPoint = (firstPoint.index > secondPoint.index) ? firstPoint : secondPoint;
2586 						}
2587 						else
2588 						{
2589 							currentPoint = (firstPoint.index < secondPoint.index) ? firstPoint : secondPoint;
2590 						}
2591 						double	currentXValue = (currentPoint.indexedSeries) ? currentPoint.index : currentPoint.dataPoint.XValue;
2592 						if(currentXValue > hAxisMax || currentXValue < hAxisMin)
2593 						{
2594 							rightPointVisible = false;
2595 						}
2596 					}
2597 				}
2598 			}
2599 
2600 			//****************************************************************
2601 			//** Get line segment
2602 			//****************************************************************
2603 			if( surfaceName == SurfaceNames.Left && !leftPointVisible)
2604 			{
2605 				if(lineSegmentType == LineSegmentType.Middle)
2606 				{
2607 					lineSegmentType = LineSegmentType.First;
2608 				}
2609 				else if(lineSegmentType == LineSegmentType.Last)
2610 				{
2611 					lineSegmentType = LineSegmentType.Single;
2612 				}
2613 			}
2614 			if( surfaceName == SurfaceNames.Right && !rightPointVisible)
2615 			{
2616 				if(lineSegmentType == LineSegmentType.Middle)
2617 				{
2618 					lineSegmentType = LineSegmentType.Last;
2619 				}
2620 				else if(lineSegmentType == LineSegmentType.First)
2621 				{
2622 					lineSegmentType = LineSegmentType.Single;
2623 				}
2624 			}
2625 
2626 
2627 			//****************************************************************
2628 			//** Check surfaces visibility
2629 			//****************************************************************
2630 			if( surfaceName == SurfaceNames.Top )
2631 			{
2632 				result = ((boundaryRectVisibleSurfaces & SurfaceNames.Top) == SurfaceNames.Top) ? 2 : 1;
2633 			}
2634 			if( surfaceName == SurfaceNames.Bottom )
2635 			{
2636 				result = ((boundaryRectVisibleSurfaces & SurfaceNames.Bottom) == SurfaceNames.Bottom) ? 2 : 1;
2637 				// Draw invisible bottom surface only if chart is transparent
2638 				if(result == 1 && !transparent)
2639 				{
2640 					result = 0;
2641 				}
2642 			}
2643 			if( surfaceName == SurfaceNames.Front )
2644 			{
2645 				result = ((boundaryRectVisibleSurfaces & SurfaceNames.Front) == SurfaceNames.Front) ? 2 : 1;
2646 				// Draw invisible front surface only if chart is transparent
2647 				if(result == 1 && !transparent)
2648 				{
2649 					result = 0;
2650 				}
2651 			}
2652 			if( surfaceName == SurfaceNames.Back )
2653 			{
2654 				result = ((boundaryRectVisibleSurfaces & SurfaceNames.Back) == SurfaceNames.Back) ? 2 : 1;
2655 				// Draw invisible back surface only if chart is transparent
2656 				if(result == 1 && !transparent)
2657 				{
2658 					result = 0;
2659 				}
2660 			}
2661 			if( surfaceName == SurfaceNames.Left )
2662 			{
2663 				result = ((boundaryRectVisibleSurfaces & SurfaceNames.Left) == SurfaceNames.Left) ? 2 : 1;
2664 				// Draw invisible left surface only if point to the left is transparent
2665 				if(leftPointVisible)
2666 				{
2667 					result = 0;
2668 				}
2669 			}
2670 			if( surfaceName == SurfaceNames.Right )
2671 			{
2672 				result = ((boundaryRectVisibleSurfaces & SurfaceNames.Right) == SurfaceNames.Right) ? 2 : 1;
2673 				// Draw invisible right surface only if point to the right is transparent
2674 				if(rightPointVisible)
2675 				{
2676 					result = 0;
2677 				}
2678 			}
2679 
2680 			return result;
2681 		}
2682 
2683 
2684 		/// <summary>
2685 		/// Helper method which finds point in the list by it's real index.
2686 		/// </summary>
2687 		/// <param name="points">List of points.</param>
2688 		/// <param name="index">Required index.</param>
2689 		/// <param name="neighborDataPoint">Neighbor point of the same series.</param>
2690 		/// <param name="neighborPointIndex">Neighbor point index in the array list.</param>
2691 		/// <returns>Data point found.</returns>
FindPointByIndex(ArrayList points, int index, DataPoint3D neighborDataPoint, ref int neighborPointIndex)2692 		internal static DataPoint3D FindPointByIndex(ArrayList points, int index, DataPoint3D neighborDataPoint, ref int neighborPointIndex)
2693 		{
2694 			// Try to look around the neighbor point index
2695 			if(neighborPointIndex != int.MinValue)
2696 			{
2697 				// Try getting the next point
2698 				if(neighborPointIndex < (points.Count - 2))
2699 				{
2700 					DataPoint3D point = (DataPoint3D)points[neighborPointIndex + 1];
2701 
2702 					// Check required point index for the first point
2703 					if( point.index == index &&
2704                         (neighborDataPoint == null || String.Compare(neighborDataPoint.dataPoint.series.Name, point.dataPoint.series.Name, StringComparison.Ordinal) == 0))
2705 					{
2706 						++neighborPointIndex;
2707 						return point;
2708 					}
2709 				}
2710 
2711 				// Try getting the prev point
2712 				if(neighborPointIndex > 0)
2713 				{
2714 					DataPoint3D point = (DataPoint3D)points[neighborPointIndex - 1];
2715 
2716 					// Check required point index for the first point
2717 					if( point.index == index &&
2718                         (neighborDataPoint == null || String.Compare(neighborDataPoint.dataPoint.series.Name, point.dataPoint.series.Name, StringComparison.Ordinal) == 0))
2719 					{
2720 						--neighborPointIndex;
2721 						return point;
2722 					}
2723 				}
2724 
2725 			}
2726 
2727 			// Loop through all points
2728 			neighborPointIndex = 0;
2729 			foreach(DataPoint3D point3D in points)
2730 			{
2731 				// Check required point index for the first point
2732 				if(point3D.index == index)
2733 				{
2734 					// Check if point belongs to the same series
2735 					if(neighborDataPoint != null)
2736 					{
2737                         if (String.Compare(neighborDataPoint.dataPoint.series.Name, point3D.dataPoint.series.Name, StringComparison.Ordinal) != 0)
2738 						{
2739 							++neighborPointIndex;
2740 							continue;
2741 						}
2742 					}
2743 
2744 					// Point found
2745 					return (DataPoint3D)point3D;
2746 				}
2747 
2748 				++neighborPointIndex;
2749 			}
2750 
2751 			// Data point was not found
2752 			return null;
2753 		}
2754 
2755 
2756 		#endregion
2757 
2758 		#region 3D Rectangle drawing methods
2759 
2760 		/// <summary>
2761 		/// Function is used to calculate the coordinates of the 2D rectangle in 3D space
2762 		/// and either draw it or/and calculate the bounding path for selection.
2763 		/// </summary>
2764 		/// <param name="position">Position of 2D rectangle.</param>
2765 		/// <param name="positionZ">Z position of the back side of the 3D rectangle.</param>
2766 		/// <param name="depth">Depth of the 3D rectangle.</param>
2767 		/// <param name="matrix">Coordinate transformation matrix.</param>
2768 		/// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
2769 		/// <param name="backColor">Color of rectangle</param>
2770 		/// <param name="borderColor">Border Color</param>
2771 		/// <param name="borderWidth">Border Width</param>
2772 		/// <param name="borderDashStyle">Border Style</param>
2773 		/// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
2774 		/// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
Fill3DRectangle( RectangleF position, float positionZ, float depth, Matrix3D matrix, LightStyle lightStyle, Color backColor, Color borderColor, int borderWidth, ChartDashStyle borderDashStyle, DrawingOperationTypes operationType)2775 		internal GraphicsPath Fill3DRectangle(
2776 			RectangleF position,
2777 			float positionZ,
2778 			float depth,
2779 			Matrix3D matrix,
2780 			LightStyle lightStyle,
2781 			Color backColor,
2782 			Color borderColor,
2783 			int borderWidth,
2784 			ChartDashStyle borderDashStyle,
2785 			DrawingOperationTypes operationType)
2786 		{
2787 			return Fill3DRectangle(
2788 				position,
2789 				positionZ,
2790 				depth,
2791 				matrix,
2792 				lightStyle,
2793 				backColor,
2794 				0f,
2795 				0f,
2796 				borderColor,
2797 				borderWidth,
2798 				borderDashStyle,
2799 				BarDrawingStyle.Default,
2800 				false,
2801 				operationType);
2802 		}
2803 
2804         /// <summary>
2805         /// Function is used to calculate the coordinates of the 2D rectangle in 3D space
2806         /// and either draw it or/and calculate the bounding path for selection.
2807         /// </summary>
2808         /// <param name="position">Position of 2D rectangle.</param>
2809         /// <param name="positionZ">Z position of the back side of the 3D rectangle.</param>
2810         /// <param name="depth">Depth of the 3D rectangle.</param>
2811         /// <param name="matrix">Coordinate transformation matrix.</param>
2812         /// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
2813         /// <param name="backColor">Color of rectangle</param>
2814         /// <param name="topRightDarkening">Top (or right in bar chart) darkening effect.</param>
2815         /// <param name="bottomLeftDarkening">Bottom (or left in bar chart) darkening effect.</param>
2816         /// <param name="borderColor">Border Color</param>
2817         /// <param name="borderWidth">Border Width</param>
2818         /// <param name="borderDashStyle">Border Style</param>
2819         /// <param name="barDrawingStyle">Bar drawing style.</param>
2820         /// <param name="veticalOrientation">Defines if bar is vertical or horizontal.</param>
2821         /// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
2822         /// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
Fill3DRectangle( RectangleF position, float positionZ, float depth, Matrix3D matrix, LightStyle lightStyle, Color backColor, float topRightDarkening, float bottomLeftDarkening, Color borderColor, int borderWidth, ChartDashStyle borderDashStyle, BarDrawingStyle barDrawingStyle, bool veticalOrientation, DrawingOperationTypes operationType)2823         internal GraphicsPath Fill3DRectangle(
2824 			RectangleF position,
2825 			float positionZ,
2826 			float depth,
2827 			Matrix3D matrix,
2828 			LightStyle lightStyle,
2829 			Color backColor,
2830 			float topRightDarkening,
2831 			float bottomLeftDarkening,
2832 			Color borderColor,
2833 			int borderWidth,
2834 			ChartDashStyle borderDashStyle,
2835 			BarDrawingStyle barDrawingStyle,
2836 			bool veticalOrientation,
2837 			DrawingOperationTypes operationType)
2838 		{
2839 
2840 			// Check if special drawing is required
2841 			if(barDrawingStyle == BarDrawingStyle.Cylinder)
2842 			{
2843 				// Draw as 3D cylinder
2844 				return Fill3DRectangleAsCylinder(
2845 					position,
2846 					positionZ,
2847 					depth,
2848 					matrix,
2849 					lightStyle,
2850 					backColor,
2851 					topRightDarkening,
2852 					bottomLeftDarkening,
2853 					borderColor,
2854 					borderWidth,
2855 					borderDashStyle,
2856 					veticalOrientation,
2857 					operationType);
2858 			}
2859 
2860 			// Declare variables
2861 			Point3D[]		cubePoints = new Point3D[8];
2862 			GraphicsPath	resultPath = ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
2863 				? new GraphicsPath() : null;
2864 
2865 			// Front Side
2866 			cubePoints[0] = new Point3D( position.X, position.Y, positionZ + depth );
2867 			cubePoints[1] = new Point3D( position.X, position.Bottom, positionZ + depth );
2868 			cubePoints[2] = new Point3D( position.Right, position.Bottom, positionZ + depth );
2869 			cubePoints[3] = new Point3D( position.Right, position.Y, positionZ + depth );
2870 
2871 			// Back Side
2872 			cubePoints[4] = new Point3D( position.X, position.Y, positionZ );
2873 			cubePoints[5] = new Point3D( position.X, position.Bottom, positionZ );
2874 			cubePoints[6] = new Point3D( position.Right, position.Bottom, positionZ );
2875 			cubePoints[7] = new Point3D( position.Right, position.Y, positionZ );
2876 
2877 			// Tranform cube coordinates
2878 			matrix.TransformPoints( cubePoints );
2879 
2880 			// For lightStyle style Non, Border color always exist.
2881 			if( lightStyle == LightStyle.None &&
2882 				(borderWidth == 0 || borderDashStyle == ChartDashStyle.NotSet || borderColor == Color.Empty) )
2883 			{
2884 				borderColor = ChartGraphics.GetGradientColor( backColor, Color.Black, 0.5 );
2885 			}
2886 
2887 			// Get surface colors
2888 			Color	frontLightColor, leftLightColor, topLightColor, backLightColor, rightLightColor, bottomLightColor;
2889 			matrix.GetLight( backColor, out frontLightColor, out backLightColor, out leftLightColor, out rightLightColor, out topLightColor, out bottomLightColor );
2890 
2891 			// Darken colors by specified values
2892 			if(topRightDarkening != 0f)
2893 			{
2894 				if(veticalOrientation)
2895 				{
2896 					topLightColor = ChartGraphics.GetGradientColor(topLightColor, Color.Black, topRightDarkening);
2897 				}
2898 				else
2899 				{
2900 					rightLightColor = ChartGraphics.GetGradientColor(rightLightColor, Color.Black, topRightDarkening);
2901 				}
2902 			}
2903 			if(bottomLeftDarkening != 0f)
2904 			{
2905 				if(veticalOrientation)
2906 				{
2907 					bottomLightColor = ChartGraphics.GetGradientColor(bottomLightColor, Color.Black, bottomLeftDarkening);
2908 				}
2909 				else
2910 				{
2911 					leftLightColor = ChartGraphics.GetGradientColor(leftLightColor, Color.Black, bottomLeftDarkening);
2912 				}
2913 			}
2914 
2915 
2916 			// Check visible surfaces
2917 			SurfaceNames visibleSurfaces = GetVisibleSurfacesWithPerspective(position,positionZ,depth,matrix);
2918 
2919 			// Draw all invisible surfaces first (if semi-transparent color is used)
2920 			for(int drawVisible = 0; drawVisible <= 1; drawVisible++)
2921 			{
2922 				// Do not draw invisible surfaces for solid colors
2923 				if(drawVisible == 0 && backColor.A == 255)
2924 				{
2925 					continue;
2926 				}
2927 
2928 				// Check visibility of all surfaces and draw them
2929 				for(int surfaceIndex = (int)SurfaceNames.Front; surfaceIndex <= (int)SurfaceNames.Bottom; surfaceIndex *= 2)
2930 				{
2931 					SurfaceNames	currentSurface = (SurfaceNames)surfaceIndex;
2932 
2933 					// If width, height or depth of the cube (3DRectangle) is zero graphical path
2934 					// should contain only one surface with 4 points.
2935 					if(depth == 0.0 && currentSurface != SurfaceNames.Front)
2936 					{
2937 						continue;
2938 					}
2939 					if(position.Width == 0.0 && currentSurface != SurfaceNames.Left && currentSurface != SurfaceNames.Right)
2940 					{
2941 						continue;
2942 					}
2943 					if(position.Height == 0.0 && currentSurface != SurfaceNames.Top && currentSurface != SurfaceNames.Bottom)
2944 					{
2945 						continue;
2946 					}
2947 
2948 					// Check if surface is visible or semi-transparent color is used
2949 					bool isVisible = (visibleSurfaces & currentSurface) != 0;
2950 					if(isVisible && drawVisible == 1 ||
2951 						!isVisible && drawVisible == 0)
2952 					{
2953 						// Fill surface coordinates and color
2954 						PointF [] pointsSurface = new PointF[4];
2955 						Color	surfaceColor = backColor;
2956 
2957 						switch(currentSurface)
2958 						{
2959 							case(SurfaceNames.Front):
2960 								surfaceColor = frontLightColor;
2961 								pointsSurface[0] = new PointF(cubePoints[0].X, cubePoints[0].Y);
2962 								pointsSurface[1] = new PointF(cubePoints[1].X, cubePoints[1].Y);
2963 								pointsSurface[2] = new PointF(cubePoints[2].X, cubePoints[2].Y);
2964 								pointsSurface[3] = new PointF(cubePoints[3].X, cubePoints[3].Y);
2965 								break;
2966 							case(SurfaceNames.Back):
2967 								surfaceColor = backLightColor;
2968 								pointsSurface[0] = new PointF(cubePoints[4].X, cubePoints[4].Y);
2969 								pointsSurface[1] = new PointF(cubePoints[5].X, cubePoints[5].Y);
2970 								pointsSurface[2] = new PointF(cubePoints[6].X, cubePoints[6].Y);
2971 								pointsSurface[3] = new PointF(cubePoints[7].X, cubePoints[7].Y);
2972 								break;
2973 							case(SurfaceNames.Left):
2974 								surfaceColor = leftLightColor;
2975 								pointsSurface[0] = new PointF(cubePoints[0].X, cubePoints[0].Y);
2976 								pointsSurface[1] = new PointF(cubePoints[1].X, cubePoints[1].Y);
2977 								pointsSurface[2] = new PointF(cubePoints[5].X, cubePoints[5].Y);
2978 								pointsSurface[3] = new PointF(cubePoints[4].X, cubePoints[4].Y);
2979 								break;
2980 							case(SurfaceNames.Right):
2981 								surfaceColor = rightLightColor;
2982 								pointsSurface[0] = new PointF(cubePoints[3].X, cubePoints[3].Y);
2983 								pointsSurface[1] = new PointF(cubePoints[2].X, cubePoints[2].Y);
2984 								pointsSurface[2] = new PointF(cubePoints[6].X, cubePoints[6].Y);
2985 								pointsSurface[3] = new PointF(cubePoints[7].X, cubePoints[7].Y);
2986 								break;
2987 							case(SurfaceNames.Top):
2988 								surfaceColor = topLightColor;
2989 								pointsSurface[0] = new PointF(cubePoints[0].X, cubePoints[0].Y);
2990 								pointsSurface[1] = new PointF(cubePoints[3].X, cubePoints[3].Y);
2991 								pointsSurface[2] = new PointF(cubePoints[7].X, cubePoints[7].Y);
2992 								pointsSurface[3] = new PointF(cubePoints[4].X, cubePoints[4].Y);
2993 								break;
2994 							case(SurfaceNames.Bottom):
2995 								surfaceColor = bottomLightColor;
2996 								pointsSurface[0] = new PointF(cubePoints[1].X, cubePoints[1].Y);
2997 								pointsSurface[1] = new PointF(cubePoints[2].X, cubePoints[2].Y);
2998 								pointsSurface[2] = new PointF(cubePoints[6].X, cubePoints[6].Y);
2999 								pointsSurface[3] = new PointF(cubePoints[5].X, cubePoints[5].Y);
3000 								break;
3001 						}
3002 
3003 						// Covert coordinates to absolute
3004 						for(int pointIndex = 0; pointIndex < pointsSurface.Length; pointIndex++)
3005 						{
3006 							pointsSurface[pointIndex] = GetAbsolutePoint(pointsSurface[pointIndex]);
3007 						}
3008 
3009 						// Draw surface
3010 						if( (operationType & DrawingOperationTypes.DrawElement) == DrawingOperationTypes.DrawElement)
3011 						{
3012 							// Draw only completly visible surfaces
3013 							if((visibleSurfaces & currentSurface) != 0)
3014 							{
3015                                 using (Brush brush = new SolidBrush(surfaceColor))
3016                                 {
3017                                     FillPolygon(brush, pointsSurface);
3018                                 }
3019 
3020 								// Check if any additional drawing should be done
3021 								if(currentSurface == SurfaceNames.Front &&
3022 									barDrawingStyle != BarDrawingStyle.Default &&
3023 									barDrawingStyle != BarDrawingStyle.Cylinder)
3024 								{
3025 									this.DrawBarStyleGradients(matrix, barDrawingStyle, position, positionZ, depth, veticalOrientation);
3026 								}
3027 							}
3028 
3029 							// Draw surface border
3030                             using (Pen pen = new Pen(borderColor, borderWidth))
3031                             {
3032                                 pen.DashStyle = GetPenStyle(borderDashStyle);
3033                                 if (lightStyle != LightStyle.None &&
3034                                     (borderWidth == 0 || borderDashStyle == ChartDashStyle.NotSet || borderColor == Color.Empty))
3035                                 {
3036                                     // Draw line of the same color inside the bar
3037                                     pen.Color = surfaceColor;
3038                                     pen.Width = 1;
3039                                     pen.Alignment = PenAlignment.Inset;
3040                                 }
3041 
3042                                 pen.StartCap = LineCap.Round;
3043                                 pen.EndCap = LineCap.Round;
3044                                 DrawLine(pen, pointsSurface[0], pointsSurface[1]);
3045                                 DrawLine(pen, pointsSurface[1], pointsSurface[2]);
3046                                 DrawLine(pen, pointsSurface[2], pointsSurface[3]);
3047                                 DrawLine(pen, pointsSurface[3], pointsSurface[0]);
3048                             }
3049 						}
3050 
3051 						// Add surface coordinate to the path
3052 						if( (operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
3053 						{
3054 							// Only if surface is completly visible
3055 							if((visibleSurfaces & currentSurface) != 0)
3056 							{
3057 								resultPath.SetMarkers();
3058 								resultPath.AddPolygon(pointsSurface);
3059 							}
3060 						}
3061 
3062 					}
3063 				}
3064 			}
3065 
3066 			return resultPath;
3067 		}
3068 
3069         /// <summary>
3070         /// Draws special bar style effect on the front surface of the bar.
3071         /// </summary>
3072         /// <param name="matrix">Drawing matrix.</param>
3073         /// <param name="barDrawingStyle">Bar drawing style.</param>
3074         /// <param name="position">Position in relative coordinates</param>
3075         /// <param name="positionZ">Z position.</param>
3076         /// <param name="depth">Depth.</param>
3077         /// <param name="isVertical">Defines if bar is vertical or horizontal.</param>
DrawBarStyleGradients( Matrix3D matrix, BarDrawingStyle barDrawingStyle, RectangleF position, float positionZ, float depth, bool isVertical)3078 		private void DrawBarStyleGradients(
3079 			Matrix3D matrix,
3080 			BarDrawingStyle barDrawingStyle,
3081 			RectangleF position,
3082 			float positionZ,
3083 			float depth,
3084 			bool isVertical)
3085 		{
3086 			if(barDrawingStyle == BarDrawingStyle.Wedge)
3087 			{
3088 				// Calculate wedge size to fit the rectangle
3089 				RectangleF positionAbs = GetAbsoluteRectangle(position);
3090 				float size = (isVertical) ? positionAbs.Width / 2f : positionAbs.Height / 2f;
3091 				if(isVertical && 2f * size > positionAbs.Height)
3092 				{
3093 					size = positionAbs.Height/2f;
3094 				}
3095 				if(!isVertical && 2f * size > positionAbs.Width)
3096 				{
3097 					size = positionAbs.Width/2f;
3098 				}
3099 				SizeF sizeRel = GetRelativeSize(new SizeF(size, size));
3100 
3101 				// Make 3D convertion of the key points
3102 				Point3D[] gradientPoints = new Point3D[6];
3103 				gradientPoints[0] = new Point3D( position.Left, position.Top, positionZ + depth );
3104 				gradientPoints[1] = new Point3D( position.Left, position.Bottom, positionZ + depth );
3105 				gradientPoints[2] = new Point3D( position.Right, position.Bottom, positionZ + depth );
3106 				gradientPoints[3] = new Point3D( position.Right, position.Top, positionZ + depth );
3107 				if(isVertical)
3108 				{
3109 					gradientPoints[4] = new Point3D( position.X + position.Width / 2f, position.Top + sizeRel.Height, positionZ + depth );
3110 					gradientPoints[5] = new Point3D( position.X + position.Width / 2f, position.Bottom - sizeRel.Height, positionZ + depth );
3111 				}
3112 				else
3113 				{
3114 					gradientPoints[4] = new Point3D( position.X + sizeRel.Width, position.Top + position.Height / 2f, positionZ + depth );
3115 					gradientPoints[5] = new Point3D( position.Right - sizeRel.Width, position.Top + position.Height / 2f, positionZ + depth );
3116 				}
3117 
3118 				// Tranform cube coordinates
3119 				matrix.TransformPoints( gradientPoints );
3120 
3121 				// Convert points to absolute
3122 				PointF [] gradientPointsAbs = new PointF[6];
3123 				for(int index = 0; index < gradientPoints.Length; index++)
3124 				{
3125 					gradientPointsAbs[index] = GetAbsolutePoint(gradientPoints[index].PointF);
3126 				}
3127 
3128 
3129 				// Draw left/bottom shadow
3130 				using(GraphicsPath path = new GraphicsPath())
3131 				{
3132 					if(isVertical)
3133 					{
3134 						path.AddLine(gradientPointsAbs[4], gradientPointsAbs[5]);
3135 						path.AddLine(gradientPointsAbs[5], gradientPointsAbs[2]);
3136 						path.AddLine(gradientPointsAbs[2], gradientPointsAbs[3]);
3137 					}
3138 					else
3139 					{
3140 						path.AddLine(gradientPointsAbs[4], gradientPointsAbs[5]);
3141 						path.AddLine(gradientPointsAbs[5], gradientPointsAbs[2]);
3142 						path.AddLine(gradientPointsAbs[2], gradientPointsAbs[1]);
3143 					}
3144 					path.CloseAllFigures();
3145 
3146 					// Create brush and fill path
3147 					using(SolidBrush brush = new SolidBrush(Color.FromArgb(90, Color.Black)))
3148 					{
3149 						this.FillPath(brush, path);
3150 					}
3151 				}
3152 
3153 				// Draw top/right triangle
3154 				using(GraphicsPath path = new GraphicsPath())
3155 				{
3156 					if(isVertical)
3157 					{
3158 						path.AddLine(gradientPointsAbs[0], gradientPointsAbs[4]);
3159 						path.AddLine(gradientPointsAbs[4], gradientPointsAbs[3]);
3160 					}
3161 					else
3162 					{
3163 						path.AddLine(gradientPointsAbs[3], gradientPointsAbs[5]);
3164 						path.AddLine(gradientPointsAbs[5], gradientPointsAbs[2]);
3165 					}
3166 
3167 					// Create brush and fill path
3168 					using(SolidBrush brush = new SolidBrush(Color.FromArgb(50, Color.Black)))
3169 					{
3170 						// Fill shadow path on the left-bottom side of the bar
3171 						this.FillPath(brush, path);
3172 
3173 						// Draw Lines
3174 						using(Pen penDark = new Pen(Color.FromArgb(20, Color.Black), 1))
3175 						{
3176 							this.DrawPath(penDark, path);
3177 							this.DrawLine(
3178 								penDark,
3179 								gradientPointsAbs[4],
3180 								gradientPointsAbs[5]);
3181 						}
3182 
3183 						// Draw Lines
3184 						using(Pen pen = new Pen(Color.FromArgb(40, Color.White), 1))
3185 						{
3186 							this.DrawPath(pen, path);
3187 							this.DrawLine(
3188 								pen,
3189 								gradientPointsAbs[4],
3190 								gradientPointsAbs[5]);
3191 						}
3192 					}
3193 				}
3194 
3195 				// Draw bottom/left triangle
3196 				using(GraphicsPath path = new GraphicsPath())
3197 				{
3198 					if(isVertical)
3199 					{
3200 						path.AddLine(gradientPointsAbs[1], gradientPointsAbs[5]);
3201 						path.AddLine(gradientPointsAbs[5], gradientPointsAbs[2]);
3202 					}
3203 					else
3204 					{
3205 						path.AddLine(gradientPointsAbs[0], gradientPointsAbs[4]);
3206 						path.AddLine(gradientPointsAbs[4], gradientPointsAbs[1]);
3207 					}
3208 
3209 					// Create brush
3210 					using(SolidBrush brush = new SolidBrush(Color.FromArgb(50, Color.Black)))
3211 					{
3212 						// Fill shadow path on the left-bottom side of the bar
3213 						this.FillPath(brush, path);
3214 
3215 						// Draw edges
3216 						using(Pen penDark = new Pen(Color.FromArgb(20, Color.Black), 1))
3217 						{
3218 							this.DrawPath(penDark, path);
3219 						}
3220 						using(Pen pen = new Pen(Color.FromArgb(40, Color.White), 1))
3221 						{
3222 							this.DrawPath(pen, path);
3223 						}
3224 					}
3225 				}
3226 
3227 
3228 			}
3229 			else if(barDrawingStyle == BarDrawingStyle.LightToDark)
3230 			{
3231 				// Calculate width of shadows used to create the effect
3232 				RectangleF positionAbs = GetAbsoluteRectangle(position);
3233 				float shadowSizeAbs = 5f;
3234 				if(positionAbs.Width < 6f || positionAbs.Height < 6f)
3235 				{
3236 					shadowSizeAbs = 2f;
3237 				}
3238 				else if(positionAbs.Width < 15f || positionAbs.Height < 15f)
3239 				{
3240 					shadowSizeAbs = 3f;
3241 				}
3242 				SizeF shadowSizeRel = GetRelativeSize(new SizeF(shadowSizeAbs, shadowSizeAbs));
3243 
3244 				// Calculate gradient position
3245 				RectangleF gradientRect = position;
3246 				gradientRect.Inflate(-shadowSizeRel.Width, -shadowSizeRel.Height);
3247 				if(isVertical)
3248 				{
3249 					gradientRect.Height = (float)Math.Floor(gradientRect.Height / 3f);
3250 				}
3251 				else
3252 				{
3253 					gradientRect.X = gradientRect.Right - (float)Math.Floor(gradientRect.Width / 3f);
3254 					gradientRect.Width = (float)Math.Floor(gradientRect.Width / 3f);
3255 				}
3256 
3257 
3258 				// Top gradient
3259 				Point3D[] gradientPoints = new Point3D[4];
3260 				gradientPoints[0] = new Point3D( gradientRect.Left, gradientRect.Top, positionZ + depth );
3261 				gradientPoints[1] = new Point3D( gradientRect.Left, gradientRect.Bottom, positionZ + depth );
3262 				gradientPoints[2] = new Point3D( gradientRect.Right, gradientRect.Bottom, positionZ + depth );
3263 				gradientPoints[3] = new Point3D( gradientRect.Right, gradientRect.Top, positionZ + depth );
3264 
3265 				// Tranform cube coordinates
3266 				matrix.TransformPoints( gradientPoints );
3267 
3268 				// Convert points to absolute
3269 				PointF [] gradientPointsAbs = new PointF[4];
3270 				for(int index = 0; index < gradientPoints.Length; index++)
3271 				{
3272 					gradientPointsAbs[index] = GetAbsolutePoint(gradientPoints[index].PointF);
3273 				}
3274 
3275 				// Create and draw top path
3276 				using(GraphicsPath path = new GraphicsPath())
3277 				{
3278 					path.AddPolygon(gradientPointsAbs);
3279 					RectangleF bounds = path.GetBounds();
3280 					bounds.Width += 1f;
3281 					bounds.Height += 1f;
3282 
3283 					// Create brush
3284 					if(bounds.Width > 0f && bounds.Height > 0f)
3285 					{
3286 						using(LinearGradientBrush topBrush = new LinearGradientBrush(
3287 								  bounds,
3288 								  (!isVertical) ? Color.Transparent : Color.FromArgb(120, Color.White),
3289 								  (!isVertical) ? Color.FromArgb(120, Color.White) : Color.Transparent,
3290 								  (isVertical) ? LinearGradientMode.Vertical : LinearGradientMode.Horizontal))
3291 						{
3292 							// Fill shadow path on the top side of the bar
3293 							this.FillPath(topBrush, path);
3294 						}
3295 					}
3296 				}
3297 
3298 
3299 
3300 				// Calculate gradient position for the bottom gradient
3301 				gradientRect = position;
3302 				gradientRect.Inflate(-shadowSizeRel.Width, -shadowSizeRel.Height);
3303 				if(isVertical)
3304 				{
3305 					gradientRect.Y = gradientRect.Bottom - (float)Math.Floor(gradientRect.Height / 3f);
3306 					gradientRect.Height = (float)Math.Floor(gradientRect.Height / 3f);
3307 				}
3308 				else
3309 				{
3310 					gradientRect.Width = (float)Math.Floor(gradientRect.Width / 3f);
3311 				}
3312 
3313 
3314 				// Top gradient
3315 				gradientPoints = new Point3D[4];
3316 				gradientPoints[0] = new Point3D( gradientRect.Left, gradientRect.Top, positionZ + depth );
3317 				gradientPoints[1] = new Point3D( gradientRect.Left, gradientRect.Bottom, positionZ + depth );
3318 				gradientPoints[2] = new Point3D( gradientRect.Right, gradientRect.Bottom, positionZ + depth );
3319 				gradientPoints[3] = new Point3D( gradientRect.Right, gradientRect.Top, positionZ + depth );
3320 
3321 				// Tranform cube coordinates
3322 				matrix.TransformPoints( gradientPoints );
3323 
3324 				// Convert points to absolute
3325 				gradientPointsAbs = new PointF[4];
3326 				for(int index = 0; index < gradientPoints.Length; index++)
3327 				{
3328 					gradientPointsAbs[index] = GetAbsolutePoint(gradientPoints[index].PointF);
3329 				}
3330 
3331 				// Create and draw top path
3332 				using(GraphicsPath path = new GraphicsPath())
3333 				{
3334 					path.AddPolygon(gradientPointsAbs);
3335 					RectangleF bounds = path.GetBounds();
3336 					bounds.Width += 1f;
3337 					bounds.Height += 1f;
3338 
3339 					// Create brush
3340 					if(bounds.Width > 0f && bounds.Height > 0f)
3341 					{
3342 						using(LinearGradientBrush topBrush = new LinearGradientBrush(
3343 								  bounds,
3344 								  (isVertical) ? Color.Transparent : Color.FromArgb(80, Color.Black),
3345 								  (isVertical) ? Color.FromArgb(80, Color.Black) : Color.Transparent,
3346 								  (isVertical) ? LinearGradientMode.Vertical : LinearGradientMode.Horizontal))
3347 						{
3348 							// Fill shadow path on the top side of the bar
3349 							this.FillPath(topBrush, path);
3350 						}
3351 					}
3352 				}
3353 
3354 			}
3355 			else if(barDrawingStyle == BarDrawingStyle.Emboss)
3356 			{
3357 				// Calculate width of shadows used to create the effect
3358 				RectangleF positionAbs = GetAbsoluteRectangle(position);
3359 				float shadowSizeAbs = 4f;
3360 				if(positionAbs.Width < 6f || positionAbs.Height < 6f)
3361 				{
3362 					shadowSizeAbs = 2f;
3363 				}
3364 				else if(positionAbs.Width < 15f || positionAbs.Height < 15f)
3365 				{
3366 					shadowSizeAbs = 3f;
3367 				}
3368 				SizeF shadowSizeRel = GetRelativeSize(new SizeF(shadowSizeAbs, shadowSizeAbs));
3369 
3370 				// Left/top Side
3371 				Point3D[] gradientPoints = new Point3D[6];
3372 				gradientPoints[0] = new Point3D( position.Left, position.Bottom, positionZ + depth );
3373 				gradientPoints[1] = new Point3D( position.Left, position.Top, positionZ + depth );
3374 				gradientPoints[2] = new Point3D( position.Right, position.Top, positionZ + depth );
3375 				gradientPoints[3] = new Point3D( position.Right - shadowSizeRel.Width, position.Top + shadowSizeRel.Height, positionZ + depth );
3376 				gradientPoints[4] = new Point3D( position.Left + shadowSizeRel.Width, position.Top + shadowSizeRel.Height, positionZ + depth );
3377 				gradientPoints[5] = new Point3D( position.Left + shadowSizeRel.Width, position.Bottom - shadowSizeRel.Height, positionZ + depth );
3378 
3379 				// Tranform cube coordinates
3380 				matrix.TransformPoints( gradientPoints );
3381 
3382 				// Convert points to absolute
3383 				PointF [] gradientPointsAbs = new PointF[6];
3384 				for(int index = 0; index < gradientPoints.Length; index++)
3385 				{
3386 					gradientPointsAbs[index] = GetAbsolutePoint(gradientPoints[index].PointF);
3387 				}
3388 
3389 				// Create and draw left/top path
3390 				using(GraphicsPath path = new GraphicsPath())
3391 				{
3392 					path.AddPolygon(gradientPointsAbs);
3393 
3394 					// Create brush
3395 					using(SolidBrush leftTopBrush = new SolidBrush(Color.FromArgb(100, Color.White)))
3396 					{
3397 						// Fill shadow path on the left-bottom side of the bar
3398 						this.FillPath(leftTopBrush, path);
3399 					}
3400 				}
3401 
3402 				// Right/bottom Side
3403 				gradientPoints[0] = new Point3D( position.Right, position.Top, positionZ + depth );
3404 				gradientPoints[1] = new Point3D( position.Right, position.Bottom, positionZ + depth );
3405 				gradientPoints[2] = new Point3D( position.Left, position.Bottom, positionZ + depth );
3406 				gradientPoints[3] = new Point3D( position.Left + shadowSizeRel.Width, position.Bottom - shadowSizeRel.Height, positionZ + depth );
3407 				gradientPoints[4] = new Point3D( position.Right - shadowSizeRel.Width, position.Bottom - shadowSizeRel.Height, positionZ + depth );
3408 				gradientPoints[5] = new Point3D( position.Right - shadowSizeRel.Width, position.Top + shadowSizeRel.Height, positionZ + depth );
3409 
3410 				// Tranform cube coordinates
3411 				matrix.TransformPoints( gradientPoints );
3412 
3413 				// Convert points to absolute
3414 				for(int index = 0; index < gradientPoints.Length; index++)
3415 				{
3416 					gradientPointsAbs[index] = GetAbsolutePoint(gradientPoints[index].PointF);
3417 				}
3418 
3419 				// Create and draw left/top path
3420 				using(GraphicsPath path = new GraphicsPath())
3421 				{
3422 					path.AddPolygon(gradientPointsAbs);
3423 
3424 					// Create brush
3425 					using(SolidBrush bottomRightBrush = new SolidBrush(Color.FromArgb(80, Color.Black)))
3426 					{
3427 						// Fill shadow path on the left-bottom side of the bar
3428 						this.FillPath(bottomRightBrush, path);
3429 					}
3430 				}
3431 			}
3432 		}
3433 
3434 		#endregion
3435 
3436 		#region 3D markers drawing methods
3437 
3438 		/// <summary>
3439 		/// Draw marker using absolute coordinates of the center.
3440 		/// </summary>
3441 		/// <param name="matrix">Coordinates transformation matrix.</param>
3442 		/// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
3443 		/// <param name="positionZ">Z position of the 3D marker center.</param>
3444 		/// <param name="point">Coordinates of the center.</param>
3445 		/// <param name="markerStyle">Marker style.</param>
3446 		/// <param name="markerSize">Marker size.</param>
3447 		/// <param name="markerColor">Marker color.</param>
3448 		/// <param name="markerBorderColor">Marker border color.</param>
3449 		/// <param name="markerBorderSize">Marker border size.</param>
3450 		/// <param name="markerImage">Marker image name.</param>
3451 		/// <param name="markerImageTransparentColor">Marker image transparent color.</param>
3452 		/// <param name="shadowSize">Marker shadow size.</param>
3453 		/// <param name="shadowColor">Marker shadow color.</param>
3454 		/// <param name="imageScaleRect">Rectangle to which marker image should be scaled.</param>
3455 		/// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
3456 		/// <returns>Returns elemnt shape path if operationType parameter is set to ElementPath, otherwise Null.</returns>
DrawMarker3D( Matrix3D matrix, LightStyle lightStyle, float positionZ, PointF point, MarkerStyle markerStyle, int markerSize, Color markerColor, Color markerBorderColor, int markerBorderSize, string markerImage, Color markerImageTransparentColor, int shadowSize, Color shadowColor, RectangleF imageScaleRect, DrawingOperationTypes operationType )3457 		internal GraphicsPath DrawMarker3D(
3458 			Matrix3D matrix,
3459 			LightStyle lightStyle,
3460 			float positionZ,
3461 			PointF point,
3462 			MarkerStyle markerStyle,
3463 			int markerSize,
3464 			Color markerColor,
3465 			Color markerBorderColor,
3466 			int markerBorderSize,
3467 			string markerImage,
3468 			Color markerImageTransparentColor,
3469 			int shadowSize,
3470 			Color shadowColor,
3471 			RectangleF imageScaleRect,
3472 			DrawingOperationTypes operationType )
3473 		{
3474 			ChartGraphics	graph = (ChartGraphics)this;
3475 			GraphicsPath	resultPath = ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
3476 				? new GraphicsPath() : null;
3477 
3478 			//************************************************************
3479 			//** Transform marker position in 3D space
3480 			//************************************************************
3481 			// Get projection coordinates
3482 			Point3D[]	marker3DPosition = new Point3D[1];
3483 			marker3DPosition[0] = new Point3D(point.X, point.Y, positionZ);
3484 
3485 			// Transform coordinates of the marker center
3486 			matrix.TransformPoints(marker3DPosition);
3487 			PointF	markerRotatedPosition = marker3DPosition[0].PointF;
3488 
3489 			// Translate to absolute coordinates
3490 			markerRotatedPosition = graph.GetAbsolutePoint(markerRotatedPosition);
3491 
3492 			//************************************************************
3493 			//** For those markers that do not have a 3D version - draw the same as in 2D
3494 			//************************************************************
3495 			if(markerImage.Length > 0 ||
3496 				!(markerStyle == MarkerStyle.Circle ||
3497 				markerStyle == MarkerStyle.Square) )
3498 			{
3499 				// Call 2D version of the method
3500 				if( (operationType & DrawingOperationTypes.DrawElement) == DrawingOperationTypes.DrawElement)
3501 				{
3502 					graph.DrawMarkerAbs(markerRotatedPosition, markerStyle, markerSize, markerColor, markerBorderColor, markerBorderSize, markerImage, markerImageTransparentColor, shadowSize, shadowColor, imageScaleRect, false);
3503 				}
3504 
3505 				// Prepare marker path
3506 				if( (operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
3507 				{
3508 					RectangleF	rect = RectangleF.Empty;
3509 					rect.X = markerRotatedPosition.X - ((float)markerSize)/2F;
3510 					rect.Y = markerRotatedPosition.Y - ((float)markerSize)/2F;
3511 					rect.Width = markerSize;
3512 					rect.Height = markerSize;
3513 					resultPath.AddRectangle(rect);
3514 				}
3515 
3516 				return resultPath;
3517 			}
3518 
3519 			//************************************************************
3520 			//** Draw marker
3521 			//************************************************************
3522 			// Check if marker properties are set
3523             if (markerStyle != MarkerStyle.None && markerSize > 0 && markerColor != Color.Empty)
3524             {
3525                 // Create solid color brush
3526                 using (SolidBrush brush = new SolidBrush(markerColor))
3527                 {
3528 
3529                     // Calculate marker rectangle
3530                     RectangleF rect = RectangleF.Empty;
3531                     rect.X = markerRotatedPosition.X - ((float)markerSize) / 2F;
3532                     rect.Y = markerRotatedPosition.Y - ((float)markerSize) / 2F;
3533                     rect.Width = markerSize;
3534                     rect.Height = markerSize;
3535 
3536                     // Calculate relative marker size
3537                     SizeF markerRelativeSize = graph.GetRelativeSize(new SizeF(markerSize, markerSize));
3538 
3539                     // Draw marker depending on style
3540                     switch (markerStyle)
3541                     {
3542                         case (MarkerStyle.Circle):
3543                             {
3544                                 if ((operationType & DrawingOperationTypes.DrawElement) == DrawingOperationTypes.DrawElement)
3545                                 {
3546                                     // Draw marker shadow
3547                                     if (shadowSize != 0 && shadowColor != Color.Empty)
3548                                     {
3549                                         if (!graph.softShadows)
3550                                         {
3551                                             using (Brush shadowBrush = new SolidBrush((shadowColor.A != 255) ? shadowColor : Color.FromArgb(markerColor.A / 2, shadowColor)))
3552                                             {
3553                                                 RectangleF shadowRect = rect;
3554                                                 shadowRect.X += shadowSize;
3555                                                 shadowRect.Y += shadowSize;
3556                                                 graph.FillEllipse(shadowBrush, shadowRect);
3557                                             }
3558                                         }
3559                                         else
3560                                         {
3561                                             // Add circle to the graphics path
3562                                             using (GraphicsPath path = new GraphicsPath())
3563                                             {
3564                                                 path.AddEllipse(rect.X + shadowSize - 1, rect.Y + shadowSize - 1, rect.Width + 2, rect.Height + 2);
3565 
3566                                                 // Create path brush
3567                                                 using (PathGradientBrush shadowBrush = new PathGradientBrush(path))
3568                                                 {
3569                                                     shadowBrush.CenterColor = shadowColor;
3570 
3571                                                     // Set the color along the entire boundary of the path
3572                                                     Color[] colors = { Color.Transparent };
3573                                                     shadowBrush.SurroundColors = colors;
3574                                                     shadowBrush.CenterPoint = new PointF(markerRotatedPosition.X, markerRotatedPosition.Y);
3575 
3576                                                     // Define brush focus scale
3577                                                     PointF focusScale = new PointF(1 - 2f * shadowSize / rect.Width, 1 - 2f * shadowSize / rect.Height);
3578                                                     if (focusScale.X < 0)
3579                                                     {
3580                                                         focusScale.X = 0;
3581                                                     }
3582                                                     if (focusScale.Y < 0)
3583                                                     {
3584                                                         focusScale.Y = 0;
3585                                                     }
3586                                                     shadowBrush.FocusScales = focusScale;
3587 
3588                                                     // Draw shadow
3589                                                     graph.FillPath(shadowBrush, path);
3590                                                 }
3591                                             }
3592                                         }
3593                                     }
3594 
3595                                     // Create path gradient brush
3596                                     using (GraphicsPath brushPath = new GraphicsPath())
3597                                     {
3598                                         RectangleF rectLightCenter = new RectangleF(rect.Location, rect.Size);
3599                                         rectLightCenter.Inflate(rectLightCenter.Width / 4f, rectLightCenter.Height / 4f);
3600                                         brushPath.AddEllipse(rectLightCenter);
3601                                         using (PathGradientBrush circleBrush = new PathGradientBrush(brushPath))
3602                                         {
3603                                             circleBrush.CenterColor = ChartGraphics.GetGradientColor(markerColor, Color.White, 0.85);
3604                                             circleBrush.SurroundColors = new Color[] { markerColor };
3605 
3606                                             // Calculate the center point of the gradient
3607                                             Point3D[] centerPoint = new Point3D[] { new Point3D(point.X, point.Y, positionZ + markerRelativeSize.Width) };
3608                                             matrix.TransformPoints(centerPoint);
3609                                             centerPoint[0].PointF = graph.GetAbsolutePoint(centerPoint[0].PointF);
3610                                             circleBrush.CenterPoint = centerPoint[0].PointF;
3611 
3612                                             // Draw circle (sphere)
3613                                             graph.FillEllipse(circleBrush, rect);
3614                                             graph.DrawEllipse(new Pen(markerBorderColor, markerBorderSize), rect);
3615                                         }
3616                                     }
3617                                 }
3618 
3619                                 // Prepare marker path
3620                                 if ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
3621                                 {
3622                                     resultPath.AddEllipse(rect);
3623                                 }
3624 
3625                                 break;
3626                             }
3627                         case (MarkerStyle.Square):
3628                             {
3629 
3630                                 // Calculate marker non-rotated rectangle
3631                                 RectangleF rectNonRotated = RectangleF.Empty;
3632                                 rectNonRotated.X = point.X - ((float)markerRelativeSize.Width) / 2F;
3633                                 rectNonRotated.Y = point.Y - ((float)markerRelativeSize.Height) / 2F;
3634                                 rectNonRotated.Width = markerRelativeSize.Width;
3635                                 rectNonRotated.Height = markerRelativeSize.Height;
3636 
3637                                 // Draw 3D bar
3638                                 resultPath = this.Fill3DRectangle(
3639                                     rectNonRotated,
3640                                     positionZ - markerRelativeSize.Width / 2f,
3641                                     markerRelativeSize.Width,
3642                                     matrix,
3643                                     lightStyle,
3644                                     markerColor,
3645                                     markerBorderColor,
3646                                     markerBorderSize,
3647                                     ChartDashStyle.Solid,
3648                                     operationType);
3649 
3650                                 break;
3651                             }
3652                         default:
3653                             {
3654                                 throw (new InvalidOperationException(SR.ExceptionGraphics3DMarkerStyleUnknown));
3655                             }
3656                     }
3657                 }
3658             }
3659 
3660 			return resultPath;
3661 		}
3662 
3663 		#endregion
3664 
3665 		#region 3D cube surface visibility methods
3666 
3667 		/// <summary>
3668 		/// Returns visible surfaces of the 3D cube.
3669 		/// </summary>
3670 		/// <param name="position">2D rectangle coordinates.</param>
3671 		/// <param name="positionZ">Z coordinate of the back side of the cube.</param>
3672 		/// <param name="depth">Cube depth.</param>
3673 		/// <param name="matrix">Coordinate transformation matrix.</param>
3674 		/// <returns>Visible surfaces.</returns>
GetVisibleSurfaces( RectangleF position, float positionZ, float depth, Matrix3D matrix )3675 		internal SurfaceNames GetVisibleSurfaces(
3676 			RectangleF position,
3677 			float positionZ,
3678 			float depth,
3679 			Matrix3D matrix
3680 			)
3681 		{
3682 			// Check if perspective is used
3683 			if(matrix.Perspective != 0)
3684 			{
3685 				// More sofisticated algorithm must be used for visibility detection.
3686 				return GetVisibleSurfacesWithPerspective(position, positionZ, depth, matrix);
3687 			}
3688 
3689 			// Front surface is always visible
3690 			SurfaceNames	result = SurfaceNames.Front;
3691 
3692 			// Left and Right surfaces depend on the Y axis angle
3693 			if (matrix.AngleY > 0)
3694 			{
3695 				result |= SurfaceNames.Right;
3696 			}
3697 			else if (matrix.AngleY < 0)
3698 			{
3699 				result |= SurfaceNames.Left;
3700 			}
3701 
3702 			// Top and Bottom surfaces depend on the X axis angle
3703             if (matrix.AngleX > 0)
3704 			{
3705 				result |= SurfaceNames.Top;
3706 			}
3707             else if (matrix.AngleX < 0)
3708 			{
3709 				result |= SurfaceNames.Bottom;
3710 			}
3711 
3712 			return result;
3713 		}
3714 
3715 		/// <summary>
3716 		/// Returns visible surfaces of the 3D cube.
3717 		/// This method takes in consideration the perspective.
3718 		/// </summary>
3719 		/// <param name="position">2D rectangle coordinates.</param>
3720 		/// <param name="positionZ">Z coordinate of the back side of the cube.</param>
3721 		/// <param name="depth">Cube depth.</param>
3722 		/// <param name="matrix">Coordinate transformation matrix.</param>
3723 		/// <returns>Visible surfaces.</returns>
GetVisibleSurfacesWithPerspective( RectangleF position, float positionZ, float depth, Matrix3D matrix)3724 		internal SurfaceNames GetVisibleSurfacesWithPerspective(
3725 			RectangleF position,
3726 			float positionZ,
3727 			float depth,
3728 			Matrix3D matrix)
3729 		{
3730 			// Create cube coordinates in 3D space
3731 			Point3D[] cubePoints = new Point3D[8];
3732 
3733 			// Front Side
3734 			cubePoints[0] = new Point3D( position.X, position.Y, positionZ + depth );
3735 			cubePoints[1] = new Point3D( position.X, position.Bottom, positionZ + depth );
3736 			cubePoints[2] = new Point3D( position.Right, position.Bottom, positionZ + depth );
3737 			cubePoints[3] = new Point3D( position.Right, position.Y, positionZ + depth );
3738 
3739 			// Back Side
3740 			cubePoints[4] = new Point3D( position.X, position.Y, positionZ );
3741 			cubePoints[5] = new Point3D( position.X, position.Bottom, positionZ );
3742 			cubePoints[6] = new Point3D( position.Right, position.Bottom, positionZ );
3743 			cubePoints[7] = new Point3D( position.Right, position.Y, positionZ );
3744 
3745 			// Tranform coordinates
3746 			matrix.TransformPoints( cubePoints );
3747 
3748 			// Detect surfaces visibility
3749 			return GetVisibleSurfacesWithPerspective(cubePoints);
3750 		}
3751 
3752 		/// <summary>
3753 		/// Returns visible surfaces of the 3D cube.
3754 		/// This method takes in consideration the perspective.
3755 		/// </summary>
3756 		/// <param name="cubePoints">Array of 8 points which define the cube.</param>
3757 		/// <returns>Visible surfaces.</returns>
GetVisibleSurfacesWithPerspective(Point3D[] cubePoints)3758 		internal SurfaceNames GetVisibleSurfacesWithPerspective(Point3D[] cubePoints)
3759 		{
3760 			// Check imput array size
3761 			if(cubePoints.Length != 8)
3762 			{
3763                 throw (new ArgumentException(SR.ExceptionGraphics3DCoordinatesInvalid, "cubePoints"));
3764 			}
3765 
3766 			// Detect surfaces visibility
3767 			SurfaceNames	result = 0;
3768 
3769 			// Check the front side
3770 			if(IsSurfaceVisible(cubePoints[0],cubePoints[3],cubePoints[2]))
3771 			{
3772 				result |= SurfaceNames.Front;
3773 			}
3774 			// Check the back side
3775 			if(IsSurfaceVisible(cubePoints[4],cubePoints[5],cubePoints[6]))
3776 			{
3777 				result |= SurfaceNames.Back;
3778 			}
3779 
3780 			// Check the left side
3781 			if(IsSurfaceVisible(cubePoints[0],cubePoints[1],cubePoints[5]))
3782 			{
3783 				result |= SurfaceNames.Left;
3784 			}
3785 
3786 			// Check the right side
3787 			if(IsSurfaceVisible(cubePoints[3],cubePoints[7],cubePoints[6]))
3788 			{
3789 				result |= SurfaceNames.Right;
3790 			}
3791 
3792 			// Check the top side
3793 			if(IsSurfaceVisible(cubePoints[4],cubePoints[7],cubePoints[3]))
3794 			{
3795 				result |= SurfaceNames.Top;
3796 			}
3797 
3798 			// Check the bottom side
3799 			if(IsSurfaceVisible(cubePoints[1],cubePoints[2],cubePoints[6]))
3800 			{
3801 				result |= SurfaceNames.Bottom;
3802 			}
3803 
3804 			return result;
3805 		}
3806 
3807         /// <summary>
3808         /// Checks surface visibility using 3 points and clockwise points index rotation.
3809         /// </summary>
3810         /// <param name="first">First point.</param>
3811         /// <param name="second">Second point.</param>
3812         /// <param name="tree">Third point.</param>
3813         /// <returns>True if surface is visible</returns>
IsSurfaceVisible( Point3D first, Point3D second, Point3D tree )3814 		internal static bool IsSurfaceVisible( Point3D first, Point3D second, Point3D tree )
3815 		{
3816 			// Check if points are oriented clocwise in 2D projection.
3817 			// If points are clockwise the surface is visible.
3818 			float a = ( first.Y - second.Y ) / ( first.X - second.X );
3819 			float b = first.Y - a * first.X;
3820 			if( first.X == second.X )
3821 			{
3822 				if( first.Y > second.Y )
3823 				{
3824 					if( tree.X > first.X )
3825 					{
3826 						return true;
3827 					}
3828 					else
3829 					{
3830 						return false;
3831 					}
3832 				}
3833 				else
3834 				{
3835 					if( tree.X > first.X )
3836 					{
3837 						return false;
3838 					}
3839 					else
3840 					{
3841 						return true;
3842 					}
3843 				}
3844 			}
3845 			else if ( first.X < second.X )
3846 			{
3847 				if( tree.Y < a * tree.X + b )
3848 				{
3849 					return false;
3850 				}
3851 				else
3852 				{
3853 					return true;
3854 				}
3855 			}
3856 			else
3857 			{
3858 				if( tree.Y <= a * tree.X + b )
3859 				{
3860 					return true;
3861 				}
3862 				else
3863 				{
3864 					return false;
3865 				}
3866 			}
3867 		}
3868 
3869 		#endregion
3870 
3871 		#region Line intersection helper method
3872 
3873         /// <summary>
3874         /// Gets intersection point of two lines
3875         /// </summary>
3876         /// <param name="x1">First X value of first line.</param>
3877         /// <param name="y1">First Y value of first line.</param>
3878         /// <param name="x2">Second X value of first line.</param>
3879         /// <param name="y2">Second Y value of first line.</param>
3880         /// <param name="x3">First X value of second line.</param>
3881         /// <param name="y3">First Y value of second line.</param>
3882         /// <param name="x4">Second X value of second line.</param>
3883         /// <param name="y4">Second Y value of second line.</param>
3884         /// <returns>Intersection coordinates.</returns>
GetLinesIntersection(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4)3885 		internal static PointF GetLinesIntersection(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4)
3886 		{
3887 			PointF result = PointF.Empty;
3888 
3889 			// Special case for horizontal & vertical lines
3890 			if(x1 == x2 && y3 == y4)
3891 			{
3892 				result.X = x1;
3893 				result.Y = y3;
3894 				return result;
3895 			}
3896 			else if(y1 == y2 && x3 == x4)
3897 			{
3898 				result.X = x3;
3899 				result.Y = y1;
3900 				return result;
3901 			}
3902 			else if(x1 == x2)
3903 			{
3904 				result.X = x1;
3905 				result.Y = (result.X - x3) * (y4 - y3);
3906 				result.Y /= x4 - x3;
3907 				result.Y += y3;
3908 				return result;
3909 			}
3910 			else if(x3 == x4)
3911 			{
3912 				result.X = x3;
3913 				result.Y = (result.X - x1) * (y2 - y1);
3914 				result.Y /= x2 - x1;
3915 				result.Y += y1;
3916 				return result;
3917 			}
3918 
3919 			// Calculate line eqaution
3920 			float a1 = ( y1 - y2 ) / ( x1 - x2 );
3921 			float b1 = y1 - a1 * x1;
3922 			float a2 = ( y3 - y4 ) / ( x3 - x4 );
3923 			float b2 = y3 - a2 * x3;
3924 
3925 			// Calculate intersection point
3926 			result.X = (b2 - b1)/(a1 - a2);
3927 			result.Y = a1*result.X + b1;
3928 
3929 			return result;
3930 		}
3931 
3932 		#endregion
3933 
3934 		#region 3D Cylinder drawing methods
3935 
3936 
3937         /// <summary>
3938         /// Function is used to calculate the coordinates of the 2D rectangle in 3D space
3939         /// and either draw it or/and calculate the bounding path for selection.
3940         /// </summary>
3941         /// <param name="position">Position of 2D rectangle.</param>
3942         /// <param name="positionZ">Z position of the back side of the 3D rectangle.</param>
3943         /// <param name="depth">Depth of the 3D rectangle.</param>
3944         /// <param name="matrix">Coordinate transformation matrix.</param>
3945         /// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
3946         /// <param name="backColor">Color of rectangle</param>
3947         /// <param name="topRightDarkening">Top (or right in bar chart) darkening effect.</param>
3948         /// <param name="bottomLeftDarkening">Bottom (or left in bar chart) darkening effect.</param>
3949         /// <param name="borderColor">Border Color</param>
3950         /// <param name="borderWidth">Border Width</param>
3951         /// <param name="borderDashStyle">Border Style</param>
3952         /// <param name="veticalOrientation">Defines if bar is vertical or horizontal.</param>
3953         /// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
3954         /// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
Fill3DRectangleAsCylinder( RectangleF position, float positionZ, float depth, Matrix3D matrix, LightStyle lightStyle, Color backColor, float topRightDarkening, float bottomLeftDarkening, Color borderColor, int borderWidth, ChartDashStyle borderDashStyle, bool veticalOrientation, DrawingOperationTypes operationType)3955 		internal GraphicsPath Fill3DRectangleAsCylinder(
3956 			RectangleF position,
3957 			float positionZ,
3958 			float depth,
3959 			Matrix3D matrix,
3960 			LightStyle lightStyle,
3961 			Color backColor,
3962 			float topRightDarkening,
3963 			float bottomLeftDarkening,
3964 			Color borderColor,
3965 			int borderWidth,
3966 			ChartDashStyle borderDashStyle,
3967 			bool veticalOrientation,
3968 			DrawingOperationTypes operationType)
3969 		{
3970 			Point3D[]		cubePoints = new Point3D[8];
3971 			GraphicsPath	resultPath = ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
3972 				? new GraphicsPath() : null;
3973 
3974 			//*******************************************************
3975 			//** Define coordinates to draw the cylinder
3976 			//*******************************************************
3977 			if(veticalOrientation)
3978 			{
3979 				cubePoints[0] = new Point3D( position.X, position.Y, positionZ + depth / 2f );
3980 				cubePoints[1] = new Point3D( position.X, position.Bottom, positionZ + depth / 2f );
3981 				cubePoints[2] = new Point3D( position.Right, position.Bottom, positionZ + depth / 2f );
3982 				cubePoints[3] = new Point3D( position.Right, position.Y, positionZ + depth / 2f );
3983 
3984 				float middleXValue = position.X + position.Width / 2f;
3985 				cubePoints[4] = new Point3D( middleXValue, position.Y, positionZ + depth );
3986 				cubePoints[5] = new Point3D( middleXValue, position.Bottom, positionZ + depth );
3987 				cubePoints[6] = new Point3D( middleXValue, position.Bottom, positionZ );
3988 				cubePoints[7] = new Point3D( middleXValue, position.Y, positionZ );
3989 			}
3990 			else
3991 			{
3992 				cubePoints[0] = new Point3D( position.Right, position.Y, positionZ + depth / 2f );
3993 				cubePoints[1] = new Point3D( position.X, position.Y, positionZ + depth / 2f );
3994 				cubePoints[2] = new Point3D( position.X, position.Bottom, positionZ + depth / 2f );
3995 				cubePoints[3] = new Point3D( position.Right, position.Bottom, positionZ + depth / 2f );
3996 
3997 				float middleYValue = position.Y + position.Height / 2f;
3998 				cubePoints[4] = new Point3D( position.Right, middleYValue, positionZ + depth );
3999 				cubePoints[5] = new Point3D( position.X, middleYValue, positionZ + depth );
4000 				cubePoints[6] = new Point3D( position.X, middleYValue, positionZ );
4001 				cubePoints[7] = new Point3D( position.Right, middleYValue, positionZ );
4002 			}
4003 
4004 			// Tranform cylinder coordinates
4005 			matrix.TransformPoints( cubePoints );
4006 
4007 			// Covert coordinates to absolute
4008 			for(int pointIndex = 0; pointIndex < cubePoints.Length; pointIndex++)
4009 			{
4010 				cubePoints[pointIndex].PointF = GetAbsolutePoint(cubePoints[pointIndex].PointF);
4011 			}
4012 
4013 			//*******************************************************
4014 			//** Get cylinder colors.
4015 			//*******************************************************
4016 			if( lightStyle == LightStyle.None &&
4017 				(borderWidth == 0 || borderDashStyle == ChartDashStyle.NotSet || borderColor == Color.Empty) )
4018 			{
4019 				borderColor = ChartGraphics.GetGradientColor( backColor, Color.Black, 0.5 );
4020 			}
4021 
4022 			// Get surface colors
4023 			Color	frontLightColor, leftLightColor, topLightColor, backLightColor, rightLightColor, bottomLightColor;
4024 			matrix.GetLight( backColor, out frontLightColor, out backLightColor, out leftLightColor, out rightLightColor, out topLightColor, out bottomLightColor );
4025 
4026 			// Darken colors by specified values
4027 			if(topRightDarkening != 0f)
4028 			{
4029 				if(veticalOrientation)
4030 				{
4031 					topLightColor = ChartGraphics.GetGradientColor(topLightColor, Color.Black, topRightDarkening);
4032 				}
4033 				else
4034 				{
4035 					rightLightColor = ChartGraphics.GetGradientColor(rightLightColor, Color.Black, topRightDarkening);
4036 				}
4037 			}
4038 			if(bottomLeftDarkening != 0f)
4039 			{
4040 				if(veticalOrientation)
4041 				{
4042 					bottomLightColor = ChartGraphics.GetGradientColor(bottomLightColor, Color.Black, bottomLeftDarkening);
4043 				}
4044 				else
4045 				{
4046 					leftLightColor = ChartGraphics.GetGradientColor(leftLightColor, Color.Black, bottomLeftDarkening);
4047 				}
4048 			}
4049 
4050 			//*******************************************************
4051 			//** Check visible surfaces
4052 			//*******************************************************
4053 			SurfaceNames visibleSurfaces = GetVisibleSurfacesWithPerspective(position,positionZ,depth,matrix);
4054 
4055 			// Front surface is always visible in cylinder
4056 			if( (visibleSurfaces & SurfaceNames.Front) !=  SurfaceNames.Front)
4057 			{
4058 				visibleSurfaces |= SurfaceNames.Front;
4059 			}
4060 
4061 			//*******************************************************
4062 			//** Create flattened paths for the sides of the
4063 			//** cylinder (top,bottom/left,rigth)
4064 			//*******************************************************
4065 			PointF[] sidePoints = new PointF[4];
4066 			sidePoints[0] = cubePoints[6].PointF;
4067 			sidePoints[1] = cubePoints[1].PointF;
4068 			sidePoints[2] = cubePoints[5].PointF;
4069 			sidePoints[3] = cubePoints[2].PointF;
4070 			GraphicsPath bottomLeftSide = new GraphicsPath();
4071 			bottomLeftSide.AddClosedCurve(sidePoints, 0.8f);
4072 			bottomLeftSide.Flatten();
4073 			sidePoints[0] = cubePoints[7].PointF;
4074 			sidePoints[1] = cubePoints[0].PointF;
4075 			sidePoints[2] = cubePoints[4].PointF;
4076 			sidePoints[3] = cubePoints[3].PointF;
4077 			GraphicsPath topRigthSide = new GraphicsPath();
4078 			topRigthSide.AddClosedCurve(sidePoints, 0.8f);
4079 			topRigthSide.Flatten();
4080 
4081 			//*******************************************************
4082 			//** Find cylinder angle
4083 			//*******************************************************
4084 			float cylinderAngle = 90f;
4085 			if(cubePoints[5].PointF.Y != cubePoints[4].PointF.Y)
4086 			{
4087 				cylinderAngle = (float)Math.Atan(
4088 					(cubePoints[4].PointF.X - cubePoints[5].PointF.X) /
4089 					(cubePoints[5].PointF.Y - cubePoints[4].PointF.Y) );
4090 				cylinderAngle = (float)Math.Round(cylinderAngle * 180f / (float)Math.PI);
4091 			}
4092 
4093 			//*******************************************************
4094 			//** Draw all invisible surfaces first (if semi-transparent color is used)
4095 			//*******************************************************
4096 			for(int drawVisible = 0; drawVisible <= 1; drawVisible++)
4097 			{
4098 				// Do not draw invisible surfaces for solid colors
4099 				if(drawVisible == 0 && backColor.A == 255)
4100 				{
4101 					continue;
4102 				}
4103 
4104 				// Check visibility of all surfaces and draw them
4105 				for(int surfaceIndex = (int)SurfaceNames.Front; surfaceIndex <= (int)SurfaceNames.Bottom; surfaceIndex *= 2)
4106 				{
4107 					SurfaceNames	currentSurface = (SurfaceNames)surfaceIndex;
4108 
4109 					// Check if surface is visible or semi-transparent color is used
4110 					bool isVisible = (visibleSurfaces & currentSurface) != 0;
4111 					if(isVisible && drawVisible == 1 ||
4112 						!isVisible && drawVisible == 0)
4113 					{
4114 						// Fill surface coordinates and color
4115 						GraphicsPath	pathToDraw = null;
4116 						Color			surfaceColor = backColor;
4117 
4118 						// Declare a special brush for the front surface
4119 						Brush			frontSurfaceBrush = null;
4120 
4121 						switch(currentSurface)
4122 						{
4123 							case(SurfaceNames.Front):
4124 							{
4125 								// Set front surface color
4126 								surfaceColor = backColor;
4127 
4128 								// Add ellipse segment of the cylinder on top/rigth (reversed)
4129 								pathToDraw = new GraphicsPath();
4130 								PointF	leftSideLinePoint = PointF.Empty;
4131 								PointF	rightSideLinePoint = PointF.Empty;
4132 								AddEllipseSegment(
4133 									pathToDraw,
4134 									topRigthSide,
4135 									bottomLeftSide,
4136 									(matrix.Perspective == 0) ? veticalOrientation : false,
4137 									cylinderAngle,
4138 									out leftSideLinePoint,
4139 									out rightSideLinePoint);
4140 								pathToDraw.Reverse();
4141 
4142 								// Add ellipse segment of the cylinder on bottom/left
4143 								PointF	leftOppSideLinePoint = PointF.Empty;
4144 								PointF	rightOppSideLinePoint = PointF.Empty;
4145 								AddEllipseSegment(
4146 									pathToDraw,
4147 									bottomLeftSide,
4148 									topRigthSide,
4149 									(matrix.Perspective == 0) ? veticalOrientation : false,
4150 									cylinderAngle,
4151 									out leftOppSideLinePoint,
4152 									out rightOppSideLinePoint);
4153 								pathToDraw.CloseAllFigures();
4154 
4155 								// Reset indexes of opposite side points
4156 								this._oppLeftBottomPoint = -1;
4157 								this._oppRigthTopPoint = -1;
4158 
4159 								// Create gradient brush for the front surface
4160 								if(lightStyle != LightStyle.None)
4161 								{
4162 									RectangleF boundsRect = pathToDraw.GetBounds();
4163 									if(boundsRect.Height > 0 && boundsRect.Width > 0)
4164 									{
4165 										Color	lightColor = ChartGraphics.GetGradientColor( backColor, Color.White, 0.3 );
4166 										Color	darkColor = ChartGraphics.GetGradientColor( backColor, Color.Black, 0.3 );
4167 
4168 										// Create gradient
4169 										if(!leftSideLinePoint.IsEmpty &&
4170 											!rightSideLinePoint.IsEmpty &&
4171 											!leftOppSideLinePoint.IsEmpty &&
4172 											!rightOppSideLinePoint.IsEmpty)
4173 										{
4174 											PointF	boundsRectMiddlePoint = PointF.Empty;
4175 											boundsRectMiddlePoint.X = boundsRect.X + boundsRect.Width/2f;
4176 											boundsRectMiddlePoint.Y = boundsRect.Y + boundsRect.Height/2f;
4177 
4178 											PointF	centralLinePoint = PointF.Empty;
4179 											double	centralLineAngle = ((cylinderAngle) * Math.PI / 180f);
4180 											if(cylinderAngle == 0 || cylinderAngle == 180 || cylinderAngle == -180)
4181 											{
4182 												centralLinePoint.X = boundsRectMiddlePoint.X + 100f;
4183 												centralLinePoint.Y = boundsRectMiddlePoint.Y;
4184 											}
4185 											else if(cylinderAngle == 90 || cylinderAngle == -90)
4186 											{
4187 												centralLinePoint.X = boundsRectMiddlePoint.X;
4188 												centralLinePoint.Y = boundsRectMiddlePoint.Y + 100f;
4189 											}
4190 											else if(cylinderAngle > -45 && cylinderAngle < 45)
4191 											{
4192 												centralLinePoint.X = boundsRectMiddlePoint.X + 100f;
4193 												centralLinePoint.Y = (float)(Math.Tan(centralLineAngle) * centralLinePoint.X);
4194 												centralLinePoint.Y += (float)(boundsRectMiddlePoint.Y - Math.Tan(centralLineAngle) * boundsRectMiddlePoint.X);
4195 											}
4196 											else
4197 											{
4198 												centralLinePoint.Y = boundsRectMiddlePoint.Y + 100f;
4199 												centralLinePoint.X = (float)(centralLinePoint.Y - (boundsRectMiddlePoint.Y - Math.Tan(centralLineAngle) * boundsRectMiddlePoint.X));
4200 												centralLinePoint.X /= (float)(Math.Tan(centralLineAngle));
4201 											}
4202 
4203 
4204 											PointF	middlePoint1 = ChartGraphics.GetLinesIntersection(
4205 												boundsRectMiddlePoint.X, boundsRectMiddlePoint.Y,
4206 												centralLinePoint.X, centralLinePoint.Y,
4207 												leftSideLinePoint.X, leftSideLinePoint.Y,
4208 												leftOppSideLinePoint.X, leftOppSideLinePoint.Y);
4209 
4210 											PointF	middlePoint2 = ChartGraphics.GetLinesIntersection(
4211 												boundsRectMiddlePoint.X, boundsRectMiddlePoint.Y,
4212 												centralLinePoint.X, centralLinePoint.Y,
4213 												rightSideLinePoint.X, rightSideLinePoint.Y,
4214 												rightOppSideLinePoint.X, rightOppSideLinePoint.Y);
4215 
4216 											// Gradient points can not have same coordinates
4217 											if(middlePoint1.X != middlePoint2.X || middlePoint1.Y != middlePoint2.Y)
4218 											{
4219 												frontSurfaceBrush = new LinearGradientBrush(
4220 													middlePoint1,
4221 													middlePoint2,
4222 													lightColor,
4223 													darkColor);
4224 
4225 
4226 												ColorBlend colorBlend = new ColorBlend(5);
4227 												colorBlend.Colors[0] = darkColor;
4228 												colorBlend.Colors[1] = darkColor;
4229 												colorBlend.Colors[2] = lightColor;
4230 												colorBlend.Colors[3] = darkColor;
4231 												colorBlend.Colors[4] = darkColor;
4232 
4233 												colorBlend.Positions[0] = 0.0f;
4234 												colorBlend.Positions[1] = 0.0f;
4235 												colorBlend.Positions[2] = 0.5f;
4236 												colorBlend.Positions[3] = 1.0f;
4237 												colorBlend.Positions[4] = 1.0f;
4238 
4239 												((LinearGradientBrush)frontSurfaceBrush).InterpolationColors = colorBlend;
4240 											}
4241 										}
4242 
4243 									}
4244 								}
4245 
4246 								break;
4247 							}
4248 							case(SurfaceNames.Top):
4249 								if(veticalOrientation)
4250 								{
4251 									surfaceColor = topLightColor;
4252 									pathToDraw = topRigthSide;
4253 								}
4254 								break;
4255 							case(SurfaceNames.Bottom):
4256 								if(veticalOrientation)
4257 								{
4258 									surfaceColor = bottomLightColor;
4259 									pathToDraw = bottomLeftSide;
4260 								}
4261 								break;
4262 							case(SurfaceNames.Right):
4263 								if(!veticalOrientation)
4264 								{
4265 									surfaceColor = rightLightColor;
4266 									pathToDraw = topRigthSide;
4267 								}
4268 								break;
4269 							case(SurfaceNames.Left):
4270 								if(!veticalOrientation)
4271 								{
4272 									surfaceColor = leftLightColor;
4273 									pathToDraw = bottomLeftSide;
4274 								}
4275 								break;
4276 						}
4277 
4278 
4279 						//*******************************************************
4280 						//** Draw surface
4281 						//*******************************************************
4282 						if(pathToDraw != null)
4283 						{
4284 							if( (operationType & DrawingOperationTypes.DrawElement) == DrawingOperationTypes.DrawElement)
4285 							{
4286 								// Draw only completly visible surfaces
4287 								if((visibleSurfaces & currentSurface) != 0)
4288 								{
4289                                     using (Brush brush = new SolidBrush(surfaceColor))
4290                                     {
4291 									    FillPath( (frontSurfaceBrush == null) ? brush : frontSurfaceBrush, pathToDraw );
4292                                     }
4293 								}
4294 
4295 								// Draw surface border
4296                                 using (Pen pen = new Pen(borderColor, borderWidth))
4297                                 {
4298                                     pen.DashStyle = GetPenStyle(borderDashStyle);
4299                                     if (lightStyle != LightStyle.None &&
4300                                         (borderWidth == 0 || borderDashStyle == ChartDashStyle.NotSet || borderColor == Color.Empty))
4301                                     {
4302                                         // Draw line of the darker color inside the cylinder
4303                                         pen.Color = frontSurfaceBrush == null ? surfaceColor : ChartGraphics.GetGradientColor(backColor, Color.Black, 0.3);
4304                                         pen.Width = 1;
4305                                         pen.Alignment = PenAlignment.Inset;
4306                                     }
4307 
4308                                     pen.StartCap = LineCap.Round;
4309                                     pen.EndCap = LineCap.Round;
4310                                     pen.LineJoin = LineJoin.Bevel;
4311                                     DrawPath(pen, pathToDraw);
4312                                 }
4313 							}
4314 
4315 							// Add surface coordinate to the path
4316 							if( (operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
4317 							{
4318 								// Only if surface is completly visible
4319 								if((visibleSurfaces & currentSurface) != 0)
4320 								{
4321 									if(pathToDraw != null  && pathToDraw.PointCount > 0)
4322 									{
4323 										resultPath.AddPath(pathToDraw, true);
4324 										resultPath.SetMarkers();
4325 									}
4326 								}
4327 							}
4328 						}
4329 
4330 					}
4331 				}
4332 			}
4333 
4334 			return resultPath;
4335 		}
4336 
4337 		/// <summary>
4338 		/// Adds segment of the ellipse to form the front surface of the cylinder
4339 		/// </summary>
AddEllipseSegment( GraphicsPath resultPath, GraphicsPath ellipseFlattenPath, GraphicsPath oppositeEllipseFlattenPath, bool veticalOrientation, float cylinderAngle, out PointF leftSideLinePoint, out PointF rightSideLinePoint)4340 		internal void AddEllipseSegment(
4341 			GraphicsPath resultPath,
4342 			GraphicsPath ellipseFlattenPath,
4343 			GraphicsPath oppositeEllipseFlattenPath,
4344 			bool veticalOrientation,
4345 			float cylinderAngle,
4346 			out PointF leftSideLinePoint,
4347 			out PointF rightSideLinePoint)
4348 		{
4349 			// Initialize return values
4350 			leftSideLinePoint = PointF.Empty;
4351 			rightSideLinePoint = PointF.Empty;
4352 
4353 			// Check if input path is empty
4354 			if(ellipseFlattenPath.PointCount == 0)
4355 			{
4356 				return;
4357 			}
4358 
4359 			// Find the index the left/bottom most and right/top most point in flatten array of ellipse points
4360 			int			leftBottomPoint = 0;
4361 			int			rigthTopPoint = 0;
4362 			PointF[]	ellipsePoints = ellipseFlattenPath.PathPoints;
4363 
4364 
4365 			if(veticalOrientation)
4366 			{
4367 				for(int pointIndex = 1; pointIndex < ellipsePoints.Length; pointIndex++)
4368 				{
4369 					if(ellipsePoints[leftBottomPoint].X > ellipsePoints[pointIndex].X)
4370 					{
4371 						leftBottomPoint = pointIndex;
4372 					}
4373 					if(ellipsePoints[rigthTopPoint].X < ellipsePoints[pointIndex].X)
4374 					{
4375 						rigthTopPoint = pointIndex;
4376 					}
4377 				}
4378 			}
4379 			else
4380 			{
4381 				bool	doneFlag = false;
4382 				leftBottomPoint = -1;
4383 				rigthTopPoint = -1;
4384 
4385 				if(this._oppLeftBottomPoint != -1 && this._oppRigthTopPoint != -1)
4386 				{
4387 					// Get index from previously calculated values
4388 					leftBottomPoint = this._oppLeftBottomPoint;
4389 					rigthTopPoint = this._oppRigthTopPoint;
4390 				}
4391 				else
4392 				{
4393 					// Loop through first ellipse points
4394 					PointF[]	oppositeEllipsePoints = oppositeEllipseFlattenPath.PathPoints;
4395 					for(int pointIndex = 0; !doneFlag && pointIndex < ellipsePoints.Length; pointIndex++)
4396 					{
4397 						// Loop through opposite ellipse points
4398 						for(int pointOppositeIndex = 0; !doneFlag && pointOppositeIndex < oppositeEllipsePoints.Length; pointOppositeIndex++)
4399 						{
4400 							bool	closeToVertical = false;
4401 							bool	pointsOnLeft = false;
4402 							bool	pointsOnRight = false;
4403 
4404 							//if(cylinderAngle == 0 || cylinderAngle == 180 || cylinderAngle == -180)
4405 							if(cylinderAngle > -30 && cylinderAngle < 30)
4406 							{
4407 								closeToVertical = true;
4408 							}
4409 
4410 							if(closeToVertical)
4411 							{
4412 								if(oppositeEllipsePoints[pointOppositeIndex].Y == ellipsePoints[pointIndex].Y)
4413 								{
4414 									continue;
4415 								}
4416 
4417 								float	linePointX = oppositeEllipsePoints[pointOppositeIndex].X - ellipsePoints[pointIndex].X;
4418 								linePointX /= oppositeEllipsePoints[pointOppositeIndex].Y - ellipsePoints[pointIndex].Y;
4419 
4420 								// Check if this line has any points to the right/left
4421 								for(int innerPointIndex = 0; innerPointIndex < ellipsePoints.Length; innerPointIndex++)
4422 								{
4423 									// Skip points used to define line function
4424 									if(innerPointIndex == pointIndex)
4425 									{
4426 										continue;
4427 									}
4428 
4429 									float	x = linePointX;
4430 									x *= ellipsePoints[innerPointIndex].Y - ellipsePoints[pointIndex].Y;
4431 									x += ellipsePoints[pointIndex].X;
4432 
4433 									if(x > ellipsePoints[innerPointIndex].X)
4434 									{
4435 										pointsOnLeft = true;
4436 									}
4437 									if(x < ellipsePoints[innerPointIndex].X)
4438 									{
4439 										pointsOnRight = true;
4440 									}
4441 									if(pointsOnLeft && pointsOnRight)
4442 									{
4443 										break;
4444 									}
4445 								}
4446 
4447 								if(pointsOnLeft == false || pointsOnRight == false)
4448 								{
4449 									for(int innerPointIndex = 0; innerPointIndex < oppositeEllipsePoints.Length; innerPointIndex++)
4450 									{
4451 										// Skip points used to define line function
4452 										if(innerPointIndex == pointOppositeIndex)
4453 										{
4454 											continue;
4455 										}
4456 
4457 										float	x = linePointX;
4458 										x *= oppositeEllipsePoints[innerPointIndex].Y - ellipsePoints[pointIndex].Y;
4459 										x += ellipsePoints[pointIndex].X;
4460 
4461 										if(x > oppositeEllipsePoints[innerPointIndex].X)
4462 										{
4463 											pointsOnLeft = true;
4464 										}
4465 										if(x < oppositeEllipsePoints[innerPointIndex].X)
4466 										{
4467 											pointsOnRight = true;
4468 										}
4469 										if(pointsOnLeft && pointsOnRight)
4470 										{
4471 											break;
4472 										}
4473 									}
4474 								}
4475 							}
4476 							else
4477 							{
4478 								if(oppositeEllipsePoints[pointOppositeIndex].X == ellipsePoints[pointIndex].X)
4479 								{
4480 									continue;
4481 								}
4482 
4483 								float	linePointY = oppositeEllipsePoints[pointOppositeIndex].Y - ellipsePoints[pointIndex].Y;
4484 								linePointY /= oppositeEllipsePoints[pointOppositeIndex].X - ellipsePoints[pointIndex].X;
4485 
4486 								// Check if this line has any points to the right/left
4487 								for(int innerPointIndex = 0; innerPointIndex < ellipsePoints.Length; innerPointIndex++)
4488 								{
4489 									// Skip points used to define line function
4490 									if(innerPointIndex == pointIndex)
4491 									{
4492 										continue;
4493 									}
4494 
4495 									float	y = linePointY;
4496 									y *= ellipsePoints[innerPointIndex].X - ellipsePoints[pointIndex].X;
4497 									y += ellipsePoints[pointIndex].Y;
4498 
4499 									if(y > ellipsePoints[innerPointIndex].Y)
4500 									{
4501 										pointsOnLeft = true;
4502 									}
4503 									if(y < ellipsePoints[innerPointIndex].Y)
4504 									{
4505 										pointsOnRight = true;
4506 									}
4507 									if(pointsOnLeft && pointsOnRight)
4508 									{
4509 										break;
4510 									}
4511 								}
4512 
4513 								if(pointsOnLeft == false || pointsOnRight == false)
4514 								{
4515 									for(int innerPointIndex = 0; innerPointIndex < oppositeEllipsePoints.Length; innerPointIndex++)
4516 									{
4517 										// Skip points used to define line function
4518 										if(innerPointIndex == pointOppositeIndex)
4519 										{
4520 											continue;
4521 										}
4522 
4523 										float	y = linePointY;
4524 										y *= oppositeEllipsePoints[innerPointIndex].X - ellipsePoints[pointIndex].X;
4525 										y += ellipsePoints[pointIndex].Y;
4526 
4527 										if(y > oppositeEllipsePoints[innerPointIndex].Y)
4528 										{
4529 											pointsOnLeft = true;
4530 										}
4531 										if(y < oppositeEllipsePoints[innerPointIndex].Y)
4532 										{
4533 											pointsOnRight = true;
4534 										}
4535 										if(pointsOnLeft && pointsOnRight)
4536 										{
4537 											break;
4538 										}
4539 									}
4540 								}
4541 							}
4542 
4543 							if(!pointsOnLeft && leftBottomPoint == -1)
4544 							{
4545 								leftBottomPoint = pointIndex;
4546 								this._oppLeftBottomPoint = pointOppositeIndex;
4547 							}
4548 							if(!pointsOnRight && rigthTopPoint == -1)
4549 							{
4550 								rigthTopPoint = pointIndex;
4551 								this._oppRigthTopPoint = pointOppositeIndex;
4552 							}
4553 
4554 							if(leftBottomPoint >= 0 && rigthTopPoint >= 0)
4555 							{
4556 								doneFlag = true;
4557 
4558 								if(closeToVertical)
4559 								{
4560 									if(ellipsePoints[leftBottomPoint].Y > oppositeEllipsePoints[this._oppLeftBottomPoint].Y)
4561 									{
4562 										int temp = leftBottomPoint;
4563 										leftBottomPoint = rigthTopPoint;
4564 										rigthTopPoint = temp;
4565 
4566 										temp = this._oppLeftBottomPoint;
4567 										this._oppLeftBottomPoint = this._oppRigthTopPoint;
4568 										this._oppRigthTopPoint = temp;
4569 									}
4570 								}
4571 
4572 							}
4573 						}
4574 					}
4575 				}
4576 			}
4577 
4578 			// Point indexes were not found
4579 			if(leftBottomPoint == rigthTopPoint ||
4580 				rigthTopPoint == -1 ||
4581 				leftBottomPoint == -1)
4582 			{
4583 				return;
4584 			}
4585 
4586 			// Set left\right line coordinates
4587 			leftSideLinePoint = ellipsePoints[leftBottomPoint];
4588 			rightSideLinePoint = ellipsePoints[rigthTopPoint];
4589 
4590 			// Add required ellipse segment to the result path
4591 			for(int pointIndex = leftBottomPoint + 1; pointIndex != rigthTopPoint + 1; pointIndex++)
4592 			{
4593 				if(pointIndex > ellipsePoints.Length - 1)
4594 				{
4595 					resultPath.AddLine(ellipsePoints[ellipsePoints.Length - 1], ellipsePoints[0]);
4596 					pointIndex = 0;
4597 					continue;
4598 				}
4599 				resultPath.AddLine(ellipsePoints[pointIndex - 1], ellipsePoints[pointIndex]);
4600 			}
4601 		}
4602 
4603 		#endregion
4604 	}
4605 
4606 	/// <summary>
4607     /// The Point3D class represents point coordinates in 3D space.
4608 	/// </summary>
4609 #if ASPPERM_35
4610 	[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
4611     [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
4612 #endif
4613     public class Point3D
4614 	{
4615 		#region Fields
4616 
4617 		// Point X and Y coordinates
4618 		private	PointF	_coordXY = new PointF(0f, 0f);
4619 
4620 		// Point Z coordinate (depth)
4621 		private float	_coordZ = 0;
4622 
4623 		#endregion
4624 
4625 		#region Properties
4626 
4627 		/// <summary>
4628         /// Gets or sets the X coordinate of the point.
4629 		/// </summary>
4630 		[
4631 		Bindable(true),
4632 		DefaultValue(0),
4633 		SRDescription("DescriptionAttributePoint3D_X")
4634 		]
4635         [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "X")]
4636         public float X
4637 		{
4638 			get
4639 			{
4640                 return this._coordXY.X;
4641 			}
4642 			set
4643 			{
4644                 this._coordXY.X = value;
4645 			}
4646 		}
4647 
4648 		/// <summary>
4649         /// Gets or sets the Y coordinate of the point.
4650 		/// </summary>
4651 		[
4652 		Bindable(true),
4653 		DefaultValue(0),
4654 		SRDescription("DescriptionAttributePoint3D_Y")
4655 		]
4656         [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Y")]
4657         public float Y
4658 		{
4659 			get
4660 			{
4661                 return this._coordXY.Y;
4662 			}
4663 			set
4664 			{
4665                 this._coordXY.Y = value;
4666 			}
4667 		}
4668 
4669 		/// <summary>
4670         /// Gets or sets the Z coordinate of the point.
4671 		/// </summary>
4672 		[
4673 		Bindable(true),
4674 		DefaultValue(0),
4675 		SRDescription("DescriptionAttributePoint3D_Z")
4676 		]
4677         [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Z")]
4678         public float Z
4679 		{
4680 			get
4681 			{
4682                 return this._coordZ;
4683 			}
4684 			set
4685 			{
4686                 this._coordZ = value;
4687 			}
4688 		}
4689 
4690 		/// <summary>
4691 		/// Gets or sets a PointF structure, which stores the X and Y coordinates of a 3D point.
4692 		/// </summary>
4693 		[
4694 		Bindable(true),
4695 		DefaultValue(0),
4696 		SRDescription("DescriptionAttributePoint3D_PointF")
4697 		]
4698 		public PointF PointF
4699 		{
4700 			get
4701 			{
4702                 return this._coordXY;
4703 			}
4704 			set
4705 			{
4706                 this._coordXY = new PointF(value.X, value.Y);
4707 			}
4708 		}
4709 
4710 		#endregion
4711 
4712 		#region Constructors
4713 
4714 		/// <summary>
4715 		/// Public constructor.
4716 		/// </summary>
4717         [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly",
4718             Justification = "X, Y and Z are cartesian coordinates and well understood")]
Point3D(float x, float y, float z)4719         public Point3D(float x, float y, float z)
4720 		{
4721 			this._coordXY = new PointF(x, y);
4722             this._coordZ = z;
4723 		}
4724 
4725 		/// <summary>
4726 		/// Public constructor.
4727 		/// </summary>
Point3D( )4728 		public Point3D( )
4729 		{
4730 
4731 		}
4732 
4733 		#endregion // Constructor
4734 	}
4735 }
4736