wordcloud.src.js 55 KB

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