TilemapShapes.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. /* *
  2. *
  3. * Tilemaps module
  4. *
  5. * (c) 2010-2021 Highsoft AS
  6. * Author: Øystein Moseng
  7. *
  8. * License: www.highcharts.com/license
  9. *
  10. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  11. *
  12. * */
  13. 'use strict';
  14. import H from '../../Core/Globals.js';
  15. var noop = H.noop;
  16. import SeriesRegistry from '../../Core/Series/SeriesRegistry.js';
  17. var _a = SeriesRegistry.seriesTypes, HeatmapSeries = _a.heatmap, ScatterSeries = _a.scatter;
  18. import U from '../../Core/Utilities.js';
  19. var clamp = U.clamp, pick = U.pick;
  20. /**
  21. * Utility func to get padding definition from tile size division
  22. * @private
  23. * @param {Highcharts.TilemapSeries} series
  24. * series
  25. * @param {Highcharts.number} xDiv
  26. * xDiv
  27. * @param {Highcharts.number} yDiv
  28. * yDiv
  29. * @return {Highcharts.TilemapPaddingObject}
  30. */
  31. function tilePaddingFromTileSize(series, xDiv, yDiv) {
  32. var options = series.options;
  33. return {
  34. xPad: (options.colsize || 1) / -xDiv,
  35. yPad: (options.rowsize || 1) / -yDiv
  36. };
  37. }
  38. /* *
  39. *
  40. * Registry
  41. *
  42. * */
  43. /**
  44. * Map of shape types.
  45. * @private
  46. */
  47. var TilemapShapes = {
  48. // Hexagon shape type.
  49. hexagon: {
  50. alignDataLabel: ScatterSeries.prototype.alignDataLabel,
  51. getSeriesPadding: function (series) {
  52. return tilePaddingFromTileSize(series, 3, 2);
  53. },
  54. haloPath: function (size) {
  55. if (!size) {
  56. return [];
  57. }
  58. var hexagon = this.tileEdges;
  59. return [
  60. ['M', hexagon.x2 - size, hexagon.y1 + size],
  61. ['L', hexagon.x3 + size, hexagon.y1 + size],
  62. ['L', hexagon.x4 + size * 1.5, hexagon.y2],
  63. ['L', hexagon.x3 + size, hexagon.y3 - size],
  64. ['L', hexagon.x2 - size, hexagon.y3 - size],
  65. ['L', hexagon.x1 - size * 1.5, hexagon.y2],
  66. ['Z']
  67. ];
  68. },
  69. translate: function () {
  70. var series = this, options = series.options, xAxis = series.xAxis, yAxis = series.yAxis, seriesPointPadding = options.pointPadding || 0, xPad = (options.colsize || 1) / 3, yPad = (options.rowsize || 1) / 2, yShift;
  71. series.generatePoints();
  72. series.points.forEach(function (point) {
  73. var x1 = clamp(Math.floor(xAxis.len -
  74. xAxis.translate(point.x - xPad * 2, 0, 1, 0, 1)), -xAxis.len, 2 * xAxis.len), x2 = clamp(Math.floor(xAxis.len -
  75. xAxis.translate(point.x - xPad, 0, 1, 0, 1)), -xAxis.len, 2 * xAxis.len), x3 = clamp(Math.floor(xAxis.len -
  76. xAxis.translate(point.x + xPad, 0, 1, 0, 1)), -xAxis.len, 2 * xAxis.len), x4 = clamp(Math.floor(xAxis.len -
  77. xAxis.translate(point.x + xPad * 2, 0, 1, 0, 1)), -xAxis.len, 2 * xAxis.len), y1 = clamp(Math.floor(yAxis.translate(point.y - yPad, 0, 1, 0, 1)), -yAxis.len, 2 * yAxis.len), y2 = clamp(Math.floor(yAxis.translate(point.y, 0, 1, 0, 1)), -yAxis.len, 2 * yAxis.len), y3 = clamp(Math.floor(yAxis.translate(point.y + yPad, 0, 1, 0, 1)), -yAxis.len, 2 * yAxis.len), pointPadding = pick(point.pointPadding, seriesPointPadding),
  78. // We calculate the point padding of the midpoints to
  79. // preserve the angles of the shape.
  80. midPointPadding = pointPadding *
  81. Math.abs(x2 - x1) / Math.abs(y3 - y2), xMidPadding = xAxis.reversed ?
  82. -midPointPadding : midPointPadding, xPointPadding = xAxis.reversed ?
  83. -pointPadding : pointPadding, yPointPadding = yAxis.reversed ?
  84. -pointPadding : pointPadding;
  85. // Shift y-values for every second grid column
  86. if (point.x % 2) {
  87. yShift = yShift || Math.round(Math.abs(y3 - y1) / 2) *
  88. // We have to reverse the shift for reversed y-axes
  89. (yAxis.reversed ? -1 : 1);
  90. y1 += yShift;
  91. y2 += yShift;
  92. y3 += yShift;
  93. }
  94. // Set plotX and plotY for use in K-D-Tree and more
  95. point.plotX = point.clientX = (x2 + x3) / 2;
  96. point.plotY = y2;
  97. // Apply point padding to translated coordinates
  98. x1 += xMidPadding + xPointPadding;
  99. x2 += xPointPadding;
  100. x3 -= xPointPadding;
  101. x4 -= xMidPadding + xPointPadding;
  102. y1 -= yPointPadding;
  103. y3 += yPointPadding;
  104. // Store points for halo creation
  105. point.tileEdges = {
  106. x1: x1, x2: x2, x3: x3, x4: x4, y1: y1, y2: y2, y3: y3
  107. };
  108. // Finally set the shape for this point
  109. point.shapeType = 'path';
  110. point.shapeArgs = {
  111. d: [
  112. ['M', x2, y1],
  113. ['L', x3, y1],
  114. ['L', x4, y2],
  115. ['L', x3, y3],
  116. ['L', x2, y3],
  117. ['L', x1, y2],
  118. ['Z']
  119. ]
  120. };
  121. });
  122. series.translateColors();
  123. }
  124. },
  125. // Diamond shape type.
  126. diamond: {
  127. alignDataLabel: ScatterSeries.prototype.alignDataLabel,
  128. getSeriesPadding: function (series) {
  129. return tilePaddingFromTileSize(series, 2, 2);
  130. },
  131. haloPath: function (size) {
  132. if (!size) {
  133. return [];
  134. }
  135. var diamond = this.tileEdges;
  136. return [
  137. ['M', diamond.x2, diamond.y1 + size],
  138. ['L', diamond.x3 + size, diamond.y2],
  139. ['L', diamond.x2, diamond.y3 - size],
  140. ['L', diamond.x1 - size, diamond.y2],
  141. ['Z']
  142. ];
  143. },
  144. translate: function () {
  145. var series = this, options = series.options, xAxis = series.xAxis, yAxis = series.yAxis, seriesPointPadding = options.pointPadding || 0, xPad = (options.colsize || 1), yPad = (options.rowsize || 1) / 2, yShift;
  146. series.generatePoints();
  147. series.points.forEach(function (point) {
  148. var x1 = clamp(Math.round(xAxis.len -
  149. xAxis.translate(point.x - xPad, 0, 1, 0, 0)), -xAxis.len, 2 * xAxis.len), x2 = clamp(Math.round(xAxis.len -
  150. xAxis.translate(point.x, 0, 1, 0, 0)), -xAxis.len, 2 * xAxis.len), x3 = clamp(Math.round(xAxis.len -
  151. xAxis.translate(point.x + xPad, 0, 1, 0, 0)), -xAxis.len, 2 * xAxis.len), y1 = clamp(Math.round(yAxis.translate(point.y - yPad, 0, 1, 0, 0)), -yAxis.len, 2 * yAxis.len), y2 = clamp(Math.round(yAxis.translate(point.y, 0, 1, 0, 0)), -yAxis.len, 2 * yAxis.len), y3 = clamp(Math.round(yAxis.translate(point.y + yPad, 0, 1, 0, 0)), -yAxis.len, 2 * yAxis.len), pointPadding = pick(point.pointPadding, seriesPointPadding),
  152. // We calculate the point padding of the midpoints to
  153. // preserve the angles of the shape.
  154. midPointPadding = pointPadding *
  155. Math.abs(x2 - x1) / Math.abs(y3 - y2), xPointPadding = xAxis.reversed ?
  156. -midPointPadding : midPointPadding, yPointPadding = yAxis.reversed ?
  157. -pointPadding : pointPadding;
  158. // Shift y-values for every second grid column
  159. // We have to reverse the shift for reversed y-axes
  160. if (point.x % 2) {
  161. yShift = Math.abs(y3 - y1) / 2 * (yAxis.reversed ? -1 : 1);
  162. y1 += yShift;
  163. y2 += yShift;
  164. y3 += yShift;
  165. }
  166. // Set plotX and plotY for use in K-D-Tree and more
  167. point.plotX = point.clientX = x2;
  168. point.plotY = y2;
  169. // Apply point padding to translated coordinates
  170. x1 += xPointPadding;
  171. x3 -= xPointPadding;
  172. y1 -= yPointPadding;
  173. y3 += yPointPadding;
  174. // Store points for halo creation
  175. point.tileEdges = {
  176. x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3
  177. };
  178. // Set this point's shape parameters
  179. point.shapeType = 'path';
  180. point.shapeArgs = {
  181. d: [
  182. ['M', x2, y1],
  183. ['L', x3, y2],
  184. ['L', x2, y3],
  185. ['L', x1, y2],
  186. ['Z']
  187. ]
  188. };
  189. });
  190. series.translateColors();
  191. }
  192. },
  193. // Circle shape type.
  194. circle: {
  195. alignDataLabel: ScatterSeries.prototype.alignDataLabel,
  196. getSeriesPadding: function (series) {
  197. return tilePaddingFromTileSize(series, 2, 2);
  198. },
  199. haloPath: function (size) {
  200. return ScatterSeries.prototype.pointClass.prototype.haloPath
  201. .call(this, size + (size && this.radius));
  202. },
  203. translate: function () {
  204. var series = this, options = series.options, xAxis = series.xAxis, yAxis = series.yAxis, seriesPointPadding = options.pointPadding || 0, yRadius = (options.rowsize || 1) / 2, colsize = (options.colsize || 1), colsizePx, yRadiusPx, xRadiusPx, radius, forceNextRadiusCompute = false;
  205. series.generatePoints();
  206. series.points.forEach(function (point) {
  207. var x = clamp(Math.round(xAxis.len -
  208. xAxis.translate(point.x, 0, 1, 0, 0)), -xAxis.len, 2 * xAxis.len), y = clamp(Math.round(yAxis.translate(point.y, 0, 1, 0, 0)), -yAxis.len, 2 * yAxis.len), pointPadding = seriesPointPadding, hasPerPointPadding = false;
  209. // If there is point padding defined on a single point, add it
  210. if (typeof point.pointPadding !== 'undefined') {
  211. pointPadding = point.pointPadding;
  212. hasPerPointPadding = true;
  213. forceNextRadiusCompute = true;
  214. }
  215. // Find radius if not found already.
  216. // Use the smallest one (x vs y) to avoid overlap.
  217. // Note that the radius will be recomputed for each series.
  218. // Ideal (max) x radius is dependent on y radius:
  219. /*
  220. * (circle 2)
  221. * (circle 3)
  222. | yRadiusPx
  223. (circle 1) *-------|
  224. colsizePx
  225. The distance between circle 1 and 3 (and circle 2 and 3) is
  226. 2r, which is the hypotenuse of the triangle created by
  227. colsizePx and yRadiusPx. If the distance between circle 2
  228. and circle 1 is less than 2r, we use half of that distance
  229. instead (yRadiusPx).
  230. */
  231. if (!radius || forceNextRadiusCompute) {
  232. colsizePx = Math.abs(clamp(Math.floor(xAxis.len -
  233. xAxis.translate(point.x + colsize, 0, 1, 0, 0)), -xAxis.len, 2 * xAxis.len) - x);
  234. yRadiusPx = Math.abs(clamp(Math.floor(yAxis.translate(point.y + yRadius, 0, 1, 0, 0)), -yAxis.len, 2 * yAxis.len) - y);
  235. xRadiusPx = Math.floor(Math.sqrt((colsizePx * colsizePx + yRadiusPx * yRadiusPx)) / 2);
  236. radius = Math.min(colsizePx, xRadiusPx, yRadiusPx) - pointPadding;
  237. // If we have per point padding we need to always compute
  238. // the radius for this point and the next. If we used to
  239. // have per point padding but don't anymore, don't force
  240. // compute next radius.
  241. if (forceNextRadiusCompute && !hasPerPointPadding) {
  242. forceNextRadiusCompute = false;
  243. }
  244. }
  245. // Shift y-values for every second grid column.
  246. // Note that we always use the optimal y axis radius for this.
  247. // Also note: We have to reverse the shift for reversed y-axes.
  248. if (point.x % 2) {
  249. y += yRadiusPx * (yAxis.reversed ? -1 : 1);
  250. }
  251. // Set plotX and plotY for use in K-D-Tree and more
  252. point.plotX = point.clientX = x;
  253. point.plotY = y;
  254. // Save radius for halo
  255. point.radius = radius;
  256. // Set this point's shape parameters
  257. point.shapeType = 'circle';
  258. point.shapeArgs = {
  259. x: x,
  260. y: y,
  261. r: radius
  262. };
  263. });
  264. series.translateColors();
  265. }
  266. },
  267. // Square shape type.
  268. square: {
  269. alignDataLabel: HeatmapSeries.prototype.alignDataLabel,
  270. translate: HeatmapSeries.prototype.translate,
  271. getSeriesPadding: noop,
  272. haloPath: HeatmapSeries.prototype.pointClass.prototype.haloPath
  273. }
  274. };
  275. /* *
  276. *
  277. * Default Export
  278. *
  279. * */
  280. export default TilemapShapes;