1 //-------------------------------------------------------------
2 // <copyright company=
\92Microsoft Corporation
\92>
3 // Copyright © Microsoft Corporation. All Rights Reserved.
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
10 // Namespace: DataVisualization.Charting
14 // Purpose: Matrix3D class is used during the 3D drawings to
15 // transform plotting area 3D coordinates into the 2D
16 // projection coordinates based on rotation and
17 // perspective settings.
19 // Reviewed: AG - Dec 4, 2002
20 // AG - Microsoft 14, 2007
22 //===================================================================
24 #region Used namespaces
28 using System.Drawing.Drawing2D;
29 using System.Drawing.Text;
30 using System.Drawing.Imaging;
31 using System.ComponentModel;
32 using System.Collections;
35 using System.Windows.Forms.DataVisualization.Charting;
36 using System.Windows.Forms.DataVisualization.Charting.Data;
37 using System.Windows.Forms.DataVisualization.Charting.ChartTypes;
38 using System.Windows.Forms.DataVisualization.Charting.Utilities;
39 using System.Windows.Forms.DataVisualization.Charting.Borders3D;
42 //using System.Web.UI.DataVisualization.Charting.Utilities;
43 //using System.Web.UI.DataVisualization.Charting.Borders3D;
49 namespace System.Windows.Forms.DataVisualization.Charting
51 namespace System.Web.UI.DataVisualization.Charting
56 /// This class is responsible for all 3D coordinates transformations: Translation,
57 /// Rotation, Scale, Perspective and RightAngle Projection. Translation
58 /// and rotation are stored in composite matrix (mainMatrix), and scaling,
59 /// projection and non-composite translation are stored in private fields.
60 /// Matrix is initialized with Chart Area 3D cube, which is invisible boundary
61 /// cube of 3D Chart area. The matrix has to be initialized every time
62 /// when angles, position or perspective parameters are changed. Method
63 /// TransformPoints will apply 3D Transformation on points using
64 /// Initialization values: Main matrix and other initialization values.
66 internal class Matrix3D
71 /// 3D Axis used for rotation
73 private enum RotationAxis
76 /// Rotation around X axis.
81 /// Rotation around Y axis.
86 /// Rotation around Z axis.
91 #endregion // Enumerations
98 private float [][] _mainMatrix;
101 /// Default translation for chart area cube ( without composition ).
103 private float _translateX;
106 /// Default translation for chart area cube ( without composition ).
108 private float _translateY;
111 /// Default translation for chart area cube ( without composition ).
113 private float _translateZ;
116 /// The value, which is used to rescale chart area.
118 private float _scale;
121 /// The value used for Isometric Shift.
123 private float _shiftX;
126 /// The value used for Isometric Shift.
128 private float _shiftY;
131 /// Perspective value.
133 internal float _perspective;
136 /// Isometric projection.
138 private bool _rightAngleAxis;
141 /// The value, which is used for perspective.
143 private float _perspectiveFactor = float.NaN;
146 /// The value, which is used to set projection plane.
148 private float _perspectiveZ;
153 private float _angleX;
158 private float _angleY;
161 /// Private fields used for lighting
163 Point3D [] _lightVectors = new Point3D[7];
168 LightStyle _lightStyle;
175 /// Gets the X Angle.
177 internal float AngleX
179 get { return _angleX; }
183 /// Gets the Y Angle.
185 internal float AngleY
187 get { return _angleY; }
191 /// Get perspective value.
193 internal float Perspective
195 get { return _perspective; }
198 #endregion // Properties
200 #region Internal and Public Methods
203 /// Constructor for Matrix 3D
210 /// Checks if 3D matrix was initialized.
212 /// <returns>True if matrix was initialized.</returns>
213 public bool IsInitialized()
215 return (this._mainMatrix != null);
219 /// Initialize Matrix 3D. This method calculates how much a chart area
220 /// cube has to be resized to fit Inner Plotting Area rectangle. Order
221 /// of operation is following: Translation for X and Y axes, Rotation
222 /// by X-axis, Rotation by Y-axis and same scaling for all axes. All
223 /// other elements, which belongs to this chart area cube (Data points,
224 /// grid lines etc.) has to follow same order. Translation and rotation
225 /// form composite matrix mainMatrix. Scale has to be allied separately.
227 /// <param name="innerPlotRectangle">Inner Plotting Area position. Chart area cube has to be inside this rectangle</param>
228 /// <param name="depth">Depth of chart area cube</param>
229 /// <param name="angleX">Angle of rotation by X axis.</param>
230 /// <param name="angleY">Angle of rotation by Y axis.</param>
231 /// <param name="perspective">Perspective in percentages</param>
232 /// <param name="rightAngleAxis">Right angle flag.</param>
233 internal void Initialize(
234 RectangleF innerPlotRectangle,
239 bool rightAngleAxis )
241 // Initialization for mainMatrix
244 // Remember non-composite translation
245 _translateX = innerPlotRectangle.X+innerPlotRectangle.Width/2;
246 _translateY = innerPlotRectangle.Y+innerPlotRectangle.Height/2;
247 _translateZ = depth / 2F;
248 float width = innerPlotRectangle.Width;
249 float height = innerPlotRectangle.Height;
250 this._perspective = perspective;
251 this._rightAngleAxis = rightAngleAxis;
254 this._angleX = angleX;
255 this._angleY = angleY;
257 // Change Degrees to radians.
258 angleX = angleX / 180F * (float)Math.PI;
259 angleY = angleY / 180F * (float)Math.PI;
261 // Set points for 3D Bar which represents 3D Chart Area Cube.
262 Point3D [] points = Set3DBarPoints( width, height, depth );
264 // Translate Chart Area Cube WITH CENTER OF ROTATION - COMPOSITE TRANSLATION.
265 Translate( _translateX, _translateY, 0 );
267 // Non Isometric projection
268 if( !rightAngleAxis )
270 // Rotate Chart Area Cube by X axis.
271 Rotate( angleX, RotationAxis.X );
273 // Rotate Chart Area Cube by Y axis.
274 Rotate( angleY, RotationAxis.Y );
278 if( this._angleY >= 45 )
280 // Rotate Chart Area Cube by Y axis.
281 Rotate( Math.PI / 2, RotationAxis.Y );
283 else if( this._angleY <= -45 )
285 // Rotate Chart Area Cube by Y axis.
286 Rotate( -Math.PI / 2, RotationAxis.Y );
290 // Apply composed transformation ( Translation and rotation ).
293 float maxZ = float.MinValue;
295 if( perspective != 0F || rightAngleAxis )
297 // Find projection plane
298 foreach( Point3D point in points )
304 // Set Projection plane
305 _perspectiveZ = maxZ;
308 if( perspective != 0F )
310 _perspectiveFactor = perspective / 2000F;
313 ApplyPerspective( points );
316 // Isometric projection is active
319 RightAngleProjection( points );
327 foreach( Point3D point in points )
329 if( point.X - _translateX < 0F && Math.Abs( point.X - _translateX ) > minX )
330 minX = Math.Abs( point.X - _translateX );
332 if( point.X - _translateX >=0F && Math.Abs( point.X - _translateX ) > maxX )
333 maxX = Math.Abs( point.X - _translateX );
335 if( point.Y - _translateY < 0F && Math.Abs( point.Y - _translateY ) > minY )
336 minY = Math.Abs( point.Y - _translateY );
338 if( point.Y - _translateY >=0F && Math.Abs( point.Y - _translateY ) > maxY )
339 maxY = Math.Abs( point.Y - _translateY );
342 _shiftX = (maxX - minX)/2F;
343 _shiftY = (maxY - minY)/2F;
344 RightAngleShift( points );
347 // This code searches for value, which will be used for scaling.
348 float maxXScale = float.MinValue;
349 float maxYScale = float.MinValue;
351 foreach( Point3D point in points )
353 // Find maximum relative distance for X axis.
354 // Relative distance is (distance from the center of plotting area
355 // position) / (distance from the edge of rectangle to
356 // the center of the rectangle).
357 if( maxXScale < Math.Abs(point.X - _translateX) / width * 2 )
358 maxXScale = Math.Abs(point.X - _translateX) / width * 2;
360 // Find maximum relative distance for Y axis.
361 if( maxYScale < Math.Abs(point.Y - _translateY) / height * 2 )
362 maxYScale = Math.Abs(point.Y - _translateY) / height * 2;
365 // Remember scale factor
366 _scale = (maxYScale > maxXScale ) ? maxYScale : maxXScale;
374 /// Apply transformations on array od 3D Points. Order of operation is
375 /// following: Translation ( Set coordinate system for 0:100 to -50:50
376 /// Center of rotation is always 0), Composite Translation for X and Y
377 /// axes ( Moving center of rotation ), Rotation by X-axis, Rotation
378 /// by Y-axis, perspective and same scaling for all axes.
380 /// <param name="points">3D Points array.</param>
381 public void TransformPoints( Point3D[] points )
383 TransformPoints( points, true );
387 /// This Method returns scale factor
389 /// <returns></returns>
390 internal float GetScale()
395 #endregion // Internal and Public Methods
397 #region Private Methods
400 /// Apply transformations on array od 3D Points. Order of operation is
401 /// following: Translation ( Set coordinate system for 0:100 to -50:50
402 /// Center of rotation is always 0), Composite Translation for X and Y
403 /// axes ( Moving center of rotation ), Rotation by X-axis, Rotation
404 /// by Y-axis, perspective and same scaling for all axes.
406 /// <param name="points">3D Points array.</param>
407 /// <param name="withPerspective">Applay Perspective</param>
408 private void TransformPoints( Point3D[] points, bool withPerspective )
410 // Matrix is not initialized.
411 if( _mainMatrix == null )
413 throw new InvalidOperationException(SR.ExceptionMatrix3DNotinitialized);
416 // Translate point. CENTER OF ROTATION is 0 and that center is in
417 // the middle of chart area 3D CUBE. Translate method cannot
418 // be used because composite translation WILL MOVE
419 // CENTER OF ROTATION.
420 foreach( Point3D point in points )
422 point.X -= _translateX;
423 point.Y -= _translateY;
424 point.Z -= _translateZ;
427 // Transform points using composite mainMatrix. (Translation of points together with
428 // Center of rotation and rotations by X and Y axes).
432 if( _perspective != 0F && withPerspective )
434 ApplyPerspective( points );
437 // RightAngle Projection
438 if( _rightAngleAxis )
440 RightAngleProjection( points );
441 RightAngleShift( points );
444 // Scales data points. Scaling has to be performed SEPARATELY from
445 // composite matrix. If scale is used with composite matrix after
446 // rotation, scaling will deform object.
451 /// This method adjusts a position of 3D Chart Area cube. This
452 /// method will translate chart for better use of the inner
453 /// plotting area. Center of rotation is shifted for
454 /// right Angle projection.
456 /// <param name="points">3D Points array.</param>
457 private void RightAngleShift( Point3D [] points )
459 foreach( Point3D point in points )
461 point.X = point.X - _shiftX;
462 point.Y = point.Y - _shiftY;
467 /// Method used to calculate right Angle projection.
469 /// <param name="points">3D points array.</param>
470 private void RightAngleProjection( Point3D [] points )
472 float coorectionAngle = 45F;
474 float xFactor = this._angleX / 45;
478 if( this._angleY >= 45 )
480 yFactor = (this._angleY - 90) / coorectionAngle;
482 else if ( this._angleY <= -45 )
484 yFactor = ( this._angleY + 90 ) / coorectionAngle;
488 yFactor = this._angleY / coorectionAngle;
491 // Projection formula
492 // perspectiveZ - Position of perspective plain.
493 // Perspective Factor - Intensity of projection.
494 foreach( Point3D point in points )
496 point.X = point.X + ( _perspectiveZ - point.Z ) * yFactor;
497 point.Y = point.Y - ( _perspectiveZ - point.Z ) * xFactor;
502 /// Method is used for Planar Geometric projection.
504 /// <param name="points">3D Points array.</param>
505 private void ApplyPerspective( Point3D [] points )
507 // Projection formula
508 // perspectiveZ - Position of perspective plain.
509 // perspectiveFactor - Intensity of projection.
510 foreach( Point3D point in points )
512 point.X = _translateX + (point.X - _translateX) / ( 1 + (_perspectiveZ - point.Z) * _perspectiveFactor);
513 point.Y = _translateY + (point.Y - _translateY) / ( 1 + (_perspectiveZ - point.Z) * _perspectiveFactor);
518 /// Scales data points. Scaling has to be performed SEPARATELY from
519 /// composite matrix. If scale is used with composite matrix after
520 /// rotation, scaling will deform object.
522 /// <param name="points">3D Points array.</param>
523 private void Scale( Point3D [] points )
525 foreach( Point3D point in points )
527 point.X = _translateX + (point.X - _translateX) / _scale;
528 point.Y = _translateY + (point.Y - _translateY) / _scale;
533 /// Prepend to this Matrix object a translation. This method is used
534 /// only if CENTER OF ROTATION HAS TO BE MOVED.
536 /// <param name="dx">Translate in x axis direction.</param>
537 /// <param name="dy">Translate in y axis direction.</param>
538 /// <param name="dz">Translate in z axis direction.</param>
539 private void Translate( float dx, float dy, float dz )
541 float [][] translationMatrix = new float[4][];
542 translationMatrix[0] = new float[4];
543 translationMatrix[1] = new float[4];
544 translationMatrix[2] = new float[4];
545 translationMatrix[3] = new float[4];
547 // Matrix initialization
549 for( int row = 0; row < 4; row ++ )
552 for( int column = 0; column < 4; column ++ )
554 // For initialization: Diagonal matrix elements are equal to one
555 // and all other elements are equal to zero.
558 translationMatrix[row][column] = 1F;
562 translationMatrix[row][column] = 0F;
567 // Set translation values to the matrix
568 translationMatrix[0][3] = dx;
569 translationMatrix[1][3] = dy;
570 translationMatrix[2][3] = dz;
572 // Translate main Matrix
573 Multiply( translationMatrix, MatrixOrder.Prepend, true );
578 /// This method initialize and set default values for mainMatrix ( there is no rotation and translation )
582 // First element is row and second element is column !!!
583 _mainMatrix = new float[4][];
584 _mainMatrix[0] = new float[4];
585 _mainMatrix[1] = new float[4];
586 _mainMatrix[2] = new float[4];
587 _mainMatrix[3] = new float[4];
589 // Matrix initialization
591 for( int row = 0; row < 4; row ++ )
594 for( int column = 0; column < 4; column ++ )
596 // For initialization: Diagonal matrix elements are equal to one
597 // and all other elements are equal to zero.
600 _mainMatrix[row][column] = 1F;
604 _mainMatrix[row][column] = 0F;
612 /// Multiplies this Matrix object by the matrix specified in the
613 /// matrix parameter, and in the order specified in the order parameter.
615 /// <param name="mulMatrix">The Matrix object by which this Matrix object is to be multiplied.</param>
616 /// <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>
617 /// <param name="setMainMatrix">Set main matrix to be result of multiplication</param>
618 /// <returns>Matrix multiplication result.</returns>
619 private float[][] Multiply( float [][] mulMatrix, MatrixOrder order, bool setMainMatrix )
621 // A matrix which is result of matrix multiplication
622 // of mulMatrix and mainMatrix
623 float [][] resultMatrix = new float[4][];
624 resultMatrix[0] = new float[4];
625 resultMatrix[1] = new float[4];
626 resultMatrix[2] = new float[4];
627 resultMatrix[3] = new float[4];
630 for( int row = 0; row < 4; row ++ )
633 for( int column = 0; column < 4; column ++ )
635 // Initialize element
636 resultMatrix[row][column ] = 0F;
637 for( int sumIndx = 0; sumIndx < 4; sumIndx ++ )
639 // Find matrix element
640 if( order == MatrixOrder.Prepend )
642 // Order of matrix multiplication
643 resultMatrix[row][column ] += _mainMatrix[row][sumIndx ] * mulMatrix[sumIndx][column];
647 // Order of matrix multiplication
648 resultMatrix[row][column] += mulMatrix[row][sumIndx] * _mainMatrix[sumIndx][column];
654 // Set result matrix to be main matrix
657 _mainMatrix = resultMatrix;
665 /// Multiplies this Matrix object by the Vector specified in the
666 /// vector parameter.
668 /// <param name="mulVector">The vector object by which this Matrix object is to be multiplied.</param>
669 /// <param name="resultVector">Vector which is result of matrix and vector multiplication.</param>
670 private void MultiplyVector( float [] mulVector, ref float [] resultVector )
673 for( int row = 0; row < 3; row ++ )
675 // Initialize element
676 resultVector[ row ] = 0F;
679 for( int column = 0; column < 4; column ++ )
681 // Find matrix element
682 resultVector[ row ] += _mainMatrix[row][column] * mulVector[ column ];
688 /// Prepend to this Matrix object a clockwise rotation, around the axis and by the specified angle.
690 /// <param name="angle">Angle to rotate</param>
691 /// <param name="axis">Axis used for rotation</param>
692 private void Rotate( double angle, RotationAxis axis )
694 float [][] rotationMatrix = new float[4][];
695 rotationMatrix[0] = new float[4];
696 rotationMatrix[1] = new float[4];
697 rotationMatrix[2] = new float[4];
698 rotationMatrix[3] = new float[4];
700 // Change angle direction
703 // Matrix initialization
705 for( int row = 0; row < 4; row ++ )
708 for( int column = 0; column < 4; column ++ )
710 // For initialization: Diagonal matrix elements are equal to one
711 // and all other elements are equal to zero.
714 rotationMatrix[row][column] = 1F;
718 rotationMatrix[row][column] = 0F;
723 // Rotation about axis
726 // Rotation about X axis
728 rotationMatrix[1][1] = (float)Math.Cos( angle );
729 rotationMatrix[1][2] = (float)-Math.Sin( angle );
730 rotationMatrix[2][1] = (float)Math.Sin( angle );
731 rotationMatrix[2][2] = (float)Math.Cos( angle );
734 // Rotation about Y axis
736 rotationMatrix[0][0] = (float)Math.Cos( angle );
737 rotationMatrix[0][2] = (float)Math.Sin( angle );
738 rotationMatrix[2][0] = (float)-Math.Sin( angle );
739 rotationMatrix[2][2] = (float)Math.Cos( angle );
742 // Rotation about Z axis
744 rotationMatrix[0][0] = (float)Math.Cos( angle );
745 rotationMatrix[0][1] = (float)-Math.Sin( angle );
746 rotationMatrix[1][0] = (float)Math.Sin( angle );
747 rotationMatrix[1][1] = (float)Math.Cos( angle );
752 // Rotate Main matrix
753 Multiply( rotationMatrix, MatrixOrder.Prepend, true );
758 /// Returns transformed x and y values from x, y and z values
759 /// and composed main matrix values (All rotations,
760 /// translations and scaling).
762 /// <param name="points">Array of 3D points.</param>
763 private void GetValues( Point3D [] points )
765 // Create one dimensional matrix (vector)
766 float [] inputVector = new float[4];
768 // A vector which is result of matrix and vector multiplication
769 float [] resultVector = new float[4];
771 foreach( Point3D point in points )
773 // Fill input vector with x, y and z coordinates
774 inputVector[0] = point.X;
775 inputVector[1] = point.Y;
776 inputVector[2] = point.Z;
779 // Apply 3D transformations.
780 MultiplyVector( inputVector, ref resultVector );
782 // Return x and y coordinates.
783 point.X = resultVector[0];
784 point.Y = resultVector[1];
785 point.Z = resultVector[2];
791 /// Set points for 3D Bar which represents 3D Chart Area.
793 /// <param name="dx">Width of the bar 3D.</param>
794 /// <param name="dy">Height of the bar 3D.</param>
795 /// <param name="dz">Depth of the bar 3D.</param>
796 /// <returns>Collection of Points 3D.</returns>
797 private Point3D [] Set3DBarPoints( float dx, float dy, float dz )
799 Point3D [] points = new Point3D[8];
801 // ********************************************
802 // 3D Bar side: Front
803 // ********************************************
804 points[0] = new Point3D(-dx/2, -dy/2, dz/2);
805 points[1] = new Point3D(dx/2, -dy/2, dz/2);
806 points[2] = new Point3D(dx/2, dy/2, dz/2);
807 points[3] = new Point3D(-dx/2, dy/2, dz/2);
809 // ********************************************
811 // ********************************************
812 points[4] = new Point3D(-dx/2, -dy/2, -dz/2);
813 points[5] = new Point3D(dx/2, -dy/2, -dz/2);
814 points[6] = new Point3D(dx/2, dy/2, -dz/2);
815 points[7] = new Point3D(-dx/2, dy/2, -dz/2);
820 #endregion // Private Methods
822 #region Lighting Methods
825 /// Initial Lighting. Use matrix transformation only once
826 /// for Normal vectors.
828 /// <param name="lightStyle">LightStyle Style</param>
829 internal void InitLight( LightStyle lightStyle )
831 // Set LightStyle Style
832 this._lightStyle = lightStyle;
834 // Center of rotation
835 _lightVectors[0] = new Point3D( 0F, 0F, 0F );
837 // Front side normal Vector.
838 _lightVectors[1] = new Point3D( 0F, 0F, 1F );
840 // Back side normal Vector.
841 _lightVectors[2] = new Point3D( 0F, 0F, -1F );
843 // Left side normal Vector.
844 _lightVectors[3] = new Point3D( -1F, 0F, 0F );
846 // Right side normal Vector.
847 _lightVectors[4] = new Point3D( 1F, 0F, 0F );
849 // Top side normal Vector.
850 _lightVectors[5] = new Point3D( 0F, -1F, 0F );
852 // Bottom side normal Vector.
853 _lightVectors[6] = new Point3D( 0F, 1F, 0F );
855 // Apply matrix transformations
856 TransformPoints( _lightVectors, false );
858 // ********************************************************
859 // LightStyle Vector and normal vectors have to have same center.
860 // Shift Normal vectors.
861 // ********************************************************
864 _lightVectors[1].X -= _lightVectors[0].X;
865 _lightVectors[1].Y -= _lightVectors[0].Y;
866 _lightVectors[1].Z -= _lightVectors[0].Z;
869 _lightVectors[2].X -= _lightVectors[0].X;
870 _lightVectors[2].Y -= _lightVectors[0].Y;
871 _lightVectors[2].Z -= _lightVectors[0].Z;
874 _lightVectors[3].X -= _lightVectors[0].X;
875 _lightVectors[3].Y -= _lightVectors[0].Y;
876 _lightVectors[3].Z -= _lightVectors[0].Z;
879 _lightVectors[4].X -= _lightVectors[0].X;
880 _lightVectors[4].Y -= _lightVectors[0].Y;
881 _lightVectors[4].Z -= _lightVectors[0].Z;
884 _lightVectors[5].X -= _lightVectors[0].X;
885 _lightVectors[5].Y -= _lightVectors[0].Y;
886 _lightVectors[5].Z -= _lightVectors[0].Z;
889 _lightVectors[6].X -= _lightVectors[0].X;
890 _lightVectors[6].Y -= _lightVectors[0].Y;
891 _lightVectors[6].Z -= _lightVectors[0].Z;
896 /// Return intensity of lightStyle for 3D Cube. There are tree types of lights: None,
897 /// Simplistic and Realistic. None Style have same lightStyle intensity on
898 /// all polygons. Normal vector doesn
\92t have influence on this type
899 /// of lighting. Simplistic style have lightStyle source, which is
900 /// rotated together with scene. Realistic lighting have fixed lightStyle
901 /// source and intensity of lightStyle is change when scene is rotated.
903 /// <param name="surfaceColor">Color used for polygons without lighting</param>
904 /// <param name="front">Color corrected with intensity of lightStyle for Front side of the 3D Rectangle</param>
905 /// <param name="back">Color corrected with intensity of lightStyle for Back side of the 3D Rectangle</param>
906 /// <param name="left">Color corrected with intensity of lightStyle for Left side of the 3D Rectangle</param>
907 /// <param name="right">Color corrected with intensity of lightStyle for Right side of the 3D Rectangle</param>
908 /// <param name="top">Color corrected with intensity of lightStyle for Top side of the 3D Rectangle</param>
909 /// <param name="bottom">Color corrected with intensity of lightStyle for Bottom side of the 3D Rectangle</param>
910 internal void GetLight( Color surfaceColor, out Color front, out Color back, out Color left, out Color right, out Color top, out Color bottom )
912 switch( _lightStyle )
914 // LightStyle style is None
915 case LightStyle.None:
917 front = surfaceColor;
921 right = surfaceColor;
922 bottom = surfaceColor;
925 // LightStyle style is Simplistic
926 case LightStyle.Simplistic:
928 front = surfaceColor;
929 left = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.25);
930 top = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
932 right = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.25);
933 bottom = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
936 // LightStyle style is Realistic
940 // For Right Axis angle Realistic lightStyle should be different
941 if( _rightAngleAxis )
943 // LightStyle source Vector
944 Point3D lightSource = new Point3D( 0F, 0F, -1F );
945 Point3D [] rightPRpoints = new Point3D[1];
946 rightPRpoints[0] = lightSource;
947 RightAngleProjection(rightPRpoints);
949 // ******************************************************************
950 // Color correction. Angle between Normal vector of polygon and
951 // vector of lightStyle source is used.
952 // ******************************************************************
953 if( this._angleY >= 45 || this._angleY <= -45 )
955 front = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[1])/Math.PI );
957 back = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[2])/Math.PI );
959 left = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0 );
961 right = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0 );
965 front = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0 );
967 back = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 1 );
969 left = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[3])/Math.PI );
971 right = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[4])/Math.PI );
974 top = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[5])/Math.PI );
976 bottom = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[6])/Math.PI );
980 // LightStyle source Vector
981 Point3D lightSource = new Point3D( 0F, 0F, 1F );
983 // ******************************************************************
984 // Color correction. Angle between Normal vector of polygon and
985 // vector of lightStyle source is used.
986 // ******************************************************************
987 front = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[1])/Math.PI );
989 back = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[2])/Math.PI );
991 left = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[3])/Math.PI );
993 right = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[4])/Math.PI );
995 top = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[5])/Math.PI );
997 bottom = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[6])/Math.PI );
1007 /// Return intensity of lightStyle for Polygons. There are tree types of lights: None,
1008 /// Simplistic and Realistic. None Style have same lightStyle intensity on
1009 /// all polygons. Normal vector doesn
\92t have influence on this type
1010 /// of lighting. Simplistic style have lightStyle source, which is
1011 /// rotated together with scene. Realistic lighting have fixed lightStyle
1012 /// source and intensity of lightStyle is change when scene is rotated.
1014 /// <param name="points">Points of the polygon</param>
1015 /// <param name="surfaceColor">Color used for polygons without lighting</param>
1016 /// <param name="visiblePolygon">This flag gets information if polygon is visible or not.</param>
1017 /// <param name="rotation">Y angle ( from -90 to 90 ) Should be used width switchSeriesOrder to get from -180 to 180</param>
1018 /// <param name="surfaceName">Used for lighting of front - back and left - right sides</param>
1019 /// <param name="switchSeriesOrder">Used to calculate real y angle</param>
1020 /// <returns>Color corrected with intensity of lightStyle</returns>
1021 internal Color GetPolygonLight(Point3D[] points, Color surfaceColor, bool visiblePolygon, float rotation, SurfaceNames surfaceName, bool switchSeriesOrder)
1024 Color color = surfaceColor;
1026 // Direction of lightStyle source
1027 Point3D lightSource;
1028 lightSource = new Point3D( 0F, 0F, 1F );
1030 // There are tree different lightStyle styles: None, Simplistic and realistic.
1031 switch( _lightStyle )
1033 // LightStyle style is None
1034 case LightStyle.None:
1039 // LightStyle style is Simplistic
1040 case LightStyle.Simplistic:
1042 // Find two vectors of polygon
1043 Point3D firstVector = new Point3D();
1044 firstVector.X = points[0].X - points[1].X;
1045 firstVector.Y = points[0].Y - points[1].Y;
1046 firstVector.Z = points[0].Z - points[1].Z;
1048 Point3D secondVector = new Point3D();
1049 secondVector.X = points[2].X - points[1].X;
1050 secondVector.Y = points[2].Y - points[1].Y;
1051 secondVector.Z = points[2].Z - points[1].Z;
1053 // Find Normal vector for Polygon
1054 Point3D normalVector = new Point3D();
1055 normalVector.X = firstVector.Y * secondVector.Z - firstVector.Z * secondVector.Y;
1056 normalVector.Y = firstVector.Z * secondVector.X - firstVector.X * secondVector.Z;
1057 normalVector.Z = firstVector.X * secondVector.Y - firstVector.Y * secondVector.X;
1059 // Polygon is left side ( like side of area chart )
1060 if( surfaceName == SurfaceNames.Left )
1062 color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
1064 // Polygon is right side ( like side of area chart )
1065 else if( surfaceName == SurfaceNames.Right )
1067 color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
1069 // Polygon is front side ( like side of area chart )
1070 else if( surfaceName == SurfaceNames.Front )
1072 color = surfaceColor;
1074 // Polygon is back side ( like side of area chart )
1075 else if( surfaceName == SurfaceNames.Back )
1077 color = surfaceColor;
1079 // Polygon has angle with bottom side ( Line chart or top of area chart )
1085 // Find angles between lightStyle and polygon for different y-axis angles.
1086 if( switchSeriesOrder )
1088 if (rotation > 0 && rotation <= 90)
1090 angleLeft = GetAngle( normalVector, _lightVectors[3] );
1091 angleRight = GetAngle( normalVector, _lightVectors[4] );
1095 angleLeft = GetAngle( normalVector, _lightVectors[4] );
1096 angleRight = GetAngle( normalVector, _lightVectors[3] );
1101 if (rotation > 0 && rotation <= 90)
1103 angleLeft = GetAngle( normalVector, _lightVectors[4] );
1104 angleRight = GetAngle( normalVector, _lightVectors[3] );
1108 angleLeft = GetAngle( normalVector, _lightVectors[3] );
1109 angleRight = GetAngle( normalVector, _lightVectors[4] );
1113 if( Math.Abs( angleLeft - angleRight ) < 0.01 )
1115 color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.25);
1117 else if( angleLeft < angleRight )
1119 color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.25);
1123 color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
1129 // LightStyle style is Realistic
1133 // Find two vectors of polygon
1134 Point3D firstVector = new Point3D();
1135 firstVector.X = points[0].X - points[1].X;
1136 firstVector.Y = points[0].Y - points[1].Y;
1137 firstVector.Z = points[0].Z - points[1].Z;
1139 Point3D secondVector = new Point3D();
1140 secondVector.X = points[2].X - points[1].X;
1141 secondVector.Y = points[2].Y - points[1].Y;
1142 secondVector.Z = points[2].Z - points[1].Z;
1144 // Find Normal vector for Polygon
1145 Point3D normalVector = new Point3D();
1146 normalVector.X = firstVector.Y * secondVector.Z - firstVector.Z * secondVector.Y;
1147 normalVector.Y = firstVector.Z * secondVector.X - firstVector.X * secondVector.Z;
1148 normalVector.Z = firstVector.X * secondVector.Y - firstVector.Y * secondVector.X;
1150 // ******************************************************************
1151 // Color correction. Angle between Normal vector of polygon and
1152 // vector of lightStyle source is used.
1153 // ******************************************************************
1154 if( surfaceName == SurfaceNames.Front )
1156 lightSource.Z *= -1;
1157 color = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[2])/Math.PI );
1159 else if( surfaceName == SurfaceNames.Back )
1161 lightSource.Z *= -1;
1162 color = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[1])/Math.PI );
1166 if( visiblePolygon )
1168 lightSource.Z *= -1;
1171 color = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,normalVector)/Math.PI );
1182 /// This method creates gradien color with brightnes.
1184 /// <param name="beginColor">Start color for gradient.</param>
1185 /// <param name="position">Position used between Start and end color.</param>
1186 /// <returns>Calculated Gradient color from gradient position</returns>
1187 private Color GetBrightGradientColor( Color beginColor, double position )
1189 position = position * 2;
1190 double brightness = 0.5;
1191 if( position < brightness )
1193 return ChartGraphics.GetGradientColor( Color.FromArgb(beginColor.A,255,255,255), beginColor, 1 - brightness + position );
1195 else if( -brightness + position < 1 )
1197 return ChartGraphics.GetGradientColor( beginColor, Color.Black, -brightness + position );
1201 return Color.FromArgb( beginColor.A, 0, 0, 0 );
1206 /// Returns the angle between two 3D vectors (a and b);
1208 /// <param name="a">First vector</param>
1209 /// <param name="b">Second Vector</param>
1210 /// <returns>Angle between vectors</returns>
1211 private float GetAngle(Point3D a,Point3D b)
1215 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 ) ) );
1217 return (float)angle;