wordcloud.src.js 64 KB

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