Math.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. /* *
  2. * (c) 2010-2019 Torstein Honsi
  3. *
  4. * License: www.highcharts.com/license
  5. */
  6. 'use strict';
  7. import H from '../parts/Globals.js';
  8. import '../parts/Utilities.js';
  9. // Mathematical Functionility
  10. var deg2rad = H.deg2rad,
  11. pick = H.pick;
  12. /* eslint-disable max-len */
  13. /**
  14. * Apply 3-D rotation
  15. * Euler Angles (XYZ):
  16. * cosA = cos(Alfa|Roll)
  17. * cosB = cos(Beta|Pitch)
  18. * cosG = cos(Gamma|Yaw)
  19. *
  20. * Composite rotation:
  21. * | cosB * cosG | cosB * sinG | -sinB |
  22. * | sinA * sinB * cosG - cosA * sinG | sinA * sinB * sinG + cosA * cosG | sinA * cosB |
  23. * | cosA * sinB * cosG + sinA * sinG | cosA * sinB * sinG - sinA * cosG | cosA * cosB |
  24. *
  25. * Now, Gamma/Yaw is not used (angle=0), so we assume cosG = 1 and sinG = 0, so
  26. * we get:
  27. * | cosB | 0 | - sinB |
  28. * | sinA * sinB | cosA | sinA * cosB |
  29. * | cosA * sinB | - sinA | cosA * cosB |
  30. *
  31. * But in browsers, y is reversed, so we get sinA => -sinA. The general result
  32. * is:
  33. * | cosB | 0 | - sinB | | x | | px |
  34. * | - sinA * sinB | cosA | - sinA * cosB | x | y | = | py |
  35. * | cosA * sinB | sinA | cosA * cosB | | z | | pz |
  36. *
  37. * @private
  38. * @function rotate3D
  39. */
  40. /* eslint-enable max-len */
  41. function rotate3D(x, y, z, angles) {
  42. return {
  43. x: angles.cosB * x - angles.sinB * z,
  44. y: -angles.sinA * angles.sinB * x + angles.cosA * y -
  45. angles.cosB * angles.sinA * z,
  46. z: angles.cosA * angles.sinB * x + angles.sinA * y +
  47. angles.cosA * angles.cosB * z
  48. };
  49. }
  50. // Perspective3D function is available in global Highcharts scope because is
  51. // needed also outside of perspective() function (#8042).
  52. H.perspective3D = function (coordinate, origin, distance) {
  53. var projection = ((distance > 0) && (distance < Number.POSITIVE_INFINITY)) ?
  54. distance / (coordinate.z + origin.z + distance) :
  55. 1;
  56. return {
  57. x: coordinate.x * projection,
  58. y: coordinate.y * projection
  59. };
  60. };
  61. /**
  62. * Transforms a given array of points according to the angles in chart.options.
  63. *
  64. * @private
  65. * @function Highcharts.perspective
  66. *
  67. * @param {Array<Highcharts.Point>} points
  68. * The array of points
  69. *
  70. * @param {Highcharts.Chart} chart
  71. * The chart
  72. *
  73. * @param {boolean} [insidePlotArea]
  74. * Wether to verifiy the points are inside the plotArea
  75. *
  76. * @return {Array<Highcharts.Point>}
  77. * An array of transformed points
  78. */
  79. H.perspective = function (points, chart, insidePlotArea) {
  80. var options3d = chart.options.chart.options3d,
  81. inverted = insidePlotArea ? chart.inverted : false,
  82. origin = {
  83. x: chart.plotWidth / 2,
  84. y: chart.plotHeight / 2,
  85. z: options3d.depth / 2,
  86. vd: pick(options3d.depth, 1) * pick(options3d.viewDistance, 0)
  87. },
  88. scale = chart.scale3d || 1,
  89. beta = deg2rad * options3d.beta * (inverted ? -1 : 1),
  90. alpha = deg2rad * options3d.alpha * (inverted ? -1 : 1),
  91. angles = {
  92. cosA: Math.cos(alpha),
  93. cosB: Math.cos(-beta),
  94. sinA: Math.sin(alpha),
  95. sinB: Math.sin(-beta)
  96. };
  97. if (!insidePlotArea) {
  98. origin.x += chart.plotLeft;
  99. origin.y += chart.plotTop;
  100. }
  101. // Transform each point
  102. return points.map(function (point) {
  103. var rotated = rotate3D(
  104. (inverted ? point.y : point.x) - origin.x,
  105. (inverted ? point.x : point.y) - origin.y,
  106. (point.z || 0) - origin.z,
  107. angles
  108. ),
  109. // Apply perspective
  110. coordinate = H.perspective3D(rotated, origin, origin.vd);
  111. // Apply translation
  112. coordinate.x = coordinate.x * scale + origin.x;
  113. coordinate.y = coordinate.y * scale + origin.y;
  114. coordinate.z = rotated.z * scale + origin.z;
  115. return {
  116. x: (inverted ? coordinate.y : coordinate.x),
  117. y: (inverted ? coordinate.x : coordinate.y),
  118. z: coordinate.z
  119. };
  120. });
  121. };
  122. /**
  123. * Calculate a distance from camera to points - made for calculating zIndex of
  124. * scatter points.
  125. *
  126. * @private
  127. * @function Highcharts.pointCameraDistance
  128. *
  129. * @param {object} coordinates
  130. * The coordinates of the specific point
  131. *
  132. * @param {Highcharts.Chart} chart
  133. * The chart
  134. *
  135. * @return {number}
  136. * A distance from camera to point
  137. */
  138. H.pointCameraDistance = function (coordinates, chart) {
  139. var options3d = chart.options.chart.options3d,
  140. cameraPosition = {
  141. x: chart.plotWidth / 2,
  142. y: chart.plotHeight / 2,
  143. z: pick(options3d.depth, 1) * pick(options3d.viewDistance, 0) +
  144. options3d.depth
  145. },
  146. distance = Math.sqrt(
  147. Math.pow(cameraPosition.x - coordinates.plotX, 2) +
  148. Math.pow(cameraPosition.y - coordinates.plotY, 2) +
  149. Math.pow(cameraPosition.z - coordinates.plotZ, 2)
  150. );
  151. return distance;
  152. };
  153. /**
  154. * Calculate area of a 2D polygon using Shoelace algorithm
  155. * http://en.wikipedia.org/wiki/Shoelace_formula
  156. *
  157. * @private
  158. * @function Highcharts.shapeArea
  159. *
  160. * @param {Array<object>} vertexes
  161. *
  162. * @return {number}
  163. */
  164. H.shapeArea = function (vertexes) {
  165. var area = 0,
  166. i,
  167. j;
  168. for (i = 0; i < vertexes.length; i++) {
  169. j = (i + 1) % vertexes.length;
  170. area += vertexes[i].x * vertexes[j].y - vertexes[j].x * vertexes[i].y;
  171. }
  172. return area / 2;
  173. };
  174. /**
  175. * Calculate area of a 3D polygon after perspective projection
  176. *
  177. * @private
  178. * @function Highcharts.shapeArea3d
  179. *
  180. * @param {Array<object>} vertexes
  181. *
  182. * @param {Highcharts.Chart} chart
  183. *
  184. * @param {boolean} [insidePlotArea]
  185. *
  186. * @return {number}
  187. */
  188. H.shapeArea3d = function (vertexes, chart, insidePlotArea) {
  189. return H.shapeArea(H.perspective(vertexes, chart, insidePlotArea));
  190. };