Networkgraph.js 39 KB

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