File: Common\General\ChartGraphics3D.cs
Project: ndp\fx\src\DataVisualization\System.Web.DataVisualization.csproj (System.Web.DataVisualization)
//-------------------------------------------------------------
// <copyright company=’Microsoft Corporation’>
//   Copyright © Microsoft Corporation. All Rights Reserved.
// </copyright>
//-------------------------------------------------------------
// @owner=alexgor, deliant
//=================================================================
//  File:		ChartGraphics3D.cs
//
//  Namespace:	System.Web.UI.WebControls[Windows.Forms].Charting
//
//	Classes:	ChartGraphics3D, Point3D
//
//  Purpose:	ChartGraphics3D class is 3D chart rendering engine. 
//              All chart 3D shapes are drawn in specific order so 
//              that correct Z order of all shapes is achieved. 3D 
//              graphics engine do not support shapes intersection. 
//              3D shapes are transformed into one or more 2D shapes 
//              and then drawn with 2D chart graphics engine.
//
//	Reviewed:	AG - Microsoft 16, 2007
//
//===================================================================
 
#region Used namespaces
 
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Drawing.Imaging;
using System.ComponentModel;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
 
#endregion
 
#if Microsoft_CONTROL
	namespace System.Windows.Forms.DataVisualization.Charting
#else
namespace System.Web.UI.DataVisualization.Charting
 
