Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Web.DataVisualization / Common / General / Matrix3D.cs
1 //-------------------------------------------------------------
2 // <copyright company=\92Microsoft Corporation\92>
3 //   Copyright © Microsoft Corporation. All Rights Reserved.
4 // </copyright>
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
8 //  File:               Matrix3D.cs
9 //
10 //  Namespace:  DataVisualization.Charting
11 //
12 //      Classes:        Matrix3D
13 //
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.
18 //
19 //      Reviewed:       AG - Dec 4, 2002
20 //              AG - Microsoft 14, 2007
21 //
22 //===================================================================
23
24 #region Used namespaces
25
26 using System;
27 using System.Drawing;
28 using System.Drawing.Drawing2D;
29 using System.Drawing.Text;
30 using System.Drawing.Imaging;
31 using System.ComponentModel;
32 using System.Collections;
33
34 #if Microsoft_CONTROL
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;
40
41 #else
42         //using System.Web.UI.DataVisualization.Charting.Utilities;
43         //using System.Web.UI.DataVisualization.Charting.Borders3D;
44 #endif
45
46 #endregion
47
48 #if Microsoft_CONTROL
49         namespace System.Windows.Forms.DataVisualization.Charting
50 #else
51 namespace System.Web.UI.DataVisualization.Charting
52
53 #endif
54 {
55         /// <summary>
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.
65         /// </summary>
66         internal class Matrix3D
67         {
68                 #region Enumerations
69
70                 /// <summary>
71                 /// 3D Axis used for rotation
72                 /// </summary>
73                 private enum RotationAxis
74                 {
75                         /// <summary>
76                         /// Rotation around X axis.
77                         /// </summary>
78                         X,
79
80                         /// <summary>
81                         /// Rotation around Y axis.
82                         /// </summary>
83                         Y,
84
85                         /// <summary>
86                         /// Rotation around Z axis.
87                         /// </summary>
88                         Z
89                 }
90
91                 #endregion // Enumerations
92
93                 #region Fields
94
95                 /// <summary>
96                 /// Composite matrix.
97                 /// </summary>
98                 private float [][] _mainMatrix;
99                 
100                 /// <summary>
101                 /// Default translation for chart area cube ( without composition ).
102                 /// </summary>
103                 private float _translateX;
104
105                 /// <summary>
106                 /// Default translation for chart area cube ( without composition ).
107                 /// </summary>
108                 private float _translateY;
109
110                 /// <summary>
111                 /// Default translation for chart area cube ( without composition ).
112                 /// </summary>
113                 private float _translateZ;
114
115                 /// <summary>
116                 /// The value, which is used to rescale chart area.
117                 /// </summary>
118                 private float _scale;
119
120                 /// <summary>
121                 /// The value used for Isometric Shift.
122                 /// </summary>
123                 private float _shiftX;
124
125                 /// <summary>
126                 /// The value used for Isometric Shift.
127                 /// </summary>
128                 private float _shiftY;
129
130                 /// <summary>
131                 /// Perspective value.
132                 /// </summary>
133                 internal float _perspective;
134
135                 /// <summary>
136                 /// Isometric projection.
137                 /// </summary>
138                 private bool _rightAngleAxis;
139
140                 /// <summary>
141                 /// The value, which is used for perspective.
142                 /// </summary>
143                 private float _perspectiveFactor = float.NaN;
144
145                 /// <summary>
146                 /// The value, which is used to set projection plane.
147                 /// </summary>
148                 private float _perspectiveZ;
149
150                 /// <summary>
151                 /// X Angle.
152                 /// </summary>
153                 private float _angleX;
154
155                 /// <summary>
156                 /// Y Angle.
157                 /// </summary>
158                 private float _angleY;
159
160                 /// <summary>
161                 /// Private fields used for lighting
162                 /// </summary>
163                 Point3D [] _lightVectors = new Point3D[7];
164
165                 /// <summary>
166                 /// LightStyle Style
167                 /// </summary>
168                 LightStyle _lightStyle;
169
170                 #endregion // Fields
171
172         #region Properties
173
174         /// <summary>
175         /// Gets the X Angle.
176         /// </summary>
177         internal float AngleX
178         {
179             get { return _angleX; }
180         }
181
182         /// <summary>
183         /// Gets the Y Angle.
184         /// </summary>
185         internal float AngleY
186         {
187             get { return _angleY; }
188         }
189
190         /// <summary>
191         /// Get perspective value.
192         /// </summary>
193         internal float Perspective
194         {
195             get { return _perspective; } 
196         }
197
198         #endregion // Properties
199
200         #region Internal and Public Methods
201
202         /// <summary>
203                 /// Constructor for Matrix 3D
204                 /// </summary>
205                 public Matrix3D()
206                 {
207                 }
208
209                 /// <summary>
210                 /// Checks if 3D matrix was initialized.
211                 /// </summary>
212                 /// <returns>True if matrix was initialized.</returns>
213                 public bool IsInitialized()
214                 {
215                         return (this._mainMatrix != null);
216                 }
217
218                 /// <summary>
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.
226                 /// </summary>
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, 
235                         float depth, 
236                         float angleX, 
237                         float angleY, 
238                         float perspective, 
239                         bool rightAngleAxis )
240                 {
241                         // Initialization for mainMatrix
242                         Reset();
243
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;
252
253                         // Remember Angles
254                         this._angleX = angleX;
255                         this._angleY = angleY;
256                         
257                         // Change Degrees to radians.
258                         angleX = angleX / 180F * (float)Math.PI;
259                         angleY = angleY / 180F * (float)Math.PI;
260
261                         // Set points for 3D Bar which represents 3D Chart Area Cube.
262                         Point3D [] points = Set3DBarPoints( width, height, depth );
263                         
264                         // Translate Chart Area Cube WITH CENTER OF ROTATION - COMPOSITE TRANSLATION.
265                         Translate( _translateX, _translateY, 0 );
266
267                         // Non Isometric projection
268                         if( !rightAngleAxis )
269                         {
270                                 // Rotate Chart Area Cube by X axis. 
271                                 Rotate( angleX, RotationAxis.X );
272
273                                 // Rotate Chart Area Cube by Y axis. 
274                                 Rotate( angleY, RotationAxis.Y );
275                         }
276                         else
277                         {
278                                 if( this._angleY >= 45 )
279                                 {
280                                         // Rotate Chart Area Cube by Y axis. 
281                                         Rotate( Math.PI / 2, RotationAxis.Y );
282                                 }
283                                 else if( this._angleY <= -45 )
284                                 {
285                                         // Rotate Chart Area Cube by Y axis. 
286                                         Rotate( -Math.PI / 2, RotationAxis.Y );
287                                 }
288                         }
289                         
290                         // Apply composed transformation ( Translation and rotation ).
291                         GetValues( points );
292
293                         float maxZ = float.MinValue;
294
295                         if( perspective != 0F || rightAngleAxis )
296                         {
297                                 // Find projection plane
298                                 foreach( Point3D point in points )
299                                 {
300                                         if( point.Z > maxZ )
301                                                 maxZ = point.Z;
302                                 }
303
304                                 // Set Projection plane
305                                 _perspectiveZ = maxZ;
306                         }
307
308                         if( perspective != 0F )
309                         {
310                                 _perspectiveFactor = perspective / 2000F;
311
312                                 // Apply perspective
313                                 ApplyPerspective( points );
314                         }
315                                 
316                         // Isometric projection is active
317                         if( rightAngleAxis )
318                         {
319                                 RightAngleProjection( points );
320
321                                 float minX = 0F;
322                                 float minY = 0F;
323                                 float maxX = 0F;
324                                 float maxY = 0F;
325
326                                 // Point loop
327                                 foreach( Point3D point in points )
328                                 {
329                                         if( point.X - _translateX < 0F  && Math.Abs( point.X - _translateX ) > minX )
330                                                 minX = Math.Abs( point.X - _translateX );
331                                         
332                                         if( point.X - _translateX >=0F  && Math.Abs( point.X - _translateX ) > maxX )
333                                                 maxX = Math.Abs( point.X - _translateX );
334
335                                         if( point.Y - _translateY < 0F  && Math.Abs( point.Y - _translateY ) > minY )
336                                                 minY = Math.Abs( point.Y - _translateY );
337                                         
338                                         if( point.Y - _translateY >=0F  && Math.Abs( point.Y - _translateY ) > maxY )
339                                                 maxY = Math.Abs( point.Y - _translateY );
340                                 }
341
342                                 _shiftX = (maxX - minX)/2F;
343                                 _shiftY = (maxY - minY)/2F;
344                                 RightAngleShift( points );
345                         }
346                                                                 
347                         // This code searches for value, which will be used for scaling.
348                         float maxXScale = float.MinValue;
349                         float maxYScale = float.MinValue;
350
351                         foreach( Point3D point in points )
352                         {
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;
359                                 
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;
363                         }
364
365                         // Remember scale factor
366                         _scale = (maxYScale > maxXScale ) ? maxYScale : maxXScale;
367
368                         // Apply scaling
369                         Scale( points );
370                         
371                 }
372
373                 /// <summary>
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.
379                 /// </summary>
380                 /// <param name="points">3D Points array.</param>
381                 public void TransformPoints( Point3D[] points )
382                 {
383                         TransformPoints( points, true );
384                 }
385 #if RS_DEADCODE
386                 /// <summary>
387                 /// This Method returns scale factor
388                 /// </summary>
389                 /// <returns></returns>
390                 internal float GetScale()
391                 {
392                         return scale;
393                 }
394 #endif //RS_DEADCODE
395                 #endregion // Internal and Public Methods
396
397                 #region Private Methods
398
399                 /// <summary>
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.
405                 /// </summary>
406                 /// <param name="points">3D Points array.</param>
407                 /// <param name="withPerspective">Applay Perspective</param>
408                 private void TransformPoints( Point3D[] points, bool withPerspective )
409                 {
410                         // Matrix is not initialized.
411                         if( _mainMatrix == null )
412                         {
413                 throw new InvalidOperationException(SR.ExceptionMatrix3DNotinitialized);
414                         }
415
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 )
421                         {
422                                 point.X -= _translateX;
423                                 point.Y -= _translateY;
424                                 point.Z -= _translateZ;
425                         }
426                 
427                         // Transform points using composite mainMatrix. (Translation of points together with 
428                         // Center of rotation and rotations by X and Y axes).
429                         GetValues( points );
430
431                         // Apply perspective
432                         if( _perspective != 0F && withPerspective )
433                         {
434                                 ApplyPerspective( points );
435                         }
436                         
437                         // RightAngle Projection
438                         if( _rightAngleAxis )
439                         {
440                                 RightAngleProjection( points );
441                                 RightAngleShift( points );
442                         }
443
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.
447                         Scale( points );
448                 }
449                 
450                 /// <summary>
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.
455                 /// </summary>
456                 /// <param name="points">3D Points array.</param>
457                 private void RightAngleShift( Point3D [] points )
458                 {
459                         foreach( Point3D point in points )
460                         {
461                                 point.X = point.X - _shiftX;   
462                                 point.Y = point.Y - _shiftY;   
463                         }
464                 }
465
466                 /// <summary>
467                 /// Method used to calculate right Angle projection.
468                 /// </summary>
469                 /// <param name="points">3D points array.</param>
470                 private void RightAngleProjection( Point3D [] points )
471                 {
472                         float coorectionAngle = 45F;
473                 
474                         float xFactor = this._angleX / 45;
475
476                         float yFactor;
477                         
478                         if( this._angleY >= 45 )
479                         {
480                                 yFactor = (this._angleY - 90) / coorectionAngle;
481                         }
482                         else if ( this._angleY <= -45 )
483                         {
484                                 yFactor = ( this._angleY + 90 ) / coorectionAngle;
485                         }
486                         else
487                         {
488                                 yFactor = this._angleY / coorectionAngle;
489                         }
490                         
491                         // Projection formula
492                         // perspectiveZ - Position of perspective plain.
493                         // Perspective Factor - Intensity of projection.
494                         foreach( Point3D point in points )
495                         {
496                                 point.X = point.X + ( _perspectiveZ - point.Z ) * yFactor;   
497                                 point.Y = point.Y - ( _perspectiveZ - point.Z ) * xFactor;  
498                         }
499                 }
500
501                 /// <summary>
502                 /// Method is used for Planar Geometric projection. 
503                 /// </summary>
504                 /// <param name="points">3D Points array.</param>
505                 private void ApplyPerspective( Point3D [] points )
506                 {
507                         // Projection formula
508                         // perspectiveZ - Position of perspective plain.
509                         // perspectiveFactor - Intensity of projection.
510                         foreach( Point3D point in points )
511                         {
512                                 point.X = _translateX + (point.X - _translateX) / ( 1 + (_perspectiveZ - point.Z) * _perspectiveFactor);   
513                                 point.Y = _translateY + (point.Y - _translateY) / ( 1 + (_perspectiveZ - point.Z) * _perspectiveFactor); 
514                         }
515                 }
516
517                 /// <summary>
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.
521                 /// </summary>
522                 /// <param name="points">3D Points array.</param>
523                 private void Scale( Point3D [] points )
524                 {
525                         foreach( Point3D point in points )
526                         {
527                                 point.X = _translateX + (point.X - _translateX) / _scale; 
528                                 point.Y = _translateY + (point.Y - _translateY) / _scale; 
529                         }
530                 }
531
532                 /// <summary>
533                 /// Prepend to this Matrix object a translation. This method is used 
534                 /// only if CENTER OF ROTATION HAS TO BE MOVED.
535                 /// </summary>
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 )
540                 {
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];
546
547                         // Matrix initialization
548                         // Row loop
549                         for( int row = 0; row < 4; row ++ )
550                         {
551                                 // Column loop
552                                 for( int column = 0; column < 4; column ++ )
553                                 {
554                                         // For initialization: Diagonal matrix elements are equal to one 
555                                         // and all other elements are equal to zero.
556                                         if( row == column )
557                                         {
558                                                 translationMatrix[row][column] = 1F;
559                                         }
560                                         else
561                                         {
562                                                 translationMatrix[row][column] = 0F;
563                                         }
564                                 }
565                         }
566                 
567                         // Set translation values to the matrix
568                         translationMatrix[0][3] = dx;
569                         translationMatrix[1][3] = dy;
570                         translationMatrix[2][3] = dz;
571                 
572                         // Translate main Matrix
573                         Multiply( translationMatrix, MatrixOrder.Prepend, true );
574                 
575                 }
576
577                 /// <summary>
578                 /// This method initialize and set default values for mainMatrix ( there is no rotation and translation )
579                 /// </summary>
580                 private void Reset()
581                 {
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];
588
589                         // Matrix initialization
590                         // Row loop
591                         for( int row = 0; row < 4; row ++ )
592                         {
593                                 // Column loop
594                                 for( int column = 0; column < 4; column ++ )
595                                 {
596                                         // For initialization: Diagonal matrix elements are equal to one 
597                                         // and all other elements are equal to zero.
598                                         if( row == column )
599                                         {
600                                                 _mainMatrix[row][column] = 1F;
601                                         }
602                                         else
603                                         {
604                                                 _mainMatrix[row][column] = 0F;
605                                         }
606                                 }
607                         }
608                 }
609
610
611                 /// <summary>
612                 /// Multiplies this Matrix object by the matrix specified in the 
613         /// matrix parameter, and in the order specified in the order parameter.
614                 /// </summary>
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 )
620                 {
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];
628
629                         // Row loop
630                         for( int row = 0; row < 4; row ++ )
631                         {
632                                 // Column loop
633                                 for( int column = 0; column < 4; column ++ )
634                                 {
635                                         // Initialize element
636                                         resultMatrix[row][column ] = 0F;
637                                         for( int sumIndx = 0; sumIndx < 4; sumIndx ++ )
638                                         {
639                                                 // Find matrix element
640                                                 if( order == MatrixOrder.Prepend )
641                                                 {
642                                                         // Order of matrix multiplication
643                                                         resultMatrix[row][column ] += _mainMatrix[row][sumIndx ] * mulMatrix[sumIndx][column];
644                                                 }
645                                                 else
646                                                 {
647                                                         // Order of matrix multiplication
648                                                         resultMatrix[row][column] += mulMatrix[row][sumIndx] * _mainMatrix[sumIndx][column];
649                                                 }
650                                         }
651                                 }
652                         }
653
654                         // Set result matrix to be main matrix
655                         if( setMainMatrix )
656                         {
657                                 _mainMatrix = resultMatrix;
658                         }
659
660                         return resultMatrix;
661                 }
662
663
664                 /// <summary>
665                 /// Multiplies this Matrix object by the Vector specified in the 
666                 /// vector parameter.
667                 /// </summary>
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 )
671                 {
672                         // Row loop
673                         for( int row = 0; row < 3; row ++ )
674                         {
675                                 // Initialize element
676                                 resultVector[ row ] = 0F;
677
678                                 // Column loop
679                                 for( int column = 0; column < 4; column ++ )
680                                 {
681                                         // Find matrix element
682                                         resultVector[ row ] += _mainMatrix[row][column] * mulVector[ column ];
683                                 }
684                         }
685                 }
686
687                 /// <summary>
688                 /// Prepend to this Matrix object a clockwise rotation, around the axis and by the specified angle.
689                 /// </summary>
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 )
693                 {
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];
699
700                         // Change angle direction
701                         angle = -1F * angle;
702
703                         // Matrix initialization
704                         // Row loop
705                         for( int row = 0; row < 4; row ++ )
706                         {
707                                 // Column loop
708                                 for( int column = 0; column < 4; column ++ )
709                                 {
710                                         // For initialization: Diagonal matrix elements are equal to one 
711                                         // and all other elements are equal to zero.
712                                         if( row == column )
713                                         {
714                                                 rotationMatrix[row][column] = 1F;
715                                         }
716                                         else
717                                         {
718                                                 rotationMatrix[row][column] = 0F;
719                                         }
720                                 }
721                         }
722
723                         // Rotation about axis
724                         switch( axis )
725                         {
726                                         // Rotation about X axis
727                                 case RotationAxis.X:
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 );
732                                         break;
733
734                                         // Rotation about Y axis
735                                 case RotationAxis.Y:
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 );
740                                         break;
741
742                                         // Rotation about Z axis
743                                 case RotationAxis.Z:
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 );
748                                         break;
749
750                         }
751
752                         // Rotate Main matrix
753                         Multiply( rotationMatrix, MatrixOrder.Prepend, true );
754                 
755                 }
756
757                 /// <summary>
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).
761                 /// </summary>
762                 /// <param name="points">Array of 3D points.</param>
763                 private void GetValues( Point3D [] points )
764                 {
765                         // Create one dimensional matrix (vector)
766                         float [] inputVector = new float[4];
767
768                         // A vector which is result of matrix and vector multiplication
769                         float [] resultVector = new float[4];
770                 
771                         foreach( Point3D point in points )
772                         {
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;
777                                 inputVector[3] = 1;
778                 
779                                 // Apply 3D transformations.
780                                 MultiplyVector( inputVector, ref resultVector );
781
782                                 // Return x and y coordinates.
783                                 point.X = resultVector[0];
784                                 point.Y = resultVector[1];
785                                 point.Z = resultVector[2];
786                         }
787                 }
788
789
790                 /// <summary>
791                 /// Set points for 3D Bar which represents 3D Chart Area.
792                 /// </summary>
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 )
798                 {
799                         Point3D [] points = new Point3D[8];
800
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);
808                         
809                         // ********************************************
810                         // 3D Bar side: Back
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);
816
817                         return points;
818                 }
819
820                 #endregion // Private Methods
821                 
822                 #region Lighting Methods
823
824                 /// <summary>
825                 /// Initial Lighting. Use matrix transformation only once 
826                 /// for Normal vectors.
827                 /// </summary>
828                 /// <param name="lightStyle">LightStyle Style</param>
829                 internal void InitLight( LightStyle lightStyle )
830                 {
831                         // Set LightStyle Style
832                         this._lightStyle = lightStyle;
833                                                                                 
834                         // Center of rotation
835                         _lightVectors[0] = new Point3D( 0F, 0F, 0F );
836
837                         // Front side normal Vector.
838                         _lightVectors[1] = new Point3D( 0F, 0F, 1F );
839
840                         // Back side normal Vector.
841                         _lightVectors[2] = new Point3D( 0F, 0F, -1F );
842
843                         // Left side normal Vector.
844                         _lightVectors[3] = new Point3D( -1F, 0F, 0F );
845
846                         // Right side normal Vector.
847                         _lightVectors[4] = new Point3D( 1F, 0F, 0F );
848
849                         // Top side normal Vector.
850                         _lightVectors[5] = new Point3D( 0F, -1F, 0F );
851
852                         // Bottom side normal Vector.
853                         _lightVectors[6] = new Point3D( 0F, 1F, 0F );
854
855                         // Apply matrix transformations
856                         TransformPoints( _lightVectors, false );
857
858                         // ********************************************************
859                         // LightStyle Vector and normal vectors have to have same center. 
860                         // Shift Normal vectors.
861                         // ********************************************************
862
863                         // Front Side shift
864                         _lightVectors[1].X -= _lightVectors[0].X;
865                         _lightVectors[1].Y -= _lightVectors[0].Y;
866                         _lightVectors[1].Z -= _lightVectors[0].Z;
867
868                         // Back Side shift
869                         _lightVectors[2].X -= _lightVectors[0].X;
870                         _lightVectors[2].Y -= _lightVectors[0].Y;
871                         _lightVectors[2].Z -= _lightVectors[0].Z;
872
873                         // Left Side shift
874                         _lightVectors[3].X -= _lightVectors[0].X;
875                         _lightVectors[3].Y -= _lightVectors[0].Y;
876                         _lightVectors[3].Z -= _lightVectors[0].Z;
877
878                         // Right Side shift
879                         _lightVectors[4].X -= _lightVectors[0].X;
880                         _lightVectors[4].Y -= _lightVectors[0].Y;
881                         _lightVectors[4].Z -= _lightVectors[0].Z;
882
883                         // Top Side shift
884                         _lightVectors[5].X -= _lightVectors[0].X;
885                         _lightVectors[5].Y -= _lightVectors[0].Y;
886                         _lightVectors[5].Z -= _lightVectors[0].Z;
887
888                         // Bottom Side shift
889                         _lightVectors[6].X -= _lightVectors[0].X;
890                         _lightVectors[6].Y -= _lightVectors[0].Y;
891                         _lightVectors[6].Z -= _lightVectors[0].Z;
892
893                 }
894
895                 /// <summary>
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.
902                 /// </summary>
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 )
911                 {
912                         switch( _lightStyle )
913                         {
914                                 // LightStyle style is None
915                                 case  LightStyle.None:
916                                 {
917                                         front = surfaceColor;
918                                         left = surfaceColor;
919                                         top = surfaceColor;
920                                         back = surfaceColor;
921                                         right = surfaceColor;
922                                         bottom = surfaceColor;
923                                         break;
924                                 }
925                                 // LightStyle style is Simplistic
926                                 case  LightStyle.Simplistic:
927                                 {
928                                         front = surfaceColor;
929                                         left = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.25);
930                                         top = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
931                                         back = surfaceColor;
932                                         right = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.25);
933                                         bottom = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
934                                         break;
935                                 }
936                                 // LightStyle style is Realistic
937                                 default:
938                                 {
939                                                                                 
940                                         // For Right Axis angle Realistic lightStyle should be different
941                                         if( _rightAngleAxis )
942                                         {
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);
948
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 )
954                                                 {
955                                                         front = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[1])/Math.PI );
956
957                                                         back = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[2])/Math.PI );
958                                                         
959                                                         left = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0 );
960
961                                                         right = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0 );
962                                                 }
963                                                 else
964                                                 {
965                                                         front = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0 );
966
967                                                         back = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 1 );
968
969                                                         left = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[3])/Math.PI );
970
971                                                         right = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[4])/Math.PI );
972                                                 }
973                         
974                                                 top = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[5])/Math.PI );
975
976                                                 bottom = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[6])/Math.PI );
977                                         }
978                                         else
979                                         {
980                                                 // LightStyle source Vector
981                                                 Point3D lightSource = new Point3D( 0F, 0F, 1F );
982
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 );
988
989                                                 back = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[2])/Math.PI );
990
991                                                 left = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[3])/Math.PI );
992
993                                                 right = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[4])/Math.PI );
994                         
995                                                 top = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[5])/Math.PI );
996
997                                                 bottom = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[6])/Math.PI );
998                                         }
999
1000                                         break;
1001                                 }
1002                         }
1003                 }
1004                 
1005
1006                 /// <summary>
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.
1013                 /// </summary>
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)
1022                 {
1023                         // Corrected color
1024                         Color color = surfaceColor;
1025
1026                         // Direction of lightStyle source
1027                         Point3D lightSource;
1028                         lightSource = new Point3D( 0F, 0F, 1F );
1029
1030                         // There are tree different lightStyle styles: None, Simplistic and realistic.
1031                         switch( _lightStyle )
1032                         {
1033                                 // LightStyle style is None
1034                                 case  LightStyle.None:
1035                                 {
1036                                         // Use same color
1037                                         break;
1038                                 }
1039                                 // LightStyle style is Simplistic
1040                                 case  LightStyle.Simplistic:
1041                                 {
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;
1047
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;
1052
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;
1058                                         
1059                                         // Polygon is left side ( like side of area chart )
1060                                         if( surfaceName == SurfaceNames.Left )
1061                                         {
1062                                                 color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
1063                                         }
1064                                         // Polygon is right side ( like side of area chart )
1065                                         else if( surfaceName == SurfaceNames.Right )
1066                                         {
1067                                                 color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
1068                                         }
1069                                         // Polygon is front side ( like side of area chart )
1070                                         else if( surfaceName == SurfaceNames.Front )
1071                                         {
1072                                                 color = surfaceColor;
1073                                         }
1074                                         // Polygon is back side ( like side of area chart )
1075                                         else if( surfaceName == SurfaceNames.Back )
1076                                         {
1077                                                 color = surfaceColor;
1078                                         }
1079                                         // Polygon has angle with bottom side ( Line chart or top of area chart )
1080                                         else
1081                                         {
1082                                                 float angleLeft;
1083                                                 float angleRight;
1084
1085                                                 // Find angles between lightStyle and polygon for different y-axis angles.
1086                                                 if( switchSeriesOrder )
1087                                                 {
1088                             if (rotation > 0 && rotation <= 90)
1089                                                         {
1090                                                                 angleLeft = GetAngle( normalVector, _lightVectors[3] );
1091                                                                 angleRight = GetAngle( normalVector, _lightVectors[4] );
1092                                                         }
1093                                                         else
1094                                                         {
1095                                                                 angleLeft = GetAngle( normalVector, _lightVectors[4] );
1096                                                                 angleRight = GetAngle( normalVector, _lightVectors[3] );
1097                                                         }
1098                                                 }
1099                                                 else
1100                                                 {
1101                             if (rotation > 0 && rotation <= 90)
1102                                                         {
1103                                                                 angleLeft = GetAngle( normalVector, _lightVectors[4] );
1104                                                                 angleRight = GetAngle( normalVector, _lightVectors[3] );
1105                                                         }
1106                                                         else
1107                                                         {
1108                                                                 angleLeft = GetAngle( normalVector, _lightVectors[3] );
1109                                                                 angleRight = GetAngle( normalVector, _lightVectors[4] );
1110                                                         }
1111                                                 }
1112
1113                                                 if( Math.Abs( angleLeft - angleRight ) < 0.01 )
1114                                                 {
1115                                                         color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.25);
1116                                                 }
1117                                                 else if( angleLeft < angleRight )
1118                                                 {
1119                                                         color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.25);
1120                                                 }
1121                                                 else
1122                                                 {
1123                                                         color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
1124                                                 }
1125                                         }
1126
1127                                         break;
1128                                 }
1129                                 // LightStyle style is Realistic
1130                                 default:
1131                                 {
1132
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;
1138
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;
1143
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;
1149
1150                                         // ******************************************************************
1151                                         // Color correction. Angle between Normal vector of polygon and 
1152                                         // vector of lightStyle source is used.
1153                                         // ******************************************************************
1154                                         if( surfaceName == SurfaceNames.Front )
1155                                         {
1156                                                 lightSource.Z *= -1;
1157                                                 color = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[2])/Math.PI );
1158                                         }
1159                                         else if( surfaceName == SurfaceNames.Back )
1160                                         {
1161                                                 lightSource.Z *= -1;
1162                                                 color = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[1])/Math.PI );
1163                                         }
1164                                         else
1165                                         {
1166                                                 if( visiblePolygon )
1167                                                 {
1168                                                         lightSource.Z *= -1;
1169                                                 }
1170
1171                                                 color = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,normalVector)/Math.PI );
1172                                         }
1173
1174                                         break;
1175                                 }
1176                         }
1177                         return color;
1178                         
1179                 }
1180
1181                 /// <summary>
1182                 /// This method creates gradien color with brightnes.
1183                 /// </summary>
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 )
1188                 {
1189                         position = position * 2;
1190                         double brightness = 0.5;
1191                         if( position < brightness )
1192                         {
1193                                 return ChartGraphics.GetGradientColor( Color.FromArgb(beginColor.A,255,255,255), beginColor, 1 - brightness + position );
1194                         }
1195                         else if( -brightness + position < 1 )
1196                         {
1197                                 return ChartGraphics.GetGradientColor( beginColor, Color.Black, -brightness + position );
1198                         }
1199                         else
1200                         {
1201                                 return Color.FromArgb( beginColor.A, 0, 0, 0 );
1202                         }
1203                 }
1204
1205                 /// <summary>
1206                 /// Returns the angle between two 3D vectors (a and b); 
1207                 /// </summary>
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)
1212                 {
1213                         double angle;
1214
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 ) ) );
1216
1217                         return (float)angle;
1218                 }
1219
1220                 #endregion
1221         }
1222 }