Polygon.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. /* *
  2. *
  3. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  4. *
  5. * */
  6. import H from '../Core/Globals.js';
  7. /**
  8. * @private
  9. * @interface Highcharts.PolygonPointObject
  10. */ /**
  11. * @name Highcharts.PolygonPointObject#0
  12. * @type {number}
  13. */ /**
  14. * @name Highcharts.PolygonPointObject#1
  15. * @type {number}
  16. */
  17. /**
  18. * @private
  19. * @interface Highcharts.PolygonObject
  20. * @extends Array<Highcharts.PolygonPointObject>
  21. */ /**
  22. * @name Highcharts.PolygonObject#axes
  23. * @type {Array<PolygonPointObject>}
  24. */
  25. import U from '../Core/Utilities.js';
  26. var find = U.find, isArray = U.isArray, isNumber = U.isNumber;
  27. var deg2rad = H.deg2rad;
  28. /* eslint-disable no-invalid-this, valid-jsdoc */
  29. /**
  30. * Alternative solution to correctFloat.
  31. * E.g Highcharts.correctFloat(123, 2) returns 120, when it should be 123.
  32. *
  33. * @private
  34. * @function correctFloat
  35. * @param {number} number
  36. * @param {number} [precision]
  37. * @return {number}
  38. */
  39. var correctFloat = function (number, precision) {
  40. var p = isNumber(precision) ? precision : 14, magnitude = Math.pow(10, p);
  41. return Math.round(number * magnitude) / magnitude;
  42. };
  43. /**
  44. * Calculates the normals to a line between two points.
  45. *
  46. * @private
  47. * @function getNormals
  48. * @param {Highcharts.PolygonPointObject} p1
  49. * Start point for the line. Array of x and y value.
  50. * @param {Highcharts.PolygonPointObject} p2
  51. * End point for the line. Array of x and y value.
  52. * @return {Highcharts.PolygonObject}
  53. * Returns the two normals in an array.
  54. */
  55. var getNormals = function getNormal(p1, p2) {
  56. var dx = p2[0] - p1[0], // x2 - x1
  57. dy = p2[1] - p1[1]; // y2 - y1
  58. return [
  59. [-dy, dx],
  60. [dy, -dx]
  61. ];
  62. };
  63. /**
  64. * Calculates the dot product of two coordinates. The result is a scalar value.
  65. *
  66. * @private
  67. * @function dotProduct
  68. * @param {Highcharts.PolygonPointObject} a
  69. * The x and y coordinates of the first point.
  70. *
  71. * @param {Highcharts.PolygonPointObject} b
  72. * The x and y coordinates of the second point.
  73. *
  74. * @return {number}
  75. * Returns the dot product of a and b.
  76. */
  77. var dotProduct = function dotProduct(a, b) {
  78. var ax = a[0], ay = a[1], bx = b[0], by = b[1];
  79. return ax * bx + ay * by;
  80. };
  81. /**
  82. * Projects a polygon onto a coordinate.
  83. *
  84. * @private
  85. * @function project
  86. * @param {Highcharts.PolygonObject} polygon
  87. * Array of points in a polygon.
  88. * @param {Highcharts.PolygonPointObject} target
  89. * The coordinate of pr
  90. * @return {Highcharts.RangeObject}
  91. */
  92. var project = function project(polygon, target) {
  93. var products = polygon.map(function (point) {
  94. return dotProduct(point, target);
  95. });
  96. return {
  97. min: Math.min.apply(this, products),
  98. max: Math.max.apply(this, products)
  99. };
  100. };
  101. /**
  102. * Rotates a point clockwise around the origin.
  103. *
  104. * @private
  105. * @function rotate2DToOrigin
  106. * @param {Highcharts.PolygonPointObject} point
  107. * The x and y coordinates for the point.
  108. * @param {number} angle
  109. * The angle of rotation.
  110. * @return {Highcharts.PolygonPointObject}
  111. * The x and y coordinate for the rotated point.
  112. */
  113. var rotate2DToOrigin = function (point, angle) {
  114. var x = point[0], y = point[1], rad = deg2rad * -angle, cosAngle = Math.cos(rad), sinAngle = Math.sin(rad);
  115. return [
  116. correctFloat(x * cosAngle - y * sinAngle),
  117. correctFloat(x * sinAngle + y * cosAngle)
  118. ];
  119. };
  120. /**
  121. * Rotate a point clockwise around another point.
  122. *
  123. * @private
  124. * @function rotate2DToPoint
  125. * @param {Highcharts.PolygonPointObject} point
  126. * The x and y coordinates for the point.
  127. * @param {Highcharts.PolygonPointObject} origin
  128. * The point to rotate around.
  129. * @param {number} angle
  130. * The angle of rotation.
  131. * @return {Highcharts.PolygonPointObject}
  132. * The x and y coordinate for the rotated point.
  133. */
  134. var rotate2DToPoint = function (point, origin, angle) {
  135. var x = point[0] - origin[0], y = point[1] - origin[1], rotated = rotate2DToOrigin([x, y], angle);
  136. return [
  137. rotated[0] + origin[0],
  138. rotated[1] + origin[1]
  139. ];
  140. };
  141. /**
  142. * @private
  143. */
  144. var isAxesEqual = function (axis1, axis2) {
  145. return (axis1[0] === axis2[0] &&
  146. axis1[1] === axis2[1]);
  147. };
  148. /**
  149. * @private
  150. */
  151. var getAxesFromPolygon = function (polygon) {
  152. var points, axes = polygon.axes;
  153. if (!isArray(axes)) {
  154. axes = [];
  155. points = points = polygon.concat([polygon[0]]);
  156. points.reduce(function findAxis(p1, p2) {
  157. var normals = getNormals(p1, p2), axis = normals[0]; // Use the left normal as axis.
  158. // Check that the axis is unique.
  159. if (!find(axes, function (existing) {
  160. return isAxesEqual(existing, axis);
  161. })) {
  162. axes.push(axis);
  163. }
  164. // Return p2 to be used as p1 in next iteration.
  165. return p2;
  166. });
  167. polygon.axes = axes;
  168. }
  169. return axes;
  170. };
  171. /**
  172. * @private
  173. */
  174. var getAxes = function (polygon1, polygon2) {
  175. // Get the axis from both polygons.
  176. var axes1 = getAxesFromPolygon(polygon1), axes2 = getAxesFromPolygon(polygon2);
  177. return axes1.concat(axes2);
  178. };
  179. /**
  180. * @private
  181. */
  182. var getPolygon = function (x, y, width, height, rotation) {
  183. var origin = [x, y], left = x - (width / 2), right = x + (width / 2), top = y - (height / 2), bottom = y + (height / 2), polygon = [
  184. [left, top],
  185. [right, top],
  186. [right, bottom],
  187. [left, bottom]
  188. ];
  189. return polygon.map(function (point) {
  190. return rotate2DToPoint(point, origin, -rotation);
  191. });
  192. };
  193. /**
  194. * @private
  195. */
  196. var getBoundingBoxFromPolygon = function (points) {
  197. return points.reduce(function (obj, point) {
  198. var x = point[0], y = point[1];
  199. obj.left = Math.min(x, obj.left);
  200. obj.right = Math.max(x, obj.right);
  201. obj.bottom = Math.max(y, obj.bottom);
  202. obj.top = Math.min(y, obj.top);
  203. return obj;
  204. }, {
  205. left: Number.MAX_VALUE,
  206. right: -Number.MAX_VALUE,
  207. bottom: -Number.MAX_VALUE,
  208. top: Number.MAX_VALUE
  209. });
  210. };
  211. /**
  212. * @private
  213. */
  214. var isPolygonsOverlappingOnAxis = function (axis, polygon1, polygon2) {
  215. var projection1 = project(polygon1, axis), projection2 = project(polygon2, axis), isOverlapping = !(projection2.min > projection1.max ||
  216. projection2.max < projection1.min);
  217. return !isOverlapping;
  218. };
  219. /**
  220. * Checks wether two convex polygons are colliding by using the Separating Axis
  221. * Theorem.
  222. *
  223. * @private
  224. * @function isPolygonsColliding
  225. * @param {Highcharts.PolygonObject} polygon1
  226. * First polygon.
  227. *
  228. * @param {Highcharts.PolygonObject} polygon2
  229. * Second polygon.
  230. *
  231. * @return {boolean}
  232. * Returns true if they are colliding, otherwise false.
  233. */
  234. var isPolygonsColliding = function isPolygonsColliding(polygon1, polygon2) {
  235. var axes = getAxes(polygon1, polygon2), overlappingOnAllAxes = !find(axes, function (axis) {
  236. return isPolygonsOverlappingOnAxis(axis, polygon1, polygon2);
  237. });
  238. return overlappingOnAllAxes;
  239. };
  240. /**
  241. * @private
  242. */
  243. var movePolygon = function (deltaX, deltaY, polygon) {
  244. return polygon.map(function (point) {
  245. return [
  246. point[0] + deltaX,
  247. point[1] + deltaY
  248. ];
  249. });
  250. };
  251. var collision = {
  252. getBoundingBoxFromPolygon: getBoundingBoxFromPolygon,
  253. getPolygon: getPolygon,
  254. isPolygonsColliding: isPolygonsColliding,
  255. movePolygon: movePolygon,
  256. rotate2DToOrigin: rotate2DToOrigin,
  257. rotate2DToPoint: rotate2DToPoint
  258. };
  259. export default collision;