Networkgraph.js 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173
  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: null,
  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: null,
  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. Series.prototype.init.apply(this, arguments);
  515. addEvent(this, 'updatedData', function () {
  516. if (this.layout) {
  517. this.layout.stop();
  518. }
  519. });
  520. return this;
  521. },
  522. /**
  523. * Extend generatePoints by adding the nodes, which are Point objects
  524. * but pushed to the this.nodes array.
  525. * @private
  526. */
  527. generatePoints: function () {
  528. var node, i;
  529. NodesMixin.generatePoints.apply(this, arguments);
  530. // In networkgraph, it's fine to define stanalone nodes, create
  531. // them:
  532. if (this.options.nodes) {
  533. this.options.nodes.forEach(function (nodeOptions) {
  534. if (!this.nodeLookup[nodeOptions.id]) {
  535. this.nodeLookup[nodeOptions.id] =
  536. this.createNode(nodeOptions.id);
  537. }
  538. }, this);
  539. }
  540. for (i = this.nodes.length - 1; i >= 0; i--) {
  541. node = this.nodes[i];
  542. node.degree = node.getDegree();
  543. node.radius = pick(node.marker && node.marker.radius, this.options.marker && this.options.marker.radius, 0);
  544. // If node exists, but it's not available in nodeLookup,
  545. // then it's leftover from previous runs (e.g. setData)
  546. if (!this.nodeLookup[node.id]) {
  547. node.remove();
  548. }
  549. }
  550. this.data.forEach(function (link) {
  551. link.formatPrefix = 'link';
  552. });
  553. this.indexateNodes();
  554. },
  555. /**
  556. * In networkgraph, series.points refers to links,
  557. * but series.nodes refers to actual points.
  558. * @private
  559. */
  560. getPointsCollection: function () {
  561. return this.nodes || [];
  562. },
  563. /**
  564. * Set index for each node. Required for proper `node.update()`.
  565. * Note that links are indexated out of the box in `generatePoints()`.
  566. *
  567. * @private
  568. */
  569. indexateNodes: function () {
  570. this.nodes.forEach(function (node, index) {
  571. node.index = index;
  572. });
  573. },
  574. /**
  575. * Extend the default marker attribs by using a non-rounded X position,
  576. * otherwise the nodes will jump from pixel to pixel which looks a bit
  577. * jaggy when approaching equilibrium.
  578. * @private
  579. */
  580. markerAttribs: function (point, state) {
  581. var attribs = Series.prototype.markerAttribs.call(this, point, state);
  582. // series.render() is called before initial positions are set:
  583. if (!defined(point.plotY)) {
  584. attribs.y = 0;
  585. }
  586. attribs.x = (point.plotX || 0) - (attribs.width / 2 || 0);
  587. return attribs;
  588. },
  589. /**
  590. * Run pre-translation and register nodes&links to the deffered layout.
  591. * @private
  592. */
  593. translate: function () {
  594. if (!this.processedXData) {
  595. this.processData();
  596. }
  597. this.generatePoints();
  598. this.deferLayout();
  599. this.nodes.forEach(function (node) {
  600. // Draw the links from this node
  601. node.isInside = true;
  602. node.linksFrom.forEach(function (point) {
  603. point.shapeType = 'path';
  604. // Pass test in drawPoints
  605. point.y = 1;
  606. });
  607. });
  608. },
  609. /**
  610. * Defer the layout.
  611. * Each series first registers all nodes and links, then layout
  612. * calculates all nodes positions and calls `series.render()` in every
  613. * simulation step.
  614. *
  615. * Note:
  616. * Animation is done through `requestAnimationFrame` directly, without
  617. * `Highcharts.animate()` use.
  618. * @private
  619. */
  620. deferLayout: function () {
  621. var layoutOptions = this.options.layoutAlgorithm, graphLayoutsStorage = this.chart.graphLayoutsStorage, graphLayoutsLookup = this.chart.graphLayoutsLookup, chartOptions = this.chart.options.chart, layout;
  622. if (!this.visible) {
  623. return;
  624. }
  625. if (!graphLayoutsStorage) {
  626. this.chart.graphLayoutsStorage = graphLayoutsStorage = {};
  627. this.chart.graphLayoutsLookup = graphLayoutsLookup = [];
  628. }
  629. layout = graphLayoutsStorage[layoutOptions.type];
  630. if (!layout) {
  631. layoutOptions.enableSimulation =
  632. !defined(chartOptions.forExport) ?
  633. layoutOptions.enableSimulation :
  634. !chartOptions.forExport;
  635. graphLayoutsStorage[layoutOptions.type] = layout =
  636. new H.layouts[layoutOptions.type]();
  637. layout.init(layoutOptions);
  638. graphLayoutsLookup.splice(layout.index, 0, layout);
  639. }
  640. this.layout = layout;
  641. layout.setArea(0, 0, this.chart.plotWidth, this.chart.plotHeight);
  642. layout.addElementsToCollection([this], layout.series);
  643. layout.addElementsToCollection(this.nodes, layout.nodes);
  644. layout.addElementsToCollection(this.points, layout.links);
  645. },
  646. /**
  647. * Extend the render function to also render this.nodes together with
  648. * the points.
  649. * @private
  650. */
  651. render: function () {
  652. var series = this, points = series.points, hoverPoint = series.chart.hoverPoint, dataLabels = [];
  653. // Render markers:
  654. series.points = series.nodes;
  655. seriesTypes.line.prototype.render.call(this);
  656. series.points = points;
  657. points.forEach(function (point) {
  658. if (point.fromNode && point.toNode) {
  659. point.renderLink();
  660. point.redrawLink();
  661. }
  662. });
  663. if (hoverPoint && hoverPoint.series === series) {
  664. series.redrawHalo(hoverPoint);
  665. }
  666. if (series.chart.hasRendered &&
  667. !series.options.dataLabels.allowOverlap) {
  668. series.nodes.concat(series.points).forEach(function (node) {
  669. if (node.dataLabel) {
  670. dataLabels.push(node.dataLabel);
  671. }
  672. });
  673. series.chart.hideOverlappingLabels(dataLabels);
  674. }
  675. },
  676. // Networkgraph has two separate collecions of nodes and lines, render
  677. // dataLabels for both sets:
  678. drawDataLabels: function () {
  679. var textPath = this.options.dataLabels.textPath;
  680. // Render node labels:
  681. Series.prototype.drawDataLabels.apply(this, arguments);
  682. // Render link labels:
  683. this.points = this.data;
  684. this.options.dataLabels.textPath =
  685. this.options.dataLabels.linkTextPath;
  686. Series.prototype.drawDataLabels.apply(this, arguments);
  687. // Restore nodes
  688. this.points = this.nodes;
  689. this.options.dataLabels.textPath = textPath;
  690. },
  691. // Return the presentational attributes.
  692. pointAttribs: function (point, state) {
  693. // By default, only `selected` state is passed on
  694. var pointState = state || point && point.state || 'normal', attribs = Series.prototype.pointAttribs.call(this, point, pointState), stateOptions = this.options.states[pointState];
  695. if (point && !point.isNode) {
  696. attribs = point.getLinkAttributes();
  697. // For link, get prefixed names:
  698. if (stateOptions) {
  699. attribs = {
  700. // TO DO: API?
  701. stroke: stateOptions.linkColor || attribs.stroke,
  702. dashstyle: (stateOptions.linkDashStyle || attribs.dashstyle),
  703. opacity: pick(stateOptions.linkOpacity, attribs.opacity),
  704. 'stroke-width': stateOptions.linkColor ||
  705. attribs['stroke-width']
  706. };
  707. }
  708. }
  709. return attribs;
  710. },
  711. // Draggable mode:
  712. /**
  713. * Redraw halo on mousemove during the drag&drop action.
  714. * @private
  715. * @param {Highcharts.Point} point The point that should show halo.
  716. */
  717. redrawHalo: dragNodesMixin.redrawHalo,
  718. /**
  719. * Mouse down action, initializing drag&drop mode.
  720. * @private
  721. * @param {global.Event} event Browser event, before normalization.
  722. * @param {Highcharts.Point} point The point that event occured.
  723. */
  724. onMouseDown: dragNodesMixin.onMouseDown,
  725. /**
  726. * Mouse move action during drag&drop.
  727. * @private
  728. * @param {global.Event} event Browser event, before normalization.
  729. * @param {Highcharts.Point} point The point that event occured.
  730. */
  731. onMouseMove: dragNodesMixin.onMouseMove,
  732. /**
  733. * Mouse up action, finalizing drag&drop.
  734. * @private
  735. * @param {Highcharts.Point} point The point that event occured.
  736. */
  737. onMouseUp: dragNodesMixin.onMouseUp,
  738. /**
  739. * When state should be passed down to all points, concat nodes and
  740. * links and apply this state to all of them.
  741. * @private
  742. */
  743. setState: function (state, inherit) {
  744. if (inherit) {
  745. this.points = this.nodes.concat(this.data);
  746. Series.prototype.setState.apply(this, arguments);
  747. this.points = this.data;
  748. }
  749. else {
  750. Series.prototype.setState.apply(this, arguments);
  751. }
  752. // If simulation is done, re-render points with new states:
  753. if (!this.layout.simulation && !state) {
  754. this.render();
  755. }
  756. }
  757. });
  758. /* *
  759. *
  760. * Class
  761. *
  762. * */
  763. var NetworkgraphPoint = /** @class */ (function (_super) {
  764. __extends(NetworkgraphPoint, _super);
  765. function NetworkgraphPoint() {
  766. /* *
  767. *
  768. * Properties
  769. *
  770. * */
  771. var _this = _super !== null && _super.apply(this, arguments) || this;
  772. _this.degree = void 0;
  773. _this.linksFrom = void 0;
  774. _this.linksTo = void 0;
  775. _this.options = void 0;
  776. _this.radius = void 0;
  777. _this.series = void 0;
  778. _this.toNode = void 0;
  779. return _this;
  780. }
  781. return NetworkgraphPoint;
  782. }(Series.prototype.pointClass));
  783. extend(NetworkgraphPoint.prototype, {
  784. setState: NodesMixin.setNodeState,
  785. /**
  786. * Basic `point.init()` and additional styles applied when
  787. * `series.draggable` is enabled.
  788. * @private
  789. */
  790. init: function () {
  791. Point.prototype.init.apply(this, arguments);
  792. if (this.series.options.draggable &&
  793. !this.series.chart.styledMode) {
  794. addEvent(this, 'mouseOver', function () {
  795. css(this.series.chart.container, { cursor: 'move' });
  796. });
  797. addEvent(this, 'mouseOut', function () {
  798. css(this.series.chart.container, { cursor: 'default' });
  799. });
  800. }
  801. return this;
  802. },
  803. /**
  804. * Return degree of a node. If node has no connections, it still has
  805. * deg=1.
  806. * @private
  807. * @return {number}
  808. */
  809. getDegree: function () {
  810. var deg = this.isNode ?
  811. this.linksFrom.length + this.linksTo.length :
  812. 0;
  813. return deg === 0 ? 1 : deg;
  814. },
  815. // Links:
  816. /**
  817. * Get presentational attributes of link connecting two nodes.
  818. * @private
  819. * @return {Highcharts.SVGAttributes}
  820. */
  821. getLinkAttributes: function () {
  822. var linkOptions = this.series.options.link, pointOptions = this.options;
  823. return {
  824. 'stroke-width': pick(pointOptions.width, linkOptions.width),
  825. stroke: (pointOptions.color || linkOptions.color),
  826. dashstyle: (pointOptions.dashStyle || linkOptions.dashStyle),
  827. opacity: pick(pointOptions.opacity, linkOptions.opacity, 1)
  828. };
  829. },
  830. /**
  831. * Render link and add it to the DOM.
  832. * @private
  833. */
  834. renderLink: function () {
  835. var attribs;
  836. if (!this.graphic) {
  837. this.graphic = this.series.chart.renderer
  838. .path(this.getLinkPath())
  839. .add(this.series.group);
  840. if (!this.series.chart.styledMode) {
  841. attribs = this.series.pointAttribs(this);
  842. this.graphic.attr(attribs);
  843. (this.dataLabels || []).forEach(function (label) {
  844. if (label) {
  845. label.attr({
  846. opacity: attribs.opacity
  847. });
  848. }
  849. });
  850. }
  851. }
  852. },
  853. /**
  854. * Redraw link's path.
  855. * @private
  856. */
  857. redrawLink: function () {
  858. var path = this.getLinkPath(), attribs;
  859. if (this.graphic) {
  860. this.shapeArgs = {
  861. d: path
  862. };
  863. if (!this.series.chart.styledMode) {
  864. attribs = this.series.pointAttribs(this);
  865. this.graphic.attr(attribs);
  866. (this.dataLabels || []).forEach(function (label) {
  867. if (label) {
  868. label.attr({
  869. opacity: attribs.opacity
  870. });
  871. }
  872. });
  873. }
  874. this.graphic.animate(this.shapeArgs);
  875. // Required for dataLabels
  876. var start = path[0];
  877. var end = path[1];
  878. if (start[0] === 'M' && end[0] === 'L') {
  879. this.plotX = (start[1] + end[1]) / 2;
  880. this.plotY = (start[2] + end[2]) / 2;
  881. }
  882. }
  883. },
  884. /**
  885. * Get mass fraction applied on two nodes connected to each other. By
  886. * default, when mass is equal to `1`, mass fraction for both nodes
  887. * equal to 0.5.
  888. * @private
  889. * @return {Highcharts.Dictionary<number>}
  890. * For example `{ fromNode: 0.5, toNode: 0.5 }`
  891. */
  892. getMass: function () {
  893. var m1 = this.fromNode.mass, m2 = this.toNode.mass, sum = m1 + m2;
  894. return {
  895. fromNode: 1 - m1 / sum,
  896. toNode: 1 - m2 / sum
  897. };
  898. },
  899. /**
  900. * Get link path connecting two nodes.
  901. * @private
  902. * @return {Array<Highcharts.SVGPathArray>}
  903. * Path: `['M', x, y, 'L', x, y]`
  904. */
  905. getLinkPath: function () {
  906. var left = this.fromNode, right = this.toNode;
  907. // Start always from left to the right node, to prevent rendering
  908. // labels upside down
  909. if (left.plotX > right.plotX) {
  910. left = this.toNode;
  911. right = this.fromNode;
  912. }
  913. return [
  914. ['M', left.plotX || 0, left.plotY || 0],
  915. ['L', right.plotX || 0, right.plotY || 0]
  916. ];
  917. /*
  918. IDEA: different link shapes?
  919. return [
  920. 'M',
  921. from.plotX,
  922. from.plotY,
  923. 'Q',
  924. (to.plotX + from.plotX) / 2,
  925. (to.plotY + from.plotY) / 2 + 15,
  926. to.plotX,
  927. to.plotY
  928. ];*/
  929. },
  930. isValid: function () {
  931. return !this.isNode || defined(this.id);
  932. },
  933. /**
  934. * Common method for removing points and nodes in networkgraph. To
  935. * remove `link`, use `series.data[index].remove()`. To remove `node`
  936. * with all connections, use `series.nodes[index].remove()`.
  937. * @private
  938. * @param {boolean} [redraw=true]
  939. * Whether to redraw the chart or wait for an explicit call. When
  940. * doing more operations on the chart, for example running
  941. * `point.remove()` in a loop, it is best practice to set
  942. * `redraw` to false and call `chart.redraw()` after.
  943. * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation=false]
  944. * Whether to apply animation, and optionally animation
  945. * configuration.
  946. * @return {void}
  947. */
  948. remove: function (redraw, animation) {
  949. var point = this, series = point.series, nodesOptions = series.options.nodes || [], index, i = nodesOptions.length;
  950. // For nodes, remove all connected links:
  951. if (point.isNode) {
  952. // Temporary disable series.points array, because
  953. // Series.removePoint() modifies it
  954. series.points = [];
  955. // Remove link from all nodes collections:
  956. []
  957. .concat(point.linksFrom)
  958. .concat(point.linksTo)
  959. .forEach(function (linkFromTo) {
  960. // Incoming links
  961. index = linkFromTo.fromNode.linksFrom.indexOf(linkFromTo);
  962. if (index > -1) {
  963. linkFromTo.fromNode.linksFrom.splice(index, 1);
  964. }
  965. // Outcoming links
  966. index = linkFromTo.toNode.linksTo.indexOf(linkFromTo);
  967. if (index > -1) {
  968. linkFromTo.toNode.linksTo.splice(index, 1);
  969. }
  970. // Remove link from data/points collections
  971. Series.prototype.removePoint.call(series, series.data.indexOf(linkFromTo), false, false);
  972. });
  973. // Restore points array, after links are removed
  974. series.points = series.data.slice();
  975. // Proceed with removing node. It's similar to
  976. // Series.removePoint() method, but doesn't modify other arrays
  977. series.nodes.splice(series.nodes.indexOf(point), 1);
  978. // Remove node options from config
  979. while (i--) {
  980. if (nodesOptions[i].id === point.options.id) {
  981. series.options.nodes.splice(i, 1);
  982. break;
  983. }
  984. }
  985. if (point) {
  986. point.destroy();
  987. }
  988. // Run redraw if requested
  989. series.isDirty = true;
  990. series.isDirtyData = true;
  991. if (redraw) {
  992. series.chart.redraw(redraw);
  993. }
  994. }
  995. else {
  996. series.removePoint(series.data.indexOf(point), redraw, animation);
  997. }
  998. },
  999. /**
  1000. * Destroy point. If it's a node, remove all links coming out of this
  1001. * node. Then remove point from the layout.
  1002. * @private
  1003. * @return {void}
  1004. */
  1005. destroy: function () {
  1006. if (this.isNode) {
  1007. this.linksFrom.concat(this.linksTo).forEach(function (link) {
  1008. // Removing multiple nodes at the same time
  1009. // will try to remove link between nodes twice
  1010. if (link.destroyElements) {
  1011. link.destroyElements();
  1012. }
  1013. });
  1014. }
  1015. this.series.layout.removeElementFromCollection(this, this.series.layout[this.isNode ? 'nodes' : 'links']);
  1016. return Point.prototype.destroy.apply(this, arguments);
  1017. }
  1018. });
  1019. NetworkgraphSeries.prototype.pointClass = NetworkgraphPoint;
  1020. SeriesRegistry.registerSeriesType('networkgraph', NetworkgraphSeries);
  1021. /* *
  1022. *
  1023. * Default Export
  1024. *
  1025. * */
  1026. export default NetworkgraphSeries;
  1027. /* *
  1028. *
  1029. * API Options
  1030. *
  1031. * */
  1032. /**
  1033. * A `networkgraph` series. If the [type](#series.networkgraph.type) option is
  1034. * not specified, it is inherited from [chart.type](#chart.type).
  1035. *
  1036. * @extends series,plotOptions.networkgraph
  1037. * @excluding boostThreshold, animation, animationLimit, connectEnds,
  1038. * connectNulls, cropThreshold, dragDrop, getExtremesFromAll, label,
  1039. * linecap, negativeColor, pointInterval, pointIntervalUnit,
  1040. * pointPlacement, pointStart, softThreshold, stack, stacking,
  1041. * step, threshold, xAxis, yAxis, zoneAxis, dataSorting,
  1042. * boostBlending
  1043. * @product highcharts
  1044. * @requires modules/networkgraph
  1045. * @apioption series.networkgraph
  1046. */
  1047. /**
  1048. * An array of data points for the series. For the `networkgraph` series type,
  1049. * points can be given in the following way:
  1050. *
  1051. * An array of objects with named values. The following snippet shows only a
  1052. * few settings, see the complete options set below. If the total number of
  1053. * data points exceeds the series'
  1054. * [turboThreshold](#series.area.turboThreshold), this option is not available.
  1055. *
  1056. * ```js
  1057. * data: [{
  1058. * from: 'Category1',
  1059. * to: 'Category2'
  1060. * }, {
  1061. * from: 'Category1',
  1062. * to: 'Category3'
  1063. * }]
  1064. * ```
  1065. *
  1066. * @type {Array<Object|Array|Number>}
  1067. * @extends series.line.data
  1068. * @excluding drilldown,marker,x,y,draDrop
  1069. * @sample {highcharts} highcharts/chart/reflow-true/
  1070. * Numerical values
  1071. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  1072. * Arrays of numeric x and y
  1073. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  1074. * Arrays of datetime x and y
  1075. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  1076. * Arrays of point.name and y
  1077. * @sample {highcharts} highcharts/series/data-array-of-objects/
  1078. * Config objects
  1079. * @product highcharts
  1080. * @apioption series.networkgraph.data
  1081. */
  1082. /**
  1083. * @type {Highcharts.SeriesNetworkgraphDataLabelsOptionsObject|Array<Highcharts.SeriesNetworkgraphDataLabelsOptionsObject>}
  1084. * @product highcharts
  1085. * @apioption series.networkgraph.data.dataLabels
  1086. */
  1087. /**
  1088. * The node that the link runs from.
  1089. *
  1090. * @type {string}
  1091. * @product highcharts
  1092. * @apioption series.networkgraph.data.from
  1093. */
  1094. /**
  1095. * The node that the link runs to.
  1096. *
  1097. * @type {string}
  1098. * @product highcharts
  1099. * @apioption series.networkgraph.data.to
  1100. */
  1101. /**
  1102. * A collection of options for the individual nodes. The nodes in a
  1103. * networkgraph diagram are auto-generated instances of `Highcharts.Point`,
  1104. * but options can be applied here and linked by the `id`.
  1105. *
  1106. * @sample highcharts/series-networkgraph/data-options/
  1107. * Networkgraph diagram with node options
  1108. *
  1109. * @type {Array<*>}
  1110. * @product highcharts
  1111. * @apioption series.networkgraph.nodes
  1112. */
  1113. /**
  1114. * The id of the auto-generated node, refering to the `from` or `to` setting of
  1115. * the link.
  1116. *
  1117. * @type {string}
  1118. * @product highcharts
  1119. * @apioption series.networkgraph.nodes.id
  1120. */
  1121. /**
  1122. * The color of the auto generated node.
  1123. *
  1124. * @type {Highcharts.ColorString}
  1125. * @product highcharts
  1126. * @apioption series.networkgraph.nodes.color
  1127. */
  1128. /**
  1129. * The color index of the auto generated node, especially for use in styled
  1130. * mode.
  1131. *
  1132. * @type {number}
  1133. * @product highcharts
  1134. * @apioption series.networkgraph.nodes.colorIndex
  1135. */
  1136. /**
  1137. * The name to display for the node in data labels and tooltips. Use this when
  1138. * the name is different from the `id`. Where the id must be unique for each
  1139. * node, this is not necessary for the name.
  1140. *
  1141. * @sample highcharts/series-networkgraph/data-options/
  1142. * Networkgraph diagram with node options
  1143. *
  1144. * @type {string}
  1145. * @product highcharts
  1146. * @apioption series.networkgraph.nodes.name
  1147. */
  1148. /**
  1149. * Mass of the node. By default, each node has mass equal to it's marker radius
  1150. * . Mass is used to determine how two connected nodes should affect
  1151. * each other:
  1152. *
  1153. * Attractive force is multiplied by the ratio of two connected
  1154. * nodes; if a big node has weights twice as the small one, then the small one
  1155. * will move towards the big one twice faster than the big one to the small one
  1156. * .
  1157. *
  1158. * @sample highcharts/series-networkgraph/ragdoll/
  1159. * Mass determined by marker.radius
  1160. *
  1161. * @type {number}
  1162. * @product highcharts
  1163. * @apioption series.networkgraph.nodes.mass
  1164. */
  1165. /**
  1166. * Individual data label for each node. The options are the same as
  1167. * the ones for [series.networkgraph.dataLabels](#series.networkgraph.dataLabels).
  1168. *
  1169. * @type {Highcharts.SeriesNetworkgraphDataLabelsOptionsObject|Array<Highcharts.SeriesNetworkgraphDataLabelsOptionsObject>}
  1170. *
  1171. * @apioption series.networkgraph.nodes.dataLabels
  1172. */
  1173. ''; // adds doclets above to transpiled file