Networkgraph.js 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182
  1. /* *
  2. *
  3. * Networkgraph series
  4. *
  5. * (c) 2010-2021 Paweł Fus
  6. *
  7. * License: www.highcharts.com/license
  8. *
  9. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  10. *
  11. * */
  12. 'use strict';
  13. var __extends = (this && this.__extends) || (function () {
  14. var extendStatics = function (d, b) {
  15. extendStatics = Object.setPrototypeOf ||
  16. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  17. function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  18. return extendStatics(d, b);
  19. };
  20. return function (d, b) {
  21. extendStatics(d, b);
  22. function __() { this.constructor = d; }
  23. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  24. };
  25. })();
  26. import H from '../../Core/Globals.js';
  27. import NodesMixin from '../../Mixins/Nodes.js';
  28. import Point from '../../Core/Series/Point.js';
  29. import Series from '../../Core/Series/Series.js';
  30. import SeriesRegistry from '../../Core/Series/SeriesRegistry.js';
  31. var seriesTypes = SeriesRegistry.seriesTypes;
  32. import U from '../../Core/Utilities.js';
  33. var addEvent = U.addEvent, css = U.css, defined = U.defined, extend = U.extend, merge = U.merge, pick = U.pick;
  34. import '../../Core/Options.js';
  35. import './Layouts.js';
  36. import './DraggableNodes.js';
  37. var dragNodesMixin = H.dragNodesMixin;
  38. /**
  39. * Formatter callback function.
  40. *
  41. * @callback Highcharts.SeriesNetworkgraphDataLabelsFormatterCallbackFunction
  42. *
  43. * @param {Highcharts.SeriesNetworkgraphDataLabelsFormatterContextObject|Highcharts.PointLabelObject} this
  44. * Data label context to format
  45. *
  46. * @return {string}
  47. * Formatted data label text
  48. */
  49. /**
  50. * Context for the formatter function.
  51. *
  52. * @interface Highcharts.SeriesNetworkgraphDataLabelsFormatterContextObject
  53. * @extends Highcharts.PointLabelObject
  54. * @since 7.0.0
  55. */ /**
  56. * The color of the node.
  57. * @name Highcharts.SeriesNetworkgraphDataLabelsFormatterContextObject#color
  58. * @type {Highcharts.ColorString}
  59. * @since 7.0.0
  60. */ /**
  61. * The point (node) object. The node name, if defined, is available through
  62. * `this.point.name`. Arrays: `this.point.linksFrom` and `this.point.linksTo`
  63. * contains all nodes connected to this point.
  64. * @name Highcharts.SeriesNetworkgraphDataLabelsFormatterContextObject#point
  65. * @type {Highcharts.Point}
  66. * @since 7.0.0
  67. */ /**
  68. * The ID of the node.
  69. * @name Highcharts.SeriesNetworkgraphDataLabelsFormatterContextObject#key
  70. * @type {string}
  71. * @since 7.0.0
  72. */
  73. ''; // detach doclets above
  74. /* *
  75. *
  76. * Class
  77. *
  78. * */
  79. /**
  80. * @private
  81. * @class
  82. * @name Highcharts.seriesTypes.networkgraph
  83. *
  84. * @extends Highcharts.Series
  85. */
  86. var NetworkgraphSeries = /** @class */ (function (_super) {
  87. __extends(NetworkgraphSeries, _super);
  88. function NetworkgraphSeries() {
  89. /* *
  90. *
  91. * Static Properties
  92. *
  93. * */
  94. var _this = _super !== null && _super.apply(this, arguments) || this;
  95. /* *
  96. *
  97. * Properties
  98. *
  99. * */
  100. _this.data = void 0;
  101. _this.nodes = void 0;
  102. _this.options = void 0;
  103. _this.points = void 0;
  104. return _this;
  105. }
  106. /**
  107. * A networkgraph is a type of relationship chart, where connnections
  108. * (links) attracts nodes (points) and other nodes repulse each other.
  109. *
  110. * @extends plotOptions.line
  111. * @product highcharts
  112. * @sample highcharts/demo/network-graph/
  113. * Networkgraph
  114. * @since 7.0.0
  115. * @excluding boostThreshold, animation, animationLimit, connectEnds,
  116. * colorAxis, colorKey, connectNulls, cropThreshold, dragDrop,
  117. * getExtremesFromAll, label, linecap, negativeColor,
  118. * pointInterval, pointIntervalUnit, pointPlacement,
  119. * pointStart, softThreshold, stack, stacking, step,
  120. * threshold, xAxis, yAxis, zoneAxis, dataSorting,
  121. * boostBlending
  122. * @requires modules/networkgraph
  123. * @optionparent plotOptions.networkgraph
  124. */
  125. NetworkgraphSeries.defaultOptions = merge(Series.defaultOptions, {
  126. stickyTracking: false,
  127. /**
  128. * @ignore-option
  129. * @private
  130. */
  131. inactiveOtherPoints: true,
  132. marker: {
  133. enabled: true,
  134. states: {
  135. /**
  136. * The opposite state of a hover for a single point node.
  137. * Applied to all not connected nodes to the hovered one.
  138. *
  139. * @declare Highcharts.PointStatesInactiveOptionsObject
  140. */
  141. inactive: {
  142. /**
  143. * Opacity of inactive markers.
  144. */
  145. opacity: 0.3,
  146. /**
  147. * Animation when not hovering over the node.
  148. *
  149. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  150. */
  151. animation: {
  152. /** @internal */
  153. duration: 50
  154. }
  155. }
  156. }
  157. },
  158. states: {
  159. /**
  160. * The opposite state of a hover for a single point link. Applied
  161. * to all links that are not comming from the hovered node.
  162. *
  163. * @declare Highcharts.SeriesStatesInactiveOptionsObject
  164. */
  165. inactive: {
  166. /**
  167. * Opacity of inactive links.
  168. */
  169. linkOpacity: 0.3,
  170. /**
  171. * Animation when not hovering over the node.
  172. *
  173. * @type {boolean|Partial<Highcharts.AnimationOptionsObject>}
  174. */
  175. animation: {
  176. /** @internal */
  177. duration: 50
  178. }
  179. }
  180. },
  181. /**
  182. * @sample highcharts/series-networkgraph/link-datalabels
  183. * Networkgraph with labels on links
  184. * @sample highcharts/series-networkgraph/textpath-datalabels
  185. * Networkgraph with labels around nodes
  186. * @sample highcharts/series-networkgraph/link-datalabels
  187. * Data labels moved into the nodes
  188. * @sample highcharts/series-networkgraph/link-datalabels
  189. * Data labels moved under the links
  190. *
  191. * @declare Highcharts.SeriesNetworkgraphDataLabelsOptionsObject
  192. *
  193. * @private
  194. */
  195. dataLabels: {
  196. /**
  197. * The
  198. * [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  199. * specifying what to show for _node_ in the networkgraph. In v7.0
  200. * defaults to `{key}`, since v7.1 defaults to `undefined` and
  201. * `formatter` is used instead.
  202. *
  203. * @type {string}
  204. * @since 7.0.0
  205. * @apioption plotOptions.networkgraph.dataLabels.format
  206. */
  207. // eslint-disable-next-line valid-jsdoc
  208. /**
  209. * Callback JavaScript function to format the data label for a node.
  210. * Note that if a `format` is defined, the format takes precedence
  211. * and the formatter is ignored.
  212. *
  213. * @type {Highcharts.SeriesNetworkgraphDataLabelsFormatterCallbackFunction}
  214. * @since 7.0.0
  215. */
  216. formatter: function () {
  217. return this.key;
  218. },
  219. /**
  220. * The
  221. * [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  222. * specifying what to show for _links_ in the networkgraph.
  223. * (Default: `undefined`)
  224. *
  225. * @type {string}
  226. * @since 7.1.0
  227. * @apioption plotOptions.networkgraph.dataLabels.linkFormat
  228. */
  229. // eslint-disable-next-line valid-jsdoc
  230. /**
  231. * Callback to format data labels for _links_ in the sankey diagram.
  232. * The `linkFormat` option takes precedence over the
  233. * `linkFormatter`.
  234. *
  235. * @type {Highcharts.SeriesNetworkgraphDataLabelsFormatterCallbackFunction}
  236. * @since 7.1.0
  237. */
  238. linkFormatter: function () {
  239. return (this.point.fromNode.name +
  240. '<br>' +
  241. this.point.toNode.name);
  242. },
  243. /**
  244. * Options for a _link_ label text which should follow link
  245. * connection. Border and background are disabled for a label that
  246. * follows a path.
  247. *
  248. * **Note:** Only SVG-based renderer supports this option. Setting
  249. * `useHTML` to true will disable this option.
  250. *
  251. * @extends plotOptions.networkgraph.dataLabels.textPath
  252. * @since 7.1.0
  253. */
  254. linkTextPath: {
  255. enabled: true
  256. },
  257. textPath: {
  258. enabled: false
  259. },
  260. style: {
  261. transition: 'opacity 2000ms'
  262. }
  263. },
  264. /**
  265. * Link style options
  266. * @private
  267. */
  268. link: {
  269. /**
  270. * A name for the dash style to use for links.
  271. *
  272. * @type {string}
  273. * @apioption plotOptions.networkgraph.link.dashStyle
  274. */
  275. /**
  276. * Color of the link between two nodes.
  277. */
  278. color: 'rgba(100, 100, 100, 0.5)',
  279. /**
  280. * Width (px) of the link between two nodes.
  281. */
  282. width: 1
  283. },
  284. /**
  285. * Flag to determine if nodes are draggable or not.
  286. * @private
  287. */
  288. draggable: true,
  289. layoutAlgorithm: {
  290. /**
  291. * Repulsive force applied on a node. Passed are two arguments:
  292. * - `d` - which is current distance between two nodes
  293. * - `k` - which is desired distance between two nodes
  294. *
  295. * In `verlet` integration, defaults to:
  296. * `function (d, k) { return (k - d) / d * (k > d ? 1 : 0) }`
  297. *
  298. * @see [layoutAlgorithm.integration](#series.networkgraph.layoutAlgorithm.integration)
  299. *
  300. * @sample highcharts/series-networkgraph/forces/
  301. * Custom forces with Euler integration
  302. * @sample highcharts/series-networkgraph/cuboids/
  303. * Custom forces with Verlet integration
  304. *
  305. * @type {Function}
  306. * @default function (d, k) { return k * k / d; }
  307. * @apioption plotOptions.networkgraph.layoutAlgorithm.repulsiveForce
  308. */
  309. /**
  310. * Attraction force applied on a node which is conected to another
  311. * node by a link. Passed are two arguments:
  312. * - `d` - which is current distance between two nodes
  313. * - `k` - which is desired distance between two nodes
  314. *
  315. * In `verlet` integration, defaults to:
  316. * `function (d, k) { return (k - d) / d; }`
  317. *
  318. * @see [layoutAlgorithm.integration](#series.networkgraph.layoutAlgorithm.integration)
  319. *
  320. * @sample highcharts/series-networkgraph/forces/
  321. * Custom forces with Euler integration
  322. * @sample highcharts/series-networkgraph/cuboids/
  323. * Custom forces with Verlet integration
  324. *
  325. * @type {Function}
  326. * @default function (d, k) { return k * k / d; }
  327. * @apioption plotOptions.networkgraph.layoutAlgorithm.attractiveForce
  328. */
  329. /**
  330. * Ideal length (px) of the link between two nodes. When not
  331. * defined, length is calculated as:
  332. * `Math.pow(availableWidth * availableHeight / nodesLength, 0.4);`
  333. *
  334. * Note: Because of the algorithm specification, length of each link
  335. * might be not exactly as specified.
  336. *
  337. * @sample highcharts/series-networkgraph/styled-links/
  338. * Numerical values
  339. *
  340. * @type {number}
  341. * @apioption plotOptions.networkgraph.layoutAlgorithm.linkLength
  342. */
  343. /**
  344. * Initial layout algorithm for positioning nodes. Can be one of
  345. * built-in options ("circle", "random") or a function where
  346. * positions should be set on each node (`this.nodes`) as
  347. * `node.plotX` and `node.plotY`
  348. *
  349. * @sample highcharts/series-networkgraph/initial-positions/
  350. * Initial positions with callback
  351. *
  352. * @type {"circle"|"random"|Function}
  353. */
  354. initialPositions: 'circle',
  355. /**
  356. * When `initialPositions` are set to 'circle',
  357. * `initialPositionRadius` is a distance from the center of circle,
  358. * in which nodes are created.
  359. *
  360. * @type {number}
  361. * @default 1
  362. * @since 7.1.0
  363. */
  364. initialPositionRadius: 1,
  365. /**
  366. * Experimental. Enables live simulation of the algorithm
  367. * implementation. All nodes are animated as the forces applies on
  368. * them.
  369. *
  370. * @sample highcharts/demo/network-graph/
  371. * Live simulation enabled
  372. */
  373. enableSimulation: false,
  374. /**
  375. * Barnes-Hut approximation only.
  376. * Deteremines when distance between cell and node is small enough
  377. * to caculate forces. Value of `theta` is compared directly with
  378. * quotient `s / d`, where `s` is the size of the cell, and `d` is
  379. * distance between center of cell's mass and currently compared
  380. * node.
  381. *
  382. * @see [layoutAlgorithm.approximation](#series.networkgraph.layoutAlgorithm.approximation)
  383. *
  384. * @since 7.1.0
  385. */
  386. theta: 0.5,
  387. /**
  388. * Verlet integration only.
  389. * Max speed that node can get in one iteration. In terms of
  390. * simulation, it's a maximum translation (in pixels) that node can
  391. * move (in both, x and y, dimensions). While `friction` is applied
  392. * on all nodes, max speed is applied only for nodes that move very
  393. * fast, for example small or disconnected ones.
  394. *
  395. * @see [layoutAlgorithm.integration](#series.networkgraph.layoutAlgorithm.integration)
  396. * @see [layoutAlgorithm.friction](#series.networkgraph.layoutAlgorithm.friction)
  397. *
  398. * @since 7.1.0
  399. */
  400. maxSpeed: 10,
  401. /**
  402. * Approximation used to calculate repulsive forces affecting nodes.
  403. * By default, when calculateing net force, nodes are compared
  404. * against each other, which gives O(N^2) complexity. Using
  405. * Barnes-Hut approximation, we decrease this to O(N log N), but the
  406. * resulting graph will have different layout. Barnes-Hut
  407. * approximation divides space into rectangles via quad tree, where
  408. * forces exerted on nodes are calculated directly for nearby cells,
  409. * and for all others, cells are treated as a separate node with
  410. * center of mass.
  411. *
  412. * @see [layoutAlgorithm.theta](#series.networkgraph.layoutAlgorithm.theta)
  413. *
  414. * @sample highcharts/series-networkgraph/barnes-hut-approximation/
  415. * A graph with Barnes-Hut approximation
  416. *
  417. * @type {string}
  418. * @validvalue ["barnes-hut", "none"]
  419. * @since 7.1.0
  420. */
  421. approximation: 'none',
  422. /**
  423. * Type of the algorithm used when positioning nodes.
  424. *
  425. * @type {string}
  426. * @validvalue ["reingold-fruchterman"]
  427. */
  428. type: 'reingold-fruchterman',
  429. /**
  430. * Integration type. Available options are `'euler'` and `'verlet'`.
  431. * Integration determines how forces are applied on particles. In
  432. * Euler integration, force is applied direct as
  433. * `newPosition += velocity;`.
  434. * In Verlet integration, new position is based on a previous
  435. * posittion without velocity:
  436. * `newPosition += previousPosition - newPosition`.
  437. *
  438. * Note that different integrations give different results as forces
  439. * are different.
  440. *
  441. * In Highcharts v7.0.x only `'euler'` integration was supported.
  442. *
  443. * @sample highcharts/series-networkgraph/integration-comparison/
  444. * Comparison of Verlet and Euler integrations
  445. *
  446. * @type {string}
  447. * @validvalue ["euler", "verlet"]
  448. * @since 7.1.0
  449. */
  450. integration: 'euler',
  451. /**
  452. * Max number of iterations before algorithm will stop. In general,
  453. * algorithm should find positions sooner, but when rendering huge
  454. * number of nodes, it is recommended to increase this value as
  455. * finding perfect graph positions can require more time.
  456. */
  457. maxIterations: 1000,
  458. /**
  459. * Gravitational const used in the barycenter force of the
  460. * algorithm.
  461. *
  462. * @sample highcharts/series-networkgraph/forces/
  463. * Custom forces with Euler integration
  464. */
  465. gravitationalConstant: 0.0625,
  466. /**
  467. * Friction applied on forces to prevent nodes rushing to fast to
  468. * the desired positions.
  469. */
  470. friction: -0.981
  471. },
  472. showInLegend: false
  473. });
  474. return NetworkgraphSeries;
  475. }(Series));
  476. extend(NetworkgraphSeries.prototype, {
  477. /**
  478. * Array of internal forces. Each force should be later defined in
  479. * integrations.js.
  480. * @private
  481. */
  482. forces: ['barycenter', 'repulsive', 'attractive'],
  483. hasDraggableNodes: true,
  484. drawGraph: void 0,
  485. isCartesian: false,
  486. requireSorting: false,
  487. directTouch: true,
  488. noSharedTooltip: true,
  489. pointArrayMap: ['from', 'to'],
  490. trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'],
  491. drawTracker: seriesTypes.column.prototype.drawTracker,
  492. // Animation is run in `series.simulation`.
  493. animate: void 0,
  494. buildKDTree: H.noop,
  495. /**
  496. * Create a single node that holds information on incoming and outgoing
  497. * links.
  498. * @private
  499. */
  500. createNode: NodesMixin.createNode,
  501. destroy: function () {
  502. if (this.layout) {
  503. this.layout.removeElementFromCollection(this, this.layout.series);
  504. }
  505. NodesMixin.destroy.call(this);
  506. },
  507. /* eslint-disable no-invalid-this, valid-jsdoc */
  508. /**
  509. * Extend init with base event, which should stop simulation during
  510. * update. After data is updated, `chart.render` resumes the simulation.
  511. * @private
  512. */
  513. init: function () {
  514. var _this = this;
  515. Series.prototype.init.apply(this, arguments);
  516. addEvent(this, 'updatedData', function () {
  517. if (_this.layout) {
  518. _this.layout.stop();
  519. }
  520. });
  521. addEvent(this, 'afterUpdate', function () {
  522. _this.nodes.forEach(function (node) {
  523. if (node && node.series) {
  524. node.resolveColor();
  525. }
  526. });
  527. });
  528. return this;
  529. },
  530. /**
  531. * Extend generatePoints by adding the nodes, which are Point objects
  532. * but pushed to the this.nodes array.
  533. * @private
  534. */
  535. generatePoints: function () {
  536. var node, i;
  537. NodesMixin.generatePoints.apply(this, arguments);
  538. // In networkgraph, it's fine to define stanalone nodes, create
  539. // them:
  540. if (this.options.nodes) {
  541. this.options.nodes.forEach(function (nodeOptions) {
  542. if (!this.nodeLookup[nodeOptions.id]) {
  543. this.nodeLookup[nodeOptions.id] =
  544. this.createNode(nodeOptions.id);
  545. }
  546. }, this);
  547. }
  548. for (i = this.nodes.length - 1; i >= 0; i--) {
  549. node = this.nodes[i];
  550. node.degree = node.getDegree();
  551. node.radius = pick(node.marker && node.marker.radius, this.options.marker && this.options.marker.radius, 0);
  552. // If node exists, but it's not available in nodeLookup,
  553. // then it's leftover from previous runs (e.g. setData)
  554. if (!this.nodeLookup[node.id]) {
  555. node.remove();
  556. }
  557. }
  558. this.data.forEach(function (link) {
  559. link.formatPrefix = 'link';
  560. });
  561. this.indexateNodes();
  562. },
  563. /**
  564. * In networkgraph, series.points refers to links,
  565. * but series.nodes refers to actual points.
  566. * @private
  567. */
  568. getPointsCollection: function () {
  569. return this.nodes || [];
  570. },
  571. /**
  572. * Set index for each node. Required for proper `node.update()`.
  573. * Note that links are indexated out of the box in `generatePoints()`.
  574. *
  575. * @private
  576. */
  577. indexateNodes: function () {
  578. this.nodes.forEach(function (node, index) {
  579. node.index = index;
  580. });
  581. },
  582. /**
  583. * Extend the default marker attribs by using a non-rounded X position,
  584. * otherwise the nodes will jump from pixel to pixel which looks a bit
  585. * jaggy when approaching equilibrium.
  586. * @private
  587. */
  588. markerAttribs: function (point, state) {
  589. var attribs = Series.prototype.markerAttribs.call(this, point, state);
  590. // series.render() is called before initial positions are set:
  591. if (!defined(point.plotY)) {
  592. attribs.y = 0;
  593. }
  594. attribs.x = (point.plotX || 0) - (attribs.width || 0) / 2;
  595. return attribs;
  596. },
  597. /**
  598. * Run pre-translation and register nodes&links to the deffered layout.
  599. * @private
  600. */
  601. translate: function () {
  602. if (!this.processedXData) {
  603. this.processData();
  604. }
  605. this.generatePoints();
  606. this.deferLayout();
  607. this.nodes.forEach(function (node) {
  608. // Draw the links from this node
  609. node.isInside = true;
  610. node.linksFrom.forEach(function (point) {
  611. point.shapeType = 'path';
  612. // Pass test in drawPoints
  613. point.y = 1;
  614. });
  615. });
  616. },
  617. /**
  618. * Defer the layout.
  619. * Each series first registers all nodes and links, then layout
  620. * calculates all nodes positions and calls `series.render()` in every
  621. * simulation step.
  622. *
  623. * Note:
  624. * Animation is done through `requestAnimationFrame` directly, without
  625. * `Highcharts.animate()` use.
  626. * @private
  627. */
  628. deferLayout: function () {
  629. var layoutOptions = this.options.layoutAlgorithm, graphLayoutsStorage = this.chart.graphLayoutsStorage, graphLayoutsLookup = this.chart.graphLayoutsLookup, chartOptions = this.chart.options.chart, layout;
  630. if (!this.visible) {
  631. return;
  632. }
  633. if (!graphLayoutsStorage) {
  634. this.chart.graphLayoutsStorage = graphLayoutsStorage = {};
  635. this.chart.graphLayoutsLookup = graphLayoutsLookup = [];
  636. }
  637. layout = graphLayoutsStorage[layoutOptions.type];
  638. if (!layout) {
  639. layoutOptions.enableSimulation =
  640. !defined(chartOptions.forExport) ?
  641. layoutOptions.enableSimulation :
  642. !chartOptions.forExport;
  643. graphLayoutsStorage[layoutOptions.type] = layout =
  644. new H.layouts[layoutOptions.type]();
  645. layout.init(layoutOptions);
  646. graphLayoutsLookup.splice(layout.index, 0, layout);
  647. }
  648. this.layout = layout;
  649. layout.setArea(0, 0, this.chart.plotWidth, this.chart.plotHeight);
  650. layout.addElementsToCollection([this], layout.series);
  651. layout.addElementsToCollection(this.nodes, layout.nodes);
  652. layout.addElementsToCollection(this.points, layout.links);
  653. },
  654. /**
  655. * Extend the render function to also render this.nodes together with
  656. * the points.
  657. * @private
  658. */
  659. render: function () {
  660. var series = this, points = series.points, hoverPoint = series.chart.hoverPoint, dataLabels = [];
  661. // Render markers:
  662. series.points = series.nodes;
  663. seriesTypes.line.prototype.render.call(this);
  664. series.points = points;
  665. points.forEach(function (point) {
  666. if (point.fromNode && point.toNode) {
  667. point.renderLink();
  668. point.redrawLink();
  669. }
  670. });
  671. if (hoverPoint && hoverPoint.series === series) {
  672. series.redrawHalo(hoverPoint);
  673. }
  674. if (series.chart.hasRendered &&
  675. !series.options.dataLabels.allowOverlap) {
  676. series.nodes.concat(series.points).forEach(function (node) {
  677. if (node.dataLabel) {
  678. dataLabels.push(node.dataLabel);
  679. }
  680. });
  681. series.chart.hideOverlappingLabels(dataLabels);
  682. }
  683. },
  684. // Networkgraph has two separate collecions of nodes and lines, render
  685. // dataLabels for both sets:
  686. drawDataLabels: function () {
  687. var textPath = this.options.dataLabels.textPath;
  688. // Render node labels:
  689. Series.prototype.drawDataLabels.apply(this, arguments);
  690. // Render link labels:
  691. this.points = this.data;
  692. this.options.dataLabels.textPath =
  693. this.options.dataLabels.linkTextPath;
  694. Series.prototype.drawDataLabels.apply(this, arguments);
  695. // Restore nodes
  696. this.points = this.nodes;
  697. this.options.dataLabels.textPath = textPath;
  698. },
  699. // Return the presentational attributes.
  700. pointAttribs: function (point, state) {
  701. // By default, only `selected` state is passed on
  702. var pointState = state || point && point.state || 'normal', attribs = Series.prototype.pointAttribs.call(this, point, pointState), stateOptions = this.options.states[pointState];
  703. if (point && !point.isNode) {
  704. attribs = point.getLinkAttributes();
  705. // For link, get prefixed names:
  706. if (stateOptions) {
  707. attribs = {
  708. // TO DO: API?
  709. stroke: stateOptions.linkColor || attribs.stroke,
  710. dashstyle: (stateOptions.linkDashStyle || attribs.dashstyle),
  711. opacity: pick(stateOptions.linkOpacity, attribs.opacity),
  712. 'stroke-width': stateOptions.linkColor ||
  713. attribs['stroke-width']
  714. };
  715. }
  716. }
  717. return attribs;
  718. },
  719. // Draggable mode:
  720. /**
  721. * Redraw halo on mousemove during the drag&drop action.
  722. * @private
  723. * @param {Highcharts.Point} point The point that should show halo.
  724. */
  725. redrawHalo: dragNodesMixin.redrawHalo,
  726. /**
  727. * Mouse down action, initializing drag&drop mode.
  728. * @private
  729. * @param {global.Event} event Browser event, before normalization.
  730. * @param {Highcharts.Point} point The point that event occured.
  731. */
  732. onMouseDown: dragNodesMixin.onMouseDown,
  733. /**
  734. * Mouse move action during drag&drop.
  735. * @private
  736. * @param {global.Event} event Browser event, before normalization.
  737. * @param {Highcharts.Point} point The point that event occured.
  738. */
  739. onMouseMove: dragNodesMixin.onMouseMove,
  740. /**
  741. * Mouse up action, finalizing drag&drop.
  742. * @private
  743. * @param {Highcharts.Point} point The point that event occured.
  744. */
  745. onMouseUp: dragNodesMixin.onMouseUp,
  746. /**
  747. * When state should be passed down to all points, concat nodes and
  748. * links and apply this state to all of them.
  749. * @private
  750. */
  751. setState: function (state, inherit) {
  752. if (inherit) {
  753. this.points = this.nodes.concat(this.data);
  754. Series.prototype.setState.apply(this, arguments);
  755. this.points = this.data;
  756. }
  757. else {
  758. Series.prototype.setState.apply(this, arguments);
  759. }
  760. // If simulation is done, re-render points with new states:
  761. if (!this.layout.simulation && !state) {
  762. this.render();
  763. }
  764. }
  765. });
  766. /* *
  767. *
  768. * Class
  769. *
  770. * */
  771. var NetworkgraphPoint = /** @class */ (function (_super) {
  772. __extends(NetworkgraphPoint, _super);
  773. function NetworkgraphPoint() {
  774. /* *
  775. *
  776. * Properties
  777. *
  778. * */
  779. var _this = _super !== null && _super.apply(this, arguments) || this;
  780. _this.degree = void 0;
  781. _this.linksFrom = void 0;
  782. _this.linksTo = void 0;
  783. _this.options = void 0;
  784. _this.radius = void 0;
  785. _this.series = void 0;
  786. _this.toNode = void 0;
  787. return _this;
  788. }
  789. return NetworkgraphPoint;
  790. }(Series.prototype.pointClass));
  791. extend(NetworkgraphPoint.prototype, {
  792. setState: NodesMixin.setNodeState,
  793. /**
  794. * Basic `point.init()` and additional styles applied when
  795. * `series.draggable` is enabled.
  796. * @private
  797. */
  798. init: function () {
  799. Point.prototype.init.apply(this, arguments);
  800. if (this.series.options.draggable &&
  801. !this.series.chart.styledMode) {
  802. addEvent(this, 'mouseOver', function () {
  803. css(this.series.chart.container, { cursor: 'move' });
  804. });
  805. addEvent(this, 'mouseOut', function () {
  806. css(this.series.chart.container, { cursor: 'default' });
  807. });
  808. }
  809. return this;
  810. },
  811. /**
  812. * Return degree of a node. If node has no connections, it still has
  813. * deg=1.
  814. * @private
  815. * @return {number}
  816. */
  817. getDegree: function () {
  818. var deg = this.isNode ?
  819. this.linksFrom.length + this.linksTo.length :
  820. 0;
  821. return deg === 0 ? 1 : deg;
  822. },
  823. // Links:
  824. /**
  825. * Get presentational attributes of link connecting two nodes.
  826. * @private
  827. * @return {Highcharts.SVGAttributes}
  828. */
  829. getLinkAttributes: function () {
  830. var linkOptions = this.series.options.link, pointOptions = this.options;
  831. return {
  832. 'stroke-width': pick(pointOptions.width, linkOptions.width),
  833. stroke: (pointOptions.color || linkOptions.color),
  834. dashstyle: (pointOptions.dashStyle || linkOptions.dashStyle),
  835. opacity: pick(pointOptions.opacity, linkOptions.opacity, 1)
  836. };
  837. },
  838. /**
  839. * Render link and add it to the DOM.
  840. * @private
  841. */
  842. renderLink: function () {
  843. var attribs;
  844. if (!this.graphic) {
  845. this.graphic = this.series.chart.renderer
  846. .path(this.getLinkPath())
  847. .addClass(this.getClassName(), true)
  848. .add(this.series.group);
  849. if (!this.series.chart.styledMode) {
  850. attribs = this.series.pointAttribs(this);
  851. this.graphic.attr(attribs);
  852. (this.dataLabels || []).forEach(function (label) {
  853. if (label) {
  854. label.attr({
  855. opacity: attribs.opacity
  856. });
  857. }
  858. });
  859. }
  860. }
  861. },
  862. /**
  863. * Redraw link's path.
  864. * @private
  865. */
  866. redrawLink: function () {
  867. var path = this.getLinkPath(), attribs;
  868. if (this.graphic) {
  869. this.shapeArgs = {
  870. d: path
  871. };
  872. if (!this.series.chart.styledMode) {
  873. attribs = this.series.pointAttribs(this);
  874. this.graphic.attr(attribs);
  875. (this.dataLabels || []).forEach(function (label) {
  876. if (label) {
  877. label.attr({
  878. opacity: attribs.opacity
  879. });
  880. }
  881. });
  882. }
  883. this.graphic.animate(this.shapeArgs);
  884. // Required for dataLabels
  885. var start = path[0];
  886. var end = path[1];
  887. if (start[0] === 'M' && end[0] === 'L') {
  888. this.plotX = (start[1] + end[1]) / 2;
  889. this.plotY = (start[2] + end[2]) / 2;
  890. }
  891. }
  892. },
  893. /**
  894. * Get mass fraction applied on two nodes connected to each other. By
  895. * default, when mass is equal to `1`, mass fraction for both nodes
  896. * equal to 0.5.
  897. * @private
  898. * @return {Highcharts.Dictionary<number>}
  899. * For example `{ fromNode: 0.5, toNode: 0.5 }`
  900. */
  901. getMass: function () {
  902. var m1 = this.fromNode.mass, m2 = this.toNode.mass, sum = m1 + m2;
  903. return {
  904. fromNode: 1 - m1 / sum,
  905. toNode: 1 - m2 / sum
  906. };
  907. },
  908. /**
  909. * Get link path connecting two nodes.
  910. * @private
  911. * @return {Array<Highcharts.SVGPathArray>}
  912. * Path: `['M', x, y, 'L', x, y]`
  913. */
  914. getLinkPath: function () {
  915. var left = this.fromNode, right = this.toNode;
  916. // Start always from left to the right node, to prevent rendering
  917. // labels upside down
  918. if (left.plotX > right.plotX) {
  919. left = this.toNode;
  920. right = this.fromNode;
  921. }
  922. return [
  923. ['M', left.plotX || 0, left.plotY || 0],
  924. ['L', right.plotX || 0, right.plotY || 0]
  925. ];
  926. /*
  927. IDEA: different link shapes?
  928. return [
  929. 'M',
  930. from.plotX,
  931. from.plotY,
  932. 'Q',
  933. (to.plotX + from.plotX) / 2,
  934. (to.plotY + from.plotY) / 2 + 15,
  935. to.plotX,
  936. to.plotY
  937. ];*/
  938. },
  939. isValid: function () {
  940. return !this.isNode || defined(this.id);
  941. },
  942. /**
  943. * Common method for removing points and nodes in networkgraph. To
  944. * remove `link`, use `series.data[index].remove()`. To remove `node`
  945. * with all connections, use `series.nodes[index].remove()`.
  946. * @private
  947. * @param {boolean} [redraw=true]
  948. * Whether to redraw the chart or wait for an explicit call. When
  949. * doing more operations on the chart, for example running
  950. * `point.remove()` in a loop, it is best practice to set
  951. * `redraw` to false and call `chart.redraw()` after.
  952. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation=false]
  953. * Whether to apply animation, and optionally animation
  954. * configuration.
  955. * @return {void}
  956. */
  957. remove: function (redraw, animation) {
  958. var point = this, series = point.series, nodesOptions = series.options.nodes || [], index, i = nodesOptions.length;
  959. // For nodes, remove all connected links:
  960. if (point.isNode) {
  961. // Temporary disable series.points array, because
  962. // Series.removePoint() modifies it
  963. series.points = [];
  964. // Remove link from all nodes collections:
  965. []
  966. .concat(point.linksFrom)
  967. .concat(point.linksTo)
  968. .forEach(function (linkFromTo) {
  969. // Incoming links
  970. index = linkFromTo.fromNode.linksFrom.indexOf(linkFromTo);
  971. if (index > -1) {
  972. linkFromTo.fromNode.linksFrom.splice(index, 1);
  973. }
  974. // Outcoming links
  975. index = linkFromTo.toNode.linksTo.indexOf(linkFromTo);
  976. if (index > -1) {
  977. linkFromTo.toNode.linksTo.splice(index, 1);
  978. }
  979. // Remove link from data/points collections
  980. Series.prototype.removePoint.call(series, series.data.indexOf(linkFromTo), false, false);
  981. });
  982. // Restore points array, after links are removed
  983. series.points = series.data.slice();
  984. // Proceed with removing node. It's similar to
  985. // Series.removePoint() method, but doesn't modify other arrays
  986. series.nodes.splice(series.nodes.indexOf(point), 1);
  987. // Remove node options from config
  988. while (i--) {
  989. if (nodesOptions[i].id === point.options.id) {
  990. series.options.nodes.splice(i, 1);
  991. break;
  992. }
  993. }
  994. if (point) {
  995. point.destroy();
  996. }
  997. // Run redraw if requested
  998. series.isDirty = true;
  999. series.isDirtyData = true;
  1000. if (redraw) {
  1001. series.chart.redraw(redraw);
  1002. }
  1003. }
  1004. else {
  1005. series.removePoint(series.data.indexOf(point), redraw, animation);
  1006. }
  1007. },
  1008. /**
  1009. * Destroy point. If it's a node, remove all links coming out of this
  1010. * node. Then remove point from the layout.
  1011. * @private
  1012. * @return {void}
  1013. */
  1014. destroy: function () {
  1015. if (this.isNode) {
  1016. this.linksFrom.concat(this.linksTo).forEach(function (link) {
  1017. // Removing multiple nodes at the same time
  1018. // will try to remove link between nodes twice
  1019. if (link.destroyElements) {
  1020. link.destroyElements();
  1021. }
  1022. });
  1023. }
  1024. this.series.layout.removeElementFromCollection(this, this.series.layout[this.isNode ? 'nodes' : 'links']);
  1025. return Point.prototype.destroy.apply(this, arguments);
  1026. }
  1027. });
  1028. NetworkgraphSeries.prototype.pointClass = NetworkgraphPoint;
  1029. SeriesRegistry.registerSeriesType('networkgraph', NetworkgraphSeries);
  1030. /* *
  1031. *
  1032. * Default Export
  1033. *
  1034. * */
  1035. export default NetworkgraphSeries;
  1036. /* *
  1037. *
  1038. * API Options
  1039. *
  1040. * */
  1041. /**
  1042. * A `networkgraph` series. If the [type](#series.networkgraph.type) option is
  1043. * not specified, it is inherited from [chart.type](#chart.type).
  1044. *
  1045. * @extends series,plotOptions.networkgraph
  1046. * @excluding boostThreshold, animation, animationLimit, connectEnds,
  1047. * connectNulls, cropThreshold, dragDrop, getExtremesFromAll, label,
  1048. * linecap, negativeColor, pointInterval, pointIntervalUnit,
  1049. * pointPlacement, pointStart, softThreshold, stack, stacking,
  1050. * step, threshold, xAxis, yAxis, zoneAxis, dataSorting,
  1051. * boostBlending
  1052. * @product highcharts
  1053. * @requires modules/networkgraph
  1054. * @apioption series.networkgraph
  1055. */
  1056. /**
  1057. * An array of data points for the series. For the `networkgraph` series type,
  1058. * points can be given in the following way:
  1059. *
  1060. * An array of objects with named values. The following snippet shows only a
  1061. * few settings, see the complete options set below. If the total number of
  1062. * data points exceeds the series'
  1063. * [turboThreshold](#series.area.turboThreshold), this option is not available.
  1064. *
  1065. * ```js
  1066. * data: [{
  1067. * from: 'Category1',
  1068. * to: 'Category2'
  1069. * }, {
  1070. * from: 'Category1',
  1071. * to: 'Category3'
  1072. * }]
  1073. * ```
  1074. *
  1075. * @type {Array<Object|Array|Number>}
  1076. * @extends series.line.data
  1077. * @excluding drilldown,marker,x,y,draDrop
  1078. * @sample {highcharts} highcharts/chart/reflow-true/
  1079. * Numerical values
  1080. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  1081. * Arrays of numeric x and y
  1082. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  1083. * Arrays of datetime x and y
  1084. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  1085. * Arrays of point.name and y
  1086. * @sample {highcharts} highcharts/series/data-array-of-objects/
  1087. * Config objects
  1088. * @product highcharts
  1089. * @apioption series.networkgraph.data
  1090. */
  1091. /**
  1092. * @type {Highcharts.SeriesNetworkgraphDataLabelsOptionsObject|Array<Highcharts.SeriesNetworkgraphDataLabelsOptionsObject>}
  1093. * @product highcharts
  1094. * @apioption series.networkgraph.data.dataLabels
  1095. */
  1096. /**
  1097. * The node that the link runs from.
  1098. *
  1099. * @type {string}
  1100. * @product highcharts
  1101. * @apioption series.networkgraph.data.from
  1102. */
  1103. /**
  1104. * The node that the link runs to.
  1105. *
  1106. * @type {string}
  1107. * @product highcharts
  1108. * @apioption series.networkgraph.data.to
  1109. */
  1110. /**
  1111. * A collection of options for the individual nodes. The nodes in a
  1112. * networkgraph diagram are auto-generated instances of `Highcharts.Point`,
  1113. * but options can be applied here and linked by the `id`.
  1114. *
  1115. * @sample highcharts/series-networkgraph/data-options/
  1116. * Networkgraph diagram with node options
  1117. *
  1118. * @type {Array<*>}
  1119. * @product highcharts
  1120. * @apioption series.networkgraph.nodes
  1121. */
  1122. /**
  1123. * The id of the auto-generated node, refering to the `from` or `to` setting of
  1124. * the link.
  1125. *
  1126. * @type {string}
  1127. * @product highcharts
  1128. * @apioption series.networkgraph.nodes.id
  1129. */
  1130. /**
  1131. * The color of the auto generated node.
  1132. *
  1133. * @type {Highcharts.ColorString}
  1134. * @product highcharts
  1135. * @apioption series.networkgraph.nodes.color
  1136. */
  1137. /**
  1138. * The color index of the auto generated node, especially for use in styled
  1139. * mode.
  1140. *
  1141. * @type {number}
  1142. * @product highcharts
  1143. * @apioption series.networkgraph.nodes.colorIndex
  1144. */
  1145. /**
  1146. * The name to display for the node in data labels and tooltips. Use this when
  1147. * the name is different from the `id`. Where the id must be unique for each
  1148. * node, this is not necessary for the name.
  1149. *
  1150. * @sample highcharts/series-networkgraph/data-options/
  1151. * Networkgraph diagram with node options
  1152. *
  1153. * @type {string}
  1154. * @product highcharts
  1155. * @apioption series.networkgraph.nodes.name
  1156. */
  1157. /**
  1158. * Mass of the node. By default, each node has mass equal to it's marker radius
  1159. * . Mass is used to determine how two connected nodes should affect
  1160. * each other:
  1161. *
  1162. * Attractive force is multiplied by the ratio of two connected
  1163. * nodes; if a big node has weights twice as the small one, then the small one
  1164. * will move towards the big one twice faster than the big one to the small one
  1165. * .
  1166. *
  1167. * @sample highcharts/series-networkgraph/ragdoll/
  1168. * Mass determined by marker.radius
  1169. *
  1170. * @type {number}
  1171. * @product highcharts
  1172. * @apioption series.networkgraph.nodes.mass
  1173. */
  1174. /**
  1175. * Individual data label for each node. The options are the same as
  1176. * the ones for [series.networkgraph.dataLabels](#series.networkgraph.dataLabels).
  1177. *
  1178. * @type {Highcharts.SeriesNetworkgraphDataLabelsOptionsObject|Array<Highcharts.SeriesNetworkgraphDataLabelsOptionsObject>}
  1179. *
  1180. * @apioption series.networkgraph.nodes.dataLabels
  1181. */
  1182. ''; // adds doclets above to transpiled file