#endif
{
	#region 3D enumerations
 
	/// <summary>
	/// 3D cube surfaces names.
	/// </summary>
	[Flags]
    internal enum SurfaceNames
	{
		/// <summary>
		/// Front.
		/// </summary>
		Front = 1,
		/// <summary>
		/// Back.
		/// </summary>
		Back = 2,
		/// <summary>
		/// Left.
		/// </summary>
		Left = 4,
		/// <summary>
		/// Right.
		/// </summary>
		Right = 8,
		/// <summary>
		/// Top.
		/// </summary>
		Top = 16,
		/// <summary>
		/// Bottom.
		/// </summary>
		Bottom = 32
	}
 
	/// <summary>
	/// This enumeration defines all significant points in a pie 
	/// slice. Only these points should be transformed for pie 
	/// chart using Matrix object.
	/// </summary>
    internal enum PiePoints
	{
		/// <summary>
		/// Angle 180 Top point on the arc
		/// </summary>
		Top180,
 
		/// <summary>
		/// Angle 180 Bottom point on the arc
		/// </summary>
		Bottom180,
 
		/// <summary>
		/// Angle 0 Top point on the arc
		/// </summary>
		Top0,
 
		/// <summary>
		/// Angle 0 Bottom point on the arc
		/// </summary>
		Bottom0,
 
		/// <summary>
		/// Top Start Angle point on the arc
		/// </summary>
		TopStart,
 
		/// <summary>
		/// Top End Angle point on the arc
		/// </summary>
		TopEnd,
 
		/// <summary>
		/// Bottom Start Angle point on the arc
		/// </summary>
		BottomStart,
 
		/// <summary>
		/// Bottom End Angle point on the arc
		/// </summary>
		BottomEnd,
 
		/// <summary>
		/// Center Top
		/// </summary>
		TopCenter,
 
		/// <summary>
		/// Center Bottom
		/// </summary>
		BottomCenter,
 
		/// <summary>
		/// Top Label Line
		/// </summary>
		TopLabelLine,
 
		/// <summary>
		/// Top Label Line Out
		/// </summary>
		TopLabelLineout,
 
		/// <summary>
		/// Top Label Center
		/// </summary>
		TopLabelCenter,
 
		/// <summary>
		/// Top Rectangle Top Left Point
		/// </summary>
		TopRectTopLeftPoint,
 
		/// <summary>
		/// Top Rectangle Right Bottom Point
		/// </summary>
		TopRectBottomRightPoint,
 
		/// <summary>
		/// Bottom Rectangle Top Left Point
		/// </summary>
		BottomRectTopLeftPoint,
 
		/// <summary>
		/// Bottom Rectangle Right Bottom Point
		/// </summary>
		BottomRectBottomRightPoint,
 
		/// <summary>
		/// Angle 180 Top point on the Doughnut arc
		/// </summary>
		DoughnutTop180,
 
		/// <summary>
		/// Angle 180 Bottom point on the Doughnut arc
		/// </summary>
		DoughnutBottom180,
 
		/// <summary>
		/// Angle 0 Top point on the Doughnut arc
		/// </summary>
		DoughnutTop0,
 
		/// <summary>
		/// Angle 0 Bottom point on the Doughnut arc
		/// </summary>
		DoughnutBottom0,
 
		/// <summary>
		/// Top Start Angle point on the Doughnut arc
		/// </summary>
		DoughnutTopStart,
 
		/// <summary>
		/// Top End Angle point on the Doughnut arc
		/// </summary>
		DoughnutTopEnd,
 
		/// <summary>
		/// Bottom Start Angle point on the Doughnut arc
		/// </summary>
		DoughnutBottomStart,
 
		/// <summary>
		/// Bottom End Angle point on the Doughnut arc
		/// </summary>
		DoughnutBottomEnd,
 
		/// <summary>
		/// Doughnut Top Rectangle Top Left Point
		/// </summary>
		DoughnutTopRectTopLeftPoint,
 
		/// <summary>
		/// Doughnut Top Rectangle Right Bottom Point
		/// </summary>
		DoughnutTopRectBottomRightPoint,
 
		/// <summary>
		/// Doughnut Bottom Rectangle Top Left Point
		/// </summary>
		DoughnutBottomRectTopLeftPoint,
 
		/// <summary>
		/// Doughnut Bottom Rectangle Right Bottom Point
		/// </summary>
		DoughnutBottomRectBottomRightPoint,
	}
		
 
	/// <summary>
	/// AxisName of drawing operation.
	/// </summary>
	[Flags]
    internal enum DrawingOperationTypes
	{
		/// <summary>
		/// Draw element.
		/// </summary>
		DrawElement = 1,
 
		/// <summary>
		/// Calculate element path. (for selection or tooltips)
		/// </summary>
		CalcElementPath = 2,
	}
 
	/// <summary>
	/// AxisName of line segment.
	/// </summary>
    internal enum LineSegmentType
	{
		/// <summary>
		/// Only one segment exists.
		/// </summary>
		Single,
 
		/// <summary>
		/// First segment.
		/// </summary>
		First,
 
		/// <summary>
		/// Middle segment.
		/// </summary>
		Middle,
 
		/// <summary>
		/// Last segment.
		/// </summary>
		Last
	}
 
	#endregion
 
	/// <summary>
	/// The ChartGraphics class is 3D chart rendering engine. All chart 
	/// 3D shapes are drawn in specific order so that correct Z order 
	/// of all shapes is achieved. 3D graphics engine do not support 
	/// shapes intersection. 3D shapes are transformed into one or 
	/// more 2D shapes and then drawn with 2D chart graphics engine.
	/// </summary>
    public partial class ChartGraphics
	{
		#region Fields
 
		/// <summary>
		/// Helper field used to store the index of cylinder left/bottom side coordinate.
		/// </summary>
		private		int _oppLeftBottomPoint = -1;
 
		/// <summary>
		/// Helper field used to store the index of cylinder right/top side coordinate.
		/// </summary>
		private		int _oppRigthTopPoint = -1;
 
		/// <summary>
		/// Point of the front line from the previous line segment.
		/// </summary>
		internal	PointF	frontLinePoint1 = PointF.Empty;
 
		/// <summary>
		/// Point of the front line from the previous line segment.
		/// </summary>
		internal	PointF	frontLinePoint2 = PointF.Empty;
 
		/// <summary>
		/// Previous line segment pen.
		/// </summary>
		internal	Pen		frontLinePen = null;
 
		#endregion
 
		#region 3D Line drawing methods
 
        /// <summary>
        /// Draws grid line in 3D space (on two area scene walls)
        /// </summary>
        /// <param name="area">Chart area.</param>
        /// <param name="color">Line color.</param>
        /// <param name="width">Line width.</param>
        /// <param name="style">Line style.</param>
        /// <param name="point1">First line point.</param>
        /// <param name="point2">Second line point.</param>
        /// <param name="horizontal">Indicates that grid line is horizontal</param>
        /// <param name="common">Common Elements</param>
        /// <param name="obj">Selected object</param>
		internal void Draw3DGridLine(
			ChartArea area,
			Color color, 
			int width, 
			ChartDashStyle style, 
			PointF point1, 
			PointF point2, 
			bool horizontal,
			CommonElements common,
			object obj
			)
		{
			float	zPositon = area.IsMainSceneWallOnFront() ? area.areaSceneDepth : 0f;
 
            ChartElementType chartElementType = obj is StripLine ? ChartElementType.StripLines : ChartElementType.Gridlines;
 
			// Draw strip line on the back/front wall
			((ChartGraphics)this).Draw3DLine( 
				area.matrix3D,
				color, width, style,
				new Point3D(point1.X, point1.Y, zPositon),
				new Point3D(point2.X, point2.Y, zPositon),
				common,
				obj,
                chartElementType
				);
 
			if(horizontal)
			{
				// Draw strip line on the side wall (left or right)
				if(area.IsSideSceneWallOnLeft())
				{
					point1.X = Math.Min(point1.X, point2.X);
				}
				else
				{
					point1.X = Math.Max(point1.X, point2.X);
				}
 
				((ChartGraphics)this).Draw3DLine( 
					area.matrix3D,
					color, width, style,
					new Point3D(point1.X, point1.Y, 0f),
					new Point3D(point1.X, point1.Y, area.areaSceneDepth),
					common,
					obj,
					chartElementType
					);
 
			}
			else if(area.IsBottomSceneWallVisible())
			{
				// Draw strip line on the bottom wall (if visible)
				point1.Y = Math.Max(point1.Y, point2.Y);
 
				((ChartGraphics)this).Draw3DLine( 
					area.matrix3D,
					color, width, style,
					new Point3D(point1.X, point1.Y, 0f),
					new Point3D(point1.X, point1.Y, area.areaSceneDepth),
					common,
					obj,
                    chartElementType
					);
			}
		}
 
		/// <summary>
		/// Draws a line connecting the two specified points.
		/// </summary>
		/// <param name="matrix">Coordinates transformation matrix.</param>
		/// <param name="color">Line color.</param>
		/// <param name="width">Line width.</param>
		/// <param name="style">Line style.</param>
		/// <param name="firstPoint">A Point that represents the first point to connect.</param>
		/// <param name="secondPoint">A Point that represents the second point to connect.</param>
		/// <param name="common">Common elements</param>
		/// <param name="obj">Selected object</param>
		/// <param name="type">Selected chart element</param>
		internal void Draw3DLine( 
			Matrix3D matrix,
			Color color, 
			int width, 
			ChartDashStyle style, 
			Point3D firstPoint, 
			Point3D secondPoint,
			CommonElements common,
			object obj,
			ChartElementType type
			)
		{
		
			// Transform coordinates
			Point3D [] points = new Point3D[] {firstPoint, secondPoint};
			matrix.TransformPoints( points );
 
			// Selection mode
            if (common.ProcessModeRegions && type != ChartElementType.Nothing)
			{
                using (GraphicsPath path = new GraphicsPath())
                {
 
                    if (Math.Abs(points[0].X - points[1].X) > Math.Abs(points[0].Y - points[1].Y))
                    {
                        path.AddLine(points[0].X, points[0].Y - 1, points[1].X, points[1].Y - 1);
                        path.AddLine(points[1].X, points[1].Y + 1, points[0].X, points[0].Y + 1);
                        path.CloseAllFigures();
                    }
                    else
                    {
                        path.AddLine(points[0].X - 1, points[0].Y, points[1].X - 1, points[1].Y);
                        path.AddLine(points[1].X + 1, points[1].Y, points[0].X + 1, points[0].Y);
                        path.CloseAllFigures();
 
                    }
                    common.HotRegionsList.AddHotRegion(path, true, type, obj);
                }
			}
			
			if( common.ProcessModePaint )
			{
				// Draw 2D line in 3D space
				((ChartGraphics)this).DrawLineRel(color, width, style, points[0].PointF, points[1].PointF);
			}
 
		}
 
		#endregion
 
		#region 3D Pie Drawing methods and enumerations
 
		
		/// <summary>
		/// This method draw and fill four point polygons which 
		/// represents sides of a pie slice.
		/// </summary>
		/// <param name="area">Chart Area</param>
		/// <param name="inclination">X angle rotation</param>
		/// <param name="startAngle">Start Angle of a pie slice</param>
		/// <param name="sweepAngle">Sweep angle of a pie slice</param>
		/// <param name="points">Significant points of a pie slice</param>
		/// <param name="brush">Brush used for fill</param>
		/// <param name="pen">Pen used for drawing</param>
		/// <param name="doughnut">Chart AxisName is Doughnut</param>
		internal void FillPieSides( 
			ChartArea area,
            float inclination,
			float startAngle,
			float sweepAngle,
			PointF [] points,
			SolidBrush brush,
			Pen pen,
			bool doughnut
			)
		{
			
			// Create a graphics path
			GraphicsPath path = new GraphicsPath();
 
			// Significant Points for Side polygons
			PointF topCenter = points[(int)PiePoints.TopCenter];
			PointF bottomCenter = points[(int)PiePoints.BottomCenter];
			PointF topStart = points[(int)PiePoints.TopStart];
			PointF bottomStart = points[(int)PiePoints.BottomStart];
			PointF topEnd = points[(int)PiePoints.TopEnd];
			PointF bottomEnd = points[(int)PiePoints.BottomEnd];
 
			// For Doughnut
			PointF topDoughnutStart = PointF.Empty;
			PointF bottomDoughnutStart = PointF.Empty;
			PointF topDoughnutEnd = PointF.Empty;
			PointF bottomDoughnutEnd = PointF.Empty;
 
			if( doughnut )
			{
				// For Doughnut
				topDoughnutStart = points[(int)PiePoints.DoughnutTopStart];
				bottomDoughnutStart = points[(int)PiePoints.DoughnutBottomStart];
				topDoughnutEnd = points[(int)PiePoints.DoughnutTopEnd];
				bottomDoughnutEnd = points[(int)PiePoints.DoughnutBottomEnd];
			}
 
			bool startSide = false;
			bool endSide = false;
			float endAngle = startAngle + sweepAngle;
 
			// If X angle is negative different side of pie slice is visible.
            if (inclination > 0)
			{
				// Enable start or/and the end side
				if( startAngle > -90 && startAngle < 90 || startAngle > 270 && startAngle < 450 )
				{
					startSide = true;
				}
				if( endAngle >= -180 && endAngle < -90 || endAngle > 90 && endAngle < 270 || endAngle > 450 && endAngle <= 540 )
				{
					endSide = true;
				}
			}
			else  
			{ 
				// Enable start or/and the end side
				if( startAngle >= -180 && startAngle < -90 || startAngle > 90 && startAngle < 270 || startAngle > 450 && startAngle <= 540 )
				{
					startSide = true;
					
				}
				if( endAngle > -90 && endAngle < 90 || endAngle > 270 && endAngle < 450 )
				{
					endSide = true;
				}
			}
			
			if( startSide )
			{
				// *****************************************
				// Draw Start Angle side
				// *****************************************
                using (path = new GraphicsPath())
                {
 
                    if (doughnut)
                    {
                        // Add Line between The Doughnut Arc and Arc
                        path.AddLine(topDoughnutStart, topStart);
 
                        // Add Line between The Top Start and Bottom Start
                        path.AddLine(topStart, bottomStart);
 
                        // Add Line between The Bottom Start and Doughnut Start
                        path.AddLine(bottomStart, bottomDoughnutStart);
 
                        // Add Line between The Bottom Doughnut Start and The Top Doughnut Start
                        path.AddLine(bottomDoughnutStart, topDoughnutStart);
                    }
                    else
                    {
                        // Add Line between The Center and Arc
                        path.AddLine(topCenter, topStart);
 
                        // Add Line between The Top Start and Bottom Start
                        path.AddLine(topStart, bottomStart);
 
                        // Add Line between The Bottom Start and The Center Bottom
                        path.AddLine(bottomStart, bottomCenter);
 
                        // Add Line between The Bottom Center and The Top Center
                        path.AddLine(bottomCenter, topCenter);
                    }
                    // Get surface colors
                    Color frontLightColor, leftLightColor, topLightColor, backLightColor, rightLightColor, bottomLightColor;
                    area.matrix3D.GetLight(brush.Color, out frontLightColor, out backLightColor, out leftLightColor, out rightLightColor, out topLightColor, out bottomLightColor);
 
                    Color lightColor;
                    if (area.Area3DStyle.Inclination < 0)
                    {
                        lightColor = bottomLightColor;
                    }
                    else
                    {
                        lightColor = topLightColor;
                    }
 
                    // Draw Path
                    using (Brush lightBrush = new SolidBrush(lightColor))
                    {
                        FillPath(lightBrush, path);
                    }
 
                    DrawGraphicsPath(pen, path);
                }
			}
 
			if( endSide )
			{
				// *****************************************
				// Draw End Angle side
				// *****************************************
                using (path = new GraphicsPath())
                {
                    if (doughnut)
                    {
                        // Add Line between The Doughnut Arc and Arc
                        path.AddLine(topDoughnutEnd, topEnd);
 
                        // Add Line between The Top End and Bottom End
                        path.AddLine(topEnd, bottomEnd);
 
                        // Add Line between The Bottom End and Doughnut End
                        path.AddLine(bottomEnd, bottomDoughnutEnd);
 
                        // Add Line between The Bottom Doughnut End and The Top Doughnut End
                        path.AddLine(bottomDoughnutEnd, topDoughnutEnd);
                    }
                    else
                    {
                        // Add Line between The Center and Arc
                        path.AddLine(topCenter, topEnd);
 
                        // Add Line between The Top End and Bottom End
                        path.AddLine(topEnd, bottomEnd);
 
                        // Add Line between The Bottom End and The Center Bottom
                        path.AddLine(bottomEnd, bottomCenter);
 
                        // Add Line between The Bottom Center and The Top Center
                        path.AddLine(bottomCenter, topCenter);
 
                    }
 
                    // Get surface colors
                    Color frontLightColor, leftLightColor, topLightColor, backLightColor, rightLightColor, bottomLightColor;
                    area.matrix3D.GetLight(brush.Color, out frontLightColor, out backLightColor, out leftLightColor, out rightLightColor, out topLightColor, out bottomLightColor);
 
                    Color lightColor;
                    if (area.Area3DStyle.Inclination < 0)
                    {
                        lightColor = bottomLightColor;
                    }
                    else
                    {
                        lightColor = topLightColor;
                    }
 
                    // Draw Path
                    using (Brush lightBrush = new SolidBrush(lightColor))
                    {
                        FillPath(lightBrush, path);
                    }
 
                    DrawGraphicsPath(pen, path);
                }
			}
		}
		
		/// <summary>
		/// This method Draw and fill pie curves.
		/// </summary>
		/// <param name="area">Chart area used for drawing</param>
		/// <param name="point">Data Point</param>
		/// <param name="brush">Graphic Brush used for drawing</param>
		/// <param name="pen">Graphic Pen used for drawing</param>
		/// <param name="topFirstRectPoint">Rotated bounded rectangle points</param>
		/// <param name="topSecondRectPoint">Rotated bounded rectangle points</param>
		/// <param name="bottomFirstRectPoint">Rotated bounded rectangle points</param>
		/// <param name="bottomSecondRectPoint">Rotated bounded rectangle points</param>
		/// <param name="topFirstPoint">Significant pie points</param>
		/// <param name="topSecondPoint">Significant pie points</param>
		/// <param name="bottomFirstPoint">Significant pie points</param>
		/// <param name="bottomSecondPoint">Significant pie points</param>
		/// <param name="startAngle">Start pie angle</param>
		/// <param name="sweepAngle">End pie angle</param>
		/// <param name="pointIndex">Data Point Index</param>
		internal void 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
			)
		{
			// Common Elements
			CommonElements common = area.Common;
 
			// Create a graphics path
            using (GraphicsPath path = new GraphicsPath())
            {
 
                // It is enough to transform only two points from 
                // rectangle. This code will create RectangleF from 
                // top left and bottom right points. 
                RectangleF pieTopRectangle = new RectangleF();
                pieTopRectangle.X = topFirstRectPoint.X;
                pieTopRectangle.Y = topFirstRectPoint.Y;
                pieTopRectangle.Height = topSecondRectPoint.Y - topFirstRectPoint.Y;
                pieTopRectangle.Width = topSecondRectPoint.X - topFirstRectPoint.X;
 
                RectangleF pieBottomRectangle = new RectangleF();
                pieBottomRectangle.X = bottomFirstRectPoint.X;
                pieBottomRectangle.Y = bottomFirstRectPoint.Y;
                pieBottomRectangle.Height = bottomSecondRectPoint.Y - bottomFirstRectPoint.Y;
                pieBottomRectangle.Width = bottomSecondRectPoint.X - bottomFirstRectPoint.X;
 
                // Angle correction algorithm. After rotation AddArc method should used 
                // different transformed angles. This method transforms angles.
                double angleCorrection = pieTopRectangle.Height / pieTopRectangle.Width;
 
                float endAngle;
                endAngle = AngleCorrection(startAngle + sweepAngle, angleCorrection);
                startAngle = AngleCorrection(startAngle, angleCorrection);
 
                sweepAngle = endAngle - startAngle;
 
                // Add Line between first points
                path.AddLine(topFirstPoint, bottomFirstPoint);
 
                if (pieBottomRectangle.Height <= 0)
                {
                    // If x angle is 0 this arc will be line in projection.
                    path.AddLine(bottomFirstPoint.X, bottomFirstPoint.Y, bottomSecondPoint.X, bottomSecondPoint.Y);
                }
                else
                {
                    // Add Arc
                    path.AddArc(pieBottomRectangle.X, pieBottomRectangle.Y, pieBottomRectangle.Width, pieBottomRectangle.Height, startAngle, sweepAngle);
                }
 
                // Add Line between second points
                path.AddLine(bottomSecondPoint, topSecondPoint);
 
                if (pieTopRectangle.Height <= 0)
                {
                    // If x angle is 0 this arc will be line in projection.
                    path.AddLine(topFirstPoint.X, topFirstPoint.Y, topSecondPoint.X, topSecondPoint.Y);
                }
                else
                {
                    path.AddArc(pieTopRectangle.X, pieTopRectangle.Y, pieTopRectangle.Width, pieTopRectangle.Height, startAngle + sweepAngle, -sweepAngle);
                }
 
                if (common.ProcessModePaint)
                {
                    // Drawing Mode
                    FillPath(brush, path);
 
                    if (point.BorderColor != Color.Empty &&
                        point.BorderWidth > 0 &&
                        point.BorderDashStyle != ChartDashStyle.NotSet)
                    {
                        DrawGraphicsPath(pen, path);
                    }
 
                }
                if (common.ProcessModeRegions)
                {
 
 
                    // Check if processing collected data point
                    if (point.IsCustomPropertySet("_COLLECTED_DATA_POINT"))
                    {
                        // Add point to the map area
                        common.HotRegionsList.AddHotRegion(
                            (ChartGraphics)this,
                            path,
                            false,
                            point.ReplaceKeywords(point.ToolTip),
#if Microsoft_CONTROL
						string.Empty,
						string.Empty,
						string.Empty,
#else // Microsoft_CONTROL
 point.ReplaceKeywords(point.Url),
                            point.ReplaceKeywords(point.MapAreaAttributes),
                            point.ReplaceKeywords(point.PostBackValue),
#endif // Microsoft_CONTROL
 point,
                            ChartElementType.DataPoint);
 
                        return;
                    }
 
 
 
                    common.HotRegionsList.AddHotRegion(
                        path,
                        false,
                        (ChartGraphics)this,
                        point,
                        point.series.Name,
                        pointIndex);
                }
            }
		}
		
		/// <summary>
		/// This method draws projection of 3D pie slice. 
		/// </summary>
		/// <param name="area">Chart area used for drawing</param>
		/// <param name="point">Data point which creates this pie slice</param>
		/// <param name="brush">Graphic Brush used for drawing</param>
		/// <param name="pen">Graphic Pen used for drawing</param>
		/// <param name="firstRectPoint">The first point of transformed bounding rectangle</param>
		/// <param name="firstPoint">The first arc point of pie slice</param>
		/// <param name="secondRectPoint">The second point of transformed bounding rectangle</param>
		/// <param name="secondPoint">The second arc point of pie slice</param>
		/// <param name="center">The center point of pie slice</param>
		/// <param name="startAngle">Start angle of pie slice</param>
		/// <param name="sweepAngle">The end angle of pie slice</param>
		/// <param name="fill">Fill pie slice with brush</param>
		/// <param name="pointIndex"></param>
		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 )
		{
			// Common elements
			CommonElements common = area.Common;
 
			// Create a graphics path
            using (GraphicsPath path = new GraphicsPath())
            {
 
                // It is enough to transform only two points from 
                // rectangle. This code will create RectangleF from 
                // top left and bottom right points. 
                RectangleF pieRectangle = new RectangleF();
                pieRectangle.X = firstRectPoint.X;
                pieRectangle.Y = firstRectPoint.Y;
                pieRectangle.Height = secondRectPoint.Y - firstRectPoint.Y;
                pieRectangle.Width = secondRectPoint.X - firstRectPoint.X;
 
                // Angle correction algorithm. After rotation AddArc method should used 
                // different transformed angles. This method transforms angles.
                double angleCorrection = pieRectangle.Height / pieRectangle.Width;
 
                float endAngle;
                endAngle = AngleCorrection(startAngle + sweepAngle, angleCorrection);
                startAngle = AngleCorrection(startAngle, angleCorrection);
 
                sweepAngle = endAngle - startAngle;
 
                // Add Line between The Center and Arc
                path.AddLine(center, firstPoint);
 
                // Add Arc
                if (pieRectangle.Height > 0)
                {
                    // If x angle is 0 this arc will be line in projection.
                    path.AddArc(pieRectangle.X, pieRectangle.Y, pieRectangle.Width, pieRectangle.Height, startAngle, sweepAngle);
                }
 
                // Add Line between the end of the arc and the centre.
                path.AddLine(secondPoint, center);
 
                if (common.ProcessModePaint)
                {
                    // Get surface colors
                    Color frontLightColor, leftLightColor, topLightColor, backLightColor, rightLightColor, bottomLightColor;
                    area.matrix3D.GetLight(brush.Color, out frontLightColor, out backLightColor, out leftLightColor, out rightLightColor, out topLightColor, out bottomLightColor);
 
                    Pen newPen = (Pen)pen.Clone();
 
                    if (area.Area3DStyle.LightStyle == LightStyle.Realistic && point.BorderColor == Color.Empty)
                    {
                        newPen.Color = frontLightColor;
                    }
 
                    // Drawing Mode
                    if (fill)
                    {
                        using (Brush lightBrush = new SolidBrush(frontLightColor))
                        {
                            FillPath(lightBrush, path);
                        }
                    }
 
                    if (point.BorderColor != Color.Empty &&
                        point.BorderWidth > 0 &&
                        point.BorderDashStyle != ChartDashStyle.NotSet)
                    {
                        DrawGraphicsPath(newPen, path);
                    }
                }
 
                if (common.ProcessModeRegions && fill)
                {
 
 
                    // Check if processing collected data point
                    if (point.IsCustomPropertySet("_COLLECTED_DATA_POINT"))
                    {
                        // Add point to the map area
                        common.HotRegionsList.AddHotRegion(
                            (ChartGraphics)this,
                            path,
                            false,
                            point.ReplaceKeywords(point.ToolTip),
#if Microsoft_CONTROL
						string.Empty,
						string.Empty,
						string.Empty,
#else // Microsoft_CONTROL
                            point.ReplaceKeywords(point.Url),
                            point.ReplaceKeywords(point.MapAreaAttributes),
                            point.ReplaceKeywords(point.PostBackValue),
#endif // Microsoft_CONTROL
                            point,
                            ChartElementType.DataPoint);
 
                        return;
                    }
 
 
 
                    common.HotRegionsList.AddHotRegion(path, false, (ChartGraphics)this, point, point.series.Name, pointIndex);
                }
            }
		}
 
		/// <summary>
		/// This method draws projection of 3D pie slice. 
		/// </summary>
		/// <param name="area">Chart area used for drawing</param>
		/// <param name="point">Data point which creates this Doughnut slice</param>
		/// <param name="brush">Graphic Brush used for drawing</param>
		/// <param name="pen">Graphic Pen used for drawing</param>
		/// <param name="firstRectPoint">The first point of transformed bounding rectangle</param>
		/// <param name="firstPoint">The first arc point of Doughnut slice</param>
		/// <param name="secondRectPoint">The second point of transformed bounding rectangle</param>
		/// <param name="secondPoint">The second arc point of Doughnut slice</param>
		/// <param name="threePoint">The three point of Doughnut slice</param>
		/// <param name="fourPoint">The four point of Doughnut slice</param>
		/// <param name="startAngle">Start angle of Doughnut slice</param>
		/// <param name="sweepAngle">The end angle of Doughnut slice</param>
		/// <param name="fill">Fill Doughnut slice with brush</param>
		/// <param name="doughnutRadius">Radius for doughnut chart</param>
		/// <param name="pointIndex">Data Point Index</param>
		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 )
		{
			// Common Elements
			CommonElements common = area.Common;
 
			doughnutRadius = 1F - doughnutRadius / 100F;
 
			// Create a graphics path
            using (GraphicsPath path = new GraphicsPath())
            {
 
                // It is enough to transform only two points from 
                // rectangle. This code will create RectangleF from 
                // top left and bottom right points. 
                RectangleF pieRectangle = new RectangleF();
                pieRectangle.X = firstRectPoint.X;
                pieRectangle.Y = firstRectPoint.Y;
                pieRectangle.Height = secondRectPoint.Y - firstRectPoint.Y;
                pieRectangle.Width = secondRectPoint.X - firstRectPoint.X;
 
                RectangleF pieDoughnutRectangle = new RectangleF();
                pieDoughnutRectangle.X = pieRectangle.X + pieRectangle.Width * (1F - doughnutRadius) / 2F;
                pieDoughnutRectangle.Y = pieRectangle.Y + pieRectangle.Height * (1F - doughnutRadius) / 2F;
                pieDoughnutRectangle.Height = pieRectangle.Height * doughnutRadius;
                pieDoughnutRectangle.Width = pieRectangle.Width * doughnutRadius;
 
                // Angle correction algorithm. After rotation AddArc method should used 
                // different transformed angles. This method transforms angles.
                double angleCorrection = pieRectangle.Height / pieRectangle.Width;
 
                float endAngle;
                endAngle = AngleCorrection(startAngle + sweepAngle, angleCorrection);
                startAngle = AngleCorrection(startAngle, angleCorrection);
 
                sweepAngle = endAngle - startAngle;
 
                // Add Line between The Doughnut Arc and Arc
                path.AddLine(fourPoint, firstPoint);
 
                // Add Arc
                if (pieRectangle.Height > 0)
                {
                    // If x angle is 0 this arc will be line in projection.
                    path.AddArc(pieRectangle.X, pieRectangle.Y, pieRectangle.Width, pieRectangle.Height, startAngle, sweepAngle);
                }
 
                // Add Line between the end of the arc and The Doughnut Arc.
                path.AddLine(secondPoint, threePoint);
 
                // Add Doughnut Arc
                if (pieDoughnutRectangle.Height > 0)
                {
                    path.AddArc(pieDoughnutRectangle.X, pieDoughnutRectangle.Y, pieDoughnutRectangle.Width, pieDoughnutRectangle.Height, startAngle + sweepAngle, -sweepAngle);
                }
 
                if (common.ProcessModePaint)
                {
                    // Get surface colors
                    Color frontLightColor, leftLightColor, topLightColor, backLightColor, rightLightColor, bottomLightColor;
                    area.matrix3D.GetLight(brush.Color, out frontLightColor, out backLightColor, out leftLightColor, out rightLightColor, out topLightColor, out bottomLightColor);
 
                    Pen newPen = (Pen)pen.Clone();
 
                    if (area.Area3DStyle.LightStyle == LightStyle.Realistic && point.BorderColor == Color.Empty)
                    {
                        newPen.Color = frontLightColor;
                    }
 
                    // Drawing Mode
                    if (fill)
                    {
                        using (Brush lightBrush = new SolidBrush(frontLightColor))
                        {
                            FillPath(lightBrush, path);
                        }
                    }
 
                    if (point.BorderColor != Color.Empty &&
                        point.BorderWidth > 0 &&
                        point.BorderDashStyle != ChartDashStyle.NotSet)
                    {
                        DrawGraphicsPath(newPen, path);
                    }
                }
 
                if (common.ProcessModeRegions && fill)
                {
 
 
                    // Check if processing collected data point
                    if (point.IsCustomPropertySet("_COLLECTED_DATA_POINT"))
                    {
                        // Add point to the map area
                        common.HotRegionsList.AddHotRegion(
                            (ChartGraphics)this,
                            path,
                            false,
                            point.ReplaceKeywords(point.ToolTip),
#if Microsoft_CONTROL
						string.Empty,
						string.Empty,
						string.Empty,
#else // Microsoft_CONTROL
                            point.ReplaceKeywords(point.Url),
                            point.ReplaceKeywords(point.MapAreaAttributes),
                            point.ReplaceKeywords(point.PostBackValue),
#endif // Microsoft_CONTROL
                            point,
                            ChartElementType.DataPoint);
 
                        return;
                    }
 
 
 
                    // Add points to the map area
                    common.HotRegionsList.AddHotRegion(
                        path,
                        false,
                        (ChartGraphics)this,
                        point,
                        point.series.Name,
                        pointIndex);
                }
            }
		}
 
		/// <summary>
		/// Draw Graphics Path. This method is introduced because of 
		/// bug in DrawPath method when Pen Width is bigger then 1.
		/// </summary>
		/// <param name="pen">Pen</param>
		/// <param name="path">Graphics Path</param>
		private void DrawGraphicsPath( Pen pen, GraphicsPath path )
		{
			// Normal case. Very fast Drawing.
			if( pen.Width < 2 )
			{
				DrawPath( pen, path );
			}
			else
			{
			
				// Converts each curve in this path into a sequence 
				// of connected line segments. Slow Drawing.
				path.Flatten();
 
				// Set Pen cap
				pen.EndCap = LineCap.Round; 
				pen.StartCap = LineCap.Round; 
 
				PointF [] pathPoints;
 
				pathPoints = path.PathPoints;
				
				// Draw any segment as a line.
				for( int point = 0; point < path.PathPoints.Length - 1; point++ )
				{
					PointF [] points;
		
					points = new PointF[2];
					points[0] = pathPoints[point];
					points[1] = pathPoints[point+1];
 
					DrawLine( pen, points[0], points[1] );
				}
			}
		}
		
		/// <summary>
		/// Angle correction algorithm. After rotation different 
		/// transformed angle should be used. This method transforms angles.
		/// </summary>
		/// <param name="angle">Not transformed angle</param>
		/// <param name="correction">Correction of bounding rectangle (change between width and height)</param>
		/// <returns>Transformed angle</returns>
		private float AngleCorrection( float angle, double correction )
		{
			// Make all angles to be between -90 and 90.			
			if( angle > -90 && angle < 90 )
			{
				angle = (float)(Math.Atan( Math.Tan( ( angle ) * Math.PI / 180 ) * correction ) * 180 / Math.PI);
			}
			else if( angle > -270 && angle < -90 )
			{
				angle = angle + 180;
				angle = (float)(Math.Atan( Math.Tan( ( angle ) * Math.PI / 180 ) * correction ) * 180 / Math.PI);
				angle = angle - 180;
			}
			else if( angle > 90 && angle < 270 )
			{
				angle = angle - 180;
				angle = (float)(Math.Atan( Math.Tan( ( angle ) * Math.PI / 180 ) * correction ) * 180 / Math.PI);
				angle = angle + 180;
			}
			else if( angle > 270 && angle < 450 )
			{
				angle = angle - 360;
				angle = (float)(Math.Atan( Math.Tan( ( angle ) * Math.PI / 180 ) * correction ) * 180 / Math.PI);
				angle = angle + 360;
			}
			else if( angle > 450 )
			{
				angle = angle - 540;
				angle = (float)(Math.Atan( Math.Tan( ( angle ) * Math.PI / 180 ) * correction ) * 180 / Math.PI);
				angle = angle + 540;
			}
			return angle;
		}
 
		#endregion
 
		#region 3D Surface drawing methods (used in Line charts)
 
		/// <summary>
		/// Draws a 3D polygon defined by 4 points in 2D space.
		/// </summary>
		/// <param name="area">Chart area reference.</param>
		/// <param name="matrix">Coordinates transformation matrix.</param>
		/// <param name="surfaceName">Name of the surface to draw.</param>
		/// <param name="positionZ">Z position of the back side of the 3D surface.</param>
		/// <param name="backColor">Color of rectangle</param>
		/// <param name="borderColor">Border Color</param>
		/// <param name="borderWidth">Border Width</param>
		/// <param name="firstPoint">First point.</param>
		/// <param name="secondPoint">Second point.</param>
		/// <param name="thirdPoint">Third point.</param>
		/// <param name="fourthPoint">Fourth point.</param>
		/// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
		/// <param name="lineSegmentType">AxisName of line segment. Used for step lines and splines.</param>
		/// <param name="thinBorders">Thin border will be drawn on specified sides.</param>
		/// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
		internal GraphicsPath 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)
		{
			// Create graphics path for selection
			bool	drawElements = ((operationType & DrawingOperationTypes.DrawElement) == DrawingOperationTypes.DrawElement);
			GraphicsPath	resultPath = ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
				? new GraphicsPath() : null;
 
			//**********************************************************************
			//** Prepare, transform polygon coordinates
			//**********************************************************************
 
			// Define 4 points polygon
			Point3D [] points3D = new Point3D[4];
			points3D[0] = new Point3D((float)firstPoint.xPosition, (float)firstPoint.yPosition, positionZ);
			points3D[1] = new Point3D((float)secondPoint.xPosition, (float)secondPoint.yPosition, positionZ);
			points3D[2] = new Point3D((float)thirdPoint.xPosition, (float)thirdPoint.yPosition, positionZ);
			points3D[3] = new Point3D((float)fourthPoint.xPosition, (float)fourthPoint.yPosition, positionZ);
 
			// Transform coordinates
			matrix.TransformPoints( points3D );
 
			// Get absolute coordinates and create array of PointF
			PointF[]	polygonPoints = new PointF[4];
			polygonPoints[0] = GetAbsolutePoint(points3D[0].PointF);
			polygonPoints[1] = GetAbsolutePoint(points3D[1].PointF);
			polygonPoints[2] = GetAbsolutePoint(points3D[2].PointF);
			polygonPoints[3] = GetAbsolutePoint(points3D[3].PointF);
 
 
			//**********************************************************************
			//** Define drawing colors
			//**********************************************************************
			bool topIsVisible = IsSurfaceVisible( points3D[0], points3D[1], points3D[2]);
			Color polygonColor = matrix.GetPolygonLight( points3D, backColor, topIsVisible, area.Area3DStyle.Rotation, surfaceName, area.ReverseSeriesOrder );
			Color	surfaceBorderColor = borderColor;
			if(surfaceBorderColor == Color.Empty)
			{
				// If border color is emty use color slightly darker than main back color
				surfaceBorderColor = ChartGraphics.GetGradientColor( backColor, Color.Black, 0.2 );
			}
 
			//**********************************************************************
			//** Draw elements if required.
			//**********************************************************************
			Pen thickBorderPen = null;
			if(drawElements)
			{
				// Remember SmoothingMode and turn off anti aliasing
				SmoothingMode oldSmoothingMode = SmoothingMode;
				SmoothingMode = SmoothingMode.Default;
 
				// Draw the polygon
                using (Brush brush = new SolidBrush(polygonColor))
                {
				    FillPolygon(brush, polygonPoints);
                }
 
				// Return old smoothing mode
				SmoothingMode = oldSmoothingMode;
			
				// Draw thin polygon border of darker color around the whole polygon
				if(thinBorders != 0)
				{
					Pen thinLinePen = new Pen(surfaceBorderColor, 1);
					if( (thinBorders & SurfaceNames.Left) != 0 )
						DrawLine(thinLinePen, polygonPoints[3], polygonPoints[0]);						
					if( (thinBorders & SurfaceNames.Right) != 0 )
						DrawLine(thinLinePen, polygonPoints[1], polygonPoints[2]);
					if( (thinBorders & SurfaceNames.Top) != 0 )
						DrawLine(thinLinePen, polygonPoints[0], polygonPoints[1]);
					if( (thinBorders & SurfaceNames.Bottom) != 0 )
						DrawLine(thinLinePen, polygonPoints[2], polygonPoints[3]);
				}
				else if(polygonColor.A == 255)
				{
					DrawPolygon(new Pen(polygonColor, 1), polygonPoints);
				}
 
				// Create thick border line pen
				thickBorderPen = new Pen(surfaceBorderColor, borderWidth);
				thickBorderPen.StartCap = LineCap.Round;
				thickBorderPen.EndCap = LineCap.Round;
 
				// Draw thick Top & Bottom lines
				DrawLine(thickBorderPen, polygonPoints[0], polygonPoints[1]);
				DrawLine(thickBorderPen, polygonPoints[2], polygonPoints[3]);
 
				// Draw thick Right & Left lines on first & last segments of the line
				if(lineSegmentType == LineSegmentType.First)
				{
					DrawLine(thickBorderPen, polygonPoints[3], polygonPoints[0]);						
				}
				else if(lineSegmentType == LineSegmentType.Last)
				{
					DrawLine(thickBorderPen, polygonPoints[1], polygonPoints[2]);
				}
			}
 
			//**********************************************************************
			//** Redraw front line of the previuos line segment.
			//**********************************************************************
			if(area.Area3DStyle.Perspective == 0)
			{
				if(frontLinePoint1 != PointF.Empty && frontLinePen != null)
				{
					if( (frontLinePoint1.X == polygonPoints[0].X &&
						frontLinePoint1.Y == polygonPoints[0].Y ||
						frontLinePoint2.X == polygonPoints[1].X &&
						frontLinePoint2.Y == polygonPoints[1].Y ) ||
 
						(frontLinePoint1.X == polygonPoints[1].X &&
						frontLinePoint1.Y == polygonPoints[1].Y ||
						frontLinePoint2.X == polygonPoints[0].X &&
						frontLinePoint2.Y == polygonPoints[0].Y ) ||
 
						(frontLinePoint1.X == polygonPoints[3].X &&
						frontLinePoint1.Y == polygonPoints[3].Y ||
						frontLinePoint2.X == polygonPoints[2].X &&
						frontLinePoint2.Y == polygonPoints[2].Y) ||						
 
						(frontLinePoint1.X == polygonPoints[2].X &&
						frontLinePoint1.Y == polygonPoints[2].Y ||
						frontLinePoint2.X == polygonPoints[3].X &&
						frontLinePoint2.Y == polygonPoints[3].Y) )
					{
						// Do not draw the line if it will be overlapped with current
					}
					else
					{
						// Draw line !!!! 
						DrawLine( 
							frontLinePen, 
							(float)Math.Round(frontLinePoint1.X), 
							(float)Math.Round(frontLinePoint1.Y), 
							(float)Math.Round(frontLinePoint2.X), 
							(float)Math.Round(frontLinePoint2.Y) );
					}
 
					// Reset line properties
					frontLinePen = null;
					frontLinePoint1 = PointF.Empty;
					frontLinePoint2 = PointF.Empty;
				}
 
				//**********************************************************************
				//** Check if front line should be redrawn whith the next segment.
				//**********************************************************************
				if(drawElements)
				{
					// Add top line
					frontLinePen = thickBorderPen;
					frontLinePoint1 = polygonPoints[0];
					frontLinePoint2 = polygonPoints[1];
				}
			}
 
			// Calculate path for selection
			if(resultPath != null)
			{
				// Add polygon to the path
				resultPath.AddPolygon(polygonPoints);
			}
 
			return resultPath;
		}
 
		/// <summary>
		/// Helper method which returns the splines flatten path.
		/// </summary>
		/// <param name="area">Chart area reference.</param>
		/// <param name="positionZ">Z position of the back side of the 3D surface.</param>
		/// <param name="firstPoint">First point.</param>
		/// <param name="secondPoint">Second point.</param>
		/// <param name="points">Array of points.</param>
		/// <param name="tension">Line tension.</param>
		/// <param name="flatten">Flatten result path.</param>
		/// <param name="translateCoordinates">Indicates that points coordinates should be translated.</param>
		/// <param name="yValueIndex">Index of the Y value to use.</param>
		/// <returns>Spline path.</returns>
		internal GraphicsPath GetSplineFlattenPath(
			ChartArea area,
			float positionZ, 
			DataPoint3D	firstPoint,
			DataPoint3D	secondPoint, 
			ArrayList points,
			float tension,
			bool flatten,
			bool translateCoordinates,
			int yValueIndex)
		{
			// Find first spline point index
			int firtsSplinePointIndex = (firstPoint.index < secondPoint.index) ? firstPoint.index : secondPoint.index;
			--firtsSplinePointIndex;
			if(firtsSplinePointIndex >= (points.Count - 2) )
			{
				--firtsSplinePointIndex;
			}
			if(firtsSplinePointIndex < 1)
			{
				firtsSplinePointIndex = 1;
			}
 
			// Find four points which are required to draw the spline
			int	pointArrayIndex = int.MinValue;
			DataPoint3D [] splineDataPoints = new DataPoint3D[4];
			splineDataPoints[0] = FindPointByIndex(points, firtsSplinePointIndex, null, ref pointArrayIndex);
			splineDataPoints[1] = FindPointByIndex(points, firtsSplinePointIndex + 1, null, ref pointArrayIndex);
			splineDataPoints[2] = FindPointByIndex(points, firtsSplinePointIndex + 2, null, ref pointArrayIndex);
			splineDataPoints[3] = FindPointByIndex(points, firtsSplinePointIndex + 3, null, ref pointArrayIndex);
 
			// Get offset of spline segment in array
			int splineSegmentOffset = 0;
			while(splineSegmentOffset < 4)
			{
				if(splineDataPoints[splineSegmentOffset].index == firstPoint.index || 
					splineDataPoints[splineSegmentOffset].index == secondPoint.index)
				{
					break;
				}
				++splineSegmentOffset;
			}
 
			// Get number of found points
			int nonNullPoints = 2;
			if(splineDataPoints[2] != null)
				++nonNullPoints;
			if(splineDataPoints[3] != null)
				++nonNullPoints;
 
 
			// Get coordinates and create array of PointF for the front spline
			PointF[]	polygonPointsFront = new PointF[nonNullPoints];
			if(yValueIndex == 0)
			{
				polygonPointsFront[0] = new PointF((float)splineDataPoints[0].xPosition, (float)splineDataPoints[0].yPosition);
				polygonPointsFront[1] = new PointF((float)splineDataPoints[1].xPosition, (float)splineDataPoints[1].yPosition);
				if(nonNullPoints > 2)
					polygonPointsFront[2] = new PointF((float)splineDataPoints[2].xPosition, (float)splineDataPoints[2].yPosition);
				if(nonNullPoints > 3)
					polygonPointsFront[3] = new PointF((float)splineDataPoints[3].xPosition, (float)splineDataPoints[3].yPosition);
			}
			else
			{
				// Set active vertical axis
				Axis	vAxis = (firstPoint.dataPoint.series.YAxisType == AxisType.Primary) ? area.AxisY : area.AxisY2;
 
				float secondYValue = (float)vAxis.GetPosition(splineDataPoints[0].dataPoint.YValues[yValueIndex]);
				polygonPointsFront[0] = new PointF((float)splineDataPoints[0].xPosition, secondYValue);
				secondYValue = (float)vAxis.GetPosition(splineDataPoints[1].dataPoint.YValues[yValueIndex]);
				polygonPointsFront[1] = new PointF((float)splineDataPoints[1].xPosition, secondYValue);
				if(nonNullPoints > 2)
				{
					secondYValue = (float)vAxis.GetPosition(splineDataPoints[2].dataPoint.YValues[yValueIndex]);
					polygonPointsFront[2] = new PointF((float)splineDataPoints[2].xPosition, secondYValue);
				}
				if(nonNullPoints > 3)
				{
					secondYValue = (float)vAxis.GetPosition(splineDataPoints[3].dataPoint.YValues[yValueIndex]);
					polygonPointsFront[3] = new PointF((float)splineDataPoints[3].xPosition, secondYValue);
				}
			}
 
			// Translate points coordinates in 3D space and get absolute coordinate
			if(translateCoordinates)
			{
				// Prepare array of points
				Point3D[]	points3D = new Point3D[nonNullPoints];
				for(int index = 0; index < nonNullPoints; index++)
				{
					points3D[index] = new Point3D(polygonPointsFront[index].X, polygonPointsFront[index].Y, positionZ);
				}
 
				// Make coordinates transformation
				area.matrix3D.TransformPoints( points3D );
 
				// Get absolute values
				for(int index = 0; index < nonNullPoints; index++)
				{
					polygonPointsFront[index] = GetAbsolutePoint(points3D[index].PointF);
				}
 
			}
 
			// Create graphics path for the front spline surface and flatten it.
			GraphicsPath	splineSurfacePath = new GraphicsPath();
			splineSurfacePath.AddCurve(polygonPointsFront, splineSegmentOffset, 1, tension);
			if(flatten)
			{
				splineSurfacePath.Flatten();
			}
 
			// IsReversed points order
			if(firstPoint.index > secondPoint.index)
			{
				splineSurfacePath.Reverse();
			}
 
			return splineSurfacePath;
		}
 
		/// <summary>
		/// Draws a 3D spline surface connecting the two specified points in 2D space.
		/// Used to draw Spline based charts.
		/// </summary>
		/// <param name="area">Chart area reference.</param>
		/// <param name="matrix">Coordinates transformation matrix.</param>
		/// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
		/// <param name="surfaceName">Name of the surface to draw.</param>
		/// <param name="positionZ">Z position of the back side of the 3D surface.</param>
		/// <param name="depth">Depth of the 3D surface.</param>
		/// <param name="backColor">Color of rectangle</param>
		/// <param name="borderColor">Border Color</param>
		/// <param name="borderWidth">Border Width</param>
		/// <param name="borderDashStyle">Border Style</param>
		/// <param name="firstPoint">First point.</param>
		/// <param name="secondPoint">Second point.</param>
		/// <param name="points">Array of points.</param>
		/// <param name="pointIndex">Index of point to draw.</param>
		/// <param name="tension">Line tension.</param>
		/// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
		/// <param name="forceThinBorder">Thin border will be drawn on all segments.</param>
		/// <param name="forceThickBorder">Thick border will be drawn on all segments.</param>
		/// <param name="reversedSeriesOrder">Series are drawn in reversed order.</param>
		/// <param name="multiSeries">Multiple series are drawn at the same time.</param>
		/// <param name="yValueIndex">Index of the Y value to use.</param>
		/// <param name="clipInsideArea">Surface should be clipped inside plotting area.</param>
		/// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
		internal GraphicsPath 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)
		{
			// If zero tension is specified - draw a Line Surface
			if(tension == 0f)
			{
				return Draw3DSurface( 
					area,
					matrix,
					lightStyle,
					surfaceName,
					positionZ, 
					depth, 
					backColor, 
					borderColor, 
					borderWidth, 
					borderDashStyle, 
					firstPoint,
					secondPoint, 
					points,
					pointIndex,
					tension,
					operationType,
					LineSegmentType.Single,
					forceThinBorder,
					forceThickBorder,
					reversedSeriesOrder,
					multiSeries,
					yValueIndex,
					clipInsideArea);
			}
 
			// Create graphics path for selection
			GraphicsPath	resultPath = ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
				? new GraphicsPath() : null;
 
			// Get spline flatten path
			GraphicsPath splineSurfacePath = GetSplineFlattenPath(
				area, positionZ,
				firstPoint, secondPoint, points, tension, true, false, yValueIndex);
 
			// Check if reversed drawing order required
			bool	reversed = false;
			if((pointIndex + 1) < points.Count)
			{
				DataPoint3D p = (DataPoint3D)points[pointIndex + 1];
				if(p.index == firstPoint.index)
				{
					reversed = true;
				}
			}
 
			if(reversed)
			{
				splineSurfacePath.Reverse();
			}
 
			// Loop through all segment lines the spline consists off
			PointF[] splinePathPoints = splineSurfacePath.PathPoints;
			DataPoint3D	dp1 = new DataPoint3D();
			DataPoint3D	dp2 = new DataPoint3D();
			LineSegmentType	lineSegmentType = LineSegmentType.Middle;
			for(int pIndex = 1; pIndex < splinePathPoints.Length; pIndex++)
			{
				bool forceSegmentThinBorder = false;
				bool forceSegmentThickBorder = false;
 
				// Calculate surface coordinates
				if(!reversed)
				{
					dp1.index = firstPoint.index;
					dp1.dataPoint = firstPoint.dataPoint;
					dp1.xPosition = splinePathPoints[pIndex - 1].X;
					dp1.yPosition = splinePathPoints[pIndex - 1].Y;
 
					dp2.index = secondPoint.index;
					dp2.index = secondPoint.index;
					dp2.xPosition = splinePathPoints[pIndex].X;
					dp2.yPosition = splinePathPoints[pIndex].Y;
				}
				else
				{
					dp2.index = firstPoint.index;
					dp2.dataPoint = firstPoint.dataPoint;
					dp2.xPosition = splinePathPoints[pIndex - 1].X;
					dp2.yPosition = splinePathPoints[pIndex - 1].Y;
 
					dp1.index = secondPoint.index;
					dp1.dataPoint = secondPoint.dataPoint;
					dp1.xPosition = splinePathPoints[pIndex].X;
					dp1.yPosition = splinePathPoints[pIndex].Y;
				}
 
				// Get sefment type
				lineSegmentType = LineSegmentType.Middle;
				if(pIndex == 1)
				{
					if(!reversed)
						lineSegmentType = LineSegmentType.First;
					else
						lineSegmentType = LineSegmentType.Last;
 
					forceSegmentThinBorder = forceThinBorder;
					forceSegmentThickBorder = forceThickBorder;
				}
				else if(pIndex == splinePathPoints.Length - 1)
				{
					if(!reversed)
						lineSegmentType = LineSegmentType.Last;
					else
						lineSegmentType = LineSegmentType.First;
 
					forceSegmentThinBorder = forceThinBorder;
					forceSegmentThickBorder = forceThickBorder;
				}
 
				// Draw flat surface
				GraphicsPath	segmentResultPath = Draw3DSurface( 
					area,
					matrix,
					lightStyle,
					surfaceName,
					positionZ, 
					depth, 
					backColor, 
					borderColor, 
					borderWidth, 
					borderDashStyle, 
					dp1,
					dp2, 
					points,
					pointIndex,
					0f,
					operationType,
					lineSegmentType,
					forceSegmentThinBorder,
					forceSegmentThickBorder,
					reversedSeriesOrder,
					multiSeries,
					yValueIndex,
					clipInsideArea);
 
				// Add selection path
				if(resultPath != null && segmentResultPath != null && segmentResultPath.PointCount > 0)
				{
					resultPath.AddPath(segmentResultPath, true);
				}
			
			}
 
			return resultPath;
		}
 
 
		/// <summary>
		/// Draws a 3D surface connecting the two specified points in 2D space.
		/// Used to draw Line based charts.
		/// </summary>
		/// <param name="area">Chart area reference.</param>
		/// <param name="matrix">Coordinates transformation matrix.</param>
		/// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
		/// <param name="surfaceName">Name of the surface to draw.</param>
		/// <param name="positionZ">Z position of the back side of the 3D surface.</param>
		/// <param name="depth">Depth of the 3D surface.</param>
		/// <param name="backColor">Color of rectangle</param>
		/// <param name="borderColor">Border Color</param>
		/// <param name="borderWidth">Border Width</param>
		/// <param name="borderDashStyle">Border Style</param>
		/// <param name="firstPoint">First point.</param>
		/// <param name="secondPoint">Second point.</param>
		/// <param name="points">Array of points.</param>
		/// <param name="pointIndex">Index of point to draw.</param>
		/// <param name="tension">Line tension.</param>
		/// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
		/// <param name="lineSegmentType">AxisName of line segment. Used for step lines and splines.</param>
		/// <param name="forceThinBorder">Thin border will be drawn on all segments.</param>
		/// <param name="forceThickBorder">Thick border will be drawn on all segments.</param>
		/// <param name="reversedSeriesOrder">Series are drawn in reversed order.</param>
		/// <param name="multiSeries">Multiple series are drawn at the same time.</param>
		/// <param name="yValueIndex">Index of the Y value to use.</param>
		/// <param name="clipInsideArea">Surface should be clipped inside plotting area.</param>
		/// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
		internal GraphicsPath 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)
		{
			// If non-zero tension is specified - draw a Spline Surface
			if(tension != 0f)
			{
				return Draw3DSplineSurface( 
					area,
					matrix,
					lightStyle,
					surfaceName,
					positionZ, 
					depth, 
					backColor, 
					borderColor, 
					borderWidth, 
					borderDashStyle, 
					firstPoint,
					secondPoint, 
					points,
					pointIndex,
					tension,
					operationType,
					forceThinBorder,
					forceThickBorder,
					reversedSeriesOrder,
					multiSeries,
					yValueIndex,
					clipInsideArea);
			}
 
			//**********************************************************************
			//** Create graphics path for selection
			//**********************************************************************
			bool	drawElements = ((operationType & DrawingOperationTypes.DrawElement) == DrawingOperationTypes.DrawElement);
			GraphicsPath	resultPath = ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
				? new GraphicsPath() : null;
 
			//**********************************************************************
			//** Check surface coordinates
			//**********************************************************************
			if((decimal)firstPoint.xPosition == (decimal)secondPoint.xPosition &&
				(decimal)firstPoint.yPosition == (decimal)secondPoint.yPosition)
			{
				return resultPath;
			}
 
			//**********************************************************************
			//** Clip surface
			//**********************************************************************
 
			// Check if line between the first and second points intersects with 
			// plotting area top or bottom boundary
			if(clipInsideArea)
			{
				//****************************************************************
				//** Round plot are position and point coordinates
				//****************************************************************
				int decimals = 3;
				decimal plotAreaPositionX = Math.Round((decimal)area.PlotAreaPosition.X, decimals);
				decimal plotAreaPositionY = Math.Round((decimal)area.PlotAreaPosition.Y, decimals);
				decimal plotAreaPositionRight = Math.Round((decimal)area.PlotAreaPosition.Right, decimals);
				decimal plotAreaPositionBottom = Math.Round((decimal)area.PlotAreaPosition.Bottom, decimals);
 
				// Make area a little bit bigger
				plotAreaPositionX -= 0.001M;
				plotAreaPositionY -= 0.001M;
				plotAreaPositionRight += 0.001M;
				plotAreaPositionBottom += 0.001M;
 
				// Chech data points X values
				if((decimal)firstPoint.xPosition < plotAreaPositionX || 
					(decimal)firstPoint.xPosition > plotAreaPositionRight ||
					(decimal)secondPoint.xPosition < plotAreaPositionX || 
					(decimal)secondPoint.xPosition > plotAreaPositionRight )
				{
					// Check if surface completly out of the plot area
					if((decimal)firstPoint.xPosition < plotAreaPositionX &&
						(decimal)secondPoint.xPosition < plotAreaPositionX)
					{
						return resultPath;
					}
					// Check if surface completly out of the plot area
					if((decimal)firstPoint.xPosition > plotAreaPositionRight &&
						(decimal)secondPoint.xPosition > plotAreaPositionRight)
					{
						return resultPath;
					}
 
					// Only part of the surface is outside - fix X value and adjust Y value
					if((decimal)firstPoint.xPosition < plotAreaPositionX)
					{
						firstPoint.yPosition = ((double)plotAreaPositionX - secondPoint.xPosition) /
							(firstPoint.xPosition - secondPoint.xPosition) *
							(firstPoint.yPosition - secondPoint.yPosition) +
							secondPoint.yPosition;
						firstPoint.xPosition = (double)plotAreaPositionX;
					}
					else if((decimal)firstPoint.xPosition > plotAreaPositionRight)
					{
						firstPoint.yPosition = ((double)plotAreaPositionRight - secondPoint.xPosition) /
							(firstPoint.xPosition - secondPoint.xPosition) *
							(firstPoint.yPosition - secondPoint.yPosition) +
							secondPoint.yPosition;
						firstPoint.xPosition = (double)plotAreaPositionRight;
					}
					if((decimal)secondPoint.xPosition < plotAreaPositionX)
					{
						secondPoint.yPosition = ((double)plotAreaPositionX - secondPoint.xPosition) /
							(firstPoint.xPosition - secondPoint.xPosition) *
							(firstPoint.yPosition - secondPoint.yPosition) +
							secondPoint.yPosition;
						secondPoint.xPosition = (double)plotAreaPositionX;
					}
					else if((decimal)secondPoint.xPosition > plotAreaPositionRight)
					{
						secondPoint.yPosition = ((double)plotAreaPositionRight - secondPoint.xPosition) /
							(firstPoint.xPosition - secondPoint.xPosition) *
							(firstPoint.yPosition - secondPoint.yPosition) +
							secondPoint.yPosition;
						secondPoint.xPosition = (double)plotAreaPositionRight;
					}
				}
 
				// Chech data points Y values
				if((decimal)firstPoint.yPosition < plotAreaPositionY || 
					(decimal)firstPoint.yPosition > plotAreaPositionBottom ||
					(decimal)secondPoint.yPosition < plotAreaPositionY || 
					(decimal)secondPoint.yPosition > plotAreaPositionBottom )
				{
					// Remember previous y positions
					double prevFirstPointY = firstPoint.yPosition;
					double prevSecondPointY = secondPoint.yPosition;
 
					// Check if whole line is outside plotting region
					bool	surfaceCompletlyOutside = false;
					if((decimal)firstPoint.yPosition < plotAreaPositionY && 
						(decimal)secondPoint.yPosition < plotAreaPositionY)
					{
						surfaceCompletlyOutside = true;
						firstPoint.yPosition = (double)plotAreaPositionY;
						secondPoint.yPosition = (double)plotAreaPositionY;
					}
					if((decimal)firstPoint.yPosition > plotAreaPositionBottom && 
						(decimal)secondPoint.yPosition > plotAreaPositionBottom)
					{
						surfaceCompletlyOutside = true;
						firstPoint.yPosition = (double)plotAreaPositionBottom;
						secondPoint.yPosition = (double)plotAreaPositionBottom;
					}
 
					// Calculate color used to draw "cut" surfaces
					Color	cutSurfaceBackColor = ChartGraphics.GetGradientColor(backColor, Color.Black, 0.5);
					Color	cutSurfaceBorderColor = ChartGraphics.GetGradientColor(borderColor, Color.Black, 0.5);
 
					// Draw just one surface
					if(surfaceCompletlyOutside)
					{
						resultPath = this.Draw3DSurface( 
							area, matrix, lightStyle, surfaceName, positionZ,  depth, 
							cutSurfaceBackColor, cutSurfaceBorderColor, borderWidth, borderDashStyle, 							
							firstPoint, secondPoint, 
							points, pointIndex, tension, operationType, lineSegmentType,
							forceThinBorder, forceThickBorder, reversedSeriesOrder,
							multiSeries, yValueIndex, clipInsideArea);
 
						// Restore previous y positions
						firstPoint.yPosition = prevFirstPointY;
						secondPoint.yPosition = prevSecondPointY;
 
						return resultPath;
					}
 
					// Get intersection point
					DataPoint3D	intersectionPoint = new DataPoint3D();
					intersectionPoint.yPosition = (double)plotAreaPositionY;
					if((decimal)firstPoint.yPosition > plotAreaPositionBottom ||
						(decimal)secondPoint.yPosition > plotAreaPositionBottom )
					{
						intersectionPoint.yPosition = (double)plotAreaPositionBottom;
					}
					intersectionPoint.xPosition = (intersectionPoint.yPosition - secondPoint.yPosition) *
						(firstPoint.xPosition - secondPoint.xPosition) / 
						(firstPoint.yPosition - secondPoint.yPosition) + 
						secondPoint.xPosition;
 
					// Check if there are 2 intersection points (3 segments)
					int		segmentNumber = 2;
					DataPoint3D	intersectionPoint2 = null;
					if( ((decimal)firstPoint.yPosition < plotAreaPositionY &&
						(decimal)secondPoint.yPosition > plotAreaPositionBottom) ||
						((decimal)firstPoint.yPosition > plotAreaPositionBottom &&
						(decimal)secondPoint.yPosition < plotAreaPositionY))
					{
						segmentNumber = 3;
						intersectionPoint2 = new DataPoint3D();
						if((decimal)intersectionPoint.yPosition == plotAreaPositionY)
						{
							intersectionPoint2.yPosition = (double)plotAreaPositionBottom;
						}
						else
						{
							intersectionPoint2.yPosition = (double)plotAreaPositionY;
						}
						intersectionPoint2.xPosition = (intersectionPoint2.yPosition - secondPoint.yPosition) *
							(firstPoint.xPosition - secondPoint.xPosition) / 
							(firstPoint.yPosition - secondPoint.yPosition) + 
							secondPoint.xPosition;
 
						// Switch intersection points
						if((decimal)firstPoint.yPosition > plotAreaPositionBottom)
						{
							DataPoint3D tempPoint = new DataPoint3D();
							tempPoint.xPosition = intersectionPoint.xPosition;
							tempPoint.yPosition = intersectionPoint.yPosition;
							intersectionPoint.xPosition = intersectionPoint2.xPosition;
							intersectionPoint.yPosition = intersectionPoint2.yPosition;
							intersectionPoint2.xPosition = tempPoint.xPosition;
							intersectionPoint2.yPosition = tempPoint.yPosition;
						}
					}
 
 
					// Adjust points Y values
					bool	firstSegmentVisible = true;
					if((decimal)firstPoint.yPosition < plotAreaPositionY)
					{
						firstSegmentVisible = false;
						firstPoint.yPosition = (double)plotAreaPositionY;
					}
					else if((decimal)firstPoint.yPosition > plotAreaPositionBottom)
					{
						firstSegmentVisible = false;
						firstPoint.yPosition = (double)plotAreaPositionBottom;
					}
					if((decimal)secondPoint.yPosition < plotAreaPositionY)
					{
						secondPoint.yPosition = (double)plotAreaPositionY;
					}
					else if((decimal)secondPoint.yPosition > plotAreaPositionBottom)
					{
						secondPoint.yPosition = (double)plotAreaPositionBottom;
					}
 
					// Check if reversed drawing order required
					bool	reversed = false;
					if((pointIndex + 1) < points.Count)
					{
						DataPoint3D p = (DataPoint3D)points[pointIndex + 1];
						if(p.index == firstPoint.index)
						{
							reversed = true;
						}
					}
 
					// Draw surfaces in 2 or 3 segments
					for(int segmentIndex = 0; segmentIndex < 3; segmentIndex++)
					{
						GraphicsPath segmentPath = null;
						if(segmentIndex == 0 && !reversed ||
							segmentIndex == 2 && reversed)
						{
							// Draw first segment
							if(intersectionPoint2 == null)
							{
								intersectionPoint2 = intersectionPoint;
							}
							intersectionPoint2.dataPoint = secondPoint.dataPoint;
							intersectionPoint2.index = secondPoint.index;
 
							segmentPath = this.Draw3DSurface( 
								area, matrix, lightStyle, surfaceName, positionZ,  depth, 
								(firstSegmentVisible && segmentNumber != 3) ? backColor : cutSurfaceBackColor, 
								(firstSegmentVisible && segmentNumber != 3) ? borderColor : cutSurfaceBorderColor, 
								borderWidth, borderDashStyle, 							
								firstPoint, intersectionPoint2, 
								points, pointIndex, tension, operationType, lineSegmentType,
								forceThinBorder, forceThickBorder, reversedSeriesOrder,
								multiSeries, yValueIndex, clipInsideArea);
						}
 
						if(segmentIndex == 1 && intersectionPoint2 != null && segmentNumber == 3)
						{
							// Draw middle segment
							intersectionPoint2.dataPoint = secondPoint.dataPoint;
							intersectionPoint2.index = secondPoint.index;
 
							segmentPath = this.Draw3DSurface( 
								area, matrix, lightStyle, surfaceName, positionZ,  depth, 
								backColor, 
								borderColor, 
								borderWidth, borderDashStyle, 							
								intersectionPoint, intersectionPoint2, 
								points, pointIndex, tension, operationType, lineSegmentType,
								forceThinBorder, forceThickBorder, reversedSeriesOrder,
								multiSeries, yValueIndex, clipInsideArea);
						}
 
						if(segmentIndex == 2 && !reversed ||
							segmentIndex == 0 && reversed)
						{
							// Draw second segment
							intersectionPoint.dataPoint = firstPoint.dataPoint;
							intersectionPoint.index = firstPoint.index;
 
							segmentPath = this.Draw3DSurface( 
								area, matrix, lightStyle, surfaceName, positionZ,  depth, 
								(!firstSegmentVisible && segmentNumber != 3) ? backColor : cutSurfaceBackColor, 
								(!firstSegmentVisible && segmentNumber != 3) ? borderColor : cutSurfaceBorderColor, 
								borderWidth, borderDashStyle, 							
								intersectionPoint, secondPoint,
								points, pointIndex, tension, operationType, lineSegmentType,
								forceThinBorder, forceThickBorder, reversedSeriesOrder,
								multiSeries, yValueIndex, clipInsideArea);
						}
 
						// Add segment path
						if(resultPath != null && segmentPath != null && segmentPath.PointCount > 0)
						{
							resultPath.SetMarkers();
							resultPath.AddPath(segmentPath, true);
						}
					}
 
					// Restore previous y positions
					firstPoint.yPosition = prevFirstPointY;
					secondPoint.yPosition = prevSecondPointY;
 
					return resultPath;
				}
			}
			
			//**********************************************************************
			//** Prepare, transform polygon coordinates
			//**********************************************************************
 
			// Define 4 points polygon
			Point3D [] points3D = new Point3D[4];
			points3D[0] = new Point3D((float)firstPoint.xPosition, (float)firstPoint.yPosition, positionZ + depth);
			points3D[1] = new Point3D((float)secondPoint.xPosition, (float)secondPoint.yPosition, positionZ + depth);
			points3D[2] = new Point3D((float)secondPoint.xPosition, (float)secondPoint.yPosition, positionZ);
			points3D[3] = new Point3D((float)firstPoint.xPosition, (float)firstPoint.yPosition, positionZ);
 
			// Transform coordinates
			matrix.TransformPoints( points3D );
 
			// Get absolute coordinates and create array of PointF
			PointF[]	polygonPoints = new PointF[4];
			polygonPoints[0] = GetAbsolutePoint(points3D[0].PointF);
			polygonPoints[1] = GetAbsolutePoint(points3D[1].PointF);
			polygonPoints[2] = GetAbsolutePoint(points3D[2].PointF);
			polygonPoints[3] = GetAbsolutePoint(points3D[3].PointF);
 
			//**********************************************************************
			//** Define drawing colors
			//**********************************************************************
			bool topIsVisible = IsSurfaceVisible( points3D[0], points3D[1], points3D[2]);
			Color polygonColor = matrix.GetPolygonLight( points3D, backColor, topIsVisible, area.Area3DStyle.Rotation, surfaceName, area.ReverseSeriesOrder );
			Color	surfaceBorderColor = borderColor;
			if(surfaceBorderColor == Color.Empty)
			{
				// If border color is emty use color slightly darker than main back color
				surfaceBorderColor = ChartGraphics.GetGradientColor( backColor, Color.Black, 0.2 );
			}
 
			//**********************************************************************
			//** Draw elements if required.
			//**********************************************************************
			Pen thinBorderPen = new Pen(surfaceBorderColor, 1);
			if(drawElements)
			{
				// Draw the polygon
				if(backColor != Color.Transparent)
				{
					// Remember SmoothingMode and turn off anti aliasing
					SmoothingMode oldSmoothingMode = SmoothingMode;
					SmoothingMode = SmoothingMode.Default;
 
					// Draw the polygon
                    using (Brush brush = new SolidBrush(polygonColor))
                    {
					    FillPolygon(brush, polygonPoints);
                    }
 
					// Return old smoothing mode
					SmoothingMode = oldSmoothingMode;
				}
 
				// Draw thin polygon border of darker color
				if(forceThinBorder || forceThickBorder)
				{
					if(forceThickBorder)
					{
						Pen linePen = new Pen(surfaceBorderColor, borderWidth);
						linePen.StartCap = LineCap.Round;
						linePen.EndCap = LineCap.Round;
 
						DrawLine(linePen, polygonPoints[0], polygonPoints[1]);
						DrawLine(linePen, polygonPoints[2], polygonPoints[3]);
						DrawLine(linePen, polygonPoints[3], polygonPoints[0]);						
						DrawLine(linePen, polygonPoints[1], polygonPoints[2]);
					}
					else
					{
						// Front & Back lines
						DrawLine(thinBorderPen, polygonPoints[0], polygonPoints[1]);
						DrawLine(thinBorderPen, polygonPoints[2], polygonPoints[3]);
						if(lineSegmentType == LineSegmentType.First)
						{
							// Left line
							DrawLine(thinBorderPen, polygonPoints[3], polygonPoints[0]);
						}
						else if(lineSegmentType == LineSegmentType.Last)
						{
							// Right Line
							DrawLine(thinBorderPen, polygonPoints[1], polygonPoints[2]);
						}
						else
						{
							// Left & Right lines
							DrawLine(thinBorderPen, polygonPoints[3], polygonPoints[0]);						
							DrawLine(thinBorderPen, polygonPoints[1], polygonPoints[2]);
						}
					}
					
				}
				else
				{
					// Draw thin polygon border of same color (solves anti-aliasing issues)
					if(polygonColor.A == 255)
					{
						DrawPolygon(new Pen(polygonColor, 1), polygonPoints);
					}
 
					// Draw thin Front & Back lines
					DrawLine(thinBorderPen, polygonPoints[0], polygonPoints[1]);
					DrawLine(thinBorderPen, polygonPoints[2], polygonPoints[3]);
				}
			}
 
			//**********************************************************************
			//** Draw thick border line on visible sides
			//**********************************************************************
			Pen	thickBorderPen = null;
			if(borderWidth > 1 && !forceThickBorder)
			{
				// Create thick border line pen
				thickBorderPen = new Pen(surfaceBorderColor, borderWidth);
				thickBorderPen.StartCap = LineCap.Round;
				thickBorderPen.EndCap = LineCap.Round;
 
				//****************************************************************
				//** Switch first and second points.
				//****************************************************************
				if(firstPoint.index > secondPoint.index)
				{
					DataPoint3D	tempPoint = firstPoint;
					firstPoint = secondPoint;
					secondPoint = tempPoint;
				}
 
				//**********************************************************************
				//** Check if there are visible (non-empty) lines to the left & right 
				//** of the current line.
				//**********************************************************************
 
				// Get visibility of bounding rectangle
				float minX = (float)Math.Min(points3D[0].X, points3D[1].X);
				float minY = (float)Math.Min(points3D[0].Y, points3D[1].Y);
				float maxX = (float)Math.Max(points3D[0].X, points3D[1].X);
				float maxY = (float)Math.Max(points3D[0].Y, points3D[1].Y);
				RectangleF position = new RectangleF(minX, minY, maxX - minX, maxY - minY);
				SurfaceNames visibleSurfaces = GetVisibleSurfaces(position,positionZ,depth,matrix);
 
				// Check left line visibility
				bool	thickBorderOnLeft = false;
				bool	thickBorderOnRight = false;
 
				if(lineSegmentType != LineSegmentType.Middle)
				{
					LineSegmentType	tempLineSegmentType = LineSegmentType.Single;
 
					// Check left line visibility
					thickBorderOnLeft = (ChartGraphics.ShouldDrawLineChartSurface(
						area,
						reversedSeriesOrder, 
						SurfaceNames.Left, 
						visibleSurfaces, 
						polygonColor,
						points, 
						firstPoint, 
						secondPoint, 
						multiSeries, 
						ref tempLineSegmentType) == 2);
 
 
					// Check right line visibility
					thickBorderOnRight = (ChartGraphics.ShouldDrawLineChartSurface(
						area,
						reversedSeriesOrder, 
						SurfaceNames.Right, 
						visibleSurfaces, 
						polygonColor,
						points, 
						firstPoint, 
						secondPoint, 
						multiSeries, 
						ref tempLineSegmentType) == 2);
				}
 
				// Switch left & right border if series is reversed
				if(reversedSeriesOrder)
				{
					bool tempVal = thickBorderOnLeft;
					thickBorderOnLeft = thickBorderOnRight;
					thickBorderOnRight = tempVal;
				}
 
				// Draw thick border for single segment lines only
				// or for the first & last segment
				if(lineSegmentType != LineSegmentType.First && lineSegmentType != LineSegmentType.Single)
				{
					thickBorderOnLeft = false;
				}
				if(lineSegmentType != LineSegmentType.Last && lineSegmentType != LineSegmentType.Single)
				{
					thickBorderOnRight = false;
				}
 
				//**********************************************************************
				//** Draw border on the front side of line surface (only when visible)
				//**********************************************************************
				if( matrix.Perspective != 0 ||
                    (matrix.AngleX != 90 && matrix.AngleX != -90 && 
					matrix.AngleY != 90 && matrix.AngleY != -90 &&
					matrix.AngleY != 180 && matrix.AngleY != -180))
				{
					// Draw thick line on the front side of the line surface
					if(drawElements)
					{
						DrawLine( 
							thickBorderPen, 
							(float)Math.Round(polygonPoints[0].X), 
							(float)Math.Round(polygonPoints[0].Y), 
							(float)Math.Round(polygonPoints[1].X), 
							(float)Math.Round(polygonPoints[1].Y) );
					}
 
					// Calculate path for selection
					if(resultPath != null)
					{
						// Add front line to the path
						resultPath.AddLine(
							(float)Math.Round(polygonPoints[0].X), 
							(float)Math.Round(polygonPoints[0].Y), 
							(float)Math.Round(polygonPoints[1].X), 
							(float)Math.Round(polygonPoints[1].Y));
					}
				}
 
 
				//**********************************************************************
				//** Draw border on the left side of line surface (only when visible)
				//**********************************************************************
 
				// Use flat end for Right & Left border
				thickBorderPen.EndCap = LineCap.Flat;
 
				// Draw border on the left side
                if (matrix.Perspective != 0 || (matrix.AngleX != 90 && matrix.AngleX != -90))
				{
					if(thickBorderOnLeft)
					{
						if(drawElements)
						{
							DrawLine( 
								thickBorderPen, 
								(float)Math.Round(polygonPoints[3].X), 
								(float)Math.Round(polygonPoints[3].Y), 
								(float)Math.Round(polygonPoints[0].X), 
								(float)Math.Round(polygonPoints[0].Y) );
						}
 
						// Calculate path for selection
						if(resultPath != null)
						{
							// Add left line to the path
							resultPath.AddLine(
								(float)Math.Round(polygonPoints[3].X), 
								(float)Math.Round(polygonPoints[3].Y), 
								(float)Math.Round(polygonPoints[0].X), 
								(float)Math.Round(polygonPoints[0].Y));
						}
					}
				}
 
				//**********************************************************************
				//** Draw border on the right side of the line surface
				//**********************************************************************
                if (matrix.Perspective != 0 || (matrix.AngleX != 90 && matrix.AngleX != -90))
				{
					if(thickBorderOnRight)
					{
						if(drawElements)
						{
							DrawLine( 
								thickBorderPen, 
								(float)Math.Round(polygonPoints[1].X), 
								(float)Math.Round(polygonPoints[1].Y), 
								(float)Math.Round(polygonPoints[2].X), 
								(float)Math.Round(polygonPoints[2].Y) );
						}
 
						// Calculate path for selection
						if(resultPath != null)
						{
							// Add right line to the path
							resultPath.AddLine(
								(float)Math.Round(polygonPoints[1].X), 
								(float)Math.Round(polygonPoints[1].Y), 
								(float)Math.Round(polygonPoints[2].X), 
								(float)Math.Round(polygonPoints[2].Y));
						}
					}
				}
			}
 
			//**********************************************************************
			// Redraw front line of the previuos line segment.
			// Solves 3D visibility problem between wide border line and line surface.
			//**********************************************************************
			if( area.Area3DStyle.Perspective == 0 )
			{
				if(frontLinePoint1 != PointF.Empty && frontLinePen != null)
				{
					// Draw line
					DrawLine( 
						frontLinePen, 
						(float)Math.Round(frontLinePoint1.X), 
						(float)Math.Round(frontLinePoint1.Y), 
						(float)Math.Round(frontLinePoint2.X), 
						(float)Math.Round(frontLinePoint2.Y) );
 
					// Reset line properties
					frontLinePen = null;
					frontLinePoint1 = PointF.Empty;
					frontLinePoint2 = PointF.Empty;
				}
 
				//**********************************************************************
				//** Check if front line should be redrawn whith the next segment.
				//**********************************************************************
				if(drawElements)
				{
					frontLinePen = (borderWidth > 1) ? thickBorderPen : thinBorderPen;
					frontLinePoint1 = polygonPoints[0];
					frontLinePoint2 = polygonPoints[1];
				}
			}
 
			//**********************************************************************
			//** Calculate path for selection
			//**********************************************************************
			if(resultPath != null)
			{
				// Widen all the lines currently in the path
				if(thickBorderPen != null)
				{
					try
					{
						resultPath.Widen(thickBorderPen);
					}
                    catch (OutOfMemoryException)
                    {
                        // GraphicsPath.Widen incorrectly throws OutOfMemoryException
                        // catching here and reacting by not widening
                    }
                    catch (ArgumentException)
                    {
                    }
				}
 
				// Add polygon to the path
				resultPath.AddPolygon(polygonPoints);
			}
 
			return resultPath;
		}
 
 
 
		/// <summary>
		/// Helper method, which indicates if area chart surface should be drawn or not.
		/// </summary>
		/// <param name="area">Chart area object.</param>
		/// <param name="reversedSeriesOrder">Series are drawn in reversed order.</param>
		/// <param name="surfaceName">Surface name.</param>
		/// <param name="boundaryRectVisibleSurfaces">Visible surfaces of the bounding rectangle.</param>
		/// <param name="color">Point back color.</param>
		/// <param name="points">Array of all points.</param>
		/// <param name="firstPoint">First point.</param>
		/// <param name="secondPoint">Second point.</param>
		/// <param name="multiSeries">Indicates that multiple series are painted at the same time (stacked or side-by-side).</param>
		/// <param name="lineSegmentType">Returns line segment type.</param>
		/// <returns>Function retrns 0, 1 or 2. 0 - Do not draw surface, 1 - draw on the back, 2 - draw in front.</returns>
		static internal int ShouldDrawLineChartSurface(
			ChartArea area,
			bool reversedSeriesOrder,
			SurfaceNames surfaceName, 
			SurfaceNames boundaryRectVisibleSurfaces,
			Color color,
			ArrayList points,
			DataPoint3D firstPoint,
			DataPoint3D secondPoint,
			bool multiSeries,
			ref LineSegmentType lineSegmentType)
		{
			int	result = 0;
			Series series = firstPoint.dataPoint.series;
 
			// Set active horizontal/vertical axis
			Axis hAxis = (series.XAxisType == AxisType.Primary) ? area.AxisX : area.AxisX2;
			double hAxisMin = hAxis.ViewMinimum;
			double hAxisMax = hAxis.ViewMaximum;
 
			//****************************************************************
			//** Check if data point and it's neigbours have non-transparent
			//** colors.
			//****************************************************************
 
            // Check if point main color has transparency
			bool transparent = color.A != 255;
 
			// Check if points on the left and right side exsit and are transparent
			bool leftPointVisible = false;
			bool rightPointVisible = false;
			if( surfaceName == SurfaceNames.Left )
			{
				// Find Left point 
				DataPoint3D leftPoint = null, leftPointAttr = null;
				int pointArrayIndex = int.MinValue;
				if(!reversedSeriesOrder)
				{
					leftPoint = ChartGraphics.FindPointByIndex(points, Math.Min(firstPoint.index, secondPoint.index) - 1, (multiSeries) ? secondPoint : null, ref pointArrayIndex);
					leftPointAttr = ChartGraphics.FindPointByIndex(points, Math.Min(firstPoint.index, secondPoint.index), (multiSeries) ? secondPoint : null, ref pointArrayIndex);
				}
				else
				{
					leftPoint = ChartGraphics.FindPointByIndex(points, Math.Max(firstPoint.index, secondPoint.index) + 1, (multiSeries) ? secondPoint : null, ref pointArrayIndex);
					leftPointAttr = leftPoint;
				}
				if(leftPoint != null)
				{
					if(leftPointAttr.dataPoint.IsEmpty)
					{
						if(leftPointAttr.dataPoint.series.EmptyPointStyle.Color == color ||
							leftPointAttr.dataPoint.series.EmptyPointStyle.Color.A == 255)
						{
							leftPointVisible = true;
						}
					}
					else
					{
						if(leftPointAttr.dataPoint.Color == color ||
							leftPointAttr.dataPoint.Color.A == 255)
						{
							leftPointVisible = true;
						}
					}
				
					// Check if found point is outside the scaleView
					double	xValue = (leftPoint.indexedSeries) ? leftPoint.index : leftPoint.dataPoint.XValue;
					if(xValue > hAxisMax || xValue < hAxisMin)											
					{
						DataPoint3D currentPoint = null;
						if(reversedSeriesOrder)
						{
							currentPoint = (firstPoint.index > secondPoint.index) ? firstPoint : secondPoint;
						}
						else
						{
							currentPoint = (firstPoint.index < secondPoint.index) ? firstPoint : secondPoint;
						}
						double	currentXValue = (currentPoint.indexedSeries) ? currentPoint.index : currentPoint.dataPoint.XValue;
						if(currentXValue > hAxisMax || currentXValue < hAxisMin)
						{
							leftPointVisible = false;
						}
					}
				}
			}
 
			// Find Right point 
			if( surfaceName == SurfaceNames.Right )
			{
				DataPoint3D rightPoint = null, rightPointAttr = null;
				int pointArrayIndex = int.MinValue;
				if(!reversedSeriesOrder)
				{
					rightPoint = ChartGraphics.FindPointByIndex(points, Math.Max(firstPoint.index, secondPoint.index) + 1, (multiSeries) ? secondPoint : null, ref pointArrayIndex);
					rightPointAttr = rightPoint;
				}
				else
				{
					rightPoint = ChartGraphics.FindPointByIndex(points, Math.Min(firstPoint.index, secondPoint.index) - 1, (multiSeries) ? secondPoint : null, ref pointArrayIndex);
					rightPointAttr = ChartGraphics.FindPointByIndex(points, Math.Min(firstPoint.index, secondPoint.index), (multiSeries) ? secondPoint : null, ref pointArrayIndex);
				}
				if(rightPoint != null)
				{
					if(rightPointAttr.dataPoint.IsEmpty)
					{
						if(rightPointAttr.dataPoint.series.EmptyPointStyle.Color == color ||
							rightPointAttr.dataPoint.series.EmptyPointStyle.Color.A == 255)
						{
							rightPointVisible = true;
						}
					}
					else
					{
						if(rightPointAttr.dataPoint.Color == color ||
							rightPointAttr.dataPoint.Color.A == 255)
						{
							rightPointVisible = true;
						}
					}
				
					// Check if found point is outside the scaleView
					double	xValue = (rightPoint.indexedSeries) ? rightPoint.index : rightPoint.dataPoint.XValue;
					if(xValue > hAxisMax || xValue < hAxisMin)											
					{
						DataPoint3D currentPoint = null;
						if(reversedSeriesOrder)
						{
							currentPoint = (firstPoint.index > secondPoint.index) ? firstPoint : secondPoint;
						}
						else
						{
							currentPoint = (firstPoint.index < secondPoint.index) ? firstPoint : secondPoint;
						}
						double	currentXValue = (currentPoint.indexedSeries) ? currentPoint.index : currentPoint.dataPoint.XValue;
						if(currentXValue > hAxisMax || currentXValue < hAxisMin)
						{
							rightPointVisible = false;
						}
					}
				}
			}
 
			//****************************************************************
			//** Get line segment
			//****************************************************************
			if( surfaceName == SurfaceNames.Left && !leftPointVisible)
			{
				if(lineSegmentType == LineSegmentType.Middle)
				{
					lineSegmentType = LineSegmentType.First;
				}
				else if(lineSegmentType == LineSegmentType.Last)
				{
					lineSegmentType = LineSegmentType.Single;
				}
			}
			if( surfaceName == SurfaceNames.Right && !rightPointVisible)
			{
				if(lineSegmentType == LineSegmentType.Middle)
				{
					lineSegmentType = LineSegmentType.Last;
				}
				else if(lineSegmentType == LineSegmentType.First)
				{
					lineSegmentType = LineSegmentType.Single;
				}
			}
			
 
			//****************************************************************
			//** Check surfaces visibility
			//****************************************************************
			if( surfaceName == SurfaceNames.Top )
			{
				result = ((boundaryRectVisibleSurfaces & SurfaceNames.Top) == SurfaceNames.Top) ? 2 : 1;
			}
			if( surfaceName == SurfaceNames.Bottom )
			{
				result = ((boundaryRectVisibleSurfaces & SurfaceNames.Bottom) == SurfaceNames.Bottom) ? 2 : 1;
				// Draw invisible bottom surface only if chart is transparent
				if(result == 1 && !transparent) 
				{
					result = 0;
				}
			}
			if( surfaceName == SurfaceNames.Front )
			{
				result = ((boundaryRectVisibleSurfaces & SurfaceNames.Front) == SurfaceNames.Front) ? 2 : 1;
				// Draw invisible front surface only if chart is transparent
				if(result == 1 && !transparent) 
				{
					result = 0;
				}
			}
			if( surfaceName == SurfaceNames.Back )
			{
				result = ((boundaryRectVisibleSurfaces & SurfaceNames.Back) == SurfaceNames.Back) ? 2 : 1;
				// Draw invisible back surface only if chart is transparent
				if(result == 1 && !transparent) 
				{
					result = 0;
				}
			}
			if( surfaceName == SurfaceNames.Left )
			{
				result = ((boundaryRectVisibleSurfaces & SurfaceNames.Left) == SurfaceNames.Left) ? 2 : 1;
				// Draw invisible left surface only if point to the left is transparent
				if(leftPointVisible) 
				{
					result = 0;
				}
			}
			if( surfaceName == SurfaceNames.Right )
			{
				result = ((boundaryRectVisibleSurfaces & SurfaceNames.Right) == SurfaceNames.Right) ? 2 : 1;
				// Draw invisible right surface only if point to the right is transparent
				if(rightPointVisible) 
				{
					result = 0;
				}
			}
 
			return result;
		}
 
 
		/// <summary>
		/// Helper method which finds point in the list by it's real index.
		/// </summary>
		/// <param name="points">List of points.</param>
		/// <param name="index">Required index.</param>
		/// <param name="neighborDataPoint">Neighbor point of the same series.</param>
		/// <param name="neighborPointIndex">Neighbor point index in the array list.</param>
		/// <returns>Data point found.</returns>
		internal static DataPoint3D FindPointByIndex(ArrayList points, int index, DataPoint3D neighborDataPoint, ref int neighborPointIndex)
		{
			// Try to look around the neighbor point index
			if(neighborPointIndex != int.MinValue)
			{
				// Try getting the next point
				if(neighborPointIndex < (points.Count - 2))
				{
					DataPoint3D point = (DataPoint3D)points[neighborPointIndex + 1];
 
					// Check required point index for the first point
					if( point.index == index &&
                        (neighborDataPoint == null || String.Compare(neighborDataPoint.dataPoint.series.Name, point.dataPoint.series.Name, StringComparison.Ordinal) == 0))
					{
						++neighborPointIndex;
						return point;
					}
				}
 
				// Try getting the prev point
				if(neighborPointIndex > 0)
				{
					DataPoint3D point = (DataPoint3D)points[neighborPointIndex - 1];
 
					// Check required point index for the first point
					if( point.index == index &&
                        (neighborDataPoint == null || String.Compare(neighborDataPoint.dataPoint.series.Name, point.dataPoint.series.Name, StringComparison.Ordinal) == 0))
					{
						--neighborPointIndex;
						return point;
					}
				}
 
			}
 
			// Loop through all points
			neighborPointIndex = 0;
			foreach(DataPoint3D point3D in points)
			{
				// Check required point index for the first point
				if(point3D.index == index)
				{
					// Check if point belongs to the same series
					if(neighborDataPoint != null)
					{
                        if (String.Compare(neighborDataPoint.dataPoint.series.Name, point3D.dataPoint.series.Name, StringComparison.Ordinal) != 0)
						{
							++neighborPointIndex;
							continue;
						}
					}
 
					// Point found
					return (DataPoint3D)point3D;
				}
 
				++neighborPointIndex;
			}
 
			// Data point was not found
			return null;
		}
 
		
		#endregion
 
		#region 3D Rectangle drawing methods
 
		/// <summary>
		/// Function is used to calculate the coordinates of the 2D rectangle in 3D space
		/// and either draw it or/and calculate the bounding path for selection.
		/// </summary>
		/// <param name="position">Position of 2D rectangle.</param>
		/// <param name="positionZ">Z position of the back side of the 3D rectangle.</param>
		/// <param name="depth">Depth of the 3D rectangle.</param>
		/// <param name="matrix">Coordinate transformation matrix.</param>
		/// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
		/// <param name="backColor">Color of rectangle</param>
		/// <param name="borderColor">Border Color</param>
		/// <param name="borderWidth">Border Width</param>
		/// <param name="borderDashStyle">Border Style</param>
		/// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
		/// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
		internal GraphicsPath Fill3DRectangle( 
			RectangleF position, 
			float positionZ, 
			float depth, 
			Matrix3D matrix, 
			LightStyle lightStyle,
			Color backColor, 
			Color borderColor, 
			int borderWidth, 
			ChartDashStyle borderDashStyle, 
			DrawingOperationTypes operationType)
		{
			return Fill3DRectangle( 
				position, 
				positionZ, 
				depth, 
				matrix, 
				lightStyle,
				backColor, 
				0f,
				0f,
				borderColor, 
				borderWidth, 
				borderDashStyle, 
				BarDrawingStyle.Default,
				false,
				operationType);
		}
 
        /// <summary>
        /// Function is used to calculate the coordinates of the 2D rectangle in 3D space
        /// and either draw it or/and calculate the bounding path for selection.
        /// </summary>
        /// <param name="position">Position of 2D rectangle.</param>
        /// <param name="positionZ">Z position of the back side of the 3D rectangle.</param>
        /// <param name="depth">Depth of the 3D rectangle.</param>
        /// <param name="matrix">Coordinate transformation matrix.</param>
        /// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
        /// <param name="backColor">Color of rectangle</param>
        /// <param name="topRightDarkening">Top (or right in bar chart) darkening effect.</param>
        /// <param name="bottomLeftDarkening">Bottom (or left in bar chart) darkening effect.</param>
        /// <param name="borderColor">Border Color</param>
        /// <param name="borderWidth">Border Width</param>
        /// <param name="borderDashStyle">Border Style</param>
        /// <param name="barDrawingStyle">Bar drawing style.</param>
        /// <param name="veticalOrientation">Defines if bar is vertical or horizontal.</param>
        /// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
        /// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
        internal GraphicsPath 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)
		{
 
			// Check if special drawing is required
			if(barDrawingStyle == BarDrawingStyle.Cylinder)
			{
				// Draw as 3D cylinder
				return Fill3DRectangleAsCylinder( 
					position, 
					positionZ, 
					depth, 
					matrix, 
					lightStyle,
					backColor, 
					topRightDarkening,
					bottomLeftDarkening,
					borderColor, 
					borderWidth, 
					borderDashStyle, 
					veticalOrientation,
					operationType);
			}
 
			// Declare variables
			Point3D[]		cubePoints = new Point3D[8];
			GraphicsPath	resultPath = ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
				? new GraphicsPath() : null;
			
			// Front Side
			cubePoints[0] = new Point3D( position.X, position.Y, positionZ + depth );
			cubePoints[1] = new Point3D( position.X, position.Bottom, positionZ + depth );
			cubePoints[2] = new Point3D( position.Right, position.Bottom, positionZ + depth );
			cubePoints[3] = new Point3D( position.Right, position.Y, positionZ + depth );
								
			// Back Side
			cubePoints[4] = new Point3D( position.X, position.Y, positionZ );
			cubePoints[5] = new Point3D( position.X, position.Bottom, positionZ );
			cubePoints[6] = new Point3D( position.Right, position.Bottom, positionZ );
			cubePoints[7] = new Point3D( position.Right, position.Y, positionZ );
 
			// Tranform cube coordinates
			matrix.TransformPoints( cubePoints );
 
			// For lightStyle style Non, Border color always exist.
			if( lightStyle == LightStyle.None &&
				(borderWidth == 0 || borderDashStyle == ChartDashStyle.NotSet || borderColor == Color.Empty) )
			{
				borderColor = ChartGraphics.GetGradientColor( backColor, Color.Black, 0.5 );
			}
 
			// Get surface colors
			Color	frontLightColor, leftLightColor, topLightColor, backLightColor, rightLightColor, bottomLightColor;
			matrix.GetLight( backColor, out frontLightColor, out backLightColor, out leftLightColor, out rightLightColor, out topLightColor, out bottomLightColor );
 
			// Darken colors by specified values
			if(topRightDarkening != 0f)
			{
				if(veticalOrientation)
				{
					topLightColor = ChartGraphics.GetGradientColor(topLightColor, Color.Black, topRightDarkening);
				}
				else
				{
					rightLightColor = ChartGraphics.GetGradientColor(rightLightColor, Color.Black, topRightDarkening);
				}
			}
			if(bottomLeftDarkening != 0f)
			{
				if(veticalOrientation)
				{
					bottomLightColor = ChartGraphics.GetGradientColor(bottomLightColor, Color.Black, bottomLeftDarkening);
				}
				else
				{
					leftLightColor = ChartGraphics.GetGradientColor(leftLightColor, Color.Black, bottomLeftDarkening);
				}
			}
 
 
			// Check visible surfaces
			SurfaceNames visibleSurfaces = GetVisibleSurfacesWithPerspective(position,positionZ,depth,matrix);
 
			// Draw all invisible surfaces first (if semi-transparent color is used)
			for(int drawVisible = 0; drawVisible <= 1; drawVisible++)
			{
				// Do not draw invisible surfaces for solid colors
				if(drawVisible == 0 && backColor.A == 255)
				{
					continue;
				}
 
				// Check visibility of all surfaces and draw them
				for(int surfaceIndex = (int)SurfaceNames.Front; surfaceIndex <= (int)SurfaceNames.Bottom; surfaceIndex *= 2)
				{
					SurfaceNames	currentSurface = (SurfaceNames)surfaceIndex;
 
					// If width, height or depth of the cube (3DRectangle) is zero graphical path 
					// should contain only one surface with 4 points.
					if(depth == 0.0 && currentSurface != SurfaceNames.Front)
					{
						continue;
					}
					if(position.Width == 0.0 && currentSurface != SurfaceNames.Left && currentSurface != SurfaceNames.Right)
					{
						continue;
					}
					if(position.Height == 0.0 && currentSurface != SurfaceNames.Top && currentSurface != SurfaceNames.Bottom)
					{
						continue;
					}
 
					// Check if surface is visible or semi-transparent color is used
					bool isVisible = (visibleSurfaces & currentSurface) != 0;
					if(isVisible && drawVisible == 1 ||
						!isVisible && drawVisible == 0)
					{
						// Fill surface coordinates and color
						PointF [] pointsSurface = new PointF[4];
						Color	surfaceColor = backColor;
 
						switch(currentSurface)
						{
							case(SurfaceNames.Front):
								surfaceColor = frontLightColor;
								pointsSurface[0] = new PointF(cubePoints[0].X, cubePoints[0].Y);
								pointsSurface[1] = new PointF(cubePoints[1].X, cubePoints[1].Y);
								pointsSurface[2] = new PointF(cubePoints[2].X, cubePoints[2].Y);
								pointsSurface[3] = new PointF(cubePoints[3].X, cubePoints[3].Y);
								break;
							case(SurfaceNames.Back):
								surfaceColor = backLightColor;
								pointsSurface[0] = new PointF(cubePoints[4].X, cubePoints[4].Y);
								pointsSurface[1] = new PointF(cubePoints[5].X, cubePoints[5].Y);
								pointsSurface[2] = new PointF(cubePoints[6].X, cubePoints[6].Y);
								pointsSurface[3] = new PointF(cubePoints[7].X, cubePoints[7].Y);
								break;
							case(SurfaceNames.Left):
								surfaceColor = leftLightColor;
								pointsSurface[0] = new PointF(cubePoints[0].X, cubePoints[0].Y);
								pointsSurface[1] = new PointF(cubePoints[1].X, cubePoints[1].Y);
								pointsSurface[2] = new PointF(cubePoints[5].X, cubePoints[5].Y);
								pointsSurface[3] = new PointF(cubePoints[4].X, cubePoints[4].Y);
								break;
							case(SurfaceNames.Right):
								surfaceColor = rightLightColor;
								pointsSurface[0] = new PointF(cubePoints[3].X, cubePoints[3].Y);
								pointsSurface[1] = new PointF(cubePoints[2].X, cubePoints[2].Y);
								pointsSurface[2] = new PointF(cubePoints[6].X, cubePoints[6].Y);
								pointsSurface[3] = new PointF(cubePoints[7].X, cubePoints[7].Y);
								break;
							case(SurfaceNames.Top):
								surfaceColor = topLightColor;
								pointsSurface[0] = new PointF(cubePoints[0].X, cubePoints[0].Y);
								pointsSurface[1] = new PointF(cubePoints[3].X, cubePoints[3].Y);
								pointsSurface[2] = new PointF(cubePoints[7].X, cubePoints[7].Y);
								pointsSurface[3] = new PointF(cubePoints[4].X, cubePoints[4].Y);
								break;
							case(SurfaceNames.Bottom):
								surfaceColor = bottomLightColor;
								pointsSurface[0] = new PointF(cubePoints[1].X, cubePoints[1].Y);
								pointsSurface[1] = new PointF(cubePoints[2].X, cubePoints[2].Y);
								pointsSurface[2] = new PointF(cubePoints[6].X, cubePoints[6].Y);
								pointsSurface[3] = new PointF(cubePoints[5].X, cubePoints[5].Y);
								break;
						}
 
						// Covert coordinates to absolute
						for(int pointIndex = 0; pointIndex < pointsSurface.Length; pointIndex++)
						{
							pointsSurface[pointIndex] = GetAbsolutePoint(pointsSurface[pointIndex]);
						}
			
						// Draw surface
						if( (operationType & DrawingOperationTypes.DrawElement) == DrawingOperationTypes.DrawElement)
						{
							// Draw only completly visible surfaces
							if((visibleSurfaces & currentSurface) != 0)
							{
                                using (Brush brush = new SolidBrush(surfaceColor))
                                {
                                    FillPolygon(brush, pointsSurface);
                                }
 
								// Check if any additional drawing should be done 
								if(currentSurface == SurfaceNames.Front &&
									barDrawingStyle != BarDrawingStyle.Default &&
									barDrawingStyle != BarDrawingStyle.Cylinder)
								{
									this.DrawBarStyleGradients(matrix, barDrawingStyle, position, positionZ, depth, veticalOrientation);
								}
							}
 
							// Draw surface border
                            using (Pen pen = new Pen(borderColor, borderWidth))
                            {
                                pen.DashStyle = GetPenStyle(borderDashStyle);
                                if (lightStyle != LightStyle.None &&
                                    (borderWidth == 0 || borderDashStyle == ChartDashStyle.NotSet || borderColor == Color.Empty))
                                {
                                    // Draw line of the same color inside the bar
                                    pen.Color = surfaceColor;
                                    pen.Width = 1;
                                    pen.Alignment = PenAlignment.Inset;
                                }
 
                                pen.StartCap = LineCap.Round;
                                pen.EndCap = LineCap.Round;
                                DrawLine(pen, pointsSurface[0], pointsSurface[1]);
                                DrawLine(pen, pointsSurface[1], pointsSurface[2]);
                                DrawLine(pen, pointsSurface[2], pointsSurface[3]);
                                DrawLine(pen, pointsSurface[3], pointsSurface[0]);
                            }
						}
 
						// Add surface coordinate to the path
						if( (operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
						{
							// Only if surface is completly visible
							if((visibleSurfaces & currentSurface) != 0)
							{
								resultPath.SetMarkers();
								resultPath.AddPolygon(pointsSurface);
							}
						}
 
					}
				}
			}
		
			return resultPath;
		}
 
        /// <summary>
        /// Draws special bar style effect on the front surface of the bar.
        /// </summary>
        /// <param name="matrix">Drawing matrix.</param>
        /// <param name="barDrawingStyle">Bar drawing style.</param>
        /// <param name="position">Position in relative coordinates</param>
        /// <param name="positionZ">Z position.</param>
        /// <param name="depth">Depth.</param>
        /// <param name="isVertical">Defines if bar is vertical or horizontal.</param>
		private void DrawBarStyleGradients(
			Matrix3D matrix, 
			BarDrawingStyle barDrawingStyle, 
			RectangleF position, 
			float positionZ, 
			float depth,
			bool isVertical)
		{
			if(barDrawingStyle == BarDrawingStyle.Wedge)
			{
				// Calculate wedge size to fit the rectangle
				RectangleF positionAbs = GetAbsoluteRectangle(position);
				float size = (isVertical) ? positionAbs.Width / 2f : positionAbs.Height / 2f;
				if(isVertical && 2f * size > positionAbs.Height)
				{
					size = positionAbs.Height/2f;
				}
				if(!isVertical && 2f * size > positionAbs.Width)
				{
					size = positionAbs.Width/2f;
				}
				SizeF sizeRel = GetRelativeSize(new SizeF(size, size));
 
				// Make 3D convertion of the key points
				Point3D[] gradientPoints = new Point3D[6];
				gradientPoints[0] = new Point3D( position.Left, position.Top, positionZ + depth );
				gradientPoints[1] = new Point3D( position.Left, position.Bottom, positionZ + depth );
				gradientPoints[2] = new Point3D( position.Right, position.Bottom, positionZ + depth );
				gradientPoints[3] = new Point3D( position.Right, position.Top, positionZ + depth );
				if(isVertical)
				{
					gradientPoints[4] = new Point3D( position.X + position.Width / 2f, position.Top + sizeRel.Height, positionZ + depth );
					gradientPoints[5] = new Point3D( position.X + position.Width / 2f, position.Bottom - sizeRel.Height, positionZ + depth );
				}
				else
				{
					gradientPoints[4] = new Point3D( position.X + sizeRel.Width, position.Top + position.Height / 2f, positionZ + depth );
					gradientPoints[5] = new Point3D( position.Right - sizeRel.Width, position.Top + position.Height / 2f, positionZ + depth );
				}
 
				// Tranform cube coordinates
				matrix.TransformPoints( gradientPoints );
 
				// Convert points to absolute
				PointF [] gradientPointsAbs = new PointF[6];
				for(int index = 0; index < gradientPoints.Length; index++)
				{
					gradientPointsAbs[index] = GetAbsolutePoint(gradientPoints[index].PointF);
				}
 
 
				// Draw left/bottom shadow
				using(GraphicsPath path = new GraphicsPath())
				{
					if(isVertical)
					{
						path.AddLine(gradientPointsAbs[4], gradientPointsAbs[5]);
						path.AddLine(gradientPointsAbs[5], gradientPointsAbs[2]);
						path.AddLine(gradientPointsAbs[2], gradientPointsAbs[3]);
					}
					else
					{
						path.AddLine(gradientPointsAbs[4], gradientPointsAbs[5]);
						path.AddLine(gradientPointsAbs[5], gradientPointsAbs[2]);
						path.AddLine(gradientPointsAbs[2], gradientPointsAbs[1]);
					}
					path.CloseAllFigures();
 
					// Create brush and fill path
					using(SolidBrush brush = new SolidBrush(Color.FromArgb(90, Color.Black)))
					{
						this.FillPath(brush, path);
					}
				}
 
				// Draw top/right triangle
				using(GraphicsPath path = new GraphicsPath())
				{
					if(isVertical)
					{
						path.AddLine(gradientPointsAbs[0], gradientPointsAbs[4]);
						path.AddLine(gradientPointsAbs[4], gradientPointsAbs[3]);
					}
					else
					{
						path.AddLine(gradientPointsAbs[3], gradientPointsAbs[5]);
						path.AddLine(gradientPointsAbs[5], gradientPointsAbs[2]);
					}
 
					// Create brush and fill path
					using(SolidBrush brush = new SolidBrush(Color.FromArgb(50, Color.Black)))
					{
						// Fill shadow path on the left-bottom side of the bar
						this.FillPath(brush, path);
 
						// Draw Lines
						using(Pen penDark = new Pen(Color.FromArgb(20, Color.Black), 1))
						{
							this.DrawPath(penDark, path);
							this.DrawLine(
								penDark,
								gradientPointsAbs[4],
								gradientPointsAbs[5]);
						}
 
						// Draw Lines
						using(Pen pen = new Pen(Color.FromArgb(40, Color.White), 1))
						{
							this.DrawPath(pen, path);
							this.DrawLine(
								pen,
								gradientPointsAbs[4],
								gradientPointsAbs[5]);
						}
					}
				}
 
				// Draw bottom/left triangle
				using(GraphicsPath path = new GraphicsPath())
				{
					if(isVertical)
					{
						path.AddLine(gradientPointsAbs[1], gradientPointsAbs[5]);
						path.AddLine(gradientPointsAbs[5], gradientPointsAbs[2]);
					}
					else
					{
						path.AddLine(gradientPointsAbs[0], gradientPointsAbs[4]);
						path.AddLine(gradientPointsAbs[4], gradientPointsAbs[1]);
					}
 
					// Create brush
					using(SolidBrush brush = new SolidBrush(Color.FromArgb(50, Color.Black)))
					{
						// Fill shadow path on the left-bottom side of the bar
						this.FillPath(brush, path);
 
						// Draw edges
						using(Pen penDark = new Pen(Color.FromArgb(20, Color.Black), 1))
						{
							this.DrawPath(penDark, path);
						}
						using(Pen pen = new Pen(Color.FromArgb(40, Color.White), 1))
						{
							this.DrawPath(pen, path);
						}
					}
				}
 
 
			}
			else if(barDrawingStyle == BarDrawingStyle.LightToDark)
			{
				// Calculate width of shadows used to create the effect
				RectangleF positionAbs = GetAbsoluteRectangle(position);
				float shadowSizeAbs = 5f;
				if(positionAbs.Width < 6f || positionAbs.Height < 6f)
				{
					shadowSizeAbs = 2f;
				}
				else if(positionAbs.Width < 15f || positionAbs.Height < 15f)
				{
					shadowSizeAbs = 3f;
				}
				SizeF shadowSizeRel = GetRelativeSize(new SizeF(shadowSizeAbs, shadowSizeAbs));
 
				// Calculate gradient position
				RectangleF gradientRect = position;
				gradientRect.Inflate(-shadowSizeRel.Width, -shadowSizeRel.Height);
				if(isVertical)
				{
					gradientRect.Height = (float)Math.Floor(gradientRect.Height / 3f);
				}
				else
				{
					gradientRect.X = gradientRect.Right - (float)Math.Floor(gradientRect.Width / 3f);
					gradientRect.Width = (float)Math.Floor(gradientRect.Width / 3f);
				}
 
 
				// Top gradient
				Point3D[] gradientPoints = new Point3D[4];
				gradientPoints[0] = new Point3D( gradientRect.Left, gradientRect.Top, positionZ + depth );
				gradientPoints[1] = new Point3D( gradientRect.Left, gradientRect.Bottom, positionZ + depth );
				gradientPoints[2] = new Point3D( gradientRect.Right, gradientRect.Bottom, positionZ + depth );
				gradientPoints[3] = new Point3D( gradientRect.Right, gradientRect.Top, positionZ + depth );
 
				// Tranform cube coordinates
				matrix.TransformPoints( gradientPoints );
 
				// Convert points to absolute
				PointF [] gradientPointsAbs = new PointF[4];
				for(int index = 0; index < gradientPoints.Length; index++)
				{
					gradientPointsAbs[index] = GetAbsolutePoint(gradientPoints[index].PointF);
				}
 
				// Create and draw top path
				using(GraphicsPath path = new GraphicsPath())
				{
					path.AddPolygon(gradientPointsAbs);
					RectangleF bounds = path.GetBounds();
					bounds.Width += 1f;
					bounds.Height += 1f;
 
					// Create brush
					if(bounds.Width > 0f && bounds.Height > 0f)
					{
						using(LinearGradientBrush topBrush = new LinearGradientBrush(
								  bounds, 
								  (!isVertical) ? Color.Transparent : Color.FromArgb(120, Color.White), 
								  (!isVertical) ? Color.FromArgb(120, Color.White) : Color.Transparent, 
								  (isVertical) ? LinearGradientMode.Vertical : LinearGradientMode.Horizontal))
						{
							// Fill shadow path on the top side of the bar
							this.FillPath(topBrush, path);
						}
					}
				}
 
 
 
				// Calculate gradient position for the bottom gradient
				gradientRect = position;
				gradientRect.Inflate(-shadowSizeRel.Width, -shadowSizeRel.Height);
				if(isVertical)
				{
					gradientRect.Y = gradientRect.Bottom - (float)Math.Floor(gradientRect.Height / 3f);
					gradientRect.Height = (float)Math.Floor(gradientRect.Height / 3f);
				}
				else
				{
					gradientRect.Width = (float)Math.Floor(gradientRect.Width / 3f);
				}
 
 
				// Top gradient
				gradientPoints = new Point3D[4];
				gradientPoints[0] = new Point3D( gradientRect.Left, gradientRect.Top, positionZ + depth );
				gradientPoints[1] = new Point3D( gradientRect.Left, gradientRect.Bottom, positionZ + depth );
				gradientPoints[2] = new Point3D( gradientRect.Right, gradientRect.Bottom, positionZ + depth );
				gradientPoints[3] = new Point3D( gradientRect.Right, gradientRect.Top, positionZ + depth );
 
				// Tranform cube coordinates
				matrix.TransformPoints( gradientPoints );
 
				// Convert points to absolute
				gradientPointsAbs = new PointF[4];
				for(int index = 0; index < gradientPoints.Length; index++)
				{
					gradientPointsAbs[index] = GetAbsolutePoint(gradientPoints[index].PointF);
				}
 
				// Create and draw top path
				using(GraphicsPath path = new GraphicsPath())
				{
					path.AddPolygon(gradientPointsAbs);
					RectangleF bounds = path.GetBounds();
					bounds.Width += 1f;
					bounds.Height += 1f;
 
					// Create brush
					if(bounds.Width > 0f && bounds.Height > 0f)
					{
						using(LinearGradientBrush topBrush = new LinearGradientBrush(
								  bounds, 
								  (isVertical) ? Color.Transparent : Color.FromArgb(80, Color.Black), 
								  (isVertical) ? Color.FromArgb(80, Color.Black) : Color.Transparent, 
								  (isVertical) ? LinearGradientMode.Vertical : LinearGradientMode.Horizontal))
						{
							// Fill shadow path on the top side of the bar
							this.FillPath(topBrush, path);
						}
					}
				}
 
			}
			else if(barDrawingStyle == BarDrawingStyle.Emboss)
			{
				// Calculate width of shadows used to create the effect
				RectangleF positionAbs = GetAbsoluteRectangle(position);
				float shadowSizeAbs = 4f;
				if(positionAbs.Width < 6f || positionAbs.Height < 6f)
				{
					shadowSizeAbs = 2f;
				}
				else if(positionAbs.Width < 15f || positionAbs.Height < 15f)
				{
					shadowSizeAbs = 3f;
				}
				SizeF shadowSizeRel = GetRelativeSize(new SizeF(shadowSizeAbs, shadowSizeAbs));
 
				// Left/top Side
				Point3D[] gradientPoints = new Point3D[6];
				gradientPoints[0] = new Point3D( position.Left, position.Bottom, positionZ + depth );
				gradientPoints[1] = new Point3D( position.Left, position.Top, positionZ + depth );
				gradientPoints[2] = new Point3D( position.Right, position.Top, positionZ + depth );
				gradientPoints[3] = new Point3D( position.Right - shadowSizeRel.Width, position.Top + shadowSizeRel.Height, positionZ + depth );
				gradientPoints[4] = new Point3D( position.Left + shadowSizeRel.Width, position.Top + shadowSizeRel.Height, positionZ + depth );
				gradientPoints[5] = new Point3D( position.Left + shadowSizeRel.Width, position.Bottom - shadowSizeRel.Height, positionZ + depth );
 
				// Tranform cube coordinates
				matrix.TransformPoints( gradientPoints );
 
				// Convert points to absolute
				PointF [] gradientPointsAbs = new PointF[6];
				for(int index = 0; index < gradientPoints.Length; index++)
				{
					gradientPointsAbs[index] = GetAbsolutePoint(gradientPoints[index].PointF);
				}
 
				// Create and draw left/top path
				using(GraphicsPath path = new GraphicsPath())
				{
					path.AddPolygon(gradientPointsAbs);
 
					// Create brush
					using(SolidBrush leftTopBrush = new SolidBrush(Color.FromArgb(100, Color.White)))
					{
						// Fill shadow path on the left-bottom side of the bar
						this.FillPath(leftTopBrush, path);
					}
				}
 
				// Right/bottom Side
				gradientPoints[0] = new Point3D( position.Right, position.Top, positionZ + depth );
				gradientPoints[1] = new Point3D( position.Right, position.Bottom, positionZ + depth );
				gradientPoints[2] = new Point3D( position.Left, position.Bottom, positionZ + depth );
				gradientPoints[3] = new Point3D( position.Left + shadowSizeRel.Width, position.Bottom - shadowSizeRel.Height, positionZ + depth );
				gradientPoints[4] = new Point3D( position.Right - shadowSizeRel.Width, position.Bottom - shadowSizeRel.Height, positionZ + depth );
				gradientPoints[5] = new Point3D( position.Right - shadowSizeRel.Width, position.Top + shadowSizeRel.Height, positionZ + depth );
 
				// Tranform cube coordinates
				matrix.TransformPoints( gradientPoints );
 
				// Convert points to absolute
				for(int index = 0; index < gradientPoints.Length; index++)
				{
					gradientPointsAbs[index] = GetAbsolutePoint(gradientPoints[index].PointF);
				}
 
				// Create and draw left/top path
				using(GraphicsPath path = new GraphicsPath())
				{
					path.AddPolygon(gradientPointsAbs);
 
					// Create brush
					using(SolidBrush bottomRightBrush = new SolidBrush(Color.FromArgb(80, Color.Black)))
					{
						// Fill shadow path on the left-bottom side of the bar
						this.FillPath(bottomRightBrush, path);
					}
				}
			}
		}
 
		#endregion
 
		#region 3D markers drawing methods
 
		/// <summary>
		/// Draw marker using absolute coordinates of the center.
		/// </summary>
		/// <param name="matrix">Coordinates transformation matrix.</param>
		/// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
		/// <param name="positionZ">Z position of the 3D marker center.</param>
		/// <param name="point">Coordinates of the center.</param>
		/// <param name="markerStyle">Marker style.</param>
		/// <param name="markerSize">Marker size.</param>
		/// <param name="markerColor">Marker color.</param>
		/// <param name="markerBorderColor">Marker border color.</param>
		/// <param name="markerBorderSize">Marker border size.</param>
		/// <param name="markerImage">Marker image name.</param>
		/// <param name="markerImageTransparentColor">Marker image transparent color.</param>
		/// <param name="shadowSize">Marker shadow size.</param>
		/// <param name="shadowColor">Marker shadow color.</param>
		/// <param name="imageScaleRect">Rectangle to which marker image should be scaled.</param>
		/// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
		/// <returns>Returns elemnt shape path if operationType parameter is set to ElementPath, otherwise Null.</returns>
		internal GraphicsPath 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 )
		{
			ChartGraphics	graph = (ChartGraphics)this;
			GraphicsPath	resultPath = ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
				? new GraphicsPath() : null;
 
			//************************************************************
			//** Transform marker position in 3D space
			//************************************************************
			// Get projection coordinates
			Point3D[]	marker3DPosition = new Point3D[1];
			marker3DPosition[0] = new Point3D(point.X, point.Y, positionZ);
 
			// Transform coordinates of the marker center
			matrix.TransformPoints(marker3DPosition);
			PointF	markerRotatedPosition = marker3DPosition[0].PointF;
 
			// Translate to absolute coordinates
			markerRotatedPosition = graph.GetAbsolutePoint(markerRotatedPosition);
 
			//************************************************************
			//** For those markers that do not have a 3D version - draw the same as in 2D
			//************************************************************
			if(markerImage.Length > 0 ||
				!(markerStyle == MarkerStyle.Circle ||
				markerStyle == MarkerStyle.Square) )
			{
				// Call 2D version of the method
				if( (operationType & DrawingOperationTypes.DrawElement) == DrawingOperationTypes.DrawElement)
				{
					graph.DrawMarkerAbs(markerRotatedPosition, markerStyle, markerSize, markerColor, markerBorderColor, markerBorderSize, markerImage, markerImageTransparentColor, shadowSize, shadowColor, imageScaleRect, false);
				}
 
				// Prepare marker path
				if( (operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
				{
					RectangleF	rect = RectangleF.Empty;
					rect.X = markerRotatedPosition.X - ((float)markerSize)/2F;
					rect.Y = markerRotatedPosition.Y - ((float)markerSize)/2F;
					rect.Width = markerSize;
					rect.Height = markerSize;
					resultPath.AddRectangle(rect);
				}
 
				return resultPath;
			}
 
			//************************************************************
			//** Draw marker
			//************************************************************
			// Check if marker properties are set
            if (markerStyle != MarkerStyle.None && markerSize > 0 && markerColor != Color.Empty)
            {
                // Create solid color brush
                using (SolidBrush brush = new SolidBrush(markerColor))
                {
 
                    // Calculate marker rectangle
                    RectangleF rect = RectangleF.Empty;
                    rect.X = markerRotatedPosition.X - ((float)markerSize) / 2F;
                    rect.Y = markerRotatedPosition.Y - ((float)markerSize) / 2F;
                    rect.Width = markerSize;
                    rect.Height = markerSize;
 
                    // Calculate relative marker size
                    SizeF markerRelativeSize = graph.GetRelativeSize(new SizeF(markerSize, markerSize));
 
                    // Draw marker depending on style
                    switch (markerStyle)
                    {
                        case (MarkerStyle.Circle):
                            {
                                if ((operationType & DrawingOperationTypes.DrawElement) == DrawingOperationTypes.DrawElement)
                                {
                                    // Draw marker shadow
                                    if (shadowSize != 0 && shadowColor != Color.Empty)
                                    {
                                        if (!graph.softShadows)
                                        {
                                            using (Brush shadowBrush = new SolidBrush((shadowColor.A != 255) ? shadowColor : Color.FromArgb(markerColor.A / 2, shadowColor)))
                                            {
                                                RectangleF shadowRect = rect;
                                                shadowRect.X += shadowSize;
                                                shadowRect.Y += shadowSize;
                                                graph.FillEllipse(shadowBrush, shadowRect);
                                            }
                                        }
                                        else
                                        {
                                            // Add circle to the graphics path
                                            using (GraphicsPath path = new GraphicsPath())
                                            {
                                                path.AddEllipse(rect.X + shadowSize - 1, rect.Y + shadowSize - 1, rect.Width + 2, rect.Height + 2);
 
                                                // Create path brush
                                                using (PathGradientBrush shadowBrush = new PathGradientBrush(path))
                                                {
                                                    shadowBrush.CenterColor = shadowColor;
 
                                                    // Set the color along the entire boundary of the path
                                                    Color[] colors = { Color.Transparent };
                                                    shadowBrush.SurroundColors = colors;
                                                    shadowBrush.CenterPoint = new PointF(markerRotatedPosition.X, markerRotatedPosition.Y);
 
                                                    // Define brush focus scale
                                                    PointF focusScale = new PointF(1 - 2f * shadowSize / rect.Width, 1 - 2f * shadowSize / rect.Height);
                                                    if (focusScale.X < 0)
                                                    {
                                                        focusScale.X = 0;
                                                    }
                                                    if (focusScale.Y < 0)
                                                    {
                                                        focusScale.Y = 0;
                                                    }
                                                    shadowBrush.FocusScales = focusScale;
 
                                                    // Draw shadow
                                                    graph.FillPath(shadowBrush, path);
                                                }
                                            }
                                        }
                                    }
 
                                    // Create path gradient brush
                                    using (GraphicsPath brushPath = new GraphicsPath())
                                    {
                                        RectangleF rectLightCenter = new RectangleF(rect.Location, rect.Size);
                                        rectLightCenter.Inflate(rectLightCenter.Width / 4f, rectLightCenter.Height / 4f);
                                        brushPath.AddEllipse(rectLightCenter);
                                        using (PathGradientBrush circleBrush = new PathGradientBrush(brushPath))
                                        {
                                            circleBrush.CenterColor = ChartGraphics.GetGradientColor(markerColor, Color.White, 0.85);
                                            circleBrush.SurroundColors = new Color[] { markerColor };
 
                                            // Calculate the center point of the gradient
                                            Point3D[] centerPoint = new Point3D[] { new Point3D(point.X, point.Y, positionZ + markerRelativeSize.Width) };
                                            matrix.TransformPoints(centerPoint);
                                            centerPoint[0].PointF = graph.GetAbsolutePoint(centerPoint[0].PointF);
                                            circleBrush.CenterPoint = centerPoint[0].PointF;
 
                                            // Draw circle (sphere)
                                            graph.FillEllipse(circleBrush, rect);
                                            graph.DrawEllipse(new Pen(markerBorderColor, markerBorderSize), rect);
                                        }
                                    }
                                }
 
                                // Prepare marker path
                                if ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
                                {
                                    resultPath.AddEllipse(rect);
                                }
 
                                break;
                            }
                        case (MarkerStyle.Square):
                            {
 
                                // Calculate marker non-rotated rectangle
                                RectangleF rectNonRotated = RectangleF.Empty;
                                rectNonRotated.X = point.X - ((float)markerRelativeSize.Width) / 2F;
                                rectNonRotated.Y = point.Y - ((float)markerRelativeSize.Height) / 2F;
                                rectNonRotated.Width = markerRelativeSize.Width;
                                rectNonRotated.Height = markerRelativeSize.Height;
 
                                // Draw 3D bar
                                resultPath = this.Fill3DRectangle(
                                    rectNonRotated,
                                    positionZ - markerRelativeSize.Width / 2f,
                                    markerRelativeSize.Width,
                                    matrix,
                                    lightStyle,
                                    markerColor,
                                    markerBorderColor,
                                    markerBorderSize,
                                    ChartDashStyle.Solid,
                                    operationType);
 
                                break;
                            }
                        default:
                            {
                                throw (new InvalidOperationException(SR.ExceptionGraphics3DMarkerStyleUnknown));
                            }
                    }
                }
            }
 
			return resultPath;
		}
 
		#endregion
 
		#region 3D cube surface visibility methods
 
		/// <summary>
		/// Returns visible surfaces of the 3D cube.
		/// </summary>
		/// <param name="position">2D rectangle coordinates.</param>
		/// <param name="positionZ">Z coordinate of the back side of the cube.</param>
		/// <param name="depth">Cube depth.</param>
		/// <param name="matrix">Coordinate transformation matrix.</param>
		/// <returns>Visible surfaces.</returns>
		internal SurfaceNames GetVisibleSurfaces(
			RectangleF position, 
			float positionZ, 
			float depth, 
			Matrix3D matrix
			)
		{
			// Check if perspective is used
			if(matrix.Perspective != 0)
			{
				// More sofisticated algorithm must be used for visibility detection.
				return GetVisibleSurfacesWithPerspective(position, positionZ, depth, matrix);
			}
 
			// Front surface is always visible
			SurfaceNames	result = SurfaceNames.Front;
 
			// Left and Right surfaces depend on the Y axis angle
			if (matrix.AngleY > 0)
			{
				result |= SurfaceNames.Right; 
			}
			else if (matrix.AngleY < 0)
			{
				result |= SurfaceNames.Left; 
			}
 
			// Top and Bottom surfaces depend on the X axis angle
            if (matrix.AngleX > 0)
			{
				result |= SurfaceNames.Top;
			}
            else if (matrix.AngleX < 0)
			{
				result |= SurfaceNames.Bottom; 
			}
 
			return result;
		}
 
		/// <summary>
		/// Returns visible surfaces of the 3D cube.
		/// This method takes in consideration the perspective.
		/// </summary>
		/// <param name="position">2D rectangle coordinates.</param>
		/// <param name="positionZ">Z coordinate of the back side of the cube.</param>
		/// <param name="depth">Cube depth.</param>
		/// <param name="matrix">Coordinate transformation matrix.</param>
		/// <returns>Visible surfaces.</returns>
		internal SurfaceNames GetVisibleSurfacesWithPerspective(
			RectangleF position, 
			float positionZ, 
			float depth, 
			Matrix3D matrix)
		{
			// Create cube coordinates in 3D space
			Point3D[] cubePoints = new Point3D[8];
 
			// Front Side
			cubePoints[0] = new Point3D( position.X, position.Y, positionZ + depth );
			cubePoints[1] = new Point3D( position.X, position.Bottom, positionZ + depth );
			cubePoints[2] = new Point3D( position.Right, position.Bottom, positionZ + depth );
			cubePoints[3] = new Point3D( position.Right, position.Y, positionZ + depth );
								
			// Back Side
			cubePoints[4] = new Point3D( position.X, position.Y, positionZ );
			cubePoints[5] = new Point3D( position.X, position.Bottom, positionZ );
			cubePoints[6] = new Point3D( position.Right, position.Bottom, positionZ );
			cubePoints[7] = new Point3D( position.Right, position.Y, positionZ );
			
			// Tranform coordinates 
			matrix.TransformPoints( cubePoints );
 
			// Detect surfaces visibility
			return GetVisibleSurfacesWithPerspective(cubePoints);
		}
 
		/// <summary>
		/// Returns visible surfaces of the 3D cube.
		/// This method takes in consideration the perspective.
		/// </summary>
		/// <param name="cubePoints">Array of 8 points which define the cube.</param>
		/// <returns>Visible surfaces.</returns>
		internal SurfaceNames GetVisibleSurfacesWithPerspective(Point3D[] cubePoints)
		{
			// Check imput array size
			if(cubePoints.Length != 8)
			{
                throw (new ArgumentException(SR.ExceptionGraphics3DCoordinatesInvalid, "cubePoints"));
			}
 
			// Detect surfaces visibility
			SurfaceNames	result = 0;
 
			// Check the front side
			if(IsSurfaceVisible(cubePoints[0],cubePoints[3],cubePoints[2]))
			{
				result |= SurfaceNames.Front;
			}
			// Check the back side
			if(IsSurfaceVisible(cubePoints[4],cubePoints[5],cubePoints[6]))
			{
				result |= SurfaceNames.Back;
			}
 
			// Check the left side
			if(IsSurfaceVisible(cubePoints[0],cubePoints[1],cubePoints[5]))
			{
				result |= SurfaceNames.Left;
			}
 
			// Check the right side
			if(IsSurfaceVisible(cubePoints[3],cubePoints[7],cubePoints[6]))
			{
				result |= SurfaceNames.Right;
			}
 
			// Check the top side
			if(IsSurfaceVisible(cubePoints[4],cubePoints[7],cubePoints[3]))
			{
				result |= SurfaceNames.Top;
			}
 
			// Check the bottom side
			if(IsSurfaceVisible(cubePoints[1],cubePoints[2],cubePoints[6]))
			{
				result |= SurfaceNames.Bottom;
			}
 
			return result;
		}
 
        /// <summary>
        /// Checks surface visibility using 3 points and clockwise points index rotation.
        /// </summary>
        /// <param name="first">First point.</param>
        /// <param name="second">Second point.</param>
        /// <param name="tree">Third point.</param>
        /// <returns>True if surface is visible</returns>
		internal static bool IsSurfaceVisible( Point3D first, Point3D second, Point3D tree )
		{
			// Check if points are oriented clocwise in 2D projection.
			// If points are clockwise the surface is visible.
			float a = ( first.Y - second.Y ) / ( first.X - second.X );
			float b = first.Y - a * first.X;
			if( first.X == second.X )
			{
				if( first.Y > second.Y )
				{
					if( tree.X > first.X )
					{
						return true;
					}
					else
					{
						return false;
					}
				}
				else
				{
					if( tree.X > first.X )
					{
						return false;
					}
					else
					{
						return true;
					}
				}
			}
			else if ( first.X < second.X )
			{
				if( tree.Y < a * tree.X + b )
				{
					return false;
				}
				else
				{
					return true;
				}
			}
			else
			{
				if( tree.Y <= a * tree.X + b )
				{
					return true;
				}
				else
				{
					return false;
				}
			}
		}
 
		#endregion
 
		#region Line intersection helper method
 
        /// <summary>
        /// Gets intersection point of two lines
        /// </summary>
        /// <param name="x1">First X value of first line.</param>
        /// <param name="y1">First Y value of first line.</param>
        /// <param name="x2">Second X value of first line.</param>
        /// <param name="y2">Second Y value of first line.</param>
        /// <param name="x3">First X value of second line.</param>
        /// <param name="y3">First Y value of second line.</param>
        /// <param name="x4">Second X value of second line.</param>
        /// <param name="y4">Second Y value of second line.</param>
        /// <returns>Intersection coordinates.</returns>
		internal static PointF GetLinesIntersection(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4)
		{
			PointF result = PointF.Empty;
 
			// Special case for horizontal & vertical lines
			if(x1 == x2 && y3 == y4)
			{
				result.X = x1;
				result.Y = y3;
				return result;
			}
			else if(y1 == y2 && x3 == x4)
			{
				result.X = x3;
				result.Y = y1;
				return result;
			}
			else if(x1 == x2)
			{
				result.X = x1;
				result.Y = (result.X - x3) * (y4 - y3);
				result.Y /= x4 - x3;
				result.Y += y3;
				return result;
			}
			else if(x3 == x4)
			{
				result.X = x3;
				result.Y = (result.X - x1) * (y2 - y1);
				result.Y /= x2 - x1;
				result.Y += y1;
				return result;
			}
 
			// Calculate line eqaution
			float a1 = ( y1 - y2 ) / ( x1 - x2 );
			float b1 = y1 - a1 * x1;
			float a2 = ( y3 - y4 ) / ( x3 - x4 );
			float b2 = y3 - a2 * x3;
 
			// Calculate intersection point
			result.X = (b2 - b1)/(a1 - a2);
			result.Y = a1*result.X + b1;
 
			return result;
		}
 
		#endregion
 
		#region 3D Cylinder drawing methods
 
 
        /// <summary>
        /// Function is used to calculate the coordinates of the 2D rectangle in 3D space
        /// and either draw it or/and calculate the bounding path for selection.
        /// </summary>
        /// <param name="position">Position of 2D rectangle.</param>
        /// <param name="positionZ">Z position of the back side of the 3D rectangle.</param>
        /// <param name="depth">Depth of the 3D rectangle.</param>
        /// <param name="matrix">Coordinate transformation matrix.</param>
        /// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
        /// <param name="backColor">Color of rectangle</param>
        /// <param name="topRightDarkening">Top (or right in bar chart) darkening effect.</param>
        /// <param name="bottomLeftDarkening">Bottom (or left in bar chart) darkening effect.</param>
        /// <param name="borderColor">Border Color</param>
        /// <param name="borderWidth">Border Width</param>
        /// <param name="borderDashStyle">Border Style</param>
        /// <param name="veticalOrientation">Defines if bar is vertical or horizontal.</param>
        /// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
        /// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
		internal GraphicsPath 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)
		{
			Point3D[]		cubePoints = new Point3D[8];
			GraphicsPath	resultPath = ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
				? new GraphicsPath() : null;
 
			//*******************************************************
			//** Define coordinates to draw the cylinder
			//*******************************************************
			if(veticalOrientation)
			{
				cubePoints[0] = new Point3D( position.X, position.Y, positionZ + depth / 2f );
				cubePoints[1] = new Point3D( position.X, position.Bottom, positionZ + depth / 2f );
				cubePoints[2] = new Point3D( position.Right, position.Bottom, positionZ + depth / 2f );
				cubePoints[3] = new Point3D( position.Right, position.Y, positionZ + depth / 2f );
							
				float middleXValue = position.X + position.Width / 2f;
				cubePoints[4] = new Point3D( middleXValue, position.Y, positionZ + depth );
				cubePoints[5] = new Point3D( middleXValue, position.Bottom, positionZ + depth );
				cubePoints[6] = new Point3D( middleXValue, position.Bottom, positionZ );
				cubePoints[7] = new Point3D( middleXValue, position.Y, positionZ );
			}
			else
			{
				cubePoints[0] = new Point3D( position.Right, position.Y, positionZ + depth / 2f );
				cubePoints[1] = new Point3D( position.X, position.Y, positionZ + depth / 2f );
				cubePoints[2] = new Point3D( position.X, position.Bottom, positionZ + depth / 2f );
				cubePoints[3] = new Point3D( position.Right, position.Bottom, positionZ + depth / 2f );
							
				float middleYValue = position.Y + position.Height / 2f;
				cubePoints[4] = new Point3D( position.Right, middleYValue, positionZ + depth );
				cubePoints[5] = new Point3D( position.X, middleYValue, positionZ + depth );
				cubePoints[6] = new Point3D( position.X, middleYValue, positionZ );
				cubePoints[7] = new Point3D( position.Right, middleYValue, positionZ );
			}
 
			// Tranform cylinder coordinates
			matrix.TransformPoints( cubePoints );
 
			// Covert coordinates to absolute
			for(int pointIndex = 0; pointIndex < cubePoints.Length; pointIndex++)
			{
				cubePoints[pointIndex].PointF = GetAbsolutePoint(cubePoints[pointIndex].PointF);
			}
 
			//*******************************************************
			//** Get cylinder colors.
			//*******************************************************
			if( lightStyle == LightStyle.None &&
				(borderWidth == 0 || borderDashStyle == ChartDashStyle.NotSet || borderColor == Color.Empty) )
			{
				borderColor = ChartGraphics.GetGradientColor( backColor, Color.Black, 0.5 );
			}
 
			// Get surface colors
			Color	frontLightColor, leftLightColor, topLightColor, backLightColor, rightLightColor, bottomLightColor;
			matrix.GetLight( backColor, out frontLightColor, out backLightColor, out leftLightColor, out rightLightColor, out topLightColor, out bottomLightColor );
 
			// Darken colors by specified values
			if(topRightDarkening != 0f)
			{
				if(veticalOrientation)
				{
					topLightColor = ChartGraphics.GetGradientColor(topLightColor, Color.Black, topRightDarkening);
				}
				else
				{
					rightLightColor = ChartGraphics.GetGradientColor(rightLightColor, Color.Black, topRightDarkening);
				}
			}
			if(bottomLeftDarkening != 0f)
			{
				if(veticalOrientation)
				{
					bottomLightColor = ChartGraphics.GetGradientColor(bottomLightColor, Color.Black, bottomLeftDarkening);
				}
				else
				{
					leftLightColor = ChartGraphics.GetGradientColor(leftLightColor, Color.Black, bottomLeftDarkening);
				}
			}
 
			//*******************************************************
			//** Check visible surfaces
			//*******************************************************
			SurfaceNames visibleSurfaces = GetVisibleSurfacesWithPerspective(position,positionZ,depth,matrix);
 
			// Front surface is always visible in cylinder
			if( (visibleSurfaces & SurfaceNames.Front) !=  SurfaceNames.Front)
			{
				visibleSurfaces |= SurfaceNames.Front;
			}
 
			//*******************************************************
			//** Create flattened paths for the sides of the 
			//** cylinder (top,bottom/left,rigth)
			//*******************************************************
			PointF[] sidePoints = new PointF[4];
			sidePoints[0] = cubePoints[6].PointF;
			sidePoints[1] = cubePoints[1].PointF;
			sidePoints[2] = cubePoints[5].PointF;
			sidePoints[3] = cubePoints[2].PointF;
			GraphicsPath bottomLeftSide = new GraphicsPath();
			bottomLeftSide.AddClosedCurve(sidePoints, 0.8f);
			bottomLeftSide.Flatten();
			sidePoints[0] = cubePoints[7].PointF;
			sidePoints[1] = cubePoints[0].PointF;
			sidePoints[2] = cubePoints[4].PointF;
			sidePoints[3] = cubePoints[3].PointF;
			GraphicsPath topRigthSide = new GraphicsPath();
			topRigthSide.AddClosedCurve(sidePoints, 0.8f);
			topRigthSide.Flatten();
 
			//*******************************************************
			//** Find cylinder angle
			//*******************************************************
			float cylinderAngle = 90f;
			if(cubePoints[5].PointF.Y != cubePoints[4].PointF.Y)
			{
				cylinderAngle = (float)Math.Atan(
					(cubePoints[4].PointF.X - cubePoints[5].PointF.X) /
					(cubePoints[5].PointF.Y - cubePoints[4].PointF.Y) );
				cylinderAngle = (float)Math.Round(cylinderAngle * 180f / (float)Math.PI);
			}
 
			//*******************************************************
			//** Draw all invisible surfaces first (if semi-transparent color is used)
			//*******************************************************
			for(int drawVisible = 0; drawVisible <= 1; drawVisible++)
			{
				// Do not draw invisible surfaces for solid colors
				if(drawVisible == 0 && backColor.A == 255)
				{
					continue;
				}
 
				// Check visibility of all surfaces and draw them
				for(int surfaceIndex = (int)SurfaceNames.Front; surfaceIndex <= (int)SurfaceNames.Bottom; surfaceIndex *= 2)
				{
					SurfaceNames	currentSurface = (SurfaceNames)surfaceIndex;
 
					// Check if surface is visible or semi-transparent color is used
					bool isVisible = (visibleSurfaces & currentSurface) != 0;
					if(isVisible && drawVisible == 1 ||
						!isVisible && drawVisible == 0)
					{
						// Fill surface coordinates and color
						GraphicsPath	pathToDraw = null;
						Color			surfaceColor = backColor;
 
						// Declare a special brush for the front surface
						Brush			frontSurfaceBrush = null;
 
						switch(currentSurface)
						{
							case(SurfaceNames.Front):
							{
								// Set front surface color
								surfaceColor = backColor;
 
								// Add ellipse segment of the cylinder on top/rigth (reversed)
								pathToDraw = new GraphicsPath();
								PointF	leftSideLinePoint = PointF.Empty;
								PointF	rightSideLinePoint = PointF.Empty;
								AddEllipseSegment(
									pathToDraw, 
									topRigthSide, 
									bottomLeftSide, 
									(matrix.Perspective == 0) ? veticalOrientation : false, 
									cylinderAngle, 
									out leftSideLinePoint,
									out rightSideLinePoint);
								pathToDraw.Reverse();
							
								// Add ellipse segment of the cylinder on bottom/left
								PointF	leftOppSideLinePoint = PointF.Empty;
								PointF	rightOppSideLinePoint = PointF.Empty;
								AddEllipseSegment(
									pathToDraw, 
									bottomLeftSide, 
									topRigthSide, 
									(matrix.Perspective == 0) ? veticalOrientation : false, 
									cylinderAngle, 
									out leftOppSideLinePoint,
									out rightOppSideLinePoint);
								pathToDraw.CloseAllFigures();
 
								// Reset indexes of opposite side points
								this._oppLeftBottomPoint = -1;
								this._oppRigthTopPoint = -1;
 
								// Create gradient brush for the front surface
								if(lightStyle != LightStyle.None)
								{
									RectangleF boundsRect = pathToDraw.GetBounds();
									if(boundsRect.Height > 0 && boundsRect.Width > 0)
									{
										Color	lightColor = ChartGraphics.GetGradientColor( backColor, Color.White, 0.3 );
										Color	darkColor = ChartGraphics.GetGradientColor( backColor, Color.Black, 0.3 );
 
										// Create gradient
										if(!leftSideLinePoint.IsEmpty && 
											!rightSideLinePoint.IsEmpty && 
											!leftOppSideLinePoint.IsEmpty && 
											!rightOppSideLinePoint.IsEmpty)
										{
											PointF	boundsRectMiddlePoint = PointF.Empty;
											boundsRectMiddlePoint.X = boundsRect.X + boundsRect.Width/2f;
											boundsRectMiddlePoint.Y = boundsRect.Y + boundsRect.Height/2f;
 
											PointF	centralLinePoint = PointF.Empty;
											double	centralLineAngle = ((cylinderAngle) * Math.PI / 180f);
											if(cylinderAngle == 0 || cylinderAngle == 180 || cylinderAngle == -180)
											{
												centralLinePoint.X = boundsRectMiddlePoint.X + 100f;
												centralLinePoint.Y = boundsRectMiddlePoint.Y;
											}
											else if(cylinderAngle == 90 || cylinderAngle == -90)
											{
												centralLinePoint.X = boundsRectMiddlePoint.X;
												centralLinePoint.Y = boundsRectMiddlePoint.Y + 100f;
											}
											else if(cylinderAngle > -45 && cylinderAngle < 45)
											{
												centralLinePoint.X = boundsRectMiddlePoint.X + 100f;
												centralLinePoint.Y = (float)(Math.Tan(centralLineAngle) * centralLinePoint.X);
												centralLinePoint.Y += (float)(boundsRectMiddlePoint.Y - Math.Tan(centralLineAngle) * boundsRectMiddlePoint.X);
											}
											else
											{
												centralLinePoint.Y = boundsRectMiddlePoint.Y + 100f;
												centralLinePoint.X = (float)(centralLinePoint.Y - (boundsRectMiddlePoint.Y - Math.Tan(centralLineAngle) * boundsRectMiddlePoint.X));
												centralLinePoint.X /= (float)(Math.Tan(centralLineAngle)); 
											}
 
 
											PointF	middlePoint1 = ChartGraphics.GetLinesIntersection(
												boundsRectMiddlePoint.X, boundsRectMiddlePoint.Y,
												centralLinePoint.X, centralLinePoint.Y,
												leftSideLinePoint.X, leftSideLinePoint.Y, 
												leftOppSideLinePoint.X, leftOppSideLinePoint.Y);
 
											PointF	middlePoint2 = ChartGraphics.GetLinesIntersection(
												boundsRectMiddlePoint.X, boundsRectMiddlePoint.Y,
												centralLinePoint.X, centralLinePoint.Y,
												rightSideLinePoint.X, rightSideLinePoint.Y,
												rightOppSideLinePoint.X, rightOppSideLinePoint.Y);
 
											// Gradient points can not have same coordinates
											if(middlePoint1.X != middlePoint2.X || middlePoint1.Y != middlePoint2.Y)
											{
												frontSurfaceBrush = new LinearGradientBrush(
													middlePoint1, 
													middlePoint2,
													lightColor, 
													darkColor);
 
 
												ColorBlend colorBlend = new ColorBlend(5);
												colorBlend.Colors[0] = darkColor;
												colorBlend.Colors[1] = darkColor;
												colorBlend.Colors[2] = lightColor;
												colorBlend.Colors[3] = darkColor;
												colorBlend.Colors[4] = darkColor;
 
												colorBlend.Positions[0] = 0.0f;
												colorBlend.Positions[1] = 0.0f;
												colorBlend.Positions[2] = 0.5f;
												colorBlend.Positions[3] = 1.0f;
												colorBlend.Positions[4] = 1.0f;
 
												((LinearGradientBrush)frontSurfaceBrush).InterpolationColors = colorBlend;
											}
										}
 
									}
								}
 
								break;
							}
							case(SurfaceNames.Top):
								if(veticalOrientation)
								{
									surfaceColor = topLightColor;
									pathToDraw = topRigthSide;
								}
								break;
							case(SurfaceNames.Bottom):
								if(veticalOrientation)
								{
									surfaceColor = bottomLightColor;
									pathToDraw = bottomLeftSide;
								}
								break;
							case(SurfaceNames.Right):
								if(!veticalOrientation)
								{
									surfaceColor = rightLightColor;
									pathToDraw = topRigthSide;
								}
								break;
							case(SurfaceNames.Left):
								if(!veticalOrientation)
								{
									surfaceColor = leftLightColor;
									pathToDraw = bottomLeftSide;
								}
								break;
						}
 
 
						//*******************************************************
						//** Draw surface
						//*******************************************************
						if(pathToDraw != null)
						{
							if( (operationType & DrawingOperationTypes.DrawElement) == DrawingOperationTypes.DrawElement)
							{
								// Draw only completly visible surfaces
								if((visibleSurfaces & currentSurface) != 0)
								{
                                    using (Brush brush = new SolidBrush(surfaceColor))
                                    {
									    FillPath( (frontSurfaceBrush == null) ? brush : frontSurfaceBrush, pathToDraw );
                                    }
								}
 
								// Draw surface border
                                using (Pen pen = new Pen(borderColor, borderWidth))
                                {
                                    pen.DashStyle = GetPenStyle(borderDashStyle);
                                    if (lightStyle != LightStyle.None &&
                                        (borderWidth == 0 || borderDashStyle == ChartDashStyle.NotSet || borderColor == Color.Empty))
                                    {
                                        // Draw line of the darker color inside the cylinder
                                        pen.Color = frontSurfaceBrush == null ? surfaceColor : ChartGraphics.GetGradientColor(backColor, Color.Black, 0.3);
                                        pen.Width = 1;
                                        pen.Alignment = PenAlignment.Inset;
                                    }
 
                                    pen.StartCap = LineCap.Round;
                                    pen.EndCap = LineCap.Round;
                                    pen.LineJoin = LineJoin.Bevel;
                                    DrawPath(pen, pathToDraw);
                                }
							}
 
							// Add surface coordinate to the path
							if( (operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
							{
								// Only if surface is completly visible
								if((visibleSurfaces & currentSurface) != 0)
								{
									if(pathToDraw != null  && pathToDraw.PointCount > 0)
									{
										resultPath.AddPath(pathToDraw, true);
										resultPath.SetMarkers();
									}
								}
							}
						}
 
					}
				}
			}
 
			return resultPath;
		}
 
		/// <summary>
		/// Adds segment of the ellipse to form the front surface of the cylinder
		/// </summary>
		internal void AddEllipseSegment(
			GraphicsPath resultPath, 
			GraphicsPath ellipseFlattenPath, 
			GraphicsPath oppositeEllipseFlattenPath, 
			bool veticalOrientation, 
			float cylinderAngle, 
			out PointF leftSideLinePoint,
			out PointF rightSideLinePoint)
		{
			// Initialize return values
			leftSideLinePoint = PointF.Empty;
			rightSideLinePoint = PointF.Empty;
 
			// Check if input path is empty
			if(ellipseFlattenPath.PointCount == 0)
			{
				return;
			}
 
			// Find the index the left/bottom most and right/top most point in flatten array of ellipse points
			int			leftBottomPoint = 0;
			int			rigthTopPoint = 0;
			PointF[]	ellipsePoints = ellipseFlattenPath.PathPoints;			
 
			
			if(veticalOrientation)
			{
				for(int pointIndex = 1; pointIndex < ellipsePoints.Length; pointIndex++)
				{
					if(ellipsePoints[leftBottomPoint].X > ellipsePoints[pointIndex].X)
					{
						leftBottomPoint = pointIndex;
					}
					if(ellipsePoints[rigthTopPoint].X < ellipsePoints[pointIndex].X)
					{
						rigthTopPoint = pointIndex;
					}
				}
			}
			else
			{
				bool	doneFlag = false;
				leftBottomPoint = -1;
				rigthTopPoint = -1;
 
				if(this._oppLeftBottomPoint != -1 && this._oppRigthTopPoint != -1)
				{
					// Get index from previously calculated values
					leftBottomPoint = this._oppLeftBottomPoint;
					rigthTopPoint = this._oppRigthTopPoint;
				}
				else
				{
					// Loop through first ellipse points
					PointF[]	oppositeEllipsePoints = oppositeEllipseFlattenPath.PathPoints;			
					for(int pointIndex = 0; !doneFlag && pointIndex < ellipsePoints.Length; pointIndex++)
					{
						// Loop through opposite ellipse points
						for(int pointOppositeIndex = 0; !doneFlag && pointOppositeIndex < oppositeEllipsePoints.Length; pointOppositeIndex++)
						{
							bool	closeToVertical = false;
							bool	pointsOnLeft = false;
							bool	pointsOnRight = false;
 
							//if(cylinderAngle == 0 || cylinderAngle == 180 || cylinderAngle == -180)
							if(cylinderAngle > -30 && cylinderAngle < 30)
							{
								closeToVertical = true;
							}
 
							if(closeToVertical)
							{
								if(oppositeEllipsePoints[pointOppositeIndex].Y == ellipsePoints[pointIndex].Y)
								{
									continue;
								}
 
								float	linePointX = oppositeEllipsePoints[pointOppositeIndex].X - ellipsePoints[pointIndex].X;
								linePointX /= oppositeEllipsePoints[pointOppositeIndex].Y - ellipsePoints[pointIndex].Y;
 
								// Check if this line has any points to the right/left
								for(int innerPointIndex = 0; innerPointIndex < ellipsePoints.Length; innerPointIndex++)
								{
									// Skip points used to define line function
									if(innerPointIndex == pointIndex)
									{
										continue;
									}
 
									float	x = linePointX;
									x *= ellipsePoints[innerPointIndex].Y - ellipsePoints[pointIndex].Y;
									x += ellipsePoints[pointIndex].X;
 
									if(x > ellipsePoints[innerPointIndex].X)
									{
										pointsOnLeft = true;
									}
									if(x < ellipsePoints[innerPointIndex].X)
									{
										pointsOnRight = true;
									}
									if(pointsOnLeft && pointsOnRight)
									{
										break;
									}
								}
 
								if(pointsOnLeft == false || pointsOnRight == false)
								{
									for(int innerPointIndex = 0; innerPointIndex < oppositeEllipsePoints.Length; innerPointIndex++)
									{
										// Skip points used to define line function
										if(innerPointIndex == pointOppositeIndex)
										{
											continue;
										}
 
										float	x = linePointX;
										x *= oppositeEllipsePoints[innerPointIndex].Y - ellipsePoints[pointIndex].Y;
										x += ellipsePoints[pointIndex].X;
 
										if(x > oppositeEllipsePoints[innerPointIndex].X)
										{
											pointsOnLeft = true;
										}
										if(x < oppositeEllipsePoints[innerPointIndex].X)
										{
											pointsOnRight = true;
										}
										if(pointsOnLeft && pointsOnRight)
										{
											break;
										}
									}
								}
							}
							else
							{
								if(oppositeEllipsePoints[pointOppositeIndex].X == ellipsePoints[pointIndex].X)
								{
									continue;
								}
 
								float	linePointY = oppositeEllipsePoints[pointOppositeIndex].Y - ellipsePoints[pointIndex].Y;
								linePointY /= oppositeEllipsePoints[pointOppositeIndex].X - ellipsePoints[pointIndex].X;
 
								// Check if this line has any points to the right/left
								for(int innerPointIndex = 0; innerPointIndex < ellipsePoints.Length; innerPointIndex++)
								{
									// Skip points used to define line function
									if(innerPointIndex == pointIndex)
									{
										continue;
									}
 
									float	y = linePointY;
									y *= ellipsePoints[innerPointIndex].X - ellipsePoints[pointIndex].X;
									y += ellipsePoints[pointIndex].Y;
 
									if(y > ellipsePoints[innerPointIndex].Y)
									{
										pointsOnLeft = true;
									}
									if(y < ellipsePoints[innerPointIndex].Y)
									{
										pointsOnRight = true;
									}
									if(pointsOnLeft && pointsOnRight)
									{
										break;
									}
								}
 
								if(pointsOnLeft == false || pointsOnRight == false)
								{
									for(int innerPointIndex = 0; innerPointIndex < oppositeEllipsePoints.Length; innerPointIndex++)
									{
										// Skip points used to define line function
										if(innerPointIndex == pointOppositeIndex)
										{
											continue;
										}
 
										float	y = linePointY;
										y *= oppositeEllipsePoints[innerPointIndex].X - ellipsePoints[pointIndex].X;
										y += ellipsePoints[pointIndex].Y;
 
										if(y > oppositeEllipsePoints[innerPointIndex].Y)
										{
											pointsOnLeft = true;
										}
										if(y < oppositeEllipsePoints[innerPointIndex].Y)
										{
											pointsOnRight = true;
										}
										if(pointsOnLeft && pointsOnRight)
										{
											break;
										}
									}
								}
							}
 
							if(!pointsOnLeft && leftBottomPoint == -1)
							{
								leftBottomPoint = pointIndex;
								this._oppLeftBottomPoint = pointOppositeIndex;
							}
							if(!pointsOnRight && rigthTopPoint == -1)
							{
								rigthTopPoint = pointIndex;
								this._oppRigthTopPoint = pointOppositeIndex;
							}
 
							if(leftBottomPoint >= 0 && rigthTopPoint >= 0)
							{
								doneFlag = true;
 
								if(closeToVertical)
								{
									if(ellipsePoints[leftBottomPoint].Y > oppositeEllipsePoints[this._oppLeftBottomPoint].Y)
									{
										int temp = leftBottomPoint;
										leftBottomPoint = rigthTopPoint;
										rigthTopPoint = temp;
 
										temp = this._oppLeftBottomPoint;
										this._oppLeftBottomPoint = this._oppRigthTopPoint;
										this._oppRigthTopPoint = temp;
									}
								}
 
							}
						}
					}
				}
			}
 
			// Point indexes were not found
			if(leftBottomPoint == rigthTopPoint ||
				rigthTopPoint == -1 ||
				leftBottomPoint == -1)
			{
				return;
			}
 
			// Set left\right line coordinates
			leftSideLinePoint = ellipsePoints[leftBottomPoint];
			rightSideLinePoint = ellipsePoints[rigthTopPoint];
 
			// Add required ellipse segment to the result path
			for(int pointIndex = leftBottomPoint + 1; pointIndex != rigthTopPoint + 1; pointIndex++)
			{
				if(pointIndex > ellipsePoints.Length - 1)
				{
					resultPath.AddLine(ellipsePoints[ellipsePoints.Length - 1], ellipsePoints[0]);
					pointIndex = 0;
					continue;
				}
				resultPath.AddLine(ellipsePoints[pointIndex - 1], ellipsePoints[pointIndex]);
			}
		}
 
		#endregion
	}
	
	/// <summary>
    /// The Point3D class represents point coordinates in 3D space.
	/// </summary>
#if ASPPERM_35
	[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
#endif
    public class Point3D
	{
		#region Fields
 
		// Point X and Y coordinates
		private	PointF	_coordXY = new PointF(0f, 0f);
		
		// Point Z coordinate (depth)
		private float	_coordZ = 0;
 
		#endregion
 
		#region Properties
 
		/// <summary>
        /// Gets or sets the X coordinate of the point.
		/// </summary>
		[
		Bindable(true),
		DefaultValue(0),
		SRDescription("DescriptionAttributePoint3D_X")
		]
        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "X")]
        public float X
		{
			get
			{
                return this._coordXY.X;
			}
			set
			{
                this._coordXY.X = value;
			}
		}
 
		/// <summary>
        /// Gets or sets the Y coordinate of the point.
		/// </summary>
		[
		Bindable(true),
		DefaultValue(0),
		SRDescription("DescriptionAttributePoint3D_Y")
		]
        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Y")]
        public float Y
		{
			get
			{
                return this._coordXY.Y;
			}
			set
			{
                this._coordXY.Y = value;
			}
		}
 
		/// <summary>
        /// Gets or sets the Z coordinate of the point.
		/// </summary>
		[
		Bindable(true),
		DefaultValue(0),
		SRDescription("DescriptionAttributePoint3D_Z")
		]
        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Z")]
        public float Z
		{
			get
			{
                return this._coordZ;
			}
			set
			{
                this._coordZ = value;
			}
		}
 
		/// <summary>
		/// Gets or sets a PointF structure, which stores the X and Y coordinates of a 3D point.
		/// </summary>
		[
		Bindable(true),
		DefaultValue(0),
		SRDescription("DescriptionAttributePoint3D_PointF")
		]
		public PointF PointF
		{
			get
			{
                return this._coordXY;
			}
			set
			{
                this._coordXY = new PointF(value.X, value.Y);
			}
		}
 
		#endregion
 
		#region Constructors
 
		/// <summary>
		/// Public constructor.
		/// </summary>
        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly",
            Justification = "X, Y and Z are cartesian coordinates and well understood")]
        public Point3D(float x, float y, float z)
		{
			this._coordXY = new PointF(x, y);
            this._coordZ = z;
		}
 
		/// <summary>
		/// Public constructor.
		/// </summary>
		public Point3D( )
		{
		
		}
 
		#endregion // Constructor
	}
}