Matrix3D.cs 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. //
  5. // Purpose: Matrix3D class is used during the 3D drawings to
  6. // transform plotting area 3D coordinates into the 2D
  7. // projection coordinates based on rotation and
  8. // perspective settings.
  9. //
  10. using System;
  11. using System.Drawing;
  12. using System.Drawing.Drawing2D;
  13. namespace FastReport.DataVisualization.Charting
  14. {
  15. /// <summary>
  16. /// This class is responsible for all 3D coordinates transformations: Translation,
  17. /// Rotation, Scale, Perspective and RightAngle Projection. Translation
  18. /// and rotation are stored in composite matrix (mainMatrix), and scaling,
  19. /// projection and non-composite translation are stored in private fields.
  20. /// Matrix is initialized with Chart Area 3D cube, which is invisible boundary
  21. /// cube of 3D Chart area. The matrix has to be initialized every time
  22. /// when angles, position or perspective parameters are changed. Method
  23. /// TransformPoints will apply 3D Transformation on points using
  24. /// Initialization values: Main matrix and other initialization values.
  25. /// </summary>
  26. internal class Matrix3D
  27. {
  28. #region Enumerations
  29. /// <summary>
  30. /// 3D Axis used for rotation
  31. /// </summary>
  32. private enum RotationAxis
  33. {
  34. /// <summary>
  35. /// Rotation around X axis.
  36. /// </summary>
  37. X,
  38. /// <summary>
  39. /// Rotation around Y axis.
  40. /// </summary>
  41. Y,
  42. /// <summary>
  43. /// Rotation around Z axis.
  44. /// </summary>
  45. Z
  46. }
  47. #endregion // Enumerations
  48. #region Fields
  49. /// <summary>
  50. /// Composite matrix.
  51. /// </summary>
  52. private float [][] _mainMatrix;
  53. /// <summary>
  54. /// Default translation for chart area cube ( without composition ).
  55. /// </summary>
  56. private float _translateX;
  57. /// <summary>
  58. /// Default translation for chart area cube ( without composition ).
  59. /// </summary>
  60. private float _translateY;
  61. /// <summary>
  62. /// Default translation for chart area cube ( without composition ).
  63. /// </summary>
  64. private float _translateZ;
  65. /// <summary>
  66. /// The value, which is used to rescale chart area.
  67. /// </summary>
  68. private float _scale;
  69. /// <summary>
  70. /// The value used for Isometric Shift.
  71. /// </summary>
  72. private float _shiftX;
  73. /// <summary>
  74. /// The value used for Isometric Shift.
  75. /// </summary>
  76. private float _shiftY;
  77. /// <summary>
  78. /// Perspective value.
  79. /// </summary>
  80. internal float _perspective;
  81. /// <summary>
  82. /// Isometric projection.
  83. /// </summary>
  84. private bool _rightAngleAxis;
  85. /// <summary>
  86. /// The value, which is used for perspective.
  87. /// </summary>
  88. private float _perspectiveFactor = float.NaN;
  89. /// <summary>
  90. /// The value, which is used to set projection plane.
  91. /// </summary>
  92. private float _perspectiveZ;
  93. /// <summary>
  94. /// X Angle.
  95. /// </summary>
  96. private float _angleX;
  97. /// <summary>
  98. /// Y Angle.
  99. /// </summary>
  100. private float _angleY;
  101. /// <summary>
  102. /// Private fields used for lighting
  103. /// </summary>
  104. Point3D [] _lightVectors = new Point3D[7];
  105. /// <summary>
  106. /// LightStyle Style
  107. /// </summary>
  108. LightStyle _lightStyle;
  109. #endregion // Fields
  110. #region Properties
  111. /// <summary>
  112. /// Gets the X Angle.
  113. /// </summary>
  114. internal float AngleX
  115. {
  116. get { return _angleX; }
  117. }
  118. /// <summary>
  119. /// Gets the Y Angle.
  120. /// </summary>
  121. internal float AngleY
  122. {
  123. get { return _angleY; }
  124. }
  125. /// <summary>
  126. /// Get perspective value.
  127. /// </summary>
  128. internal float Perspective
  129. {
  130. get { return _perspective; }
  131. }
  132. #endregion // Properties
  133. #region Internal and Public Methods
  134. /// <summary>
  135. /// Constructor for Matrix 3D
  136. /// </summary>
  137. public Matrix3D()
  138. {
  139. }
  140. /// <summary>
  141. /// Checks if 3D matrix was initialized.
  142. /// </summary>
  143. /// <returns>True if matrix was initialized.</returns>
  144. public bool IsInitialized()
  145. {
  146. return (this._mainMatrix != null);
  147. }
  148. /// <summary>
  149. /// Initialize Matrix 3D. This method calculates how much a chart area
  150. /// cube has to be resized to fit Inner Plotting Area rectangle. Order
  151. /// of operation is following: Translation for X and Y axes, Rotation
  152. /// by X-axis, Rotation by Y-axis and same scaling for all axes. All
  153. /// other elements, which belongs to this chart area cube (Data points,
  154. /// grid lines etc.) has to follow same order. Translation and rotation
  155. /// form composite matrix mainMatrix. Scale has to be allied separately.
  156. /// </summary>
  157. /// <param name="innerPlotRectangle">Inner Plotting Area position. Chart area cube has to be inside this rectangle</param>
  158. /// <param name="depth">Depth of chart area cube</param>
  159. /// <param name="angleX">Angle of rotation by X axis.</param>
  160. /// <param name="angleY">Angle of rotation by Y axis.</param>
  161. /// <param name="perspective">Perspective in percentages</param>
  162. /// <param name="rightAngleAxis">Right angle flag.</param>
  163. internal void Initialize(
  164. RectangleF innerPlotRectangle,
  165. float depth,
  166. float angleX,
  167. float angleY,
  168. float perspective,
  169. bool rightAngleAxis )
  170. {
  171. // Initialization for mainMatrix
  172. Reset();
  173. // Remember non-composite translation
  174. _translateX = innerPlotRectangle.X+innerPlotRectangle.Width/2;
  175. _translateY = innerPlotRectangle.Y+innerPlotRectangle.Height/2;
  176. _translateZ = depth / 2F;
  177. float width = innerPlotRectangle.Width;
  178. float height = innerPlotRectangle.Height;
  179. this._perspective = perspective;
  180. this._rightAngleAxis = rightAngleAxis;
  181. // Remember Angles
  182. this._angleX = angleX;
  183. this._angleY = angleY;
  184. // Change Degrees to radians.
  185. angleX = angleX / 180F * (float)Math.PI;
  186. angleY = angleY / 180F * (float)Math.PI;
  187. // Set points for 3D Bar which represents 3D Chart Area Cube.
  188. Point3D [] points = Set3DBarPoints( width, height, depth );
  189. // Translate Chart Area Cube WITH CENTER OF ROTATION - COMPOSITE TRANSLATION.
  190. Translate( _translateX, _translateY, 0 );
  191. // Non Isometric projection
  192. if( !rightAngleAxis )
  193. {
  194. // Rotate Chart Area Cube by X axis.
  195. Rotate( angleX, RotationAxis.X );
  196. // Rotate Chart Area Cube by Y axis.
  197. Rotate( angleY, RotationAxis.Y );
  198. }
  199. else
  200. {
  201. if( this._angleY >= 45 )
  202. {
  203. // Rotate Chart Area Cube by Y axis.
  204. Rotate( Math.PI / 2, RotationAxis.Y );
  205. }
  206. else if( this._angleY <= -45 )
  207. {
  208. // Rotate Chart Area Cube by Y axis.
  209. Rotate( -Math.PI / 2, RotationAxis.Y );
  210. }
  211. }
  212. // Apply composed transformation ( Translation and rotation ).
  213. GetValues( points );
  214. float maxZ = float.MinValue;
  215. if( perspective != 0F || rightAngleAxis )
  216. {
  217. // Find projection plane
  218. foreach( Point3D point in points )
  219. {
  220. if( point.Z > maxZ )
  221. maxZ = point.Z;
  222. }
  223. // Set Projection plane
  224. _perspectiveZ = maxZ;
  225. }
  226. if( perspective != 0F )
  227. {
  228. _perspectiveFactor = perspective / 2000F;
  229. // Apply perspective
  230. ApplyPerspective( points );
  231. }
  232. // Isometric projection is active
  233. if( rightAngleAxis )
  234. {
  235. RightAngleProjection( points );
  236. float minX = 0F;
  237. float minY = 0F;
  238. float maxX = 0F;
  239. float maxY = 0F;
  240. // Point loop
  241. foreach( Point3D point in points )
  242. {
  243. if( point.X - _translateX < 0F && Math.Abs( point.X - _translateX ) > minX )
  244. minX = Math.Abs( point.X - _translateX );
  245. if( point.X - _translateX >=0F && Math.Abs( point.X - _translateX ) > maxX )
  246. maxX = Math.Abs( point.X - _translateX );
  247. if( point.Y - _translateY < 0F && Math.Abs( point.Y - _translateY ) > minY )
  248. minY = Math.Abs( point.Y - _translateY );
  249. if( point.Y - _translateY >=0F && Math.Abs( point.Y - _translateY ) > maxY )
  250. maxY = Math.Abs( point.Y - _translateY );
  251. }
  252. _shiftX = (maxX - minX)/2F;
  253. _shiftY = (maxY - minY)/2F;
  254. RightAngleShift( points );
  255. }
  256. // This code searches for value, which will be used for scaling.
  257. float maxXScale = float.MinValue;
  258. float maxYScale = float.MinValue;
  259. foreach( Point3D point in points )
  260. {
  261. // Find maximum relative distance for X axis.
  262. // Relative distance is (distance from the center of plotting area
  263. // position) / (distance from the edge of rectangle to
  264. // the center of the rectangle).
  265. if( maxXScale < Math.Abs(point.X - _translateX) / width * 2 )
  266. maxXScale = Math.Abs(point.X - _translateX) / width * 2;
  267. // Find maximum relative distance for Y axis.
  268. if( maxYScale < Math.Abs(point.Y - _translateY) / height * 2 )
  269. maxYScale = Math.Abs(point.Y - _translateY) / height * 2;
  270. }
  271. // Remember scale factor
  272. _scale = (maxYScale > maxXScale ) ? maxYScale : maxXScale;
  273. // Apply scaling
  274. Scale( points );
  275. }
  276. /// <summary>
  277. /// Apply transformations on array od 3D Points. Order of operation is
  278. /// following: Translation ( Set coordinate system for 0:100 to -50:50
  279. /// Center of rotation is always 0), Composite Translation for X and Y
  280. /// axes ( Moving center of rotation ), Rotation by X-axis, Rotation
  281. /// by Y-axis, perspective and same scaling for all axes.
  282. /// </summary>
  283. /// <param name="points">3D Points array.</param>
  284. public void TransformPoints( Point3D[] points )
  285. {
  286. TransformPoints( points, true );
  287. }
  288. #if RS_DEADCODE
  289. /// <summary>
  290. /// This Method returns scale factor
  291. /// </summary>
  292. /// <returns></returns>
  293. internal float GetScale()
  294. {
  295. return scale;
  296. }
  297. #endif //RS_DEADCODE
  298. #endregion // Internal and Public Methods
  299. #region Private Methods
  300. /// <summary>
  301. /// Apply transformations on array od 3D Points. Order of operation is
  302. /// following: Translation ( Set coordinate system for 0:100 to -50:50
  303. /// Center of rotation is always 0), Composite Translation for X and Y
  304. /// axes ( Moving center of rotation ), Rotation by X-axis, Rotation
  305. /// by Y-axis, perspective and same scaling for all axes.
  306. /// </summary>
  307. /// <param name="points">3D Points array.</param>
  308. /// <param name="withPerspective">Applay Perspective</param>
  309. private void TransformPoints( Point3D[] points, bool withPerspective )
  310. {
  311. // Matrix is not initialized.
  312. if( _mainMatrix == null )
  313. {
  314. throw new InvalidOperationException(SR.ExceptionMatrix3DNotinitialized);
  315. }
  316. // Translate point. CENTER OF ROTATION is 0 and that center is in
  317. // the middle of chart area 3D CUBE. Translate method cannot
  318. // be used because composite translation WILL MOVE
  319. // CENTER OF ROTATION.
  320. foreach( Point3D point in points )
  321. {
  322. point.X -= _translateX;
  323. point.Y -= _translateY;
  324. point.Z -= _translateZ;
  325. }
  326. // Transform points using composite mainMatrix. (Translation of points together with
  327. // Center of rotation and rotations by X and Y axes).
  328. GetValues( points );
  329. // Apply perspective
  330. if( _perspective != 0F && withPerspective )
  331. {
  332. ApplyPerspective( points );
  333. }
  334. // RightAngle Projection
  335. if( _rightAngleAxis )
  336. {
  337. RightAngleProjection( points );
  338. RightAngleShift( points );
  339. }
  340. // Scales data points. Scaling has to be performed SEPARATELY from
  341. // composite matrix. If scale is used with composite matrix after
  342. // rotation, scaling will deform object.
  343. Scale( points );
  344. }
  345. /// <summary>
  346. /// This method adjusts a position of 3D Chart Area cube. This
  347. /// method will translate chart for better use of the inner
  348. /// plotting area. Center of rotation is shifted for
  349. /// right Angle projection.
  350. /// </summary>
  351. /// <param name="points">3D Points array.</param>
  352. private void RightAngleShift( Point3D [] points )
  353. {
  354. foreach( Point3D point in points )
  355. {
  356. point.X = point.X - _shiftX;
  357. point.Y = point.Y - _shiftY;
  358. }
  359. }
  360. /// <summary>
  361. /// Method used to calculate right Angle projection.
  362. /// </summary>
  363. /// <param name="points">3D points array.</param>
  364. private void RightAngleProjection( Point3D [] points )
  365. {
  366. float coorectionAngle = 45F;
  367. float xFactor = this._angleX / 45;
  368. float yFactor;
  369. if( this._angleY >= 45 )
  370. {
  371. yFactor = (this._angleY - 90) / coorectionAngle;
  372. }
  373. else if ( this._angleY <= -45 )
  374. {
  375. yFactor = ( this._angleY + 90 ) / coorectionAngle;
  376. }
  377. else
  378. {
  379. yFactor = this._angleY / coorectionAngle;
  380. }
  381. // Projection formula
  382. // perspectiveZ - Position of perspective plain.
  383. // Perspective Factor - Intensity of projection.
  384. foreach( Point3D point in points )
  385. {
  386. point.X = point.X + ( _perspectiveZ - point.Z ) * yFactor;
  387. point.Y = point.Y - ( _perspectiveZ - point.Z ) * xFactor;
  388. }
  389. }
  390. /// <summary>
  391. /// Method is used for Planar Geometric projection.
  392. /// </summary>
  393. /// <param name="points">3D Points array.</param>
  394. private void ApplyPerspective( Point3D [] points )
  395. {
  396. // Projection formula
  397. // perspectiveZ - Position of perspective plain.
  398. // perspectiveFactor - Intensity of projection.
  399. foreach( Point3D point in points )
  400. {
  401. point.X = _translateX + (point.X - _translateX) / ( 1 + (_perspectiveZ - point.Z) * _perspectiveFactor);
  402. point.Y = _translateY + (point.Y - _translateY) / ( 1 + (_perspectiveZ - point.Z) * _perspectiveFactor);
  403. }
  404. }
  405. /// <summary>
  406. /// Scales data points. Scaling has to be performed SEPARATELY from
  407. /// composite matrix. If scale is used with composite matrix after
  408. /// rotation, scaling will deform object.
  409. /// </summary>
  410. /// <param name="points">3D Points array.</param>
  411. private void Scale( Point3D [] points )
  412. {
  413. foreach( Point3D point in points )
  414. {
  415. point.X = _translateX + (point.X - _translateX) / _scale;
  416. point.Y = _translateY + (point.Y - _translateY) / _scale;
  417. }
  418. }
  419. /// <summary>
  420. /// Prepend to this Matrix object a translation. This method is used
  421. /// only if CENTER OF ROTATION HAS TO BE MOVED.
  422. /// </summary>
  423. /// <param name="dx">Translate in x axis direction.</param>
  424. /// <param name="dy">Translate in y axis direction.</param>
  425. /// <param name="dz">Translate in z axis direction.</param>
  426. private void Translate( float dx, float dy, float dz )
  427. {
  428. float [][] translationMatrix = new float[4][];
  429. translationMatrix[0] = new float[4];
  430. translationMatrix[1] = new float[4];
  431. translationMatrix[2] = new float[4];
  432. translationMatrix[3] = new float[4];
  433. // Matrix initialization
  434. // Row loop
  435. for( int row = 0; row < 4; row ++ )
  436. {
  437. // Column loop
  438. for( int column = 0; column < 4; column ++ )
  439. {
  440. // For initialization: Diagonal matrix elements are equal to one
  441. // and all other elements are equal to zero.
  442. if( row == column )
  443. {
  444. translationMatrix[row][column] = 1F;
  445. }
  446. else
  447. {
  448. translationMatrix[row][column] = 0F;
  449. }
  450. }
  451. }
  452. // Set translation values to the matrix
  453. translationMatrix[0][3] = dx;
  454. translationMatrix[1][3] = dy;
  455. translationMatrix[2][3] = dz;
  456. // Translate main Matrix
  457. Multiply( translationMatrix, MatrixOrder.Prepend, true );
  458. }
  459. /// <summary>
  460. /// This method initialize and set default values for mainMatrix ( there is no rotation and translation )
  461. /// </summary>
  462. private void Reset()
  463. {
  464. // First element is row and second element is column !!!
  465. _mainMatrix = new float[4][];
  466. _mainMatrix[0] = new float[4];
  467. _mainMatrix[1] = new float[4];
  468. _mainMatrix[2] = new float[4];
  469. _mainMatrix[3] = new float[4];
  470. // Matrix initialization
  471. // Row loop
  472. for( int row = 0; row < 4; row ++ )
  473. {
  474. // Column loop
  475. for( int column = 0; column < 4; column ++ )
  476. {
  477. // For initialization: Diagonal matrix elements are equal to one
  478. // and all other elements are equal to zero.
  479. if( row == column )
  480. {
  481. _mainMatrix[row][column] = 1F;
  482. }
  483. else
  484. {
  485. _mainMatrix[row][column] = 0F;
  486. }
  487. }
  488. }
  489. }
  490. /// <summary>
  491. /// Multiplies this Matrix object by the matrix specified in the
  492. /// matrix parameter, and in the order specified in the order parameter.
  493. /// </summary>
  494. /// <param name="mulMatrix">The Matrix object by which this Matrix object is to be multiplied.</param>
  495. /// <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>
  496. /// <param name="setMainMatrix">Set main matrix to be result of multiplication</param>
  497. /// <returns>Matrix multiplication result.</returns>
  498. private float[][] Multiply( float [][] mulMatrix, MatrixOrder order, bool setMainMatrix )
  499. {
  500. // A matrix which is result of matrix multiplication
  501. // of mulMatrix and mainMatrix
  502. float [][] resultMatrix = new float[4][];
  503. resultMatrix[0] = new float[4];
  504. resultMatrix[1] = new float[4];
  505. resultMatrix[2] = new float[4];
  506. resultMatrix[3] = new float[4];
  507. // Row loop
  508. for( int row = 0; row < 4; row ++ )
  509. {
  510. // Column loop
  511. for( int column = 0; column < 4; column ++ )
  512. {
  513. // Initialize element
  514. resultMatrix[row][column ] = 0F;
  515. for( int sumIndx = 0; sumIndx < 4; sumIndx ++ )
  516. {
  517. // Find matrix element
  518. if( order == MatrixOrder.Prepend )
  519. {
  520. // Order of matrix multiplication
  521. resultMatrix[row][column ] += _mainMatrix[row][sumIndx ] * mulMatrix[sumIndx][column];
  522. }
  523. else
  524. {
  525. // Order of matrix multiplication
  526. resultMatrix[row][column] += mulMatrix[row][sumIndx] * _mainMatrix[sumIndx][column];
  527. }
  528. }
  529. }
  530. }
  531. // Set result matrix to be main matrix
  532. if( setMainMatrix )
  533. {
  534. _mainMatrix = resultMatrix;
  535. }
  536. return resultMatrix;
  537. }
  538. /// <summary>
  539. /// Multiplies this Matrix object by the Vector specified in the
  540. /// vector parameter.
  541. /// </summary>
  542. /// <param name="mulVector">The vector object by which this Matrix object is to be multiplied.</param>
  543. /// <param name="resultVector">Vector which is result of matrix and vector multiplication.</param>
  544. private void MultiplyVector( float [] mulVector, ref float [] resultVector )
  545. {
  546. // Row loop
  547. for( int row = 0; row < 3; row ++ )
  548. {
  549. // Initialize element
  550. resultVector[ row ] = 0F;
  551. // Column loop
  552. for( int column = 0; column < 4; column ++ )
  553. {
  554. // Find matrix element
  555. resultVector[ row ] += _mainMatrix[row][column] * mulVector[ column ];
  556. }
  557. }
  558. }
  559. /// <summary>
  560. /// Prepend to this Matrix object a clockwise rotation, around the axis and by the specified angle.
  561. /// </summary>
  562. /// <param name="angle">Angle to rotate</param>
  563. /// <param name="axis">Axis used for rotation</param>
  564. private void Rotate( double angle, RotationAxis axis )
  565. {
  566. float [][] rotationMatrix = new float[4][];
  567. rotationMatrix[0] = new float[4];
  568. rotationMatrix[1] = new float[4];
  569. rotationMatrix[2] = new float[4];
  570. rotationMatrix[3] = new float[4];
  571. // Change angle direction
  572. angle = -1F * angle;
  573. // Matrix initialization
  574. // Row loop
  575. for( int row = 0; row < 4; row ++ )
  576. {
  577. // Column loop
  578. for( int column = 0; column < 4; column ++ )
  579. {
  580. // For initialization: Diagonal matrix elements are equal to one
  581. // and all other elements are equal to zero.
  582. if( row == column )
  583. {
  584. rotationMatrix[row][column] = 1F;
  585. }
  586. else
  587. {
  588. rotationMatrix[row][column] = 0F;
  589. }
  590. }
  591. }
  592. // Rotation about axis
  593. switch( axis )
  594. {
  595. // Rotation about X axis
  596. case RotationAxis.X:
  597. rotationMatrix[1][1] = (float)Math.Cos( angle );
  598. rotationMatrix[1][2] = (float)-Math.Sin( angle );
  599. rotationMatrix[2][1] = (float)Math.Sin( angle );
  600. rotationMatrix[2][2] = (float)Math.Cos( angle );
  601. break;
  602. // Rotation about Y axis
  603. case RotationAxis.Y:
  604. rotationMatrix[0][0] = (float)Math.Cos( angle );
  605. rotationMatrix[0][2] = (float)Math.Sin( angle );
  606. rotationMatrix[2][0] = (float)-Math.Sin( angle );
  607. rotationMatrix[2][2] = (float)Math.Cos( angle );
  608. break;
  609. // Rotation about Z axis
  610. case RotationAxis.Z:
  611. rotationMatrix[0][0] = (float)Math.Cos( angle );
  612. rotationMatrix[0][1] = (float)-Math.Sin( angle );
  613. rotationMatrix[1][0] = (float)Math.Sin( angle );
  614. rotationMatrix[1][1] = (float)Math.Cos( angle );
  615. break;
  616. }
  617. // Rotate Main matrix
  618. Multiply( rotationMatrix, MatrixOrder.Prepend, true );
  619. }
  620. /// <summary>
  621. /// Returns transformed x and y values from x, y and z values
  622. /// and composed main matrix values (All rotations,
  623. /// translations and scaling).
  624. /// </summary>
  625. /// <param name="points">Array of 3D points.</param>
  626. private void GetValues( Point3D [] points )
  627. {
  628. // Create one dimensional matrix (vector)
  629. float [] inputVector = new float[4];
  630. // A vector which is result of matrix and vector multiplication
  631. float [] resultVector = new float[4];
  632. foreach( Point3D point in points )
  633. {
  634. // Fill input vector with x, y and z coordinates
  635. inputVector[0] = point.X;
  636. inputVector[1] = point.Y;
  637. inputVector[2] = point.Z;
  638. inputVector[3] = 1;
  639. // Apply 3D transformations.
  640. MultiplyVector( inputVector, ref resultVector );
  641. // Return x and y coordinates.
  642. point.X = resultVector[0];
  643. point.Y = resultVector[1];
  644. point.Z = resultVector[2];
  645. }
  646. }
  647. /// <summary>
  648. /// Set points for 3D Bar which represents 3D Chart Area.
  649. /// </summary>
  650. /// <param name="dx">Width of the bar 3D.</param>
  651. /// <param name="dy">Height of the bar 3D.</param>
  652. /// <param name="dz">Depth of the bar 3D.</param>
  653. /// <returns>Collection of Points 3D.</returns>
  654. private Point3D [] Set3DBarPoints( float dx, float dy, float dz )
  655. {
  656. Point3D [] points = new Point3D[8];
  657. // ********************************************
  658. // 3D Bar side: Front
  659. // ********************************************
  660. points[0] = new Point3D(-dx/2, -dy/2, dz/2);
  661. points[1] = new Point3D(dx/2, -dy/2, dz/2);
  662. points[2] = new Point3D(dx/2, dy/2, dz/2);
  663. points[3] = new Point3D(-dx/2, dy/2, dz/2);
  664. // ********************************************
  665. // 3D Bar side: Back
  666. // ********************************************
  667. points[4] = new Point3D(-dx/2, -dy/2, -dz/2);
  668. points[5] = new Point3D(dx/2, -dy/2, -dz/2);
  669. points[6] = new Point3D(dx/2, dy/2, -dz/2);
  670. points[7] = new Point3D(-dx/2, dy/2, -dz/2);
  671. return points;
  672. }
  673. #endregion // Private Methods
  674. #region Lighting Methods
  675. /// <summary>
  676. /// Initial Lighting. Use matrix transformation only once
  677. /// for Normal vectors.
  678. /// </summary>
  679. /// <param name="lightStyle">LightStyle Style</param>
  680. internal void InitLight( LightStyle lightStyle )
  681. {
  682. // Set LightStyle Style
  683. this._lightStyle = lightStyle;
  684. // Center of rotation
  685. _lightVectors[0] = new Point3D( 0F, 0F, 0F );
  686. // Front side normal Vector.
  687. _lightVectors[1] = new Point3D( 0F, 0F, 1F );
  688. // Back side normal Vector.
  689. _lightVectors[2] = new Point3D( 0F, 0F, -1F );
  690. // Left side normal Vector.
  691. _lightVectors[3] = new Point3D( -1F, 0F, 0F );
  692. // Right side normal Vector.
  693. _lightVectors[4] = new Point3D( 1F, 0F, 0F );
  694. // Top side normal Vector.
  695. _lightVectors[5] = new Point3D( 0F, -1F, 0F );
  696. // Bottom side normal Vector.
  697. _lightVectors[6] = new Point3D( 0F, 1F, 0F );
  698. // Apply matrix transformations
  699. TransformPoints( _lightVectors, false );
  700. // ********************************************************
  701. // LightStyle Vector and normal vectors have to have same center.
  702. // Shift Normal vectors.
  703. // ********************************************************
  704. // Front Side shift
  705. _lightVectors[1].X -= _lightVectors[0].X;
  706. _lightVectors[1].Y -= _lightVectors[0].Y;
  707. _lightVectors[1].Z -= _lightVectors[0].Z;
  708. // Back Side shift
  709. _lightVectors[2].X -= _lightVectors[0].X;
  710. _lightVectors[2].Y -= _lightVectors[0].Y;
  711. _lightVectors[2].Z -= _lightVectors[0].Z;
  712. // Left Side shift
  713. _lightVectors[3].X -= _lightVectors[0].X;
  714. _lightVectors[3].Y -= _lightVectors[0].Y;
  715. _lightVectors[3].Z -= _lightVectors[0].Z;
  716. // Right Side shift
  717. _lightVectors[4].X -= _lightVectors[0].X;
  718. _lightVectors[4].Y -= _lightVectors[0].Y;
  719. _lightVectors[4].Z -= _lightVectors[0].Z;
  720. // Top Side shift
  721. _lightVectors[5].X -= _lightVectors[0].X;
  722. _lightVectors[5].Y -= _lightVectors[0].Y;
  723. _lightVectors[5].Z -= _lightVectors[0].Z;
  724. // Bottom Side shift
  725. _lightVectors[6].X -= _lightVectors[0].X;
  726. _lightVectors[6].Y -= _lightVectors[0].Y;
  727. _lightVectors[6].Z -= _lightVectors[0].Z;
  728. }
  729. /// <summary>
  730. /// Return intensity of lightStyle for 3D Cube. There are tree types of lights: None,
  731. /// Simplistic and Realistic. None Style have same lightStyle intensity on
  732. /// all polygons. Normal vector doesn’t have influence on this type
  733. /// of lighting. Simplistic style have lightStyle source, which is
  734. /// rotated together with scene. Realistic lighting have fixed lightStyle
  735. /// source and intensity of lightStyle is change when scene is rotated.
  736. /// </summary>
  737. /// <param name="surfaceColor">Color used for polygons without lighting</param>
  738. /// <param name="front">Color corrected with intensity of lightStyle for Front side of the 3D Rectangle</param>
  739. /// <param name="back">Color corrected with intensity of lightStyle for Back side of the 3D Rectangle</param>
  740. /// <param name="left">Color corrected with intensity of lightStyle for Left side of the 3D Rectangle</param>
  741. /// <param name="right">Color corrected with intensity of lightStyle for Right side of the 3D Rectangle</param>
  742. /// <param name="top">Color corrected with intensity of lightStyle for Top side of the 3D Rectangle</param>
  743. /// <param name="bottom">Color corrected with intensity of lightStyle for Bottom side of the 3D Rectangle</param>
  744. internal void GetLight( Color surfaceColor, out Color front, out Color back, out Color left, out Color right, out Color top, out Color bottom )
  745. {
  746. switch( _lightStyle )
  747. {
  748. // LightStyle style is None
  749. case LightStyle.None:
  750. {
  751. front = surfaceColor;
  752. left = surfaceColor;
  753. top = surfaceColor;
  754. back = surfaceColor;
  755. right = surfaceColor;
  756. bottom = surfaceColor;
  757. break;
  758. }
  759. // LightStyle style is Simplistic
  760. case LightStyle.Simplistic:
  761. {
  762. front = surfaceColor;
  763. left = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.25);
  764. top = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
  765. back = surfaceColor;
  766. right = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.25);
  767. bottom = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
  768. break;
  769. }
  770. // LightStyle style is Realistic
  771. default:
  772. {
  773. // For Right Axis angle Realistic lightStyle should be different
  774. if( _rightAngleAxis )
  775. {
  776. // LightStyle source Vector
  777. Point3D lightSource = new Point3D( 0F, 0F, -1F );
  778. Point3D [] rightPRpoints = new Point3D[1];
  779. rightPRpoints[0] = lightSource;
  780. RightAngleProjection(rightPRpoints);
  781. // ******************************************************************
  782. // Color correction. Angle between Normal vector of polygon and
  783. // vector of lightStyle source is used.
  784. // ******************************************************************
  785. if( this._angleY >= 45 || this._angleY <= -45 )
  786. {
  787. front = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[1])/Math.PI );
  788. back = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[2])/Math.PI );
  789. left = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0 );
  790. right = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0 );
  791. }
  792. else
  793. {
  794. front = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0 );
  795. back = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 1 );
  796. left = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[3])/Math.PI );
  797. right = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[4])/Math.PI );
  798. }
  799. top = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[5])/Math.PI );
  800. bottom = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, GetAngle(lightSource,_lightVectors[6])/Math.PI );
  801. }
  802. else
  803. {
  804. // LightStyle source Vector
  805. Point3D lightSource = new Point3D( 0F, 0F, 1F );
  806. // ******************************************************************
  807. // Color correction. Angle between Normal vector of polygon and
  808. // vector of lightStyle source is used.
  809. // ******************************************************************
  810. front = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[1])/Math.PI );
  811. back = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[2])/Math.PI );
  812. left = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[3])/Math.PI );
  813. right = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[4])/Math.PI );
  814. top = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[5])/Math.PI );
  815. bottom = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[6])/Math.PI );
  816. }
  817. break;
  818. }
  819. }
  820. }
  821. /// <summary>
  822. /// Return intensity of lightStyle for Polygons. There are tree types of lights: None,
  823. /// Simplistic and Realistic. None Style have same lightStyle intensity on
  824. /// all polygons. Normal vector doesn’t have influence on this type
  825. /// of lighting. Simplistic style have lightStyle source, which is
  826. /// rotated together with scene. Realistic lighting have fixed lightStyle
  827. /// source and intensity of lightStyle is change when scene is rotated.
  828. /// </summary>
  829. /// <param name="points">Points of the polygon</param>
  830. /// <param name="surfaceColor">Color used for polygons without lighting</param>
  831. /// <param name="visiblePolygon">This flag gets information if polygon is visible or not.</param>
  832. /// <param name="rotation">Y angle ( from -90 to 90 ) Should be used width switchSeriesOrder to get from -180 to 180</param>
  833. /// <param name="surfaceName">Used for lighting of front - back and left - right sides</param>
  834. /// <param name="switchSeriesOrder">Used to calculate real y angle</param>
  835. /// <returns>Color corrected with intensity of lightStyle</returns>
  836. internal Color GetPolygonLight(Point3D[] points, Color surfaceColor, bool visiblePolygon, float rotation, SurfaceNames surfaceName, bool switchSeriesOrder)
  837. {
  838. // Corrected color
  839. Color color = surfaceColor;
  840. // Direction of lightStyle source
  841. Point3D lightSource;
  842. lightSource = new Point3D( 0F, 0F, 1F );
  843. // There are tree different lightStyle styles: None, Simplistic and realistic.
  844. switch( _lightStyle )
  845. {
  846. // LightStyle style is None
  847. case LightStyle.None:
  848. {
  849. // Use same color
  850. break;
  851. }
  852. // LightStyle style is Simplistic
  853. case LightStyle.Simplistic:
  854. {
  855. // Find two vectors of polygon
  856. Point3D firstVector = new Point3D();
  857. firstVector.X = points[0].X - points[1].X;
  858. firstVector.Y = points[0].Y - points[1].Y;
  859. firstVector.Z = points[0].Z - points[1].Z;
  860. Point3D secondVector = new Point3D();
  861. secondVector.X = points[2].X - points[1].X;
  862. secondVector.Y = points[2].Y - points[1].Y;
  863. secondVector.Z = points[2].Z - points[1].Z;
  864. // Find Normal vector for Polygon
  865. Point3D normalVector = new Point3D();
  866. normalVector.X = firstVector.Y * secondVector.Z - firstVector.Z * secondVector.Y;
  867. normalVector.Y = firstVector.Z * secondVector.X - firstVector.X * secondVector.Z;
  868. normalVector.Z = firstVector.X * secondVector.Y - firstVector.Y * secondVector.X;
  869. // Polygon is left side ( like side of area chart )
  870. if( surfaceName == SurfaceNames.Left )
  871. {
  872. color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
  873. }
  874. // Polygon is right side ( like side of area chart )
  875. else if( surfaceName == SurfaceNames.Right )
  876. {
  877. color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
  878. }
  879. // Polygon is front side ( like side of area chart )
  880. else if( surfaceName == SurfaceNames.Front )
  881. {
  882. color = surfaceColor;
  883. }
  884. // Polygon is back side ( like side of area chart )
  885. else if( surfaceName == SurfaceNames.Back )
  886. {
  887. color = surfaceColor;
  888. }
  889. // Polygon has angle with bottom side ( Line chart or top of area chart )
  890. else
  891. {
  892. float angleLeft;
  893. float angleRight;
  894. // Find angles between lightStyle and polygon for different y-axis angles.
  895. if( switchSeriesOrder )
  896. {
  897. if (rotation > 0 && rotation <= 90)
  898. {
  899. angleLeft = GetAngle( normalVector, _lightVectors[3] );
  900. angleRight = GetAngle( normalVector, _lightVectors[4] );
  901. }
  902. else
  903. {
  904. angleLeft = GetAngle( normalVector, _lightVectors[4] );
  905. angleRight = GetAngle( normalVector, _lightVectors[3] );
  906. }
  907. }
  908. else
  909. {
  910. if (rotation > 0 && rotation <= 90)
  911. {
  912. angleLeft = GetAngle( normalVector, _lightVectors[4] );
  913. angleRight = GetAngle( normalVector, _lightVectors[3] );
  914. }
  915. else
  916. {
  917. angleLeft = GetAngle( normalVector, _lightVectors[3] );
  918. angleRight = GetAngle( normalVector, _lightVectors[4] );
  919. }
  920. }
  921. if( Math.Abs( angleLeft - angleRight ) < 0.01 )
  922. {
  923. color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.25);
  924. }
  925. else if( angleLeft < angleRight )
  926. {
  927. color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.25);
  928. }
  929. else
  930. {
  931. color = ChartGraphics.GetGradientColor( surfaceColor, Color.Black, 0.15);
  932. }
  933. }
  934. break;
  935. }
  936. // LightStyle style is Realistic
  937. default:
  938. {
  939. // Find two vectors of polygon
  940. Point3D firstVector = new Point3D();
  941. firstVector.X = points[0].X - points[1].X;
  942. firstVector.Y = points[0].Y - points[1].Y;
  943. firstVector.Z = points[0].Z - points[1].Z;
  944. Point3D secondVector = new Point3D();
  945. secondVector.X = points[2].X - points[1].X;
  946. secondVector.Y = points[2].Y - points[1].Y;
  947. secondVector.Z = points[2].Z - points[1].Z;
  948. // Find Normal vector for Polygon
  949. Point3D normalVector = new Point3D();
  950. normalVector.X = firstVector.Y * secondVector.Z - firstVector.Z * secondVector.Y;
  951. normalVector.Y = firstVector.Z * secondVector.X - firstVector.X * secondVector.Z;
  952. normalVector.Z = firstVector.X * secondVector.Y - firstVector.Y * secondVector.X;
  953. // ******************************************************************
  954. // Color correction. Angle between Normal vector of polygon and
  955. // vector of lightStyle source is used.
  956. // ******************************************************************
  957. if( surfaceName == SurfaceNames.Front )
  958. {
  959. lightSource.Z *= -1;
  960. color = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[2])/Math.PI );
  961. }
  962. else if( surfaceName == SurfaceNames.Back )
  963. {
  964. lightSource.Z *= -1;
  965. color = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,_lightVectors[1])/Math.PI );
  966. }
  967. else
  968. {
  969. if( visiblePolygon )
  970. {
  971. lightSource.Z *= -1;
  972. }
  973. color = GetBrightGradientColor( surfaceColor, GetAngle(lightSource,normalVector)/Math.PI );
  974. }
  975. break;
  976. }
  977. }
  978. return color;
  979. }
  980. /// <summary>
  981. /// This method creates gradien color with brightnes.
  982. /// </summary>
  983. /// <param name="beginColor">Start color for gradient.</param>
  984. /// <param name="position">Position used between Start and end color.</param>
  985. /// <returns>Calculated Gradient color from gradient position</returns>
  986. private Color GetBrightGradientColor( Color beginColor, double position )
  987. {
  988. position = position * 2;
  989. double brightness = 0.5;
  990. if( position < brightness )
  991. {
  992. return ChartGraphics.GetGradientColor( Color.FromArgb(beginColor.A,255,255,255), beginColor, 1 - brightness + position );
  993. }
  994. else if( -brightness + position < 1 )
  995. {
  996. return ChartGraphics.GetGradientColor( beginColor, Color.Black, -brightness + position );
  997. }
  998. else
  999. {
  1000. return Color.FromArgb( beginColor.A, 0, 0, 0 );
  1001. }
  1002. }
  1003. /// <summary>
  1004. /// Returns the angle between two 3D vectors (a and b);
  1005. /// </summary>
  1006. /// <param name="a">First vector</param>
  1007. /// <param name="b">Second Vector</param>
  1008. /// <returns>Angle between vectors</returns>
  1009. private float GetAngle(Point3D a,Point3D b)
  1010. {
  1011. double angle;
  1012. 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 ) ) );
  1013. return (float)angle;
  1014. }
  1015. #endregion
  1016. }
  1017. }