wordcloud.src.js 64 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639
  1. /**
  2. * @license Highcharts JS v9.1.1 (2021-06-04)
  3. *
  4. * (c) 2016-2021 Highsoft AS
  5. * Authors: Jon Arild Nygard
  6. *
  7. * License: www.highcharts.com/license
  8. */
  9. 'use strict';
  10. (function (factory) {
  11. if (typeof module === 'object' && module.exports) {
  12. factory['default'] = factory;
  13. module.exports = factory;
  14. } else if (typeof define === 'function' && define.amd) {
  15. define('highcharts/modules/wordcloud', ['highcharts'], function (Highcharts) {
  16. factory(Highcharts);
  17. factory.Highcharts = Highcharts;
  18. return factory;
  19. });
  20. } else {
  21. factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
  22. }
  23. }(function (Highcharts) {
  24. var _modules = Highcharts ? Highcharts._modules : {};
  25. function _registerModule(obj, path, args, fn) {
  26. if (!obj.hasOwnProperty(path)) {
  27. obj[path] = fn.apply(null, args);
  28. }
  29. }
  30. _registerModule(_modules, 'Mixins/Polygon.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  31. /* *
  32. *
  33. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  34. *
  35. * */
  36. /**
  37. * @private
  38. * @interface Highcharts.PolygonPointObject
  39. */ /**
  40. * @name Highcharts.PolygonPointObject#0
  41. * @type {number}
  42. */ /**
  43. * @name Highcharts.PolygonPointObject#1
  44. * @type {number}
  45. */
  46. /**
  47. * @private
  48. * @interface Highcharts.PolygonObject
  49. * @extends Array<Highcharts.PolygonPointObject>
  50. */ /**
  51. * @name Highcharts.PolygonObject#axes
  52. * @type {Array<PolygonPointObject>}
  53. */
  54. var find = U.find,
  55. isArray = U.isArray,
  56. isNumber = U.isNumber;
  57. var deg2rad = H.deg2rad;
  58. /* eslint-disable no-invalid-this, valid-jsdoc */
  59. /**
  60. * Alternative solution to correctFloat.
  61. * E.g Highcharts.correctFloat(123, 2) returns 120, when it should be 123.
  62. *
  63. * @private
  64. * @function correctFloat
  65. * @param {number} number
  66. * @param {number} [precision]
  67. * @return {number}
  68. */
  69. var correctFloat = function (number,
  70. precision) {
  71. var p = isNumber(precision) ? precision : 14,
  72. magnitude = Math.pow(10,
  73. p);
  74. return Math.round(number * magnitude) / magnitude;
  75. };
  76. /**
  77. * Calculates the normals to a line between two points.
  78. *
  79. * @private
  80. * @function getNormals
  81. * @param {Highcharts.PolygonPointObject} p1
  82. * Start point for the line. Array of x and y value.
  83. * @param {Highcharts.PolygonPointObject} p2
  84. * End point for the line. Array of x and y value.
  85. * @return {Highcharts.PolygonObject}
  86. * Returns the two normals in an array.
  87. */
  88. var getNormals = function getNormal(p1,
  89. p2) {
  90. var dx = p2[0] - p1[0], // x2 - x1
  91. dy = p2[1] - p1[1]; // y2 - y1
  92. return [
  93. [-dy,
  94. dx],
  95. [dy, -dx]
  96. ];
  97. };
  98. /**
  99. * Calculates the dot product of two coordinates. The result is a scalar value.
  100. *
  101. * @private
  102. * @function dotProduct
  103. * @param {Highcharts.PolygonPointObject} a
  104. * The x and y coordinates of the first point.
  105. *
  106. * @param {Highcharts.PolygonPointObject} b
  107. * The x and y coordinates of the second point.
  108. *
  109. * @return {number}
  110. * Returns the dot product of a and b.
  111. */
  112. var dotProduct = function dotProduct(a,
  113. b) {
  114. var ax = a[0],
  115. ay = a[1],
  116. bx = b[0],
  117. by = b[1];
  118. return ax * bx + ay * by;
  119. };
  120. /**
  121. * Projects a polygon onto a coordinate.
  122. *
  123. * @private
  124. * @function project
  125. * @param {Highcharts.PolygonObject} polygon
  126. * Array of points in a polygon.
  127. * @param {Highcharts.PolygonPointObject} target
  128. * The coordinate of pr
  129. * @return {Highcharts.RangeObject}
  130. */
  131. var project = function project(polygon,
  132. target) {
  133. var products = polygon.map(function (point) {
  134. return dotProduct(point,
  135. target);
  136. });
  137. return {
  138. min: Math.min.apply(this, products),
  139. max: Math.max.apply(this, products)
  140. };
  141. };
  142. /**
  143. * Rotates a point clockwise around the origin.
  144. *
  145. * @private
  146. * @function rotate2DToOrigin
  147. * @param {Highcharts.PolygonPointObject} point
  148. * The x and y coordinates for the point.
  149. * @param {number} angle
  150. * The angle of rotation.
  151. * @return {Highcharts.PolygonPointObject}
  152. * The x and y coordinate for the rotated point.
  153. */
  154. var rotate2DToOrigin = function (point,
  155. angle) {
  156. var x = point[0],
  157. y = point[1],
  158. rad = deg2rad * -angle,
  159. cosAngle = Math.cos(rad),
  160. sinAngle = Math.sin(rad);
  161. return [
  162. correctFloat(x * cosAngle - y * sinAngle),
  163. correctFloat(x * sinAngle + y * cosAngle)
  164. ];
  165. };
  166. /**
  167. * Rotate a point clockwise around another point.
  168. *
  169. * @private
  170. * @function rotate2DToPoint
  171. * @param {Highcharts.PolygonPointObject} point
  172. * The x and y coordinates for the point.
  173. * @param {Highcharts.PolygonPointObject} origin
  174. * The point to rotate around.
  175. * @param {number} angle
  176. * The angle of rotation.
  177. * @return {Highcharts.PolygonPointObject}
  178. * The x and y coordinate for the rotated point.
  179. */
  180. var rotate2DToPoint = function (point,
  181. origin,
  182. angle) {
  183. var x = point[0] - origin[0],
  184. y = point[1] - origin[1],
  185. rotated = rotate2DToOrigin([x,
  186. y],
  187. angle);
  188. return [
  189. rotated[0] + origin[0],
  190. rotated[1] + origin[1]
  191. ];
  192. };
  193. /**
  194. * @private
  195. */
  196. var isAxesEqual = function (axis1,
  197. axis2) {
  198. return (axis1[0] === axis2[0] &&
  199. axis1[1] === axis2[1]);
  200. };
  201. /**
  202. * @private
  203. */
  204. var getAxesFromPolygon = function (polygon) {
  205. var points,
  206. axes = polygon.axes;
  207. if (!isArray(axes)) {
  208. axes = [];
  209. points = points = polygon.concat([polygon[0]]);
  210. points.reduce(function findAxis(p1, p2) {
  211. var normals = getNormals(p1,
  212. p2),
  213. axis = normals[0]; // Use the left normal as axis.
  214. // Check that the axis is unique.
  215. if (!find(axes,
  216. function (existing) {
  217. return isAxesEqual(existing,
  218. axis);
  219. })) {
  220. axes.push(axis);
  221. }
  222. // Return p2 to be used as p1 in next iteration.
  223. return p2;
  224. });
  225. polygon.axes = axes;
  226. }
  227. return axes;
  228. };
  229. /**
  230. * @private
  231. */
  232. var getAxes = function (polygon1,
  233. polygon2) {
  234. // Get the axis from both polygons.
  235. var axes1 = getAxesFromPolygon(polygon1),
  236. axes2 = getAxesFromPolygon(polygon2);
  237. return axes1.concat(axes2);
  238. };
  239. /**
  240. * @private
  241. */
  242. var getPolygon = function (x,
  243. y,
  244. width,
  245. height,
  246. rotation) {
  247. var origin = [x,
  248. y],
  249. left = x - (width / 2),
  250. right = x + (width / 2),
  251. top = y - (height / 2),
  252. bottom = y + (height / 2),
  253. polygon = [
  254. [left,
  255. top],
  256. [right,
  257. top],
  258. [right,
  259. bottom],
  260. [left,
  261. bottom]
  262. ];
  263. return polygon.map(function (point) {
  264. return rotate2DToPoint(point, origin, -rotation);
  265. });
  266. };
  267. /**
  268. * @private
  269. */
  270. var getBoundingBoxFromPolygon = function (points) {
  271. return points.reduce(function (obj,
  272. point) {
  273. var x = point[0],
  274. y = point[1];
  275. obj.left = Math.min(x, obj.left);
  276. obj.right = Math.max(x, obj.right);
  277. obj.bottom = Math.max(y, obj.bottom);
  278. obj.top = Math.min(y, obj.top);
  279. return obj;
  280. }, {
  281. left: Number.MAX_VALUE,
  282. right: -Number.MAX_VALUE,
  283. bottom: -Number.MAX_VALUE,
  284. top: Number.MAX_VALUE
  285. });
  286. };
  287. /**
  288. * @private
  289. */
  290. var isPolygonsOverlappingOnAxis = function (axis,
  291. polygon1,
  292. polygon2) {
  293. var projection1 = project(polygon1,
  294. axis),
  295. projection2 = project(polygon2,
  296. axis),
  297. isOverlapping = !(projection2.min > projection1.max ||
  298. projection2.max < projection1.min);
  299. return !isOverlapping;
  300. };
  301. /**
  302. * Checks wether two convex polygons are colliding by using the Separating Axis
  303. * Theorem.
  304. *
  305. * @private
  306. * @function isPolygonsColliding
  307. * @param {Highcharts.PolygonObject} polygon1
  308. * First polygon.
  309. *
  310. * @param {Highcharts.PolygonObject} polygon2
  311. * Second polygon.
  312. *
  313. * @return {boolean}
  314. * Returns true if they are colliding, otherwise false.
  315. */
  316. var isPolygonsColliding = function isPolygonsColliding(polygon1,
  317. polygon2) {
  318. var axes = getAxes(polygon1,
  319. polygon2),
  320. overlappingOnAllAxes = !find(axes,
  321. function (axis) {
  322. return isPolygonsOverlappingOnAxis(axis,
  323. polygon1,
  324. polygon2);
  325. });
  326. return overlappingOnAllAxes;
  327. };
  328. /**
  329. * @private
  330. */
  331. var movePolygon = function (deltaX,
  332. deltaY,
  333. polygon) {
  334. return polygon.map(function (point) {
  335. return [
  336. point[0] + deltaX,
  337. point[1] + deltaY
  338. ];
  339. });
  340. };
  341. var collision = {
  342. getBoundingBoxFromPolygon: getBoundingBoxFromPolygon,
  343. getPolygon: getPolygon,
  344. isPolygonsColliding: isPolygonsColliding,
  345. movePolygon: movePolygon,
  346. rotate2DToOrigin: rotate2DToOrigin,
  347. rotate2DToPoint: rotate2DToPoint
  348. };
  349. return collision;
  350. });
  351. _registerModule(_modules, 'Mixins/DrawPoint.js', [], function () {
  352. /* *
  353. *
  354. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  355. *
  356. * */
  357. var isFn = function (x) {
  358. return typeof x === 'function';
  359. };
  360. /* eslint-disable no-invalid-this, valid-jsdoc */
  361. /**
  362. * Handles the drawing of a component.
  363. * Can be used for any type of component that reserves the graphic property, and
  364. * provides a shouldDraw on its context.
  365. *
  366. * @private
  367. * @function draw
  368. * @param {DrawPointParams} params
  369. * Parameters.
  370. *
  371. * @todo add type checking.
  372. * @todo export this function to enable usage
  373. */
  374. var draw = function draw(params) {
  375. var _this = this;
  376. var animatableAttribs = params.animatableAttribs,
  377. onComplete = params.onComplete,
  378. css = params.css,
  379. renderer = params.renderer;
  380. var animation = (this.series && this.series.chart.hasRendered) ?
  381. // Chart-level animation on updates
  382. void 0 :
  383. // Series-level animation on new points
  384. (this.series &&
  385. this.series.options.animation);
  386. var graphic = this.graphic;
  387. if (this.shouldDraw()) {
  388. if (!graphic) {
  389. this.graphic = graphic =
  390. renderer[params.shapeType](params.shapeArgs)
  391. .add(params.group);
  392. }
  393. graphic
  394. .css(css)
  395. .attr(params.attribs)
  396. .animate(animatableAttribs, params.isNew ? false : animation, onComplete);
  397. }
  398. else if (graphic) {
  399. var destroy_1 = function () {
  400. _this.graphic = graphic = (graphic && graphic.destroy());
  401. if (isFn(onComplete)) {
  402. onComplete();
  403. }
  404. };
  405. // animate only runs complete callback if something was animated.
  406. if (Object.keys(animatableAttribs).length) {
  407. graphic.animate(animatableAttribs, void 0, function () {
  408. destroy_1();
  409. });
  410. }
  411. else {
  412. destroy_1();
  413. }
  414. }
  415. };
  416. /**
  417. * An extended version of draw customized for points.
  418. * It calls additional methods that is expected when rendering a point.
  419. * @private
  420. * @param {Highcharts.Dictionary<any>} params Parameters
  421. */
  422. var drawPoint = function drawPoint(params) {
  423. var point = this,
  424. attribs = params.attribs = params.attribs || {};
  425. // Assigning class in dot notation does go well in IE8
  426. // eslint-disable-next-line dot-notation
  427. attribs['class'] = point.getClassName();
  428. // Call draw to render component
  429. draw.call(point, params);
  430. };
  431. var drawPointModule = {
  432. draw: draw,
  433. drawPoint: drawPoint,
  434. isFn: isFn
  435. };
  436. return drawPointModule;
  437. });
  438. _registerModule(_modules, 'Series/Wordcloud/WordcloudPoint.js', [_modules['Mixins/DrawPoint.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (DrawPointMixin, SeriesRegistry, U) {
  439. /* *
  440. *
  441. * Experimental Highcharts module which enables visualization of a word cloud.
  442. *
  443. * (c) 2016-2021 Highsoft AS
  444. * Authors: Jon Arild Nygard
  445. *
  446. * License: www.highcharts.com/license
  447. *
  448. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  449. * */
  450. var __extends = (this && this.__extends) || (function () {
  451. var extendStatics = function (d,
  452. b) {
  453. extendStatics = Object.setPrototypeOf ||
  454. ({ __proto__: [] } instanceof Array && function (d,
  455. b) { d.__proto__ = b; }) ||
  456. function (d,
  457. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  458. return extendStatics(d, b);
  459. };
  460. return function (d, b) {
  461. extendStatics(d, b);
  462. function __() { this.constructor = d; }
  463. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  464. };
  465. })();
  466. var ColumnSeries = SeriesRegistry.seriesTypes.column;
  467. var extend = U.extend;
  468. var WordcloudPoint = /** @class */ (function (_super) {
  469. __extends(WordcloudPoint, _super);
  470. function WordcloudPoint() {
  471. var _this = _super !== null && _super.apply(this,
  472. arguments) || this;
  473. /* *
  474. *
  475. * Properties
  476. *
  477. * */
  478. _this.dimensions = void 0;
  479. _this.options = void 0;
  480. _this.polygon = void 0;
  481. _this.rect = void 0;
  482. _this.series = void 0;
  483. return _this;
  484. }
  485. /* *
  486. *
  487. * Functions
  488. *
  489. * */
  490. WordcloudPoint.prototype.shouldDraw = function () {
  491. var point = this;
  492. return !point.isNull;
  493. };
  494. WordcloudPoint.prototype.isValid = function () {
  495. return true;
  496. };
  497. return WordcloudPoint;
  498. }(ColumnSeries.prototype.pointClass));
  499. extend(WordcloudPoint.prototype, {
  500. draw: DrawPointMixin.drawPoint,
  501. weight: 1
  502. });
  503. return WordcloudPoint;
  504. });
  505. _registerModule(_modules, 'Series/Wordcloud/WordcloudUtils.js', [_modules['Mixins/Polygon.js'], _modules['Core/Utilities.js']], function (PolygonMixin, U) {
  506. /* *
  507. *
  508. * Experimental Highcharts module which enables visualization of a word cloud.
  509. *
  510. * (c) 2016-2021 Highsoft AS
  511. * Authors: Jon Arild Nygard
  512. *
  513. * License: www.highcharts.com/license
  514. *
  515. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  516. * */
  517. var isPolygonsColliding = PolygonMixin.isPolygonsColliding,
  518. movePolygon = PolygonMixin.movePolygon;
  519. var extend = U.extend,
  520. find = U.find,
  521. isNumber = U.isNumber,
  522. isObject = U.isObject,
  523. merge = U.merge;
  524. /* *
  525. *
  526. * Namespace
  527. *
  528. * */
  529. var WordcloudUtils;
  530. (function (WordcloudUtils) {
  531. /* *
  532. *
  533. * Functions
  534. *
  535. * */
  536. /**
  537. * Detects if there is a collision between two rectangles.
  538. *
  539. * @private
  540. * @function isRectanglesIntersecting
  541. *
  542. * @param {Highcharts.PolygonBoxObject} r1
  543. * First rectangle.
  544. *
  545. * @param {Highcharts.PolygonBoxObject} r2
  546. * Second rectangle.
  547. *
  548. * @return {boolean}
  549. * Returns true if the rectangles overlap.
  550. */
  551. function isRectanglesIntersecting(r1, r2) {
  552. return !(r2.left > r1.right ||
  553. r2.right < r1.left ||
  554. r2.top > r1.bottom ||
  555. r2.bottom < r1.top);
  556. }
  557. WordcloudUtils.isRectanglesIntersecting = isRectanglesIntersecting;
  558. /**
  559. * Detects if a word collides with any previously placed words.
  560. *
  561. * @private
  562. * @function intersectsAnyWord
  563. *
  564. * @param {Highcharts.Point} point
  565. * Point which the word is connected to.
  566. *
  567. * @param {Array<Highcharts.Point>} points
  568. * Previously placed points to check against.
  569. *
  570. * @return {boolean}
  571. * Returns true if there is collision.
  572. */
  573. function intersectsAnyWord(point, points) {
  574. var intersects = false,
  575. rect = point.rect,
  576. polygon = point.polygon,
  577. lastCollidedWith = point.lastCollidedWith,
  578. isIntersecting = function (p) {
  579. var result = isRectanglesIntersecting(rect,
  580. p.rect);
  581. if (result &&
  582. (point.rotation % 90 || p.rotation % 90)) {
  583. result = isPolygonsColliding(polygon, p.polygon);
  584. }
  585. return result;
  586. };
  587. // If the point has already intersected a different point, chances are
  588. // they are still intersecting. So as an enhancement we check this
  589. // first.
  590. if (lastCollidedWith) {
  591. intersects = isIntersecting(lastCollidedWith);
  592. // If they no longer intersects, remove the cache from the point.
  593. if (!intersects) {
  594. delete point.lastCollidedWith;
  595. }
  596. }
  597. // If not already found, then check if we can find a point that is
  598. // intersecting.
  599. if (!intersects) {
  600. intersects = !!find(points, function (p) {
  601. var result = isIntersecting(p);
  602. if (result) {
  603. point.lastCollidedWith = p;
  604. }
  605. return result;
  606. });
  607. }
  608. return intersects;
  609. }
  610. WordcloudUtils.intersectsAnyWord = intersectsAnyWord;
  611. /**
  612. * Gives a set of cordinates for an Archimedian Spiral.
  613. *
  614. * @private
  615. * @function archimedeanSpiral
  616. *
  617. * @param {number} attempt
  618. * How far along the spiral we have traversed.
  619. *
  620. * @param {Highcharts.WordcloudSpiralParamsObject} [params]
  621. * Additional parameters.
  622. *
  623. * @return {boolean|Highcharts.PositionObject}
  624. * Resulting coordinates, x and y. False if the word should be dropped from
  625. * the visualization.
  626. */
  627. function archimedeanSpiral(attempt, params) {
  628. var field = params.field,
  629. result = false,
  630. maxDelta = (field.width * field.width) + (field.height * field.height),
  631. t = attempt * 0.8; // 0.2 * 4 = 0.8. Enlarging the spiral.
  632. // Emergency brake. TODO make spiralling logic more foolproof.
  633. if (attempt <= 10000) {
  634. result = {
  635. x: t * Math.cos(t),
  636. y: t * Math.sin(t)
  637. };
  638. if (!(Math.min(Math.abs(result.x), Math.abs(result.y)) < maxDelta)) {
  639. result = false;
  640. }
  641. }
  642. return result;
  643. }
  644. WordcloudUtils.archimedeanSpiral = archimedeanSpiral;
  645. /**
  646. * Gives a set of cordinates for an rectangular spiral.
  647. *
  648. * @private
  649. * @function squareSpiral
  650. *
  651. * @param {number} attempt
  652. * How far along the spiral we have traversed.
  653. *
  654. * @param {Highcharts.WordcloudSpiralParamsObject} [params]
  655. * Additional parameters.
  656. *
  657. * @return {boolean|Highcharts.PositionObject}
  658. * Resulting coordinates, x and y. False if the word should be dropped from
  659. * the visualization.
  660. */
  661. function squareSpiral(attempt, params) {
  662. var a = attempt * 4,
  663. k = Math.ceil((Math.sqrt(a) - 1) / 2),
  664. t = 2 * k + 1,
  665. m = Math.pow(t, 2),
  666. isBoolean = function (x) {
  667. return typeof x === 'boolean';
  668. }, result = false;
  669. t -= 1;
  670. if (attempt <= 10000) {
  671. if (isBoolean(result) && a >= m - t) {
  672. result = {
  673. x: k - (m - a),
  674. y: -k
  675. };
  676. }
  677. m -= t;
  678. if (isBoolean(result) && a >= m - t) {
  679. result = {
  680. x: -k,
  681. y: -k + (m - a)
  682. };
  683. }
  684. m -= t;
  685. if (isBoolean(result)) {
  686. if (a >= m - t) {
  687. result = {
  688. x: -k + (m - a),
  689. y: k
  690. };
  691. }
  692. else {
  693. result = {
  694. x: k,
  695. y: k - (m - a - t)
  696. };
  697. }
  698. }
  699. result.x *= 5;
  700. result.y *= 5;
  701. }
  702. return result;
  703. }
  704. WordcloudUtils.squareSpiral = squareSpiral;
  705. /**
  706. * Gives a set of cordinates for an rectangular spiral.
  707. *
  708. * @private
  709. * @function rectangularSpiral
  710. *
  711. * @param {number} attempt
  712. * How far along the spiral we have traversed.
  713. *
  714. * @param {Highcharts.WordcloudSpiralParamsObject} [params]
  715. * Additional parameters.
  716. *
  717. * @return {boolean|Higcharts.PositionObject}
  718. * Resulting coordinates, x and y. False if the word should be dropped from
  719. * the visualization.
  720. */
  721. function rectangularSpiral(attempt, params) {
  722. var result = squareSpiral(attempt,
  723. params),
  724. field = params.field;
  725. if (result) {
  726. result.x *= field.ratioX;
  727. result.y *= field.ratioY;
  728. }
  729. return result;
  730. }
  731. WordcloudUtils.rectangularSpiral = rectangularSpiral;
  732. /**
  733. * @private
  734. * @function getRandomPosition
  735. *
  736. * @param {number} size
  737. * Random factor.
  738. *
  739. * @return {number}
  740. * Random position.
  741. */
  742. function getRandomPosition(size) {
  743. return Math.round((size * (Math.random() + 0.5)) / 2);
  744. }
  745. WordcloudUtils.getRandomPosition = getRandomPosition;
  746. /**
  747. * Calculates the proper scale to fit the cloud inside the plotting area.
  748. *
  749. * @private
  750. * @function getScale
  751. *
  752. * @param {number} targetWidth
  753. * Width of target area.
  754. *
  755. * @param {number} targetHeight
  756. * Height of target area.
  757. *
  758. * @param {object} field
  759. * The playing field.
  760. *
  761. * @param {Highcharts.Series} series
  762. * Series object.
  763. *
  764. * @return {number}
  765. * Returns the value to scale the playing field up to the size of the target
  766. * area.
  767. */
  768. function getScale(targetWidth, targetHeight, field) {
  769. var height = Math.max(Math.abs(field.top),
  770. Math.abs(field.bottom)) * 2,
  771. width = Math.max(Math.abs(field.left),
  772. Math.abs(field.right)) * 2,
  773. scaleX = width > 0 ? 1 / width * targetWidth : 1,
  774. scaleY = height > 0 ? 1 / height * targetHeight : 1;
  775. return Math.min(scaleX, scaleY);
  776. }
  777. WordcloudUtils.getScale = getScale;
  778. /**
  779. * Calculates what is called the playing field. The field is the area which
  780. * all the words are allowed to be positioned within. The area is
  781. * proportioned to match the target aspect ratio.
  782. *
  783. * @private
  784. * @function getPlayingField
  785. *
  786. * @param {number} targetWidth
  787. * Width of the target area.
  788. *
  789. * @param {number} targetHeight
  790. * Height of the target area.
  791. *
  792. * @param {Array<Highcharts.Point>} data
  793. * Array of points.
  794. *
  795. * @param {object} data.dimensions
  796. * The height and width of the word.
  797. *
  798. * @return {object}
  799. * The width and height of the playing field.
  800. */
  801. function getPlayingField(targetWidth, targetHeight, data) {
  802. var info = data.reduce(function (obj,
  803. point) {
  804. var dimensions = point.dimensions,
  805. x = Math.max(dimensions.width,
  806. dimensions.height);
  807. // Find largest height.
  808. obj.maxHeight = Math.max(obj.maxHeight, dimensions.height);
  809. // Find largest width.
  810. obj.maxWidth = Math.max(obj.maxWidth, dimensions.width);
  811. // Sum up the total maximum area of all the words.
  812. obj.area += x * x;
  813. return obj;
  814. }, {
  815. maxHeight: 0,
  816. maxWidth: 0,
  817. area: 0
  818. }),
  819. /**
  820. * Use largest width, largest height, or root of total area to give
  821. * size to the playing field.
  822. */
  823. x = Math.max(info.maxHeight, // Have enough space for the tallest word
  824. info.maxWidth, // Have enough space for the broadest word
  825. // Adjust 15% to account for close packing of words
  826. Math.sqrt(info.area) * 0.85), ratioX = targetWidth > targetHeight ? targetWidth / targetHeight : 1, ratioY = targetHeight > targetWidth ? targetHeight / targetWidth : 1;
  827. return {
  828. width: x * ratioX,
  829. height: x * ratioY,
  830. ratioX: ratioX,
  831. ratioY: ratioY
  832. };
  833. }
  834. WordcloudUtils.getPlayingField = getPlayingField;
  835. /**
  836. * Calculates a number of degrees to rotate, based upon a number of
  837. * orientations within a range from-to.
  838. *
  839. * @private
  840. * @function getRotation
  841. *
  842. * @param {number} [orientations]
  843. * Number of orientations.
  844. *
  845. * @param {number} [index]
  846. * Index of point, used to decide orientation.
  847. *
  848. * @param {number} [from]
  849. * The smallest degree of rotation.
  850. *
  851. * @param {number} [to]
  852. * The largest degree of rotation.
  853. *
  854. * @return {boolean|number}
  855. * Returns the resulting rotation for the word. Returns false if invalid
  856. * input parameters.
  857. */
  858. function getRotation(orientations, index, from, to) {
  859. var result = false, // Default to false
  860. range,
  861. intervals,
  862. orientation;
  863. // Check if we have valid input parameters.
  864. if (isNumber(orientations) &&
  865. isNumber(index) &&
  866. isNumber(from) &&
  867. isNumber(to) &&
  868. orientations > 0 &&
  869. index > -1 &&
  870. to > from) {
  871. range = to - from;
  872. intervals = range / (orientations - 1 || 1);
  873. orientation = index % orientations;
  874. result = from + (orientation * intervals);
  875. }
  876. return result;
  877. }
  878. WordcloudUtils.getRotation = getRotation;
  879. /**
  880. * Calculates the spiral positions and store them in scope for quick access.
  881. *
  882. * @private
  883. * @function getSpiral
  884. *
  885. * @param {Function} fn
  886. * The spiral function.
  887. *
  888. * @param {object} params
  889. * Additional parameters for the spiral.
  890. *
  891. * @return {Function}
  892. * Function with access to spiral positions.
  893. */
  894. function getSpiral(fn, params) {
  895. var length = 10000,
  896. i,
  897. arr = [];
  898. for (i = 1; i < length; i++) {
  899. // @todo unnecessary amount of precaclulation
  900. arr.push(fn(i, params));
  901. }
  902. return function (attempt) {
  903. return attempt <= length ? arr[attempt - 1] : false;
  904. };
  905. }
  906. WordcloudUtils.getSpiral = getSpiral;
  907. /**
  908. * Detects if a word is placed outside the playing field.
  909. *
  910. * @private
  911. * @function outsidePlayingField
  912. *
  913. * @param {Highcharts.PolygonBoxObject} rect
  914. * The word box.
  915. *
  916. * @param {Highcharts.WordcloudFieldObject} field
  917. * The width and height of the playing field.
  918. *
  919. * @return {boolean}
  920. * Returns true if the word is placed outside the field.
  921. */
  922. function outsidePlayingField(rect, field) {
  923. var playingField = {
  924. left: -(field.width / 2),
  925. right: field.width / 2,
  926. top: -(field.height / 2),
  927. bottom: field.height / 2
  928. };
  929. return !(playingField.left < rect.left &&
  930. playingField.right > rect.right &&
  931. playingField.top < rect.top &&
  932. playingField.bottom > rect.bottom);
  933. }
  934. WordcloudUtils.outsidePlayingField = outsidePlayingField;
  935. /**
  936. * Check if a point intersects with previously placed words, or if it goes
  937. * outside the field boundaries. If a collision, then try to adjusts the
  938. * position.
  939. *
  940. * @private
  941. * @function intersectionTesting
  942. *
  943. * @param {Highcharts.Point} point
  944. * Point to test for intersections.
  945. *
  946. * @param {Highcharts.WordcloudTestOptionsObject} options
  947. * Options object.
  948. *
  949. * @return {boolean|Highcharts.PositionObject}
  950. * Returns an object with how much to correct the positions. Returns false
  951. * if the word should not be placed at all.
  952. */
  953. function intersectionTesting(point, options) {
  954. var placed = options.placed,
  955. field = options.field,
  956. rectangle = options.rectangle,
  957. polygon = options.polygon,
  958. spiral = options.spiral,
  959. attempt = 1,
  960. delta = {
  961. x: 0,
  962. y: 0
  963. },
  964. // Make a copy to update values during intersection testing.
  965. rect = point.rect = extend({},
  966. rectangle);
  967. point.polygon = polygon;
  968. point.rotation = options.rotation;
  969. /* while w intersects any previously placed words:
  970. do {
  971. move w a little bit along a spiral path
  972. } while any part of w is outside the playing field and
  973. the spiral radius is still smallish */
  974. while (delta !== false &&
  975. (intersectsAnyWord(point, placed) ||
  976. outsidePlayingField(rect, field))) {
  977. delta = spiral(attempt);
  978. if (isObject(delta)) {
  979. // Update the DOMRect with new positions.
  980. rect.left = rectangle.left + delta.x;
  981. rect.right = rectangle.right + delta.x;
  982. rect.top = rectangle.top + delta.y;
  983. rect.bottom = rectangle.bottom + delta.y;
  984. point.polygon = movePolygon(delta.x, delta.y, polygon);
  985. }
  986. attempt++;
  987. }
  988. return delta;
  989. }
  990. WordcloudUtils.intersectionTesting = intersectionTesting;
  991. /**
  992. * Extends the playing field to have enough space to fit a given word.
  993. *
  994. * @private
  995. * @function extendPlayingField
  996. *
  997. * @param {Highcharts.WordcloudFieldObject} field
  998. * The width, height and ratios of a playing field.
  999. *
  1000. * @param {Highcharts.PolygonBoxObject} rectangle
  1001. * The bounding box of the word to add space for.
  1002. *
  1003. * @return {Highcharts.WordcloudFieldObject}
  1004. * Returns the extended playing field with updated height and width.
  1005. */
  1006. function extendPlayingField(field, rectangle) {
  1007. var height,
  1008. width,
  1009. ratioX,
  1010. ratioY,
  1011. x,
  1012. extendWidth,
  1013. extendHeight,
  1014. result;
  1015. if (isObject(field) && isObject(rectangle)) {
  1016. height = (rectangle.bottom - rectangle.top);
  1017. width = (rectangle.right - rectangle.left);
  1018. ratioX = field.ratioX;
  1019. ratioY = field.ratioY;
  1020. // Use the same variable to extend both the height and width.
  1021. x = ((width * ratioX) > (height * ratioY)) ? width : height;
  1022. // Multiply variable with ratios to preserve aspect ratio.
  1023. extendWidth = x * ratioX;
  1024. extendHeight = x * ratioY;
  1025. // Calculate the size of the new field after adding
  1026. // space for the word.
  1027. result = merge(field, {
  1028. // Add space on the left and right.
  1029. width: field.width + (extendWidth * 2),
  1030. // Add space on the top and bottom.
  1031. height: field.height + (extendHeight * 2)
  1032. });
  1033. }
  1034. else {
  1035. result = field;
  1036. }
  1037. // Return the new extended field.
  1038. return result;
  1039. }
  1040. WordcloudUtils.extendPlayingField = extendPlayingField;
  1041. /**
  1042. * If a rectangle is outside a give field, then the boundaries of the field
  1043. * is adjusted accordingly. Modifies the field object which is passed as the
  1044. * first parameter.
  1045. *
  1046. * @private
  1047. * @function updateFieldBoundaries
  1048. *
  1049. * @param {Highcharts.WordcloudFieldObject} field
  1050. * The bounding box of a playing field.
  1051. *
  1052. * @param {Highcharts.PolygonBoxObject} rectangle
  1053. * The bounding box for a placed point.
  1054. *
  1055. * @return {Highcharts.WordcloudFieldObject}
  1056. * Returns a modified field object.
  1057. */
  1058. function updateFieldBoundaries(field, rectangle) {
  1059. // @todo improve type checking.
  1060. if (!isNumber(field.left) || field.left > rectangle.left) {
  1061. field.left = rectangle.left;
  1062. }
  1063. if (!isNumber(field.right) || field.right < rectangle.right) {
  1064. field.right = rectangle.right;
  1065. }
  1066. if (!isNumber(field.top) || field.top > rectangle.top) {
  1067. field.top = rectangle.top;
  1068. }
  1069. if (!isNumber(field.bottom) || field.bottom < rectangle.bottom) {
  1070. field.bottom = rectangle.bottom;
  1071. }
  1072. return field;
  1073. }
  1074. WordcloudUtils.updateFieldBoundaries = updateFieldBoundaries;
  1075. })(WordcloudUtils || (WordcloudUtils = {}));
  1076. /* *
  1077. *
  1078. * Default export
  1079. *
  1080. * */
  1081. return WordcloudUtils;
  1082. });
  1083. _registerModule(_modules, 'Series/Wordcloud/WordcloudSeries.js', [_modules['Core/Globals.js'], _modules['Mixins/Polygon.js'], _modules['Core/Series/Series.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js'], _modules['Series/Wordcloud/WordcloudPoint.js'], _modules['Series/Wordcloud/WordcloudUtils.js']], function (H, PolygonMixin, Series, SeriesRegistry, U, WordcloudPoint, WordcloudUtils) {
  1084. /* *
  1085. *
  1086. * Experimental Highcharts module which enables visualization of a word cloud.
  1087. *
  1088. * (c) 2016-2021 Highsoft AS
  1089. * Authors: Jon Arild Nygard
  1090. *
  1091. * License: www.highcharts.com/license
  1092. *
  1093. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  1094. * */
  1095. var __extends = (this && this.__extends) || (function () {
  1096. var extendStatics = function (d,
  1097. b) {
  1098. extendStatics = Object.setPrototypeOf ||
  1099. ({ __proto__: [] } instanceof Array && function (d,
  1100. b) { d.__proto__ = b; }) ||
  1101. function (d,
  1102. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  1103. return extendStatics(d, b);
  1104. };
  1105. return function (d, b) {
  1106. extendStatics(d, b);
  1107. function __() { this.constructor = d; }
  1108. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  1109. };
  1110. })();
  1111. var noop = H.noop;
  1112. var getBoundingBoxFromPolygon = PolygonMixin.getBoundingBoxFromPolygon,
  1113. getPolygon = PolygonMixin.getPolygon,
  1114. isPolygonsColliding = PolygonMixin.isPolygonsColliding,
  1115. rotate2DToOrigin = PolygonMixin.rotate2DToOrigin,
  1116. rotate2DToPoint = PolygonMixin.rotate2DToPoint;
  1117. var ColumnSeries = SeriesRegistry.seriesTypes.column;
  1118. var extend = U.extend,
  1119. find = U.find,
  1120. isArray = U.isArray,
  1121. isNumber = U.isNumber,
  1122. isObject = U.isObject,
  1123. merge = U.merge;
  1124. /**
  1125. * @private
  1126. * @class
  1127. * @name Highcharts.seriesTypes.wordcloud
  1128. *
  1129. * @augments Highcharts.Series
  1130. */
  1131. var WordcloudSeries = /** @class */ (function (_super) {
  1132. __extends(WordcloudSeries, _super);
  1133. function WordcloudSeries() {
  1134. /* *
  1135. *
  1136. * Static properties
  1137. *
  1138. * */
  1139. var _this = _super !== null && _super.apply(this,
  1140. arguments) || this;
  1141. /* *
  1142. *
  1143. * Properties
  1144. *
  1145. * */
  1146. _this.data = void 0;
  1147. _this.options = void 0;
  1148. _this.points = void 0;
  1149. return _this;
  1150. }
  1151. /**
  1152. *
  1153. * Functions
  1154. *
  1155. */
  1156. WordcloudSeries.prototype.bindAxes = function () {
  1157. var wordcloudAxis = {
  1158. endOnTick: false,
  1159. gridLineWidth: 0,
  1160. lineWidth: 0,
  1161. maxPadding: 0,
  1162. startOnTick: false,
  1163. title: void 0,
  1164. tickPositions: []
  1165. };
  1166. Series.prototype.bindAxes.call(this);
  1167. extend(this.yAxis.options, wordcloudAxis);
  1168. extend(this.xAxis.options, wordcloudAxis);
  1169. };
  1170. WordcloudSeries.prototype.pointAttribs = function (point, state) {
  1171. var attribs = H.seriesTypes.column.prototype
  1172. .pointAttribs.call(this,
  1173. point,
  1174. state);
  1175. delete attribs.stroke;
  1176. delete attribs['stroke-width'];
  1177. return attribs;
  1178. };
  1179. /**
  1180. * Calculates the fontSize of a word based on its weight.
  1181. *
  1182. * @private
  1183. * @function Highcharts.Series#deriveFontSize
  1184. *
  1185. * @param {number} [relativeWeight=0]
  1186. * The weight of the word, on a scale 0-1.
  1187. *
  1188. * @param {number} [maxFontSize=1]
  1189. * The maximum font size of a word.
  1190. *
  1191. * @param {number} [minFontSize=1]
  1192. * The minimum font size of a word.
  1193. *
  1194. * @return {number}
  1195. * Returns the resulting fontSize of a word. If minFontSize is larger then
  1196. * maxFontSize the result will equal minFontSize.
  1197. */
  1198. WordcloudSeries.prototype.deriveFontSize = function (relativeWeight, maxFontSize, minFontSize) {
  1199. var weight = isNumber(relativeWeight) ? relativeWeight : 0,
  1200. max = isNumber(maxFontSize) ? maxFontSize : 1,
  1201. min = isNumber(minFontSize) ? minFontSize : 1;
  1202. return Math.floor(Math.max(min, weight * max));
  1203. };
  1204. WordcloudSeries.prototype.drawPoints = function () {
  1205. var series = this,
  1206. hasRendered = series.hasRendered,
  1207. xAxis = series.xAxis,
  1208. yAxis = series.yAxis,
  1209. chart = series.chart,
  1210. group = series.group,
  1211. options = series.options,
  1212. animation = options.animation,
  1213. allowExtendPlayingField = options.allowExtendPlayingField,
  1214. renderer = chart.renderer,
  1215. testElement = renderer.text().add(group),
  1216. placed = [],
  1217. placementStrategy = series.placementStrategy[options.placementStrategy],
  1218. spiral,
  1219. rotation = options.rotation,
  1220. scale,
  1221. weights = series.points.map(function (p) {
  1222. return p.weight;
  1223. }), maxWeight = Math.max.apply(null, weights),
  1224. // concat() prevents from sorting the original array.
  1225. data = series.points.concat().sort(function (a, b) {
  1226. return b.weight - a.weight; // Sort descending
  1227. }), field;
  1228. // Reset the scale before finding the dimensions (#11993).
  1229. // SVGGRaphicsElement.getBBox() (used in SVGElement.getBBox(boolean))
  1230. // returns slightly different values for the same element depending on
  1231. // whether it is rendered in a group which has already defined scale
  1232. // (e.g. 6) or in the group without a scale (scale = 1).
  1233. series.group.attr({
  1234. scaleX: 1,
  1235. scaleY: 1
  1236. });
  1237. // Get the dimensions for each word.
  1238. // Used in calculating the playing field.
  1239. data.forEach(function (point) {
  1240. var relativeWeight = 1 / maxWeight * point.weight,
  1241. fontSize = series.deriveFontSize(relativeWeight,
  1242. options.maxFontSize,
  1243. options.minFontSize),
  1244. css = extend({
  1245. fontSize: fontSize + 'px'
  1246. },
  1247. options.style),
  1248. bBox;
  1249. testElement.css(css).attr({
  1250. x: 0,
  1251. y: 0,
  1252. text: point.name
  1253. });
  1254. bBox = testElement.getBBox(true);
  1255. point.dimensions = {
  1256. height: bBox.height,
  1257. width: bBox.width
  1258. };
  1259. });
  1260. // Calculate the playing field.
  1261. field = WordcloudUtils.getPlayingField(xAxis.len, yAxis.len, data);
  1262. spiral = WordcloudUtils.getSpiral(series.spirals[options.spiral], {
  1263. field: field
  1264. });
  1265. // Draw all the points.
  1266. data.forEach(function (point) {
  1267. var relativeWeight = 1 / maxWeight * point.weight,
  1268. fontSize = series.deriveFontSize(relativeWeight,
  1269. options.maxFontSize,
  1270. options.minFontSize),
  1271. css = extend({
  1272. fontSize: fontSize + 'px'
  1273. },
  1274. options.style),
  1275. placement = placementStrategy(point, {
  1276. data: data,
  1277. field: field,
  1278. placed: placed,
  1279. rotation: rotation
  1280. }),
  1281. attr = extend(series.pointAttribs(point, (point.selected && 'select')), {
  1282. align: 'center',
  1283. 'alignment-baseline': 'middle',
  1284. x: placement.x,
  1285. y: placement.y,
  1286. text: point.name,
  1287. rotation: isNumber(placement.rotation) ?
  1288. placement.rotation :
  1289. void 0
  1290. }),
  1291. polygon = getPolygon(placement.x,
  1292. placement.y,
  1293. point.dimensions.width,
  1294. point.dimensions.height,
  1295. placement.rotation),
  1296. rectangle = getBoundingBoxFromPolygon(polygon),
  1297. delta = WordcloudUtils.intersectionTesting(point, {
  1298. rectangle: rectangle,
  1299. polygon: polygon,
  1300. field: field,
  1301. placed: placed,
  1302. spiral: spiral,
  1303. rotation: placement.rotation
  1304. }),
  1305. animate;
  1306. // If there is no space for the word, extend the playing field.
  1307. if (!delta && allowExtendPlayingField) {
  1308. // Extend the playing field to fit the word.
  1309. field = WordcloudUtils.extendPlayingField(field, rectangle);
  1310. // Run intersection testing one more time to place the word.
  1311. delta = WordcloudUtils.intersectionTesting(point, {
  1312. rectangle: rectangle,
  1313. polygon: polygon,
  1314. field: field,
  1315. placed: placed,
  1316. spiral: spiral,
  1317. rotation: placement.rotation
  1318. });
  1319. }
  1320. // Check if point was placed, if so delete it, otherwise place it
  1321. // on the correct positions.
  1322. if (isObject(delta)) {
  1323. attr.x = (attr.x || 0) + delta.x;
  1324. attr.y = (attr.y || 0) + delta.y;
  1325. rectangle.left += delta.x;
  1326. rectangle.right += delta.x;
  1327. rectangle.top += delta.y;
  1328. rectangle.bottom += delta.y;
  1329. field = WordcloudUtils.updateFieldBoundaries(field, rectangle);
  1330. placed.push(point);
  1331. point.isNull = false;
  1332. point.isInside = true; // #15447
  1333. }
  1334. else {
  1335. point.isNull = true;
  1336. }
  1337. if (animation) {
  1338. // Animate to new positions
  1339. animate = {
  1340. x: attr.x,
  1341. y: attr.y
  1342. };
  1343. // Animate from center of chart
  1344. if (!hasRendered) {
  1345. attr.x = 0;
  1346. attr.y = 0;
  1347. // or animate from previous position
  1348. }
  1349. else {
  1350. delete attr.x;
  1351. delete attr.y;
  1352. }
  1353. }
  1354. point.draw({
  1355. animatableAttribs: animate,
  1356. attribs: attr,
  1357. css: css,
  1358. group: group,
  1359. renderer: renderer,
  1360. shapeArgs: void 0,
  1361. shapeType: 'text'
  1362. });
  1363. });
  1364. // Destroy the element after use.
  1365. testElement = testElement.destroy();
  1366. // Scale the series group to fit within the plotArea.
  1367. scale = WordcloudUtils.getScale(xAxis.len, yAxis.len, field);
  1368. series.group.attr({
  1369. scaleX: scale,
  1370. scaleY: scale
  1371. });
  1372. };
  1373. WordcloudSeries.prototype.hasData = function () {
  1374. var series = this;
  1375. return (isObject(series) &&
  1376. series.visible === true &&
  1377. isArray(series.points) &&
  1378. series.points.length > 0);
  1379. };
  1380. WordcloudSeries.prototype.getPlotBox = function () {
  1381. var series = this, chart = series.chart, inverted = chart.inverted,
  1382. // Swap axes for inverted (#2339)
  1383. xAxis = series[(inverted ? 'yAxis' : 'xAxis')], yAxis = series[(inverted ? 'xAxis' : 'yAxis')], width = xAxis ? xAxis.len : chart.plotWidth, height = yAxis ? yAxis.len : chart.plotHeight, x = xAxis ? xAxis.left : chart.plotLeft, y = yAxis ? yAxis.top : chart.plotTop;
  1384. return {
  1385. translateX: x + (width / 2),
  1386. translateY: y + (height / 2),
  1387. scaleX: 1,
  1388. scaleY: 1
  1389. };
  1390. };
  1391. /**
  1392. * A word cloud is a visualization of a set of words, where the size and
  1393. * placement of a word is determined by how it is weighted.
  1394. *
  1395. * @sample highcharts/demo/wordcloud
  1396. * Word Cloud chart
  1397. *
  1398. * @extends plotOptions.column
  1399. * @excluding allAreas, boostThreshold, clip, colorAxis, compare,
  1400. * compareBase, crisp, cropTreshold, dataGrouping, dataLabels,
  1401. * depth, dragDrop, edgeColor, findNearestPointBy,
  1402. * getExtremesFromAll, grouping, groupPadding, groupZPadding,
  1403. * joinBy, maxPointWidth, minPointLength, navigatorOptions,
  1404. * negativeColor, pointInterval, pointIntervalUnit,
  1405. * pointPadding, pointPlacement, pointRange, pointStart,
  1406. * pointWidth, pointStart, pointWidth, shadow, showCheckbox,
  1407. * showInNavigator, softThreshold, stacking, threshold,
  1408. * zoneAxis, zones, dataSorting, boostBlending
  1409. * @product highcharts
  1410. * @since 6.0.0
  1411. * @requires modules/wordcloud
  1412. * @optionparent plotOptions.wordcloud
  1413. */
  1414. WordcloudSeries.defaultOptions = merge(ColumnSeries.defaultOptions, {
  1415. /**
  1416. * If there is no space for a word on the playing field, then this
  1417. * option will allow the playing field to be extended to fit the word.
  1418. * If false then the word will be dropped from the visualization.
  1419. *
  1420. * NB! This option is currently not decided to be published in the API,
  1421. * and is therefore marked as private.
  1422. *
  1423. * @private
  1424. */
  1425. allowExtendPlayingField: true,
  1426. animation: {
  1427. /** @internal */
  1428. duration: 500
  1429. },
  1430. borderWidth: 0,
  1431. clip: false,
  1432. colorByPoint: true,
  1433. /**
  1434. * A threshold determining the minimum font size that can be applied to
  1435. * a word.
  1436. */
  1437. minFontSize: 1,
  1438. /**
  1439. * The word with the largest weight will have a font size equal to this
  1440. * value. The font size of a word is the ratio between its weight and
  1441. * the largest occuring weight, multiplied with the value of
  1442. * maxFontSize.
  1443. */
  1444. maxFontSize: 25,
  1445. /**
  1446. * This option decides which algorithm is used for placement, and
  1447. * rotation of a word. The choice of algorith is therefore a crucial
  1448. * part of the resulting layout of the wordcloud. It is possible for
  1449. * users to add their own custom placement strategies for use in word
  1450. * cloud. Read more about it in our
  1451. * [documentation](https://www.highcharts.com/docs/chart-and-series-types/word-cloud-series#custom-placement-strategies)
  1452. *
  1453. * @validvalue: ["center", "random"]
  1454. */
  1455. placementStrategy: 'center',
  1456. /**
  1457. * Rotation options for the words in the wordcloud.
  1458. *
  1459. * @sample highcharts/plotoptions/wordcloud-rotation
  1460. * Word cloud with rotation
  1461. */
  1462. rotation: {
  1463. /**
  1464. * The smallest degree of rotation for a word.
  1465. */
  1466. from: 0,
  1467. /**
  1468. * The number of possible orientations for a word, within the range
  1469. * of `rotation.from` and `rotation.to`. Must be a number larger
  1470. * than 0.
  1471. */
  1472. orientations: 2,
  1473. /**
  1474. * The largest degree of rotation for a word.
  1475. */
  1476. to: 90
  1477. },
  1478. showInLegend: false,
  1479. /**
  1480. * Spiral used for placing a word after the initial position
  1481. * experienced a collision with either another word or the borders.
  1482. * It is possible for users to add their own custom spiralling
  1483. * algorithms for use in word cloud. Read more about it in our
  1484. * [documentation](https://www.highcharts.com/docs/chart-and-series-types/word-cloud-series#custom-spiralling-algorithm)
  1485. *
  1486. * @validvalue: ["archimedean", "rectangular", "square"]
  1487. */
  1488. spiral: 'rectangular',
  1489. /**
  1490. * CSS styles for the words.
  1491. *
  1492. * @type {Highcharts.CSSObject}
  1493. * @default {"fontFamily":"sans-serif", "fontWeight": "900"}
  1494. */
  1495. style: {
  1496. /** @ignore-option */
  1497. fontFamily: 'sans-serif',
  1498. /** @ignore-option */
  1499. fontWeight: '900',
  1500. /** @ignore-option */
  1501. whiteSpace: 'nowrap'
  1502. },
  1503. tooltip: {
  1504. followPointer: true,
  1505. pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.weight}</b><br/>'
  1506. }
  1507. });
  1508. return WordcloudSeries;
  1509. }(ColumnSeries));
  1510. extend(WordcloudSeries.prototype, {
  1511. animate: Series.prototype.animate,
  1512. animateDrilldown: noop,
  1513. animateDrillupFrom: noop,
  1514. pointClass: WordcloudPoint,
  1515. setClip: noop,
  1516. // Strategies used for deciding rotation and initial position of a word. To
  1517. // implement a custom strategy, have a look at the function random for
  1518. // example.
  1519. placementStrategy: {
  1520. random: function (point, options) {
  1521. var field = options.field,
  1522. r = options.rotation;
  1523. return {
  1524. x: WordcloudUtils.getRandomPosition(field.width) - (field.width / 2),
  1525. y: WordcloudUtils.getRandomPosition(field.height) - (field.height / 2),
  1526. rotation: WordcloudUtils.getRotation(r.orientations, point.index, r.from, r.to)
  1527. };
  1528. },
  1529. center: function (point, options) {
  1530. var r = options.rotation;
  1531. return {
  1532. x: 0,
  1533. y: 0,
  1534. rotation: WordcloudUtils.getRotation(r.orientations, point.index, r.from, r.to)
  1535. };
  1536. }
  1537. },
  1538. pointArrayMap: ['weight'],
  1539. // Spirals used for placing a word after the initial position experienced a
  1540. // collision with either another word or the borders. To implement a custom
  1541. // spiral, look at the function archimedeanSpiral for example.
  1542. spirals: {
  1543. 'archimedean': WordcloudUtils.archimedeanSpiral,
  1544. 'rectangular': WordcloudUtils.rectangularSpiral,
  1545. 'square': WordcloudUtils.squareSpiral
  1546. },
  1547. utils: {
  1548. extendPlayingField: WordcloudUtils.extendPlayingField,
  1549. getRotation: WordcloudUtils.getRotation,
  1550. isPolygonsColliding: isPolygonsColliding,
  1551. rotate2DToOrigin: rotate2DToOrigin,
  1552. rotate2DToPoint: rotate2DToPoint
  1553. }
  1554. });
  1555. SeriesRegistry.registerSeriesType('wordcloud', WordcloudSeries);
  1556. /* *
  1557. *
  1558. * Export Default
  1559. *
  1560. * */
  1561. /* *
  1562. *
  1563. * API Options
  1564. *
  1565. * */
  1566. /**
  1567. * A `wordcloud` series. If the [type](#series.wordcloud.type) option is not
  1568. * specified, it is inherited from [chart.type](#chart.type).
  1569. *
  1570. * @extends series,plotOptions.wordcloud
  1571. * @exclude dataSorting, boostThreshold, boostBlending
  1572. * @product highcharts
  1573. * @requires modules/wordcloud
  1574. * @apioption series.wordcloud
  1575. */
  1576. /**
  1577. * An array of data points for the series. For the `wordcloud` series type,
  1578. * points can be given in the following ways:
  1579. *
  1580. * 1. An array of arrays with 2 values. In this case, the values correspond to
  1581. * `name,weight`.
  1582. * ```js
  1583. * data: [
  1584. * ['Lorem', 4],
  1585. * ['Ipsum', 1]
  1586. * ]
  1587. * ```
  1588. *
  1589. * 2. An array of objects with named values. The following snippet shows only a
  1590. * few settings, see the complete options set below. If the total number of
  1591. * data points exceeds the series'
  1592. * [turboThreshold](#series.arearange.turboThreshold), this option is not
  1593. * available.
  1594. * ```js
  1595. * data: [{
  1596. * name: "Lorem",
  1597. * weight: 4
  1598. * }, {
  1599. * name: "Ipsum",
  1600. * weight: 1
  1601. * }]
  1602. * ```
  1603. *
  1604. * @type {Array<Array<string,number>|*>}
  1605. * @extends series.line.data
  1606. * @excluding drilldown, marker, x, y
  1607. * @product highcharts
  1608. * @apioption series.wordcloud.data
  1609. */
  1610. /**
  1611. * The name decides the text for a word.
  1612. *
  1613. * @type {string}
  1614. * @since 6.0.0
  1615. * @product highcharts
  1616. * @apioption series.sunburst.data.name
  1617. */
  1618. /**
  1619. * The weighting of a word. The weight decides the relative size of a word
  1620. * compared to the rest of the collection.
  1621. *
  1622. * @type {number}
  1623. * @since 6.0.0
  1624. * @product highcharts
  1625. * @apioption series.sunburst.data.weight
  1626. */
  1627. ''; // detach doclets above
  1628. return WordcloudSeries;
  1629. });
  1630. _registerModule(_modules, 'masters/modules/wordcloud.src.js', [], function () {
  1631. });
  1632. }));