File: Common\General\Matrix3D.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:		Matrix3D.cs
//
//  Namespace:	DataVisualization.Charting
//
//	Classes:	Matrix3D
//
//  Purpose:	Matrix3D class is used during the 3D drawings to 
//              transform plotting area 3D coordinates into the 2D 
//              projection coordinates based on rotation and 
//              perspective settings.
//
//	Reviewed:	AG - Dec 4, 2002
//              AG - Microsoft 14, 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;
 
#if Microsoft_CONTROL
	using System.Windows.Forms.DataVisualization.Charting;
	using System.Windows.Forms.DataVisualization.Charting.Data;
	using System.Windows.Forms.DataVisualization.Charting.ChartTypes;
	using System.Windows.Forms.DataVisualization.Charting.Utilities;
	using System.Windows.Forms.DataVisualization.Charting.Borders3D;
 
#else
	//using System.Web.UI.DataVisualization.Charting.Utilities;
	//using System.Web.UI.DataVisualization.Charting.Borders3D;
#endif
 
#endregion
 
#if Microsoft_CONTROL
	namespace System.Windows.Forms.DataVisualization.Charting
#else
namespace System.Web.UI.DataVisualization.Charting
 
#endif
{
	/// <summary>
	/// This class is responsible for all 3D coordinates transformations: Translation, 
	/// Rotation, Scale, Perspective and RightAngle Projection. Translation 
	/// and rotation are stored in composite matrix (mainMatrix), and scaling, 
	/// projection and non-composite translation are stored in private fields. 
	/// Matrix is initialized with Chart Area 3D cube, which is invisible boundary 
	/// cube of 3D Chart area. The matrix has to be initialized every time 
	/// when angles, position or perspective parameters are changed. Method 
	/// TransformPoints will apply 3D Transformation on points using 
	/// Initialization values: Main matrix and other initialization values.
	/// </summary>
	internal class Matrix3D
	{
		#region Enumerations
 
		/// <summary>
		/// 3D Axis used for rotation
		/// </summary>
		private enum RotationAxis
		{
			/// <summary>
			/// Rotation around X axis.
			/// </summary>
			X,
 
			/// <summary>
			/// Rotation around Y axis.
			/// </summary>
			Y,
 
			/// <summary>
			/// Rotation around Z axis.
			/// </summary>
			Z
		}
 
		#endregion // Enumerations
 
		#region Fields
 
		/// <summary>
		/// Composite matrix.
		/// </summary>
		private float [][] _mainMatrix;
		
		/// <summary>
		/// Default translation for chart area cube ( without composition ).
		/// </summary>
		private float _translateX;
 
		/// <summary>
		/// Default translation for chart area cube ( without composition ).
		/// </summary>
		private float _translateY;
 
		/// <summary>
		/// Default translation for chart area cube ( without composition ).
		/// </summary>
		private float _translateZ;
 
		/// <summary>
		/// The value, which is used to rescale chart area.
		/// </summary>
		private float _scale;
 
		/// <summary>
		/// The value used for Isometric Shift.
		/// </summary>
		private float _shiftX;
 
		/// <summary>
		/// The value used for Isometric Shift.
		/// </summary>
		private float _shiftY;
 
		/// <summary>
		/// Perspective value.
		/// </summary>
		internal float _perspective;
 
		/// <summary>
		/// Isometric projection.
		/// </summary>
		private bool _rightAngleAxis;
 
		/// <summary>
		/// The value, which is used for perspective.
		/// </summary>
		private float _perspectiveFactor = float.NaN;
 
		/// <summary>
		/// The value, which is used to set projection plane.
		/// </summary>
		private float _perspectiveZ;
 
		/// <summary>
		/// X Angle.
		/// </summary>
		private float _angleX;
 
		/// <summary>
		/// Y Angle.
		/// </summary>
		private float _angleY;
 
		/// <summary>
		/// Private fields used for lighting
		/// </summary>
		Point3D [] _lightVectors = new Point3D[7];
 
		/// <summary>
		/// LightStyle Style
		/// </summary>
		LightStyle _lightStyle;
 
		#endregion // Fields
 
        #region Properties
 
        /// <summary>
        /// Gets the X Angle.
        /// </summary>
        internal float AngleX
        {
            get { return _angleX; }
        }
 
        /// <summary>
        /// Gets the Y Angle.
        /// </summary>
        internal float AngleY
        {
            get { return _angleY; }
        }
 
        /// <summary>
        /// Get perspective value.
        /// </summary>
        internal float Perspective
        {
            get { return _perspective; } 
        }
 
        #endregion // Properties
 
        #region Internal and Public Methods
 
        /// <summary>
		/// Constructor for Matrix 3D
		/// </summary>
		public Matrix3D()
		{
		}
 
		/// <summary>
		/// Checks if 3D matrix was initialized.
		/// </summary>
		/// <returns>True if matrix was initialized.</returns>
		public bool IsInitialized()
		{
			return (this._mainMatrix != null);
		}
 
		/// <summary>
		/// Initialize Matrix 3D. This method calculates how much a chart area 
		/// cube has to be resized to fit Inner Plotting Area rectangle. Order 
		/// of operation is following: Translation for X and Y axes, Rotation 
		/// by X-axis, Rotation by Y-axis and same scaling for all axes. All 
		/// other elements, which belongs to this chart area cube (Data points, 
		/// grid lines etc.) has to follow same order. Translation and rotation 
		/// form composite matrix mainMatrix. Scale has to be allied separately.
		/// </summary>
		/// <param name="innerPlotRectangle">Inner Plotting Area position. Chart area cube has to be inside this rectangle</param>
		/// <param name="depth">Depth of chart area cube</param>
		/// <param name="angleX">Angle of rotation by X axis.</param>
		/// <param name="angleY">Angle of rotation by Y axis.</param>
		/// <param name="perspective">Perspective in percentages</param>
		/// <param name="rightAngleAxis">Right angle flag.</param>
		internal void Initialize( 
			RectangleF innerPlotRectangle, 
			float depth, 
			float angleX, 
			float angleY, 
			float perspective, 
			bool rightAngleAxis )
		{
			// Initialization for mainMatrix
			Reset();
 
			// Remember non-composite translation
			_translateX = innerPlotRectangle.X+innerPlotRectangle.Width/2;
			_translateY = innerPlotRectangle.Y+innerPlotRectangle.Height/2;
			_translateZ = depth / 2F;
			float width = innerPlotRectangle.Width;
			float height = innerPlotRectangle.Height;
			this._perspective = perspective;
			this._rightAngleAxis = rightAngleAxis;
 
			// Remember Angles
			this._angleX = angleX;
			this._angleY = angleY;
			
			// Change Degrees to radians.
			angleX = angleX / 180F * (float)Math.PI;
			angleY = angleY / 180F * (float)Math.PI;
 
			// Set points for 3D Bar which represents 3D Chart Area Cube.
			Point3D [] points = Set3DBarPoints( width, height, depth );
			
			// Translate Chart Area Cube WITH CENTER OF ROTATION - COMPOSITE TRANSLATION.
			Translate( _translateX, _translateY, 0 );
 
			// Non Isometric projection
			if( !rightAngleAxis )
			{
				// Rotate Chart Area Cube by X axis. 
				Rotate( angleX, RotationAxis.X );
 
				// Rotate Chart Area Cube by Y axis. 
				Rotate( angleY, RotationAxis.Y );
			}
			else
			{
				if( this._angleY >= 45 )
				{
					// Rotate Chart Area Cube by Y axis. 
					Rotate( Math.PI / 2, RotationAxis.Y );
				}
				else if( this._angleY <= -45 )
				{
					// Rotate Chart Area Cube by Y axis. 
					Rotate( -Math.PI / 2, RotationAxis.Y );
				}
			}
			
			// Apply composed transformation ( Translation and rotation ).
			GetValues( points );
 
			float maxZ = float.MinValue;
 
			if( perspective != 0F || rightAngleAxis )
			{
				// Find projection plane
				foreach( Point3D point in points )
				{
					if( point.Z > maxZ )
						maxZ = point.Z;
				}
 
				// Set Projection plane
				_perspectiveZ = maxZ;
			}
 
			if( perspective != 0F )
			{
				_perspectiveFactor = perspective / 2000F;
 
				// Apply perspective
				ApplyPerspective( points );
			}
				
			// Isometric projection is active
			if( rightAngleAxis )
			{
				RightAngleProjection( points );
 
				float minX = 0F;
				float minY = 0F;
				float maxX = 0F;
				float maxY = 0F;
 
				// Point loop
				foreach( Point3D point in points )
				{
					if( point.X - _translateX < 0F  && Math.Abs( point.X - _translateX ) > minX )
						minX = Math.Abs( point.X - _translateX );
					
					if( point.X - _translateX >=0F  && Math.Abs( point.X - _translateX ) > maxX )
						maxX = Math.Abs( point.X - _translateX );
 
					if( point.Y - _translateY < 0F  && Math.Abs( point.Y - _translateY ) > minY )
						minY = Math.Abs( point.Y - _translateY );
					
					if( point.Y - _translateY >=0F  && Math.Abs( point.Y - _translateY ) > maxY )
						maxY = Math.Abs( point.Y - _translateY );
				}
 
				_shiftX = (maxX - minX)/2F;
				_shiftY = (maxY - minY)/2F;
				RightAngleShift( points );
			}
								
			// This code searches for value, which will be used for scaling.
			float maxXScale = float.MinValue;
			float maxYScale = float.MinValue;
 
			foreach( Point3D point in points )
			{
				// Find maximum relative distance for X axis.
				// Relative distance is (distance from the center of plotting area 
				// position) / (distance from the edge of rectangle to 
				// the center of the rectangle).
				if( maxXScale < Math.Abs(point.X - _translateX) / width * 2 )
					maxXScale = Math.Abs(point.X - _translateX) / width * 2;
				
				// Find maximum relative distance for Y axis.
				if( maxYScale < Math.Abs(point.Y - _translateY) / height * 2 )
					maxYScale = Math.Abs(point.Y - _translateY) / height * 2;
			}
 
			// Remember scale factor
			_scale = (maxYScale > maxXScale ) ? maxYScale : maxXScale;
 
			// Apply scaling
			Scale( points );
			
		}
 
		/// <summary>
		/// Apply transformations on array od 3D Points. Order of operation is 
		/// following: Translation ( Set coordinate system for 0:100 to -50:50 
		/// Center of rotation is always 0), Composite Translation for X and Y 
		/// axes ( Moving center of rotation ), Rotation by X-axis, Rotation 
		/// by Y-axis, perspective and same scaling for all axes.
		/// </summary>
		/// <param name="points">3D Points array.</param>
		public void TransformPoints( Point3D[] points )
		{
			TransformPoints( points, true );
		}
#if RS_DEADCODE
		/// <summary>
		/// This Method returns scale factor
		/// </summary>
		/// <returns></returns>
		internal float GetScale()
		{
			return scale;
		}
#endif //RS_DEADCODE
		#endregion // Internal and Public Methods
 
		#region Private Methods
 
		/// <summary>
		/// Apply transformations on array od 3D Points. Order of operation is 
		/// following: Translation ( Set coordinate system for 0:100 to -50:50 
		/// Center of rotation is always 0), Composite Translation for X and Y 
		/// axes ( Moving center of rotation ), Rotation by X-axis, Rotation 
		/// by Y-axis, perspective and same scaling for all axes.
		/// </summary>
		/// <param name="points">3D Points array.</param>
		/// <param name="withPerspective">Applay Perspective</param>
		private void TransformPoints( Point3D[] points, bool withPerspective )
		{
			// Matrix is not initialized.
			if( _mainMatrix == null )
			{
                throw new InvalidOperationException(SR.ExceptionMatrix3DNotinitialized);
			}
 
			// Translate point. CENTER OF ROTATION is 0 and that center is in 
			// the middle of chart area 3D CUBE. Translate method cannot 
			// be used because composite translation WILL MOVE 
			// CENTER OF ROTATION.
			foreach( Point3D point in points )
			{
				point.X -= _translateX;
				point.Y -= _translateY;
				point.Z -= _translateZ;
			}
		
			// Transform points using composite mainMatrix. (Translation of points together with 
			// Center of rotation and rotations by X and Y axes).
			GetValues( points );
 
			// Apply perspective
			if( _perspective != 0F && withPerspective )
			{
				ApplyPerspective( points );
			}
			
			// RightAngle Projection
			if( _rightAngleAxis )
			{
				RightAngleProjection( points );
				RightAngleShift( points );
			}
 
			// Scales data points. Scaling has to be performed SEPARATELY from 
			// composite matrix. If scale is used with composite matrix after 
			// rotation, scaling will deform object.
			Scale( points );
		}
		
		/// <summary>
		/// This method adjusts a position of 3D Chart Area cube. This 
		/// method will translate chart for better use of the inner 
		/// plotting area. Center of rotation is shifted for 
		/// right Angle projection.
		/// </summary>
		/// <param name="points">3D Points array.</param>
		private void RightAngleShift( Point3D [] points )
		{
			foreach( Point3D point in points )
			{
				point.X = point.X - _shiftX;   
				point.Y = point.Y - _shiftY;   
			}
		}
 
		/// <summary>
		/// Method used to calculate right Angle projection.
		/// </summary>
		/// <param name="points">3D points array.</param>
		private void RightAngleProjection( Point3D [] points )
		{
			float coorectionAngle = 45F;
		
			float xFactor = this._angleX / 45;
 
			float yFactor;
			
			if( this._angleY >= 45 )
			{
				yFactor = (this._angleY - 90) / coorectionAngle;
			}
			else if ( this._angleY <= -45 )
			{
				yFactor = ( this._angleY + 90 ) / coorectionAngle;
			}
			else
			{
				yFactor = this._angleY / coorectionAngle;
			}
			
			// Projection formula
			// perspectiveZ - Position of perspective plain.
			// Perspective Factor - Intensity of projection.
			foreach( Point3D point in points )
			{
				point.X = point.X + ( _perspectiveZ - point.Z ) * yFactor;   
				point.Y = point.Y - ( _perspectiveZ - point.Z ) * xFactor;  
			}
		}
 
		/// <summary>
		/// Method is used for Planar Geometric projection. 
		/// </summary>
		/// <param name="points">3D Points array.</param>
		private void ApplyPerspective( Point3D [] points )
		{
			// Projection formula
			// perspectiveZ - Position of perspective plain.
			// perspectiveFactor - Intensity of projection.
			foreach( Point3D point in points )
			{
				point.X = _translateX + (point.X - _translateX) / ( 1 + (_perspectiveZ - point.Z) * _perspectiveFactor);   
				point.Y = _translateY + (point.Y - _translateY) / ( 1 + (_perspectiveZ - point.Z) * _perspectiveFactor); 
			}
		}
 
		/// <summary>
		/// Scales data points. Scaling has to be performed SEPARATELY from 
		/// composite matrix. If scale is used with composite matrix after 
		/// rotation, scaling will deform object.
		/// </summary>
		/// <param name="points">3D Points array.</param>
		private void Scale( Point3D [] points )
		{
			foreach( Point3D point in points )
			{
				point.X = _translateX + (point.X - _translateX) / _scale; 
				point.Y = _translateY + (point.Y - _translateY) / _scale; 
			}
		}
 
		/// <summary>
		/// Prepend to this Matrix object a translation. This method is used 
		/// only if CENTER OF ROTATION HAS TO BE MOVED.
		/// </summary>
		/// <param name="dx">Translate in x axis direction.</param>
		/// <param name="dy">Translate in y axis direction.</param>
		/// <param name="dz">Translate in z axis direction.</param>
		private void Translate( float dx, float dy, float dz )
		{
			float [][] translationMatrix = new float[4][];
			translationMatrix[0] = new float[4];
			translationMatrix[1] = new float[4];
			translationMatrix[2] = new float[4];
			translationMatrix[3] = new float[4];
 
			// Matrix initialization
			// Row loop
			for( int row = 0; row < 4; row ++ )
			{
				// Column loop
				for( int column = 0; column < 4; column ++ )
				{
					// For initialization: Diagonal matrix elements are equal to one 
					// and all other elements are equal to zero.
					if( row == column )
					{
						translationMatrix[row][column] = 1F;
					}
					else
					{
						translationMatrix[row][column] = 0F;
					}
				}
			}
		
			// Set translation values to the matrix
			translationMatrix[0][3] = dx;
			translationMatrix[1][3] = dy;
			translationMatrix[2][3] = dz;
		
			// Translate main Matrix
			Multiply( translationMatrix, MatrixOrder.Prepend, true );
		
		}
 
		/// <summary>
		/// This method initialize and set default values for mainMatrix ( there is no rotation and translation )
		/// </summary>
		private void Reset()
		{
			// First element is row and second element is column !!!
			_mainMatrix = new float[4][];
			_mainMatrix[0] = new float[4];
			_mainMatrix[1] = new float[4];
			_mainMatrix[2] = new float[4];
			_mainMatrix[3] = new float[4];
 
			// Matrix initialization
			// Row loop
			for( int row = 0; row < 4; row ++ )
			{
				// Column loop
				for( int column = 0; column < 4; column ++ )
				{
					// For initialization: Diagonal matrix elements are equal to one 
					// and all other elements are equal to zero.
					if( row == column )
					{
						_mainMatrix[row][column] = 1F;
					}
					else
					{
						_mainMatrix[row][column] = 0F;
					}
				}
			}
		}
 
 
		/// <summary>
		/// Multiplies this Matrix object by the matrix specified in the 
        /// matrix parameter, and in the order specified in the order parameter.
		/// </summary>
		/// <param name="mulMatrix">The Matrix object by which this Matrix object is to be multiplied.</param>
		/// <param name="order">The MatrixOrder enumeration that represents the order of the multiplication. If the specified order is MatrixOrder.Prepend, this Matrix object is multiplied by the specified matrix in a prepended order. If the specified order is MatrixOrder.Append, this Matrix object is multiplied by the specified matrix in an appended order.</param>
		/// <param name="setMainMatrix">Set main matrix to be result of multiplication</param>
		/// <returns>Matrix multiplication result.</returns>
		private float[][] Multiply( float [][] mulMatrix, MatrixOrder order, bool setMainMatrix )
		{
			// A matrix which is result of matrix multiplication
			// of mulMatrix and mainMatrix
			float [][] resultMatrix = new float[4][];
			resultMatrix[0] = new float[4];
			resultMatrix[1] = new float[4];
			resultMatrix[2] = new float[4];
			resultMatrix[3] = new float[4];
 
			// Row loop
			for( int row = 0; row < 4; row ++ )
			{
				// Column loop
				for( int column = 0; column < 4; column ++ )
				{
					// Initialize element
					resultMatrix[row][column ] = 0F;
					for( int sumIndx = 0; sumIndx < 4; sumIndx ++ )
					{
						// Find matrix element
						if( order == MatrixOrder.Prepend )
						{
							// Order of matrix multiplication
							resultMatrix[row][column ] += _mainMatrix[row][sumIndx ] * mulMatrix[sumIndx][column];
						}
						else
						{
							// Order of matrix multiplication
							resultMatrix[row][column] += mulMatrix[row][sumIndx] * _mainMatrix[sumIndx][column];
						}
					}
				}
			}
 
			// Set result matrix to be main matrix
			if( setMainMatrix )
			{
				_mainMatrix = resultMatrix;
			}
 
			return resultMatrix;
		}
 
 
		/// <summary>
		/// Multiplies this Matrix object by the Vector specified in the 
		/// vector parameter.
		/// </summary>
		/// <param name="mulVector">The vector object by which this Matrix object is to be multiplied.</param>
		/// <param name="resultVector">Vector which is result of matrix and vector multiplication.</param>
		private void MultiplyVector( float [] mulVector, ref float [] resultVector )
		{
			// Row loop
			for( int row = 0; row < 3; row ++ )
			{
				// Initialize element
				resultVector[ row ] = 0F;
 
				// Column loop
				for( int column = 0; column < 4; column ++ )
				{
					// Find matrix element
					resultVector[ row ] += _mainMatrix[row][column] * mulVector[ column ];
				}
			}
		}
 
		/// <summary>
		/// Prepend to this Matrix object a clockwise rotation, around the axis and by the specified angle.
		/// </summary>
		/// <param name="angle">Angle to rotate</param>
		/// <param name="axis">Axis used for rotation</param>
		private void Rotate( double angle, RotationAxis axis )
		{
			float [][] rotationMatrix = new float[4][];
			rotationMatrix[0] = new float[4];
			rotationMatrix[1] = new float[4];
			rotationMatrix[2] = new float[4];
			rotationMatrix[3] = new float[4];
 
			// Change angle direction
			angle = -1F * angle;
 
			// Matrix initialization
			// Row loop
			for( int row = 0; row < 4; row ++ )
			{
				// Column loop
				for( int column = 0; column < 4; column ++ )
				{
					// For initialization: Diagonal matrix elements are equal to one 
					// and all other elements are equal to zero.
					if( row == column )
					{
						rotationMatrix[row][column] = 1F;
					}
					else
					{
						rotationMatrix[row][column] = 0F;
					}
				}
			}
 
			// Rotation about axis
			switch( axis )
			{
					// Rotation about X axis
				case RotationAxis.X:
					rotationMatrix[1][1] = (float)Math.Cos( angle );
					rotationMatrix[1][2] = (float)-Math.Sin( angle );
					rotationMatrix[2][1] = (float)Math.Sin( angle );
					rotationMatrix[2][2] = (float)Math.Cos( angle );
					break;
 
					// Rotation about Y axis
				case RotationAxis.Y:
					rotationMatrix[0][0] = (float)Math.Cos( angle );
					rotationMatrix[0][2] = (float)Math.Sin( angle );
					rotationMatrix[2][0] = (float)-Math.Sin( angle );
					rotationMatrix[2][2] = (float)Math.Cos( angle );
					break;
 
					// Rotation about Z axis
				case RotationAxis.Z:
					rotationMatrix[0][0] = (float)Math.Cos( angle );
					rotationMatrix[0][1] = (float)-Math.Sin( angle );
					rotationMatrix[1][0] = (float)Math.Sin( angle );
					rotationMatrix[1][1] = (float)Math.Cos( angle );
					break;
 
			}
 
			// Rotate Main matrix
			Multiply( rotationMatrix, MatrixOrder.Prepend, true );
		
		}
 
		/// <summary>
		/// Returns transformed x and y values from x, y and z values 
		/// and composed main matrix values (All rotations, 
		/// translations and scaling).
		/// </summary>
		/// <param name="points">Array of 3D points.</param>
		private void GetValues( Point3D [] points )
		{
			// Create one dimensional matrix (vector)
			float [] inputVector = new float[4];
 
			// A vector which is result of matrix and vector multiplication
			float [] resultVector = new float[4];
		
			foreach( Point3D point in points )
			{
				// Fill input vector with x, y and z coordinates
				inputVector[0] = point.X;
				inputVector[1] = point.Y;
				inputVector[2] = point.Z;
				inputVector[3] = 1;
		
				// Apply 3D transformations.
				MultiplyVector( inputVector, ref resultVector );
 
				// Return x and y coordinates.
				point.X = resultVector[0];
				point.Y = resultVector[1];
				point.Z = resultVector[2];
			}
		}
 
 
		/// <summary>
		/// Set points for 3D Bar which represents 3D Chart Area.
		/// </summary>
		/// <param name="dx">Width of the bar 3D.</param>
		/// <param name="dy">Height of the bar 3D.</param>
		/// <param name="dz">Depth of the bar 3D.</param>
		/// <returns>Collection of Points 3D.</returns>
		private Point3D [] Set3DBarPoints( float dx, float dy, float dz )
		{
			Point3D [] points = new Point3D[8];
 
			// ********************************************
			// 3D Bar side: Front
			// ********************************************
			points[0] = new Point3D(-dx/2, -dy/2, dz/2);
			points[1] = new Point3D(dx/2, -dy/2, dz/2);
			points[2] = new Point3D(dx/2, dy/2, dz/2);
			points[3] = new Point3D(-dx/2, dy/2, dz/2);
			
			// ********************************************
			// 3D Bar side: Back
			// ********************************************
			points[4] = new Point3D(-dx/2, -dy/2, -dz/2);
			points[5] = new Point3D(dx/2, -dy/2, -dz/2);
			points[6] = new Point3D(dx/2, dy/2, -dz/2);
			points[7] = new Point3D(-dx/2, dy/2, -dz/2);
 
			return points;
		}
 
		#endregion // Private Methods
		
		#region Lighting Methods
 
		/// <summary>
		/// Initial Lighting. Use matrix transformation only once 
		/// for Normal vectors.
		/// </summary>
		/// <param name="lightStyle">LightStyle Style</param>
		internal void InitLight( LightStyle lightStyle )
		{
			// Set LightStyle Style
			this._lightStyle = lightStyle;
										
			// Center of rotation
			_lightVectors[0] = new Point3D( 0F, 0F, 0F );
 
			// Front side normal Vector.
			_lightVectors[1] = new Point3D( 0F, 0F, 1F );
 
			// Back side normal Vector.
			_lightVectors[2] = new Point3D( 0F, 0F, -1F );
 
			// Left side normal Vector.
			_lightVectors[3] = new Point3D( -1F, 0F, 0F );
 
			// Right side normal Vector.
			_lightVectors[4] = new Point3D( 1F, 0F, 0F );
 
			// Top side normal Vector.
			_lightVectors[5] = new Point3D( 0F, -1F, 0F );
 
			// Bottom side normal Vector.
			_lightVectors[6] = new Point3D( 0F, 1F, 0F );
 
			// Apply matrix transformations
			TransformPoints( _lightVectors, false );
 
			// ********************************************************
			// LightStyle Vector and normal vectors have to have same center. 
			// Shift Normal vectors.
			// ********************************************************
 
			// Front Side shift
			_lightVectors[1].X -= _lightVectors[0].X;
			_lightVectors[1].Y -= _lightVectors[0].Y;
			_lightVectors[1].Z -= _lightVectors[0].Z;
 
			// Back Side shift
			_lightVectors[2].X -= _lightVectors[0].X;
			_lightVectors[2].Y -= _lightVectors[0].Y;
			_lightVectors[2].Z -= _lightVectors[0].Z;
 
			// Left Side shift
			_lightVectors[3].X -= _lightVectors[0].X;
			_lightVectors[3].Y -= _lightVectors[0].Y;
			_lightVectors[3].Z -= _lightVectors[0].Z;
 
			// Right Side shift
			_lightVectors[4].X -= _lightVectors[0].X;
			_lightVectors[4].Y -= _lightVectors[0].Y;
			_lightVectors[4].Z -= _lightVectors[0].Z;
 
			// Top Side shift
			_lightVectors[5].X -= _lightVectors[0].X;
			_lightVectors[5].Y -= _lightVectors[0].Y;
			_lightVectors[5].Z -= _lightVectors[0].Z;
 
			// Bottom Side shift
			_lightVectors[6].X -= _lightVectors[0].X;
			_lightVectors[6].Y -= _lightVectors[0].Y;
			_lightVectors[6].Z -= _lightVectors[0].Z;
 
		}
 
		/// <summary>
		/// Return intensity of lightStyle for 3D Cube. There are tree types of lights: None, 
		/// Simplistic and Realistic. None Style have same lightStyle intensity on 
		/// all polygons. Normal vector doesn’t have influence on this type 
		/// of lighting. Simplistic style have lightStyle source, which is 
		/// rotated together with scene. Realistic lighting have fixed lightStyle 
		/// source and intensity of lightStyle is change when scene is rotated.
		/// </summary>
		/// <param name="surfaceColor">Color used for polygons without lighting</param>
		/// <param name="front">Color corrected with intensity of lightStyle for Front side of the 3D Rectangle</param>
		/// <param name="back">Color corrected with intensity of lightStyle for Back side of the 3D Rectangle</param>
		/// <param name="left">Color corrected with intensity of lightStyle for Left side of the 3D Rectangle</param>
		/// <param name="right">Color corrected with intensity of lightStyle for Right side of the 3D Rectangle</param>
		/// <param name="top">Color corrected with intensity of lightStyle for Top side of the 3D Rectangle</param>
		/// <param name="bottom">Color corrected with intensity of lightStyle for Bottom side of the 3D Rectangle</param>
		internal void GetLight( Color surfaceColor, out Color front, out Color back, out Color left, out Color right, out Color top, out Color bottom )
		{
			switch( _lightStyle )
			{
				// LightStyle style is None
				case  LightStyle.None:
				{
					front = surfaceColor;
					left = surfaceColor;
					top = surfaceColor;
					back = surfaceColor;
					right = surfaceColor;
					bottom = surfaceColor;
					break;
				}
				// LightStyle style is Simplistic
				case  LightStyle.Simplistic:
				{
					front = surfaceColor;
					left = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.25);
					top = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
					back = surfaceColor;
					right = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.25);
					bottom = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
					break;
				}
				// LightStyle style is Realistic
				default:
				{
										
					// For Right Axis angle Realistic lightStyle should be different
					if( _rightAngleAxis )
					{
						// LightStyle source Vector
						Point3D lightSource = new Point3D( 0F, 0F, -1F );
						Point3D [] rightPRpoints = new Point3D[1];
						rightPRpoints[0] = lightSource;
						RightAngleProjection(rightPRpoints);
 
						// ******************************************************************
						// Color correction. Angle between Normal vector of polygon and 
						// vector of lightStyle source is used.
						// ******************************************************************
						if( this._angleY >= 45 || this._angleY <= -45 )
						{
							front = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[1])/Math.PI );
 
							back = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[2])/Math.PI );
							
							left = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0 );
 
							right = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0 );
						}
						else
						{
							front = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0 );
 
							back = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 1 );
 
							left = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[3])/Math.PI );
 
							right = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[4])/Math.PI );
						}
			
						top = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[5])/Math.PI );
 
						bottom = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[6])/Math.PI );
					}
					else
					{
						// LightStyle source Vector
						Point3D lightSource = new Point3D( 0F, 0F, 1F );
 
						// ******************************************************************
						// Color correction. Angle between Normal vector of polygon and 
						// vector of lightStyle source is used.
						// ******************************************************************
						front = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[1])/Math.PI );
 
						back = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[2])/Math.PI );
 
						left = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[3])/Math.PI );
 
						right = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[4])/Math.PI );
			
						top = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[5])/Math.PI );
 
						bottom = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[6])/Math.PI );
					}
 
					break;
				}
			}
		}
		
 
		/// <summary>
		/// Return intensity of lightStyle for Polygons. There are tree types of lights: None, 
		/// Simplistic and Realistic. None Style have same lightStyle intensity on 
		/// all polygons. Normal vector doesn’t have influence on this type 
		/// of lighting. Simplistic style have lightStyle source, which is 
		/// rotated together with scene. Realistic lighting have fixed lightStyle 
		/// source and intensity of lightStyle is change when scene is rotated.
		/// </summary>
		/// <param name="points">Points of the polygon</param>
		/// <param name="surfaceColor">Color used for polygons without lighting</param>
		/// <param name="visiblePolygon">This flag gets information if  polygon is visible or not.</param>
		/// <param name="rotation">Y angle ( from -90 to 90 ) Should be used width switchSeriesOrder to get from -180 to 180</param>
		/// <param name="surfaceName">Used for lighting of front - back and left - right sides</param>
		/// <param name="switchSeriesOrder">Used to calculate real y angle</param>
		/// <returns>Color corrected with intensity of lightStyle</returns>
        internal Color GetPolygonLight(Point3D[] points, Color surfaceColor, bool visiblePolygon, float rotation, SurfaceNames surfaceName, bool switchSeriesOrder)
		{
			// Corrected color
			Color color = surfaceColor;
 
			// Direction of lightStyle source
			Point3D lightSource;
			lightSource = new Point3D( 0F, 0F, 1F );
 
			// There are tree different lightStyle styles: None, Simplistic and realistic.
			switch( _lightStyle )
			{
				// LightStyle style is None
				case  LightStyle.None:
				{
					// Use same color
					break;
				}
				// LightStyle style is Simplistic
				case  LightStyle.Simplistic:
				{
					// Find two vectors of polygon
					Point3D firstVector = new Point3D();
					firstVector.X = points[0].X - points[1].X;
					firstVector.Y = points[0].Y - points[1].Y;
					firstVector.Z = points[0].Z - points[1].Z;
 
					Point3D secondVector = new Point3D();
					secondVector.X = points[2].X - points[1].X;
					secondVector.Y = points[2].Y - points[1].Y;
					secondVector.Z = points[2].Z - points[1].Z;
 
					// Find Normal vector for Polygon
					Point3D normalVector = new Point3D();
					normalVector.X = firstVector.Y * secondVector.Z - firstVector.Z * secondVector.Y;
					normalVector.Y = firstVector.Z * secondVector.X - firstVector.X * secondVector.Z;
					normalVector.Z = firstVector.X * secondVector.Y - firstVector.Y * secondVector.X;
					
					// Polygon is left side ( like side of area chart )
					if( surfaceName == SurfaceNames.Left )
					{
						color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
					}
					// Polygon is right side ( like side of area chart )
					else if( surfaceName == SurfaceNames.Right )
					{
						color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
					}
					// Polygon is front side ( like side of area chart )
					else if( surfaceName == SurfaceNames.Front )
					{
						color = surfaceColor;
					}
					// Polygon is back side ( like side of area chart )
					else if( surfaceName == SurfaceNames.Back )
					{
						color = surfaceColor;
					}
					// Polygon has angle with bottom side ( Line chart or top of area chart )
					else
					{
						float angleLeft;
						float angleRight;
 
						// Find angles between lightStyle and polygon for different y-axis angles.
						if( switchSeriesOrder )
						{
                            if (rotation > 0 && rotation <= 90)
							{
								angleLeft = GetAngle( normalVector, _lightVectors[3] );
								angleRight = GetAngle( normalVector, _lightVectors[4] );
							}
							else
							{
								angleLeft = GetAngle( normalVector, _lightVectors[4] );
								angleRight = GetAngle( normalVector, _lightVectors[3] );
							}
						}
						else
						{
                            if (rotation > 0 && rotation <= 90)
							{
								angleLeft = GetAngle( normalVector, _lightVectors[4] );
								angleRight = GetAngle( normalVector, _lightVectors[3] );
							}
							else
							{
								angleLeft = GetAngle( normalVector, _lightVectors[3] );
								angleRight = GetAngle( normalVector, _lightVectors[4] );
							}
						}
 
						if( Math.Abs( angleLeft - angleRight ) < 0.01 )
						{
							color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.25);
						}
						else if( angleLeft < angleRight )
						{
							color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.25);
						}
						else
						{
							color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
						}
					}
 
					break;
				}
				// LightStyle style is Realistic
				default:
				{
 
					// Find two vectors of polygon
					Point3D firstVector = new Point3D();
					firstVector.X = points[0].X - points[1].X;
					firstVector.Y = points[0].Y - points[1].Y;
					firstVector.Z = points[0].Z - points[1].Z;
 
					Point3D secondVector = new Point3D();
					secondVector.X = points[2].X - points[1].X;
					secondVector.Y = points[2].Y - points[1].Y;
					secondVector.Z = points[2].Z - points[1].Z;
 
					// Find Normal vector for Polygon
					Point3D normalVector = new Point3D();
					normalVector.X = firstVector.Y * secondVector.Z - firstVector.Z * secondVector.Y;
					normalVector.Y = firstVector.Z * secondVector.X - firstVector.X * secondVector.Z;
					normalVector.Z = firstVector.X * secondVector.Y - firstVector.Y * secondVector.X;
 
					// ******************************************************************
					// Color correction. Angle between Normal vector of polygon and 
					// vector of lightStyle source is used.
					// ******************************************************************
					if( surfaceName == SurfaceNames.Front )
					{
						lightSource.Z *= -1;
						color = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[2])/Math.PI );
					}
					else if( surfaceName == SurfaceNames.Back )
					{
						lightSource.Z *= -1;
						color = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[1])/Math.PI );
					}
					else
					{
						if( visiblePolygon )
						{
							lightSource.Z *= -1;
						}
 
						color = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,normalVector)/Math.PI );
					}
 
					break;
				}
			}
			return color;
			
		}
 
		/// <summary>
		/// This method creates gradien color with brightnes.
		/// </summary>
		/// <param name="beginColor">Start color for gradient.</param>
		/// <param name="position">Position used between Start and end color.</param>
		/// <returns>Calculated Gradient color from gradient position</returns>
		private Color GetBrightGradientColor( Color beginColor, double position )
		{
			position = position * 2;
			double brightness = 0.5;
			if( position < brightness )
			{
				return ChartGraphics.GetGradientColor( Color.FromArgb(beginColor.A,255,255,255), beginColor, 1 - brightness + position );
			}
			else if( -brightness + position < 1 )
			{
				return ChartGraphics.GetGradientColor( beginColor, Color.Black, -brightness + position );
			}
			else
			{
				return Color.FromArgb( beginColor.A, 0, 0, 0 );
			}
		}
 
		/// <summary>
		/// Returns the angle between two 3D vectors (a and b); 
		/// </summary>
		/// <param name="a">First vector</param>
		/// <param name="b">Second Vector</param>
		/// <returns>Angle between vectors</returns>
		private float GetAngle(Point3D a,Point3D b)
		{
			double angle;
 
			angle = Math.Acos( ( a.X * b.X + a.Y * b.Y + a.Z * b.Z ) / ( Math.Sqrt( a.X * a.X + a.Y * a.Y + a.Z * a.Z ) * Math.Sqrt( b.X * b.X + b.Y * b.Y + b.Z * b.Z ) ) );
 
			return (float)angle;
		}
 
		#endregion
	}
}