networkgraph.src.js 112 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653
  1. /**
  2. * @license Highcharts JS v8.0.0 (2019-12-10)
  3. *
  4. * Force directed graph module
  5. *
  6. * (c) 2010-2019 Torstein Honsi
  7. *
  8. * License: www.highcharts.com/license
  9. */
  10. 'use strict';
  11. (function (factory) {
  12. if (typeof module === 'object' && module.exports) {
  13. factory['default'] = factory;
  14. module.exports = factory;
  15. } else if (typeof define === 'function' && define.amd) {
  16. define('highcharts/modules/networkgraph', ['highcharts'], function (Highcharts) {
  17. factory(Highcharts);
  18. factory.Highcharts = Highcharts;
  19. return factory;
  20. });
  21. } else {
  22. factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
  23. }
  24. }(function (Highcharts) {
  25. var _modules = Highcharts ? Highcharts._modules : {};
  26. function _registerModule(obj, path, args, fn) {
  27. if (!obj.hasOwnProperty(path)) {
  28. obj[path] = fn.apply(null, args);
  29. }
  30. }
  31. _registerModule(_modules, 'mixins/nodes.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  32. /* *
  33. *
  34. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  35. *
  36. * */
  37. var defined = U.defined, extend = U.extend, pick = U.pick;
  38. var Point = H.Point;
  39. H.NodesMixin = {
  40. /* eslint-disable valid-jsdoc */
  41. /**
  42. * Create a single node that holds information on incoming and outgoing
  43. * links.
  44. * @private
  45. */
  46. createNode: function (id) {
  47. /**
  48. * @private
  49. */
  50. function findById(nodes, id) {
  51. return H.find(nodes, function (node) {
  52. return node.id === id;
  53. });
  54. }
  55. var node = findById(this.nodes, id), PointClass = this.pointClass, options;
  56. if (!node) {
  57. options = this.options.nodes && findById(this.options.nodes, id);
  58. node = (new PointClass()).init(this, extend({
  59. className: 'highcharts-node',
  60. isNode: true,
  61. id: id,
  62. y: 1 // Pass isNull test
  63. }, options));
  64. node.linksTo = [];
  65. node.linksFrom = [];
  66. node.formatPrefix = 'node';
  67. node.name = node.name || node.options.id; // for use in formats
  68. // Mass is used in networkgraph:
  69. node.mass = pick(
  70. // Node:
  71. node.options.mass, node.options.marker && node.options.marker.radius,
  72. // Series:
  73. this.options.marker && this.options.marker.radius,
  74. // Default:
  75. 4);
  76. /**
  77. * Return the largest sum of either the incoming or outgoing links.
  78. * @private
  79. */
  80. node.getSum = function () {
  81. var sumTo = 0, sumFrom = 0;
  82. node.linksTo.forEach(function (link) {
  83. sumTo += link.weight;
  84. });
  85. node.linksFrom.forEach(function (link) {
  86. sumFrom += link.weight;
  87. });
  88. return Math.max(sumTo, sumFrom);
  89. };
  90. /**
  91. * Get the offset in weight values of a point/link.
  92. * @private
  93. */
  94. node.offset = function (point, coll) {
  95. var offset = 0;
  96. for (var i = 0; i < node[coll].length; i++) {
  97. if (node[coll][i] === point) {
  98. return offset;
  99. }
  100. offset += node[coll][i].weight;
  101. }
  102. };
  103. // Return true if the node has a shape, otherwise all links are
  104. // outgoing.
  105. node.hasShape = function () {
  106. var outgoing = 0;
  107. node.linksTo.forEach(function (link) {
  108. if (link.outgoing) {
  109. outgoing++;
  110. }
  111. });
  112. return (!node.linksTo.length ||
  113. outgoing !== node.linksTo.length);
  114. };
  115. this.nodes.push(node);
  116. }
  117. return node;
  118. },
  119. /**
  120. * Extend generatePoints by adding the nodes, which are Point objects
  121. * but pushed to the this.nodes array.
  122. */
  123. generatePoints: function () {
  124. var chart = this.chart, nodeLookup = {};
  125. H.Series.prototype.generatePoints.call(this);
  126. if (!this.nodes) {
  127. this.nodes = []; // List of Point-like node items
  128. }
  129. this.colorCounter = 0;
  130. // Reset links from previous run
  131. this.nodes.forEach(function (node) {
  132. node.linksFrom.length = 0;
  133. node.linksTo.length = 0;
  134. node.level = node.options.level;
  135. });
  136. // Create the node list and set up links
  137. this.points.forEach(function (point) {
  138. if (defined(point.from)) {
  139. if (!nodeLookup[point.from]) {
  140. nodeLookup[point.from] = this.createNode(point.from);
  141. }
  142. nodeLookup[point.from].linksFrom.push(point);
  143. point.fromNode = nodeLookup[point.from];
  144. // Point color defaults to the fromNode's color
  145. if (chart.styledMode) {
  146. point.colorIndex = pick(point.options.colorIndex, nodeLookup[point.from].colorIndex);
  147. }
  148. else {
  149. point.color =
  150. point.options.color || nodeLookup[point.from].color;
  151. }
  152. }
  153. if (defined(point.to)) {
  154. if (!nodeLookup[point.to]) {
  155. nodeLookup[point.to] = this.createNode(point.to);
  156. }
  157. nodeLookup[point.to].linksTo.push(point);
  158. point.toNode = nodeLookup[point.to];
  159. }
  160. point.name = point.name || point.id; // for use in formats
  161. }, this);
  162. // Store lookup table for later use
  163. this.nodeLookup = nodeLookup;
  164. },
  165. // Destroy all nodes on setting new data
  166. setData: function () {
  167. if (this.nodes) {
  168. this.nodes.forEach(function (node) {
  169. node.destroy();
  170. });
  171. this.nodes.length = 0;
  172. }
  173. H.Series.prototype.setData.apply(this, arguments);
  174. },
  175. // Destroy alll nodes and links
  176. destroy: function () {
  177. // Nodes must also be destroyed (#8682, #9300)
  178. this.data = []
  179. .concat(this.points || [], this.nodes);
  180. return H.Series.prototype.destroy.apply(this, arguments);
  181. },
  182. /**
  183. * When hovering node, highlight all connected links. When hovering a link,
  184. * highlight all connected nodes.
  185. */
  186. setNodeState: function (state) {
  187. var args = arguments, others = this.isNode ? this.linksTo.concat(this.linksFrom) :
  188. [this.fromNode, this.toNode];
  189. if (state !== 'select') {
  190. others.forEach(function (linkOrNode) {
  191. if (linkOrNode.series) {
  192. Point.prototype.setState.apply(linkOrNode, args);
  193. if (!linkOrNode.isNode) {
  194. if (linkOrNode.fromNode.graphic) {
  195. Point.prototype.setState.apply(linkOrNode.fromNode, args);
  196. }
  197. if (linkOrNode.toNode.graphic) {
  198. Point.prototype.setState.apply(linkOrNode.toNode, args);
  199. }
  200. }
  201. }
  202. });
  203. }
  204. Point.prototype.setState.apply(this, args);
  205. }
  206. /* eslint-enable valid-jsdoc */
  207. };
  208. });
  209. _registerModule(_modules, 'modules/networkgraph/integrations.js', [_modules['parts/Globals.js']], function (H) {
  210. /* *
  211. *
  212. * Networkgraph series
  213. *
  214. * (c) 2010-2019 Paweł Fus
  215. *
  216. * License: www.highcharts.com/license
  217. *
  218. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  219. *
  220. * */
  221. /* eslint-disable no-invalid-this, valid-jsdoc */
  222. H.networkgraphIntegrations = {
  223. verlet: {
  224. /**
  225. * Attractive force funtion. Can be replaced by API's
  226. * `layoutAlgorithm.attractiveForce`
  227. *
  228. * @private
  229. * @param {number} d current distance between two nodes
  230. * @param {number} k expected distance between two nodes
  231. * @return {number} force
  232. */
  233. attractiveForceFunction: function (d, k) {
  234. // Used in API:
  235. return (k - d) / d;
  236. },
  237. /**
  238. * Repulsive force funtion. Can be replaced by API's
  239. * `layoutAlgorithm.repulsiveForce`
  240. *
  241. * @private
  242. * @param {number} d current distance between two nodes
  243. * @param {number} k expected distance between two nodes
  244. * @return {number} force
  245. */
  246. repulsiveForceFunction: function (d, k) {
  247. // Used in API:
  248. return (k - d) / d * (k > d ? 1 : 0); // Force only for close nodes
  249. },
  250. /**
  251. * Barycenter force. Calculate and applys barycenter forces on the
  252. * nodes. Making them closer to the center of their barycenter point.
  253. *
  254. * In Verlet integration, force is applied on a node immidatelly to it's
  255. * `plotX` and `plotY` position.
  256. *
  257. * @private
  258. * @return {void}
  259. */
  260. barycenter: function () {
  261. var gravitationalConstant = this.options.gravitationalConstant, xFactor = this.barycenter.xFactor, yFactor = this.barycenter.yFactor;
  262. // To consider:
  263. xFactor = (xFactor - (this.box.left + this.box.width) / 2) *
  264. gravitationalConstant;
  265. yFactor = (yFactor - (this.box.top + this.box.height) / 2) *
  266. gravitationalConstant;
  267. this.nodes.forEach(function (node) {
  268. if (!node.fixedPosition) {
  269. node.plotX -=
  270. xFactor / node.mass / node.degree;
  271. node.plotY -=
  272. yFactor / node.mass / node.degree;
  273. }
  274. });
  275. },
  276. /**
  277. * Repulsive force.
  278. *
  279. * In Verlet integration, force is applied on a node immidatelly to it's
  280. * `plotX` and `plotY` position.
  281. *
  282. * @private
  283. * @param {Highcharts.Point} node
  284. * Node that should be translated by force.
  285. * @param {number} force
  286. * Force calcualated in `repulsiveForceFunction`
  287. * @param {Highcharts.PositionObject} distance
  288. * Distance between two nodes e.g. `{x, y}`
  289. * @return {void}
  290. */
  291. repulsive: function (node, force, distanceXY) {
  292. var factor = force * this.diffTemperature / node.mass / node.degree;
  293. if (!node.fixedPosition) {
  294. node.plotX += distanceXY.x * factor;
  295. node.plotY += distanceXY.y * factor;
  296. }
  297. },
  298. /**
  299. * Attractive force.
  300. *
  301. * In Verlet integration, force is applied on a node immidatelly to it's
  302. * `plotX` and `plotY` position.
  303. *
  304. * @private
  305. * @param {Highcharts.Point} link
  306. * Link that connects two nodes
  307. * @param {number} force
  308. * Force calcualated in `repulsiveForceFunction`
  309. * @param {Highcharts.PositionObject} distance
  310. * Distance between two nodes e.g. `{x, y}`
  311. * @return {void}
  312. */
  313. attractive: function (link, force, distanceXY) {
  314. var massFactor = link.getMass(), translatedX = -distanceXY.x * force * this.diffTemperature, translatedY = -distanceXY.y * force * this.diffTemperature;
  315. if (!link.fromNode.fixedPosition) {
  316. link.fromNode.plotX -=
  317. translatedX * massFactor.fromNode / link.fromNode.degree;
  318. link.fromNode.plotY -=
  319. translatedY * massFactor.fromNode / link.fromNode.degree;
  320. }
  321. if (!link.toNode.fixedPosition) {
  322. link.toNode.plotX +=
  323. translatedX * massFactor.toNode / link.toNode.degree;
  324. link.toNode.plotY +=
  325. translatedY * massFactor.toNode / link.toNode.degree;
  326. }
  327. },
  328. /**
  329. * Integration method.
  330. *
  331. * In Verlet integration, forces are applied on node immidatelly to it's
  332. * `plotX` and `plotY` position.
  333. *
  334. * Verlet without velocity:
  335. *
  336. * x(n+1) = 2 * x(n) - x(n-1) + A(T) * deltaT ^ 2
  337. *
  338. * where:
  339. * - x(n+1) - new position
  340. * - x(n) - current position
  341. * - x(n-1) - previous position
  342. *
  343. * Assuming A(t) = 0 (no acceleration) and (deltaT = 1) we get:
  344. *
  345. * x(n+1) = x(n) + (x(n) - x(n-1))
  346. *
  347. * where:
  348. * - (x(n) - x(n-1)) - position change
  349. *
  350. * TO DO:
  351. * Consider Verlet with velocity to support additional
  352. * forces. Or even Time-Corrected Verlet by Jonathan
  353. * "lonesock" Dummer
  354. *
  355. * @private
  356. * @param {Highcharts.NetworkgraphLayout} layout layout object
  357. * @param {Highcharts.Point} node node that should be translated
  358. * @return {void}
  359. */
  360. integrate: function (layout, node) {
  361. var friction = -layout.options.friction, maxSpeed = layout.options.maxSpeed, prevX = node.prevX, prevY = node.prevY,
  362. // Apply friciton:
  363. diffX = ((node.plotX + node.dispX -
  364. prevX) * friction), diffY = ((node.plotY + node.dispY -
  365. prevY) * friction), abs = Math.abs, signX = abs(diffX) / (diffX || 1), // need to deal with 0
  366. signY = abs(diffY) / (diffY || 1);
  367. // Apply max speed:
  368. diffX = signX * Math.min(maxSpeed, Math.abs(diffX));
  369. diffY = signY * Math.min(maxSpeed, Math.abs(diffY));
  370. // Store for the next iteration:
  371. node.prevX = node.plotX + node.dispX;
  372. node.prevY = node.plotY + node.dispY;
  373. // Update positions:
  374. node.plotX += diffX;
  375. node.plotY += diffY;
  376. node.temperature = layout.vectorLength({
  377. x: diffX,
  378. y: diffY
  379. });
  380. },
  381. /**
  382. * Estiamte the best possible distance between two nodes, making graph
  383. * readable.
  384. *
  385. * @private
  386. * @param {Highcharts.NetworkgraphLayout} layout layout object
  387. * @return {number}
  388. */
  389. getK: function (layout) {
  390. return Math.pow(layout.box.width * layout.box.height / layout.nodes.length, 0.5);
  391. }
  392. },
  393. euler: {
  394. /**
  395. * Attractive force funtion. Can be replaced by API's
  396. * `layoutAlgorithm.attractiveForce`
  397. *
  398. * Other forces that can be used:
  399. *
  400. * basic, not recommended:
  401. * `function (d, k) { return d / k }`
  402. *
  403. * @private
  404. * @param {number} d current distance between two nodes
  405. * @param {number} k expected distance between two nodes
  406. * @return {number} force
  407. */
  408. attractiveForceFunction: function (d, k) {
  409. return d * d / k;
  410. },
  411. /**
  412. * Repulsive force funtion. Can be replaced by API's
  413. * `layoutAlgorithm.repulsiveForce`.
  414. *
  415. * Other forces that can be used:
  416. *
  417. * basic, not recommended:
  418. * `function (d, k) { return k / d }`
  419. *
  420. * standard:
  421. * `function (d, k) { return k * k / d }`
  422. *
  423. * grid-variant:
  424. * `function (d, k) { return k * k / d * (2 * k - d > 0 ? 1 : 0) }`
  425. *
  426. * @private
  427. * @param {number} d current distance between two nodes
  428. * @param {number} k expected distance between two nodes
  429. * @return {number} force
  430. */
  431. repulsiveForceFunction: function (d, k) {
  432. return k * k / d;
  433. },
  434. /**
  435. * Barycenter force. Calculate and applys barycenter forces on the
  436. * nodes. Making them closer to the center of their barycenter point.
  437. *
  438. * In Euler integration, force is stored in a node, not changing it's
  439. * position. Later, in `integrate()` forces are applied on nodes.
  440. *
  441. * @private
  442. * @return {void}
  443. */
  444. barycenter: function () {
  445. var gravitationalConstant = this.options.gravitationalConstant, xFactor = this.barycenter.xFactor, yFactor = this.barycenter.yFactor;
  446. this.nodes.forEach(function (node) {
  447. if (!node.fixedPosition) {
  448. var degree = node.getDegree(), phi = degree * (1 + degree / 2);
  449. node.dispX += ((xFactor - node.plotX) *
  450. gravitationalConstant *
  451. phi / node.degree);
  452. node.dispY += ((yFactor - node.plotY) *
  453. gravitationalConstant *
  454. phi / node.degree);
  455. }
  456. });
  457. },
  458. /**
  459. * Repulsive force.
  460. *
  461. * @private
  462. * @param {Highcharts.Point} node
  463. * Node that should be translated by force.
  464. * @param {number} force
  465. * Force calcualated in `repulsiveForceFunction`
  466. * @param {Highcharts.PositionObject} distanceXY
  467. * Distance between two nodes e.g. `{x, y}`
  468. * @return {void}
  469. */
  470. repulsive: function (node, force, distanceXY, distanceR) {
  471. node.dispX +=
  472. (distanceXY.x / distanceR) * force / node.degree;
  473. node.dispY +=
  474. (distanceXY.y / distanceR) * force / node.degree;
  475. },
  476. /**
  477. * Attractive force.
  478. *
  479. * In Euler integration, force is stored in a node, not changing it's
  480. * position. Later, in `integrate()` forces are applied on nodes.
  481. *
  482. * @private
  483. * @param {Highcharts.Point} link
  484. * Link that connects two nodes
  485. * @param {number} force
  486. * Force calcualated in `repulsiveForceFunction`
  487. * @param {Highcharts.PositionObject} distanceXY
  488. * Distance between two nodes e.g. `{x, y}`
  489. * @param {number} distanceR
  490. * @return {void}
  491. */
  492. attractive: function (link, force, distanceXY, distanceR) {
  493. var massFactor = link.getMass(), translatedX = (distanceXY.x / distanceR) * force, translatedY = (distanceXY.y / distanceR) * force;
  494. if (!link.fromNode.fixedPosition) {
  495. link.fromNode.dispX -=
  496. translatedX * massFactor.fromNode / link.fromNode.degree;
  497. link.fromNode.dispY -=
  498. translatedY * massFactor.fromNode / link.fromNode.degree;
  499. }
  500. if (!link.toNode.fixedPosition) {
  501. link.toNode.dispX +=
  502. translatedX * massFactor.toNode / link.toNode.degree;
  503. link.toNode.dispY +=
  504. translatedY * massFactor.toNode / link.toNode.degree;
  505. }
  506. },
  507. /**
  508. * Integration method.
  509. *
  510. * In Euler integration, force were stored in a node, not changing it's
  511. * position. Now, in the integrator method, we apply changes.
  512. *
  513. * Euler:
  514. *
  515. * Basic form: `x(n+1) = x(n) + v(n)`
  516. *
  517. * With Rengoild-Fruchterman we get:
  518. * `x(n+1) = x(n) + v(n) / length(v(n)) * min(v(n), temperature(n))`
  519. * where:
  520. * - `x(n+1)`: next position
  521. * - `x(n)`: current position
  522. * - `v(n)`: velocity (comes from net force)
  523. * - `temperature(n)`: current temperature
  524. *
  525. * Known issues:
  526. * Oscillations when force vector has the same magnitude but opposite
  527. * direction in the next step. Potentially solved by decreasing force by
  528. * `v * (1 / node.degree)`
  529. *
  530. * Note:
  531. * Actually `min(v(n), temperature(n))` replaces simulated annealing.
  532. *
  533. * @private
  534. * @param {Highcharts.NetworkgraphLayout} layout
  535. * Layout object
  536. * @param {Highcharts.Point} node
  537. * Node that should be translated
  538. * @return {void}
  539. */
  540. integrate: function (layout, node) {
  541. var distanceR;
  542. node.dispX +=
  543. node.dispX * layout.options.friction;
  544. node.dispY +=
  545. node.dispY * layout.options.friction;
  546. distanceR = node.temperature = layout.vectorLength({
  547. x: node.dispX,
  548. y: node.dispY
  549. });
  550. if (distanceR !== 0) {
  551. node.plotX += (node.dispX / distanceR *
  552. Math.min(Math.abs(node.dispX), layout.temperature));
  553. node.plotY += (node.dispY / distanceR *
  554. Math.min(Math.abs(node.dispY), layout.temperature));
  555. }
  556. },
  557. /**
  558. * Estiamte the best possible distance between two nodes, making graph
  559. * readable.
  560. *
  561. * @private
  562. * @param {object} layout layout object
  563. * @return {number}
  564. */
  565. getK: function (layout) {
  566. return Math.pow(layout.box.width * layout.box.height / layout.nodes.length, 0.3);
  567. }
  568. }
  569. };
  570. });
  571. _registerModule(_modules, 'modules/networkgraph/QuadTree.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  572. /* *
  573. *
  574. * Networkgraph series
  575. *
  576. * (c) 2010-2019 Paweł Fus
  577. *
  578. * License: www.highcharts.com/license
  579. *
  580. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  581. *
  582. * */
  583. var extend = U.extend;
  584. /* eslint-disable no-invalid-this, valid-jsdoc */
  585. /**
  586. * The QuadTree node class. Used in Networkgraph chart as a base for Barnes-Hut
  587. * approximation.
  588. *
  589. * @private
  590. * @class
  591. * @name Highcharts.QuadTreeNode
  592. *
  593. * @param {Highcharts.Dictionary<number>} box Available space for the node
  594. */
  595. var QuadTreeNode = H.QuadTreeNode = function (box) {
  596. /**
  597. * Read only. The available space for node.
  598. *
  599. * @name Highcharts.QuadTreeNode#box
  600. * @type {Highcharts.Dictionary<number>}
  601. */
  602. this.box = box;
  603. /**
  604. * Read only. The minium of width and height values.
  605. *
  606. * @name Highcharts.QuadTreeNode#boxSize
  607. * @type {number}
  608. */
  609. this.boxSize = Math.min(box.width, box.height);
  610. /**
  611. * Read only. Array of subnodes. Empty if QuadTreeNode has just one Point.
  612. * When added another Point to this QuadTreeNode, array is filled with four
  613. * subnodes.
  614. *
  615. * @name Highcharts.QuadTreeNode#nodes
  616. * @type {Array<Highcharts.QuadTreeNode>}
  617. */
  618. this.nodes = [];
  619. /**
  620. * Read only. Flag to determine if QuadTreeNode is internal (and has
  621. * subnodes with mass and central position) or external (bound to Point).
  622. *
  623. * @name Highcharts.QuadTreeNode#isInternal
  624. * @type {boolean}
  625. */
  626. this.isInternal = false;
  627. /**
  628. * Read only. If QuadTreeNode is an external node, Point is stored in
  629. * `this.body`.
  630. *
  631. * @name Highcharts.QuadTreeNode#body
  632. * @type {boolean|Highcharts.Point}
  633. */
  634. this.body = false;
  635. /**
  636. * Read only. Internal nodes when created are empty to reserve the space. If
  637. * Point is added to this QuadTreeNode, QuadTreeNode is no longer empty.
  638. *
  639. * @name Highcharts.QuadTreeNode#isEmpty
  640. * @type {boolean}
  641. */
  642. this.isEmpty = true;
  643. };
  644. extend(QuadTreeNode.prototype,
  645. /** @lends Highcharts.QuadTreeNode.prototype */
  646. {
  647. /**
  648. * Insert recursively point(node) into the QuadTree. If the given
  649. * quadrant is already occupied, divide it into smaller quadrants.
  650. *
  651. * @param {Highcharts.Point} point
  652. * Point/node to be inserted
  653. * @param {number} depth
  654. * Max depth of the QuadTree
  655. */
  656. insert: function (point, depth) {
  657. var newQuadTreeNode;
  658. if (this.isInternal) {
  659. // Internal node:
  660. this.nodes[this.getBoxPosition(point)].insert(point, depth - 1);
  661. }
  662. else {
  663. this.isEmpty = false;
  664. if (!this.body) {
  665. // First body in a quadrant:
  666. this.isInternal = false;
  667. this.body = point;
  668. }
  669. else {
  670. if (depth) {
  671. // Every other body in a quadrant:
  672. this.isInternal = true;
  673. this.divideBox();
  674. // Reinsert main body only once:
  675. if (this.body !== true) {
  676. this.nodes[this.getBoxPosition(this.body)]
  677. .insert(this.body, depth - 1);
  678. this.body = true;
  679. }
  680. // Add second body:
  681. this.nodes[this.getBoxPosition(point)]
  682. .insert(point, depth - 1);
  683. }
  684. else {
  685. // We are below max allowed depth. That means either:
  686. // - really huge number of points
  687. // - falling two points into exactly the same position
  688. // In this case, create another node in the QuadTree.
  689. //
  690. // Alternatively we could add some noise to the
  691. // position, but that could result in different
  692. // rendered chart in exporting.
  693. newQuadTreeNode = new QuadTreeNode({
  694. top: point.plotX,
  695. left: point.plotY,
  696. // Width/height below 1px
  697. width: 0.1,
  698. height: 0.1
  699. });
  700. newQuadTreeNode.body = point;
  701. newQuadTreeNode.isInternal = false;
  702. this.nodes.push(newQuadTreeNode);
  703. }
  704. }
  705. }
  706. },
  707. /**
  708. * Each quad node requires it's mass and center position. That mass and
  709. * position is used to imitate real node in the layout by approximation.
  710. */
  711. updateMassAndCenter: function () {
  712. var mass = 0, plotX = 0, plotY = 0;
  713. if (this.isInternal) {
  714. // Calcualte weightened mass of the quad node:
  715. this.nodes.forEach(function (pointMass) {
  716. if (!pointMass.isEmpty) {
  717. mass += pointMass.mass;
  718. plotX +=
  719. pointMass.plotX * pointMass.mass;
  720. plotY +=
  721. pointMass.plotY * pointMass.mass;
  722. }
  723. });
  724. plotX /= mass;
  725. plotY /= mass;
  726. }
  727. else if (this.body) {
  728. // Just one node, use coordinates directly:
  729. mass = this.body.mass;
  730. plotX = this.body.plotX;
  731. plotY = this.body.plotY;
  732. }
  733. // Store details:
  734. this.mass = mass;
  735. this.plotX = plotX;
  736. this.plotY = plotY;
  737. },
  738. /**
  739. * When inserting another node into the box, that already hove one node,
  740. * divide the available space into another four quadrants.
  741. *
  742. * Indexes of quadrants are:
  743. * ```
  744. * ------------- -------------
  745. * | | | | |
  746. * | | | 0 | 1 |
  747. * | | divide() | | |
  748. * | 1 | -----------> -------------
  749. * | | | | |
  750. * | | | 3 | 2 |
  751. * | | | | |
  752. * ------------- -------------
  753. * ```
  754. */
  755. divideBox: function () {
  756. var halfWidth = this.box.width / 2, halfHeight = this.box.height / 2;
  757. // Top left
  758. this.nodes[0] = new QuadTreeNode({
  759. left: this.box.left,
  760. top: this.box.top,
  761. width: halfWidth,
  762. height: halfHeight
  763. });
  764. // Top right
  765. this.nodes[1] = new QuadTreeNode({
  766. left: this.box.left + halfWidth,
  767. top: this.box.top,
  768. width: halfWidth,
  769. height: halfHeight
  770. });
  771. // Bottom right
  772. this.nodes[2] = new QuadTreeNode({
  773. left: this.box.left + halfWidth,
  774. top: this.box.top + halfHeight,
  775. width: halfWidth,
  776. height: halfHeight
  777. });
  778. // Bottom left
  779. this.nodes[3] = new QuadTreeNode({
  780. left: this.box.left,
  781. top: this.box.top + halfHeight,
  782. width: halfWidth,
  783. height: halfHeight
  784. });
  785. },
  786. /**
  787. * Determine which of the quadrants should be used when placing node in
  788. * the QuadTree. Returned index is always in range `< 0 , 3 >`.
  789. *
  790. * @param {Highcharts.Point} point
  791. * @return {number}
  792. */
  793. getBoxPosition: function (point) {
  794. var left = point.plotX < this.box.left + this.box.width / 2, top = point.plotY < this.box.top + this.box.height / 2, index;
  795. if (left) {
  796. if (top) {
  797. // Top left
  798. index = 0;
  799. }
  800. else {
  801. // Bottom left
  802. index = 3;
  803. }
  804. }
  805. else {
  806. if (top) {
  807. // Top right
  808. index = 1;
  809. }
  810. else {
  811. // Bottom right
  812. index = 2;
  813. }
  814. }
  815. return index;
  816. }
  817. });
  818. /**
  819. * The QuadTree class. Used in Networkgraph chart as a base for Barnes-Hut
  820. * approximation.
  821. *
  822. * @private
  823. * @class
  824. * @name Highcharts.QuadTree
  825. *
  826. * @param {number} x left position of the plotting area
  827. * @param {number} y top position of the plotting area
  828. * @param {number} width width of the plotting area
  829. * @param {number} height height of the plotting area
  830. */
  831. var QuadTree = H.QuadTree = function (x, y, width, height) {
  832. // Boundary rectangle:
  833. this.box = {
  834. left: x,
  835. top: y,
  836. width: width,
  837. height: height
  838. };
  839. this.maxDepth = 25;
  840. this.root = new QuadTreeNode(this.box, '0');
  841. this.root.isInternal = true;
  842. this.root.isRoot = true;
  843. this.root.divideBox();
  844. };
  845. extend(QuadTree.prototype,
  846. /** @lends Highcharts.QuadTree.prototype */
  847. {
  848. /**
  849. * Insert nodes into the QuadTree
  850. *
  851. * @param {Array<Highcharts.Point>} points
  852. */
  853. insertNodes: function (points) {
  854. points.forEach(function (point) {
  855. this.root.insert(point, this.maxDepth);
  856. }, this);
  857. },
  858. /**
  859. * Depfth first treversal (DFS). Using `before` and `after` callbacks,
  860. * we can get two results: preorder and postorder traversals, reminder:
  861. *
  862. * ```
  863. * (a)
  864. * / \
  865. * (b) (c)
  866. * / \
  867. * (d) (e)
  868. * ```
  869. *
  870. * DFS (preorder): `a -> b -> d -> e -> c`
  871. *
  872. * DFS (postorder): `d -> e -> b -> c -> a`
  873. *
  874. * @param {Highcharts.QuadTreeNode|null} node
  875. * @param {Function} [beforeCallback] function to be called before
  876. * visiting children nodes
  877. * @param {Function} [afterCallback] function to be called after
  878. * visiting children nodes
  879. */
  880. visitNodeRecursive: function (node, beforeCallback, afterCallback) {
  881. var goFurther;
  882. if (!node) {
  883. node = this.root;
  884. }
  885. if (node === this.root && beforeCallback) {
  886. goFurther = beforeCallback(node);
  887. }
  888. if (goFurther === false) {
  889. return;
  890. }
  891. node.nodes.forEach(function (qtNode) {
  892. if (qtNode.isInternal) {
  893. if (beforeCallback) {
  894. goFurther = beforeCallback(qtNode);
  895. }
  896. if (goFurther === false) {
  897. return;
  898. }
  899. this.visitNodeRecursive(qtNode, beforeCallback, afterCallback);
  900. }
  901. else if (qtNode.body) {
  902. if (beforeCallback) {
  903. beforeCallback(qtNode.body);
  904. }
  905. }
  906. if (afterCallback) {
  907. afterCallback(qtNode);
  908. }
  909. }, this);
  910. if (node === this.root && afterCallback) {
  911. afterCallback(node);
  912. }
  913. },
  914. /**
  915. * Calculate mass of the each QuadNode in the tree.
  916. */
  917. calculateMassAndCenter: function () {
  918. this.visitNodeRecursive(null, null, function (node) {
  919. node.updateMassAndCenter();
  920. });
  921. }
  922. });
  923. });
  924. _registerModule(_modules, 'modules/networkgraph/layouts.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  925. /* *
  926. *
  927. * Networkgraph series
  928. *
  929. * (c) 2010-2019 Paweł Fus
  930. *
  931. * License: www.highcharts.com/license
  932. *
  933. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  934. *
  935. * */
  936. var clamp = U.clamp, defined = U.defined, extend = U.extend, pick = U.pick, setAnimation = U.setAnimation;
  937. var addEvent = H.addEvent, Chart = H.Chart;
  938. /* eslint-disable no-invalid-this, valid-jsdoc */
  939. H.layouts = {
  940. 'reingold-fruchterman': function () {
  941. }
  942. };
  943. extend(
  944. /**
  945. * Reingold-Fruchterman algorithm from
  946. * "Graph Drawing by Force-directed Placement" paper.
  947. * @private
  948. */
  949. H.layouts['reingold-fruchterman'].prototype, {
  950. init: function (options) {
  951. this.options = options;
  952. this.nodes = [];
  953. this.links = [];
  954. this.series = [];
  955. this.box = {
  956. x: 0,
  957. y: 0,
  958. width: 0,
  959. height: 0
  960. };
  961. this.setInitialRendering(true);
  962. this.integration =
  963. H.networkgraphIntegrations[options.integration];
  964. this.attractiveForce = pick(options.attractiveForce, this.integration.attractiveForceFunction);
  965. this.repulsiveForce = pick(options.repulsiveForce, this.integration.repulsiveForceFunction);
  966. this.approximation = options.approximation;
  967. },
  968. start: function () {
  969. var layout = this, series = this.series, options = this.options;
  970. layout.currentStep = 0;
  971. layout.forces = series[0] && series[0].forces || [];
  972. if (layout.initialRendering) {
  973. layout.initPositions();
  974. // Render elements in initial positions:
  975. series.forEach(function (s) {
  976. s.render();
  977. });
  978. }
  979. layout.setK();
  980. layout.resetSimulation(options);
  981. if (options.enableSimulation) {
  982. layout.step();
  983. }
  984. },
  985. step: function () {
  986. var layout = this, series = this.series, options = this.options;
  987. // Algorithm:
  988. layout.currentStep++;
  989. if (layout.approximation === 'barnes-hut') {
  990. layout.createQuadTree();
  991. layout.quadTree.calculateMassAndCenter();
  992. }
  993. layout.forces.forEach(function (forceName) {
  994. layout[forceName + 'Forces'](layout.temperature);
  995. });
  996. // Limit to the plotting area and cool down:
  997. layout.applyLimits(layout.temperature);
  998. // Cool down the system:
  999. layout.temperature = layout.coolDown(layout.startTemperature, layout.diffTemperature, layout.currentStep);
  1000. layout.prevSystemTemperature = layout.systemTemperature;
  1001. layout.systemTemperature = layout.getSystemTemperature();
  1002. if (options.enableSimulation) {
  1003. series.forEach(function (s) {
  1004. // Chart could be destroyed during the simulation
  1005. if (s.chart) {
  1006. s.render();
  1007. }
  1008. });
  1009. if (layout.maxIterations-- &&
  1010. isFinite(layout.temperature) &&
  1011. !layout.isStable()) {
  1012. if (layout.simulation) {
  1013. H.win.cancelAnimationFrame(layout.simulation);
  1014. }
  1015. layout.simulation = H.win.requestAnimationFrame(function () {
  1016. layout.step();
  1017. });
  1018. }
  1019. else {
  1020. layout.simulation = false;
  1021. }
  1022. }
  1023. },
  1024. stop: function () {
  1025. if (this.simulation) {
  1026. H.win.cancelAnimationFrame(this.simulation);
  1027. }
  1028. },
  1029. setArea: function (x, y, w, h) {
  1030. this.box = {
  1031. left: x,
  1032. top: y,
  1033. width: w,
  1034. height: h
  1035. };
  1036. },
  1037. setK: function () {
  1038. // Optimal distance between nodes,
  1039. // available space around the node:
  1040. this.k = this.options.linkLength || this.integration.getK(this);
  1041. },
  1042. addElementsToCollection: function (elements, collection) {
  1043. elements.forEach(function (elem) {
  1044. if (collection.indexOf(elem) === -1) {
  1045. collection.push(elem);
  1046. }
  1047. });
  1048. },
  1049. removeElementFromCollection: function (element, collection) {
  1050. var index = collection.indexOf(element);
  1051. if (index !== -1) {
  1052. collection.splice(index, 1);
  1053. }
  1054. },
  1055. clear: function () {
  1056. this.nodes.length = 0;
  1057. this.links.length = 0;
  1058. this.series.length = 0;
  1059. this.resetSimulation();
  1060. },
  1061. resetSimulation: function () {
  1062. this.forcedStop = false;
  1063. this.systemTemperature = 0;
  1064. this.setMaxIterations();
  1065. this.setTemperature();
  1066. this.setDiffTemperature();
  1067. },
  1068. setMaxIterations: function (maxIterations) {
  1069. this.maxIterations = pick(maxIterations, this.options.maxIterations);
  1070. },
  1071. setTemperature: function () {
  1072. this.temperature = this.startTemperature =
  1073. Math.sqrt(this.nodes.length);
  1074. },
  1075. setDiffTemperature: function () {
  1076. this.diffTemperature = this.startTemperature /
  1077. (this.options.maxIterations + 1);
  1078. },
  1079. setInitialRendering: function (enable) {
  1080. this.initialRendering = enable;
  1081. },
  1082. createQuadTree: function () {
  1083. this.quadTree = new H.QuadTree(this.box.left, this.box.top, this.box.width, this.box.height);
  1084. this.quadTree.insertNodes(this.nodes);
  1085. },
  1086. initPositions: function () {
  1087. var initialPositions = this.options.initialPositions;
  1088. if (H.isFunction(initialPositions)) {
  1089. initialPositions.call(this);
  1090. this.nodes.forEach(function (node) {
  1091. if (!defined(node.prevX)) {
  1092. node.prevX = node.plotX;
  1093. }
  1094. if (!defined(node.prevY)) {
  1095. node.prevY = node.plotY;
  1096. }
  1097. node.dispX = 0;
  1098. node.dispY = 0;
  1099. });
  1100. }
  1101. else if (initialPositions === 'circle') {
  1102. this.setCircularPositions();
  1103. }
  1104. else {
  1105. this.setRandomPositions();
  1106. }
  1107. },
  1108. setCircularPositions: function () {
  1109. var box = this.box, nodes = this.nodes, nodesLength = nodes.length + 1, angle = 2 * Math.PI / nodesLength, rootNodes = nodes.filter(function (node) {
  1110. return node.linksTo.length === 0;
  1111. }), sortedNodes = [], visitedNodes = {}, radius = this.options.initialPositionRadius;
  1112. /**
  1113. * @private
  1114. */
  1115. function addToNodes(node) {
  1116. node.linksFrom.forEach(function (link) {
  1117. if (!visitedNodes[link.toNode.id]) {
  1118. visitedNodes[link.toNode.id] = true;
  1119. sortedNodes.push(link.toNode);
  1120. addToNodes(link.toNode);
  1121. }
  1122. });
  1123. }
  1124. // Start with identified root nodes an sort the nodes by their
  1125. // hierarchy. In trees, this ensures that branches don't cross
  1126. // eachother.
  1127. rootNodes.forEach(function (rootNode) {
  1128. sortedNodes.push(rootNode);
  1129. addToNodes(rootNode);
  1130. });
  1131. // Cyclic tree, no root node found
  1132. if (!sortedNodes.length) {
  1133. sortedNodes = nodes;
  1134. // Dangling, cyclic trees
  1135. }
  1136. else {
  1137. nodes.forEach(function (node) {
  1138. if (sortedNodes.indexOf(node) === -1) {
  1139. sortedNodes.push(node);
  1140. }
  1141. });
  1142. }
  1143. // Initial positions are laid out along a small circle, appearing
  1144. // as a cluster in the middle
  1145. sortedNodes.forEach(function (node, index) {
  1146. node.plotX = node.prevX = pick(node.plotX, box.width / 2 + radius * Math.cos(index * angle));
  1147. node.plotY = node.prevY = pick(node.plotY, box.height / 2 + radius * Math.sin(index * angle));
  1148. node.dispX = 0;
  1149. node.dispY = 0;
  1150. });
  1151. },
  1152. setRandomPositions: function () {
  1153. var box = this.box, nodes = this.nodes, nodesLength = nodes.length + 1;
  1154. /**
  1155. * Return a repeatable, quasi-random number based on an integer
  1156. * input. For the initial positions
  1157. * @private
  1158. */
  1159. function unrandom(n) {
  1160. var rand = n * n / Math.PI;
  1161. rand = rand - Math.floor(rand);
  1162. return rand;
  1163. }
  1164. // Initial positions:
  1165. nodes.forEach(function (node, index) {
  1166. node.plotX = node.prevX = pick(node.plotX, box.width * unrandom(index));
  1167. node.plotY = node.prevY = pick(node.plotY, box.height * unrandom(nodesLength + index));
  1168. node.dispX = 0;
  1169. node.dispY = 0;
  1170. });
  1171. },
  1172. force: function (name) {
  1173. this.integration[name].apply(this, Array.prototype.slice.call(arguments, 1));
  1174. },
  1175. barycenterForces: function () {
  1176. this.getBarycenter();
  1177. this.force('barycenter');
  1178. },
  1179. getBarycenter: function () {
  1180. var systemMass = 0, cx = 0, cy = 0;
  1181. this.nodes.forEach(function (node) {
  1182. cx += node.plotX * node.mass;
  1183. cy += node.plotY * node.mass;
  1184. systemMass += node.mass;
  1185. });
  1186. this.barycenter = {
  1187. x: cx,
  1188. y: cy,
  1189. xFactor: cx / systemMass,
  1190. yFactor: cy / systemMass
  1191. };
  1192. return this.barycenter;
  1193. },
  1194. barnesHutApproximation: function (node, quadNode) {
  1195. var layout = this, distanceXY = layout.getDistXY(node, quadNode), distanceR = layout.vectorLength(distanceXY), goDeeper, force;
  1196. if (node !== quadNode && distanceR !== 0) {
  1197. if (quadNode.isInternal) {
  1198. // Internal node:
  1199. if (quadNode.boxSize / distanceR <
  1200. layout.options.theta &&
  1201. distanceR !== 0) {
  1202. // Treat as an external node:
  1203. force = layout.repulsiveForce(distanceR, layout.k);
  1204. layout.force('repulsive', node, force * quadNode.mass, distanceXY, distanceR);
  1205. goDeeper = false;
  1206. }
  1207. else {
  1208. // Go deeper:
  1209. goDeeper = true;
  1210. }
  1211. }
  1212. else {
  1213. // External node, direct force:
  1214. force = layout.repulsiveForce(distanceR, layout.k);
  1215. layout.force('repulsive', node, force * quadNode.mass, distanceXY, distanceR);
  1216. }
  1217. }
  1218. return goDeeper;
  1219. },
  1220. repulsiveForces: function () {
  1221. var layout = this;
  1222. if (layout.approximation === 'barnes-hut') {
  1223. layout.nodes.forEach(function (node) {
  1224. layout.quadTree.visitNodeRecursive(null, function (quadNode) {
  1225. return layout.barnesHutApproximation(node, quadNode);
  1226. });
  1227. });
  1228. }
  1229. else {
  1230. layout.nodes.forEach(function (node) {
  1231. layout.nodes.forEach(function (repNode) {
  1232. var force, distanceR, distanceXY;
  1233. if (
  1234. // Node can not repulse itself:
  1235. node !== repNode &&
  1236. // Only close nodes affect each other:
  1237. // layout.getDistR(node, repNode) < 2 * k &&
  1238. // Not dragged:
  1239. !node.fixedPosition) {
  1240. distanceXY = layout.getDistXY(node, repNode);
  1241. distanceR = layout.vectorLength(distanceXY);
  1242. if (distanceR !== 0) {
  1243. force = layout.repulsiveForce(distanceR, layout.k);
  1244. layout.force('repulsive', node, force * repNode.mass, distanceXY, distanceR);
  1245. }
  1246. }
  1247. });
  1248. });
  1249. }
  1250. },
  1251. attractiveForces: function () {
  1252. var layout = this, distanceXY, distanceR, force;
  1253. layout.links.forEach(function (link) {
  1254. if (link.fromNode && link.toNode) {
  1255. distanceXY = layout.getDistXY(link.fromNode, link.toNode);
  1256. distanceR = layout.vectorLength(distanceXY);
  1257. if (distanceR !== 0) {
  1258. force = layout.attractiveForce(distanceR, layout.k);
  1259. layout.force('attractive', link, force, distanceXY, distanceR);
  1260. }
  1261. }
  1262. });
  1263. },
  1264. applyLimits: function () {
  1265. var layout = this, nodes = layout.nodes;
  1266. nodes.forEach(function (node) {
  1267. if (node.fixedPosition) {
  1268. return;
  1269. }
  1270. layout.integration.integrate(layout, node);
  1271. layout.applyLimitBox(node, layout.box);
  1272. // Reset displacement:
  1273. node.dispX = 0;
  1274. node.dispY = 0;
  1275. });
  1276. },
  1277. /**
  1278. * External box that nodes should fall. When hitting an edge, node
  1279. * should stop or bounce.
  1280. * @private
  1281. */
  1282. applyLimitBox: function (node, box) {
  1283. var radius = node.radius;
  1284. /*
  1285. TO DO: Consider elastic collision instead of stopping.
  1286. o' means end position when hitting plotting area edge:
  1287. - "inelastic":
  1288. o
  1289. \
  1290. ______
  1291. | o'
  1292. | \
  1293. | \
  1294. - "elastic"/"bounced":
  1295. o
  1296. \
  1297. ______
  1298. | ^
  1299. | / \
  1300. |o' \
  1301. Euler sample:
  1302. if (plotX < 0) {
  1303. plotX = 0;
  1304. dispX *= -1;
  1305. }
  1306. if (plotX > box.width) {
  1307. plotX = box.width;
  1308. dispX *= -1;
  1309. }
  1310. */
  1311. // Limit X-coordinates:
  1312. node.plotX = clamp(node.plotX, box.left + radius, box.width - radius);
  1313. // Limit Y-coordinates:
  1314. node.plotY = clamp(node.plotY, box.top + radius, box.height - radius);
  1315. },
  1316. /**
  1317. * From "A comparison of simulated annealing cooling strategies" by
  1318. * Nourani and Andresen work.
  1319. * @private
  1320. */
  1321. coolDown: function (temperature, temperatureStep, currentStep) {
  1322. // Logarithmic:
  1323. /*
  1324. return Math.sqrt(this.nodes.length) -
  1325. Math.log(
  1326. currentStep * layout.diffTemperature
  1327. );
  1328. */
  1329. // Exponential:
  1330. /*
  1331. var alpha = 0.1;
  1332. layout.temperature = Math.sqrt(layout.nodes.length) *
  1333. Math.pow(alpha, layout.diffTemperature);
  1334. */
  1335. // Linear:
  1336. return temperature - temperatureStep * currentStep;
  1337. },
  1338. isStable: function () {
  1339. return Math.abs(this.systemTemperature -
  1340. this.prevSystemTemperature) < 0.00001 || this.temperature <= 0;
  1341. },
  1342. getSystemTemperature: function () {
  1343. return this.nodes.reduce(function (value, node) {
  1344. return value + node.temperature;
  1345. }, 0);
  1346. },
  1347. vectorLength: function (vector) {
  1348. return Math.sqrt(vector.x * vector.x + vector.y * vector.y);
  1349. },
  1350. getDistR: function (nodeA, nodeB) {
  1351. var distance = this.getDistXY(nodeA, nodeB);
  1352. return this.vectorLength(distance);
  1353. },
  1354. getDistXY: function (nodeA, nodeB) {
  1355. var xDist = nodeA.plotX - nodeB.plotX, yDist = nodeA.plotY - nodeB.plotY;
  1356. return {
  1357. x: xDist,
  1358. y: yDist,
  1359. absX: Math.abs(xDist),
  1360. absY: Math.abs(yDist)
  1361. };
  1362. }
  1363. });
  1364. /* ************************************************************************** *
  1365. * Multiple series support:
  1366. * ************************************************************************** */
  1367. // Clear previous layouts
  1368. addEvent(Chart, 'predraw', function () {
  1369. if (this.graphLayoutsLookup) {
  1370. this.graphLayoutsLookup.forEach(function (layout) {
  1371. layout.stop();
  1372. });
  1373. }
  1374. });
  1375. addEvent(Chart, 'render', function () {
  1376. var systemsStable, afterRender = false;
  1377. /**
  1378. * @private
  1379. */
  1380. function layoutStep(layout) {
  1381. if (layout.maxIterations-- &&
  1382. isFinite(layout.temperature) &&
  1383. !layout.isStable() &&
  1384. !layout.options.enableSimulation) {
  1385. // Hook similar to build-in addEvent, but instead of
  1386. // creating whole events logic, use just a function.
  1387. // It's faster which is important for rAF code.
  1388. // Used e.g. in packed-bubble series for bubble radius
  1389. // calculations
  1390. if (layout.beforeStep) {
  1391. layout.beforeStep();
  1392. }
  1393. layout.step();
  1394. systemsStable = false;
  1395. afterRender = true;
  1396. }
  1397. }
  1398. if (this.graphLayoutsLookup) {
  1399. setAnimation(false, this);
  1400. // Start simulation
  1401. this.graphLayoutsLookup.forEach(function (layout) {
  1402. layout.start();
  1403. });
  1404. // Just one sync step, to run different layouts similar to
  1405. // async mode.
  1406. while (!systemsStable) {
  1407. systemsStable = true;
  1408. this.graphLayoutsLookup.forEach(layoutStep);
  1409. }
  1410. if (afterRender) {
  1411. this.series.forEach(function (s) {
  1412. if (s && s.layout) {
  1413. s.render();
  1414. }
  1415. });
  1416. }
  1417. }
  1418. });
  1419. });
  1420. _registerModule(_modules, 'modules/networkgraph/draggable-nodes.js', [_modules['parts/Globals.js']], function (H) {
  1421. /* *
  1422. *
  1423. * Networkgraph series
  1424. *
  1425. * (c) 2010-2019 Paweł Fus
  1426. *
  1427. * License: www.highcharts.com/license
  1428. *
  1429. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  1430. *
  1431. * */
  1432. var Chart = H.Chart, addEvent = H.addEvent;
  1433. /* eslint-disable no-invalid-this, valid-jsdoc */
  1434. H.dragNodesMixin = {
  1435. /**
  1436. * Mouse down action, initializing drag&drop mode.
  1437. *
  1438. * @private
  1439. * @param {Highcharts.Point} point The point that event occured.
  1440. * @param {Highcharts.PointerEventObject} event Browser event, before normalization.
  1441. * @return {void}
  1442. */
  1443. onMouseDown: function (point, event) {
  1444. var normalizedEvent = this.chart.pointer.normalize(event);
  1445. point.fixedPosition = {
  1446. chartX: normalizedEvent.chartX,
  1447. chartY: normalizedEvent.chartY,
  1448. plotX: point.plotX,
  1449. plotY: point.plotY
  1450. };
  1451. point.inDragMode = true;
  1452. },
  1453. /**
  1454. * Mouse move action during drag&drop.
  1455. *
  1456. * @private
  1457. *
  1458. * @param {global.Event} event Browser event, before normalization.
  1459. * @param {Highcharts.Point} point The point that event occured.
  1460. *
  1461. * @return {void}
  1462. */
  1463. onMouseMove: function (point, event) {
  1464. if (point.fixedPosition && point.inDragMode) {
  1465. var series = this, chart = series.chart, normalizedEvent = chart.pointer.normalize(event), diffX = point.fixedPosition.chartX - normalizedEvent.chartX, diffY = point.fixedPosition.chartY - normalizedEvent.chartY, newPlotX, newPlotY;
  1466. // At least 5px to apply change (avoids simple click):
  1467. if (Math.abs(diffX) > 5 || Math.abs(diffY) > 5) {
  1468. newPlotX = point.fixedPosition.plotX - diffX;
  1469. newPlotY = point.fixedPosition.plotY - diffY;
  1470. if (chart.isInsidePlot(newPlotX, newPlotY)) {
  1471. point.plotX = newPlotX;
  1472. point.plotY = newPlotY;
  1473. point.hasDragged = true;
  1474. this.redrawHalo(point);
  1475. if (!series.layout.simulation) {
  1476. // When dragging nodes, we don't need to calculate
  1477. // initial positions and rendering nodes:
  1478. series.layout.setInitialRendering(false);
  1479. // Start new simulation:
  1480. if (!series.layout.enableSimulation) {
  1481. // Run only one iteration to speed things up:
  1482. series.layout.setMaxIterations(1);
  1483. }
  1484. else {
  1485. series.layout.start();
  1486. }
  1487. series.chart.redraw();
  1488. // Restore defaults:
  1489. series.layout.setInitialRendering(true);
  1490. }
  1491. else {
  1492. // Extend current simulation:
  1493. series.layout.resetSimulation();
  1494. }
  1495. }
  1496. }
  1497. }
  1498. },
  1499. /**
  1500. * Mouse up action, finalizing drag&drop.
  1501. *
  1502. * @private
  1503. * @param {Highcharts.Point} point The point that event occured.
  1504. * @return {void}
  1505. */
  1506. onMouseUp: function (point, event) {
  1507. if (point.fixedPosition && point.hasDragged) {
  1508. if (this.layout.enableSimulation) {
  1509. this.layout.start();
  1510. }
  1511. else {
  1512. this.chart.redraw();
  1513. }
  1514. point.inDragMode = point.hasDragged = false;
  1515. if (!this.options.fixedDraggable) {
  1516. delete point.fixedPosition;
  1517. }
  1518. }
  1519. },
  1520. // Draggable mode:
  1521. /**
  1522. * Redraw halo on mousemove during the drag&drop action.
  1523. *
  1524. * @private
  1525. * @param {Highcharts.Point} point The point that should show halo.
  1526. * @return {void}
  1527. */
  1528. redrawHalo: function (point) {
  1529. if (point && this.halo) {
  1530. this.halo.attr({
  1531. d: point.haloPath(this.options.states.hover.halo.size)
  1532. });
  1533. }
  1534. }
  1535. };
  1536. /*
  1537. * Draggable mode:
  1538. */
  1539. addEvent(Chart, 'load', function () {
  1540. var chart = this, mousedownUnbinder, mousemoveUnbinder, mouseupUnbinder;
  1541. if (chart.container) {
  1542. mousedownUnbinder = addEvent(chart.container, 'mousedown', function (event) {
  1543. var point = chart.hoverPoint;
  1544. if (point &&
  1545. point.series &&
  1546. point.series.hasDraggableNodes &&
  1547. point.series.options.draggable) {
  1548. point.series.onMouseDown(point, event);
  1549. mousemoveUnbinder = addEvent(chart.container, 'mousemove', function (e) {
  1550. return point &&
  1551. point.series &&
  1552. point.series.onMouseMove(point, e);
  1553. });
  1554. mouseupUnbinder = addEvent(chart.container.ownerDocument, 'mouseup', function (e) {
  1555. mousemoveUnbinder();
  1556. mouseupUnbinder();
  1557. return point &&
  1558. point.series &&
  1559. point.series.onMouseUp(point, e);
  1560. });
  1561. }
  1562. });
  1563. }
  1564. addEvent(chart, 'destroy', function () {
  1565. mousedownUnbinder();
  1566. });
  1567. });
  1568. });
  1569. _registerModule(_modules, 'modules/networkgraph/networkgraph.src.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  1570. /* *
  1571. *
  1572. * Networkgraph series
  1573. *
  1574. * (c) 2010-2019 Paweł Fus
  1575. *
  1576. * License: www.highcharts.com/license
  1577. *
  1578. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  1579. *
  1580. * */
  1581. /**
  1582. * Formatter callback function.
  1583. *
  1584. * @callback Highcharts.SeriesNetworkgraphDataLabelsFormatterCallbackFunction
  1585. *
  1586. * @param {Highcharts.SeriesNetworkgraphDataLabelsFormatterContextObject|Highcharts.DataLabelsFormatterContextObject} this
  1587. * Data label context to format
  1588. *
  1589. * @return {string}
  1590. * Formatted data label text
  1591. */
  1592. /**
  1593. * Context for the formatter function.
  1594. *
  1595. * @interface Highcharts.SeriesNetworkgraphDataLabelsFormatterContextObject
  1596. * @extends Highcharts.DataLabelsFormatterContextObject
  1597. * @since 7.0.0
  1598. */ /**
  1599. * The color of the node.
  1600. * @name Highcharts.SeriesNetworkgraphDataLabelsFormatterContextObject#color
  1601. * @type {Highcharts.ColorString}
  1602. * @since 7.0.0
  1603. */ /**
  1604. * The point (node) object. The node name, if defined, is available through
  1605. * `this.point.name`. Arrays: `this.point.linksFrom` and `this.point.linksTo`
  1606. * contains all nodes connected to this point.
  1607. * @name Highcharts.SeriesNetworkgraphDataLabelsFormatterContextObject#point
  1608. * @type {Highcharts.Point}
  1609. * @since 7.0.0
  1610. */ /**
  1611. * The ID of the node.
  1612. * @name Highcharts.SeriesNetworkgraphDataLabelsFormatterContextObject#key
  1613. * @type {string}
  1614. * @since 7.0.0
  1615. */
  1616. var defined = U.defined, pick = U.pick;
  1617. var addEvent = H.addEvent, seriesType = H.seriesType, seriesTypes = H.seriesTypes, Point = H.Point, Series = H.Series, dragNodesMixin = H.dragNodesMixin;
  1618. /**
  1619. * @private
  1620. * @class
  1621. * @name Highcharts.seriesTypes.networkgraph
  1622. *
  1623. * @extends Highcharts.Series
  1624. */
  1625. seriesType('networkgraph', 'line',
  1626. /**
  1627. * A networkgraph is a type of relationship chart, where connnections
  1628. * (links) attracts nodes (points) and other nodes repulse each other.
  1629. *
  1630. * @extends plotOptions.line
  1631. * @product highcharts
  1632. * @sample highcharts/demo/network-graph/
  1633. * Networkgraph
  1634. * @since 7.0.0
  1635. * @excluding boostThreshold, animation, animationLimit, connectEnds,
  1636. * colorAxis, colorKey, connectNulls, dragDrop,
  1637. * getExtremesFromAll, label, linecap, negativeColor,
  1638. * pointInterval, pointIntervalUnit, pointPlacement,
  1639. * pointStart, softThreshold, stack, stacking, step,
  1640. * threshold, xAxis, yAxis, zoneAxis
  1641. * @requires modules/networkgraph
  1642. * @optionparent plotOptions.networkgraph
  1643. */
  1644. {
  1645. stickyTracking: false,
  1646. /**
  1647. * @ignore-option
  1648. * @private
  1649. */
  1650. inactiveOtherPoints: true,
  1651. marker: {
  1652. enabled: true,
  1653. states: {
  1654. /**
  1655. * The opposite state of a hover for a single point node.
  1656. * Applied to all not connected nodes to the hovered one.
  1657. *
  1658. * @declare Highcharts.PointStatesInactiveOptionsObject
  1659. */
  1660. inactive: {
  1661. /**
  1662. * Opacity of inactive markers.
  1663. */
  1664. opacity: 0.3,
  1665. /**
  1666. * Animation when not hovering over the node.
  1667. *
  1668. * @type {boolean|Highcharts.AnimationOptionsObject}
  1669. */
  1670. animation: {
  1671. /** @internal */
  1672. duration: 50
  1673. }
  1674. }
  1675. }
  1676. },
  1677. states: {
  1678. /**
  1679. * The opposite state of a hover for a single point link. Applied
  1680. * to all links that are not comming from the hovered node.
  1681. *
  1682. * @declare Highcharts.SeriesStatesInactiveOptionsObject
  1683. */
  1684. inactive: {
  1685. /**
  1686. * Opacity of inactive links.
  1687. */
  1688. linkOpacity: 0.3,
  1689. /**
  1690. * Animation when not hovering over the node.
  1691. *
  1692. * @type {boolean|Highcharts.AnimationOptionsObject}
  1693. */
  1694. animation: {
  1695. /** @internal */
  1696. duration: 50
  1697. }
  1698. }
  1699. },
  1700. /**
  1701. * @sample highcharts/series-networkgraph/link-datalabels
  1702. * Networkgraph with labels on links
  1703. * @sample highcharts/series-networkgraph/textpath-datalabels
  1704. * Networkgraph with labels around nodes
  1705. * @sample highcharts/series-networkgraph/link-datalabels
  1706. * Data labels moved into the nodes
  1707. * @sample highcharts/series-networkgraph/link-datalabels
  1708. * Data labels moved under the links
  1709. *
  1710. * @declare Highcharts.SeriesNetworkgraphDataLabelsOptionsObject
  1711. *
  1712. * @private
  1713. */
  1714. dataLabels: {
  1715. /**
  1716. * The
  1717. * [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  1718. * specifying what to show for _node_ in the networkgraph. In v7.0
  1719. * defaults to `{key}`, since v7.1 defaults to `undefined` and
  1720. * `formatter` is used instead.
  1721. *
  1722. * @type {string}
  1723. * @since 7.0.0
  1724. * @apioption plotOptions.networkgraph.dataLabels.format
  1725. */
  1726. // eslint-disable-next-line valid-jsdoc
  1727. /**
  1728. * Callback JavaScript function to format the data label for a node.
  1729. * Note that if a `format` is defined, the format takes precedence
  1730. * and the formatter is ignored.
  1731. *
  1732. * @type {Highcharts.SeriesNetworkgraphDataLabelsFormatterCallbackFunction}
  1733. * @since 7.0.0
  1734. */
  1735. formatter: function () {
  1736. return this.key;
  1737. },
  1738. /**
  1739. * The
  1740. * [format string](https://www.highcharts.com/docs/chart-concepts/labels-and-string-formatting)
  1741. * specifying what to show for _links_ in the networkgraph.
  1742. * (Default: `undefined`)
  1743. *
  1744. * @type {string}
  1745. * @since 7.1.0
  1746. * @apioption plotOptions.networkgraph.dataLabels.linkFormat
  1747. */
  1748. // eslint-disable-next-line valid-jsdoc
  1749. /**
  1750. * Callback to format data labels for _links_ in the sankey diagram.
  1751. * The `linkFormat` option takes precedence over the
  1752. * `linkFormatter`.
  1753. *
  1754. * @type {Highcharts.SeriesNetworkgraphDataLabelsFormatterCallbackFunction}
  1755. * @since 7.1.0
  1756. */
  1757. linkFormatter: function () {
  1758. return (this.point.fromNode.name +
  1759. '<br>' +
  1760. this.point.toNode.name);
  1761. },
  1762. /**
  1763. * Options for a _link_ label text which should follow link
  1764. * connection. Border and background are disabled for a label that
  1765. * follows a path.
  1766. *
  1767. * **Note:** Only SVG-based renderer supports this option. Setting
  1768. * `useHTML` to true will disable this option.
  1769. *
  1770. * @extends plotOptions.networkgraph.dataLabels.textPath
  1771. * @since 7.1.0
  1772. */
  1773. linkTextPath: {
  1774. enabled: true
  1775. },
  1776. textPath: {
  1777. enabled: false
  1778. }
  1779. },
  1780. /**
  1781. * Link style options
  1782. * @private
  1783. */
  1784. link: {
  1785. /**
  1786. * A name for the dash style to use for links.
  1787. *
  1788. * @type {string}
  1789. * @apioption plotOptions.networkgraph.link.dashStyle
  1790. */
  1791. /**
  1792. * Color of the link between two nodes.
  1793. */
  1794. color: 'rgba(100, 100, 100, 0.5)',
  1795. /**
  1796. * Width (px) of the link between two nodes.
  1797. */
  1798. width: 1
  1799. },
  1800. /**
  1801. * Flag to determine if nodes are draggable or not.
  1802. * @private
  1803. */
  1804. draggable: true,
  1805. layoutAlgorithm: {
  1806. /**
  1807. * Repulsive force applied on a node. Passed are two arguments:
  1808. * - `d` - which is current distance between two nodes
  1809. * - `k` - which is desired distance between two nodes
  1810. *
  1811. * In `verlet` integration, defaults to:
  1812. * `function (d, k) { return (k - d) / d * (k > d ? 1 : 0) }`
  1813. *
  1814. * @see [layoutAlgorithm.integration](#series.networkgraph.layoutAlgorithm.integration)
  1815. *
  1816. * @sample highcharts/series-networkgraph/forces/
  1817. * Custom forces with Euler integration
  1818. * @sample highcharts/series-networkgraph/cuboids/
  1819. * Custom forces with Verlet integration
  1820. *
  1821. * @type {Function}
  1822. * @default function (d, k) { return k * k / d; }
  1823. * @apioption plotOptions.networkgraph.layoutAlgorithm.repulsiveForce
  1824. */
  1825. /**
  1826. * Attraction force applied on a node which is conected to another
  1827. * node by a link. Passed are two arguments:
  1828. * - `d` - which is current distance between two nodes
  1829. * - `k` - which is desired distance between two nodes
  1830. *
  1831. * In `verlet` integration, defaults to:
  1832. * `function (d, k) { return (k - d) / d; }`
  1833. *
  1834. * @see [layoutAlgorithm.integration](#series.networkgraph.layoutAlgorithm.integration)
  1835. *
  1836. * @sample highcharts/series-networkgraph/forces/
  1837. * Custom forces with Euler integration
  1838. * @sample highcharts/series-networkgraph/cuboids/
  1839. * Custom forces with Verlet integration
  1840. *
  1841. * @type {Function}
  1842. * @default function (d, k) { return k * k / d; }
  1843. * @apioption plotOptions.networkgraph.layoutAlgorithm.attractiveForce
  1844. */
  1845. /**
  1846. * Ideal length (px) of the link between two nodes. When not
  1847. * defined, length is calculated as:
  1848. * `Math.pow(availableWidth * availableHeight / nodesLength, 0.4);`
  1849. *
  1850. * Note: Because of the algorithm specification, length of each link
  1851. * might be not exactly as specified.
  1852. *
  1853. * @sample highcharts/series-networkgraph/styled-links/
  1854. * Numerical values
  1855. *
  1856. * @type {number}
  1857. * @apioption plotOptions.networkgraph.layoutAlgorithm.linkLength
  1858. */
  1859. /**
  1860. * Initial layout algorithm for positioning nodes. Can be one of
  1861. * built-in options ("circle", "random") or a function where
  1862. * positions should be set on each node (`this.nodes`) as
  1863. * `node.plotX` and `node.plotY`
  1864. *
  1865. * @sample highcharts/series-networkgraph/initial-positions/
  1866. * Initial positions with callback
  1867. *
  1868. * @type {"circle"|"random"|Function}
  1869. */
  1870. initialPositions: 'circle',
  1871. /**
  1872. * When `initialPositions` are set to 'circle',
  1873. * `initialPositionRadius` is a distance from the center of circle,
  1874. * in which nodes are created.
  1875. *
  1876. * @type {number}
  1877. * @default 1
  1878. * @since 7.1.0
  1879. */
  1880. initialPositionRadius: 1,
  1881. /**
  1882. * Experimental. Enables live simulation of the algorithm
  1883. * implementation. All nodes are animated as the forces applies on
  1884. * them.
  1885. *
  1886. * @sample highcharts/demo/network-graph/
  1887. * Live simulation enabled
  1888. */
  1889. enableSimulation: false,
  1890. /**
  1891. * Barnes-Hut approximation only.
  1892. * Deteremines when distance between cell and node is small enough
  1893. * to caculate forces. Value of `theta` is compared directly with
  1894. * quotient `s / d`, where `s` is the size of the cell, and `d` is
  1895. * distance between center of cell's mass and currently compared
  1896. * node.
  1897. *
  1898. * @see [layoutAlgorithm.approximation](#series.networkgraph.layoutAlgorithm.approximation)
  1899. *
  1900. * @since 7.1.0
  1901. */
  1902. theta: 0.5,
  1903. /**
  1904. * Verlet integration only.
  1905. * Max speed that node can get in one iteration. In terms of
  1906. * simulation, it's a maximum translation (in pixels) that node can
  1907. * move (in both, x and y, dimensions). While `friction` is applied
  1908. * on all nodes, max speed is applied only for nodes that move very
  1909. * fast, for example small or disconnected ones.
  1910. *
  1911. * @see [layoutAlgorithm.integration](#series.networkgraph.layoutAlgorithm.integration)
  1912. * @see [layoutAlgorithm.friction](#series.networkgraph.layoutAlgorithm.friction)
  1913. *
  1914. * @since 7.1.0
  1915. */
  1916. maxSpeed: 10,
  1917. /**
  1918. * Approximation used to calculate repulsive forces affecting nodes.
  1919. * By default, when calculateing net force, nodes are compared
  1920. * against each other, which gives O(N^2) complexity. Using
  1921. * Barnes-Hut approximation, we decrease this to O(N log N), but the
  1922. * resulting graph will have different layout. Barnes-Hut
  1923. * approximation divides space into rectangles via quad tree, where
  1924. * forces exerted on nodes are calculated directly for nearby cells,
  1925. * and for all others, cells are treated as a separate node with
  1926. * center of mass.
  1927. *
  1928. * @see [layoutAlgorithm.theta](#series.networkgraph.layoutAlgorithm.theta)
  1929. *
  1930. * @sample highcharts/series-networkgraph/barnes-hut-approximation/
  1931. * A graph with Barnes-Hut approximation
  1932. *
  1933. * @type {string}
  1934. * @validvalue ["barnes-hut", "none"]
  1935. * @since 7.1.0
  1936. */
  1937. approximation: 'none',
  1938. /**
  1939. * Type of the algorithm used when positioning nodes.
  1940. *
  1941. * @type {string}
  1942. * @validvalue ["reingold-fruchterman"]
  1943. */
  1944. type: 'reingold-fruchterman',
  1945. /**
  1946. * Integration type. Available options are `'euler'` and `'verlet'`.
  1947. * Integration determines how forces are applied on particles. In
  1948. * Euler integration, force is applied direct as
  1949. * `newPosition += velocity;`.
  1950. * In Verlet integration, new position is based on a previous
  1951. * posittion without velocity:
  1952. * `newPosition += previousPosition - newPosition`.
  1953. *
  1954. * Note that different integrations give different results as forces
  1955. * are different.
  1956. *
  1957. * In Highcharts v7.0.x only `'euler'` integration was supported.
  1958. *
  1959. * @sample highcharts/series-networkgraph/integration-comparison/
  1960. * Comparison of Verlet and Euler integrations
  1961. *
  1962. * @type {string}
  1963. * @validvalue ["euler", "verlet"]
  1964. * @since 7.1.0
  1965. */
  1966. integration: 'euler',
  1967. /**
  1968. * Max number of iterations before algorithm will stop. In general,
  1969. * algorithm should find positions sooner, but when rendering huge
  1970. * number of nodes, it is recommended to increase this value as
  1971. * finding perfect graph positions can require more time.
  1972. */
  1973. maxIterations: 1000,
  1974. /**
  1975. * Gravitational const used in the barycenter force of the
  1976. * algorithm.
  1977. *
  1978. * @sample highcharts/series-networkgraph/forces/
  1979. * Custom forces with Euler integration
  1980. */
  1981. gravitationalConstant: 0.0625,
  1982. /**
  1983. * Friction applied on forces to prevent nodes rushing to fast to
  1984. * the desired positions.
  1985. */
  1986. friction: -0.981
  1987. },
  1988. showInLegend: false
  1989. }, {
  1990. /**
  1991. * Array of internal forces. Each force should be later defined in
  1992. * integrations.js.
  1993. * @private
  1994. */
  1995. forces: ['barycenter', 'repulsive', 'attractive'],
  1996. hasDraggableNodes: true,
  1997. drawGraph: null,
  1998. isCartesian: false,
  1999. requireSorting: false,
  2000. directTouch: true,
  2001. noSharedTooltip: true,
  2002. pointArrayMap: ['from', 'to'],
  2003. trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'],
  2004. drawTracker: H.TrackerMixin.drawTrackerPoint,
  2005. // Animation is run in `series.simulation`.
  2006. animate: null,
  2007. buildKDTree: H.noop,
  2008. /**
  2009. * Create a single node that holds information on incoming and outgoing
  2010. * links.
  2011. * @private
  2012. */
  2013. createNode: H.NodesMixin.createNode,
  2014. destroy: function () {
  2015. this.layout.removeElementFromCollection(this, this.layout.series);
  2016. H.NodesMixin.destroy.call(this);
  2017. },
  2018. /* eslint-disable no-invalid-this, valid-jsdoc */
  2019. /**
  2020. * Extend init with base event, which should stop simulation during
  2021. * update. After data is updated, `chart.render` resumes the simulation.
  2022. * @private
  2023. */
  2024. init: function () {
  2025. Series.prototype.init.apply(this, arguments);
  2026. addEvent(this, 'updatedData', function () {
  2027. if (this.layout) {
  2028. this.layout.stop();
  2029. }
  2030. });
  2031. return this;
  2032. },
  2033. /**
  2034. * Extend generatePoints by adding the nodes, which are Point objects
  2035. * but pushed to the this.nodes array.
  2036. * @private
  2037. */
  2038. generatePoints: function () {
  2039. var node, i;
  2040. H.NodesMixin.generatePoints.apply(this, arguments);
  2041. // In networkgraph, it's fine to define stanalone nodes, create
  2042. // them:
  2043. if (this.options.nodes) {
  2044. this.options.nodes.forEach(function (nodeOptions) {
  2045. if (!this.nodeLookup[nodeOptions.id]) {
  2046. this.nodeLookup[nodeOptions.id] =
  2047. this.createNode(nodeOptions.id);
  2048. }
  2049. }, this);
  2050. }
  2051. for (i = this.nodes.length - 1; i >= 0; i--) {
  2052. node = this.nodes[i];
  2053. node.degree = node.getDegree();
  2054. node.radius = pick(node.marker && node.marker.radius, this.options.marker && this.options.marker.radius, 0);
  2055. // If node exists, but it's not available in nodeLookup,
  2056. // then it's leftover from previous runs (e.g. setData)
  2057. if (!this.nodeLookup[node.id]) {
  2058. node.remove();
  2059. }
  2060. }
  2061. this.data.forEach(function (link) {
  2062. link.formatPrefix = 'link';
  2063. });
  2064. this.indexateNodes();
  2065. },
  2066. /**
  2067. * Set index for each node. Required for proper `node.update()`.
  2068. * Note that links are indexated out of the box in `generatePoints()`.
  2069. *
  2070. * @private
  2071. */
  2072. indexateNodes: function () {
  2073. this.nodes.forEach(function (node, index) {
  2074. node.index = index;
  2075. });
  2076. },
  2077. /**
  2078. * Extend the default marker attribs by using a non-rounded X position,
  2079. * otherwise the nodes will jump from pixel to pixel which looks a bit
  2080. * jaggy when approaching equilibrium.
  2081. * @private
  2082. */
  2083. markerAttribs: function (point, state) {
  2084. var attribs = Series.prototype.markerAttribs.call(this, point, state);
  2085. // series.render() is called before initial positions are set:
  2086. if (!defined(point.plotY)) {
  2087. attribs.y = 0;
  2088. }
  2089. attribs.x = (point.plotX || 0) - (attribs.width / 2 || 0);
  2090. return attribs;
  2091. },
  2092. /**
  2093. * Run pre-translation and register nodes&links to the deffered layout.
  2094. * @private
  2095. */
  2096. translate: function () {
  2097. if (!this.processedXData) {
  2098. this.processData();
  2099. }
  2100. this.generatePoints();
  2101. this.deferLayout();
  2102. this.nodes.forEach(function (node) {
  2103. // Draw the links from this node
  2104. node.isInside = true;
  2105. node.linksFrom.forEach(function (point) {
  2106. point.shapeType = 'path';
  2107. // Pass test in drawPoints
  2108. point.y = 1;
  2109. });
  2110. });
  2111. },
  2112. /**
  2113. * Defer the layout.
  2114. * Each series first registers all nodes and links, then layout
  2115. * calculates all nodes positions and calls `series.render()` in every
  2116. * simulation step.
  2117. *
  2118. * Note:
  2119. * Animation is done through `requestAnimationFrame` directly, without
  2120. * `Highcharts.animate()` use.
  2121. * @private
  2122. */
  2123. deferLayout: function () {
  2124. var layoutOptions = this.options.layoutAlgorithm, graphLayoutsStorage = this.chart.graphLayoutsStorage, graphLayoutsLookup = this.chart.graphLayoutsLookup, chartOptions = this.chart.options.chart, layout;
  2125. if (!this.visible) {
  2126. return;
  2127. }
  2128. if (!graphLayoutsStorage) {
  2129. this.chart.graphLayoutsStorage = graphLayoutsStorage = {};
  2130. this.chart.graphLayoutsLookup = graphLayoutsLookup = [];
  2131. }
  2132. layout = graphLayoutsStorage[layoutOptions.type];
  2133. if (!layout) {
  2134. layoutOptions.enableSimulation =
  2135. !defined(chartOptions.forExport) ?
  2136. layoutOptions.enableSimulation :
  2137. !chartOptions.forExport;
  2138. graphLayoutsStorage[layoutOptions.type] = layout =
  2139. new H.layouts[layoutOptions.type]();
  2140. layout.init(layoutOptions);
  2141. graphLayoutsLookup.splice(layout.index, 0, layout);
  2142. }
  2143. this.layout = layout;
  2144. layout.setArea(0, 0, this.chart.plotWidth, this.chart.plotHeight);
  2145. layout.addElementsToCollection([this], layout.series);
  2146. layout.addElementsToCollection(this.nodes, layout.nodes);
  2147. layout.addElementsToCollection(this.points, layout.links);
  2148. },
  2149. /**
  2150. * Extend the render function to also render this.nodes together with
  2151. * the points.
  2152. * @private
  2153. */
  2154. render: function () {
  2155. var points = this.points, hoverPoint = this.chart.hoverPoint, dataLabels = [];
  2156. // Render markers:
  2157. this.points = this.nodes;
  2158. seriesTypes.line.prototype.render.call(this);
  2159. this.points = points;
  2160. points.forEach(function (point) {
  2161. if (point.fromNode && point.toNode) {
  2162. point.renderLink();
  2163. point.redrawLink();
  2164. }
  2165. });
  2166. if (hoverPoint && hoverPoint.series === this) {
  2167. this.redrawHalo(hoverPoint);
  2168. }
  2169. if (this.chart.hasRendered &&
  2170. !this.options.dataLabels.allowOverlap) {
  2171. this.nodes.concat(this.points).forEach(function (node) {
  2172. if (node.dataLabel) {
  2173. dataLabels.push(node.dataLabel);
  2174. }
  2175. });
  2176. this.chart.hideOverlappingLabels(dataLabels);
  2177. }
  2178. },
  2179. // Networkgraph has two separate collecions of nodes and lines, render
  2180. // dataLabels for both sets:
  2181. drawDataLabels: function () {
  2182. var textPath = this.options.dataLabels.textPath;
  2183. // Render node labels:
  2184. Series.prototype.drawDataLabels.apply(this, arguments);
  2185. // Render link labels:
  2186. this.points = this.data;
  2187. this.options.dataLabels.textPath =
  2188. this.options.dataLabels.linkTextPath;
  2189. Series.prototype.drawDataLabels.apply(this, arguments);
  2190. // Restore nodes
  2191. this.points = this.nodes;
  2192. this.options.dataLabels.textPath = textPath;
  2193. },
  2194. // Return the presentational attributes.
  2195. pointAttribs: function (point, state) {
  2196. // By default, only `selected` state is passed on
  2197. var pointState = state || point.state || 'normal', attribs = Series.prototype.pointAttribs.call(this, point, pointState), stateOptions = this.options.states[pointState];
  2198. if (!point.isNode) {
  2199. attribs = point.getLinkAttributes();
  2200. // For link, get prefixed names:
  2201. if (stateOptions) {
  2202. attribs = {
  2203. // TO DO: API?
  2204. stroke: stateOptions.linkColor || attribs.stroke,
  2205. dashstyle: (stateOptions.linkDashStyle || attribs.dashstyle),
  2206. opacity: pick(stateOptions.linkOpacity, attribs.opacity),
  2207. 'stroke-width': stateOptions.linkColor ||
  2208. attribs['stroke-width']
  2209. };
  2210. }
  2211. }
  2212. return attribs;
  2213. },
  2214. // Draggable mode:
  2215. /**
  2216. * Redraw halo on mousemove during the drag&drop action.
  2217. * @private
  2218. * @param {Highcharts.Point} point The point that should show halo.
  2219. */
  2220. redrawHalo: dragNodesMixin.redrawHalo,
  2221. /**
  2222. * Mouse down action, initializing drag&drop mode.
  2223. * @private
  2224. * @param {global.Event} event Browser event, before normalization.
  2225. * @param {Highcharts.Point} point The point that event occured.
  2226. */
  2227. onMouseDown: dragNodesMixin.onMouseDown,
  2228. /**
  2229. * Mouse move action during drag&drop.
  2230. * @private
  2231. * @param {global.Event} event Browser event, before normalization.
  2232. * @param {Highcharts.Point} point The point that event occured.
  2233. */
  2234. onMouseMove: dragNodesMixin.onMouseMove,
  2235. /**
  2236. * Mouse up action, finalizing drag&drop.
  2237. * @private
  2238. * @param {Highcharts.Point} point The point that event occured.
  2239. */
  2240. onMouseUp: dragNodesMixin.onMouseUp,
  2241. /**
  2242. * When state should be passed down to all points, concat nodes and
  2243. * links and apply this state to all of them.
  2244. * @private
  2245. */
  2246. setState: function (state, inherit) {
  2247. if (inherit) {
  2248. this.points = this.nodes.concat(this.data);
  2249. Series.prototype.setState.apply(this, arguments);
  2250. this.points = this.data;
  2251. }
  2252. else {
  2253. Series.prototype.setState.apply(this, arguments);
  2254. }
  2255. // If simulation is done, re-render points with new states:
  2256. if (!this.layout.simulation && !state) {
  2257. this.render();
  2258. }
  2259. }
  2260. }, {
  2261. setState: H.NodesMixin.setNodeState,
  2262. /**
  2263. * Basic `point.init()` and additional styles applied when
  2264. * `series.draggable` is enabled.
  2265. * @private
  2266. */
  2267. init: function () {
  2268. Point.prototype.init.apply(this, arguments);
  2269. if (this.series.options.draggable &&
  2270. !this.series.chart.styledMode) {
  2271. addEvent(this, 'mouseOver', function () {
  2272. H.css(this.series.chart.container, { cursor: 'move' });
  2273. });
  2274. addEvent(this, 'mouseOut', function () {
  2275. H.css(this.series.chart.container, { cursor: 'default' });
  2276. });
  2277. }
  2278. return this;
  2279. },
  2280. /**
  2281. * Return degree of a node. If node has no connections, it still has
  2282. * deg=1.
  2283. * @private
  2284. * @return {number}
  2285. */
  2286. getDegree: function () {
  2287. var deg = this.isNode ?
  2288. this.linksFrom.length + this.linksTo.length :
  2289. 0;
  2290. return deg === 0 ? 1 : deg;
  2291. },
  2292. // Links:
  2293. /**
  2294. * Get presentational attributes of link connecting two nodes.
  2295. * @private
  2296. * @return {Highcharts.SVGAttributes}
  2297. */
  2298. getLinkAttributes: function () {
  2299. var linkOptions = this.series.options.link, pointOptions = this.options;
  2300. return {
  2301. 'stroke-width': pick(pointOptions.width, linkOptions.width),
  2302. stroke: (pointOptions.color || linkOptions.color),
  2303. dashstyle: (pointOptions.dashStyle || linkOptions.dashStyle),
  2304. opacity: pick(pointOptions.opacity, linkOptions.opacity, 1)
  2305. };
  2306. },
  2307. /**
  2308. * Render link and add it to the DOM.
  2309. * @private
  2310. */
  2311. renderLink: function () {
  2312. var attribs;
  2313. if (!this.graphic) {
  2314. this.graphic = this.series.chart.renderer
  2315. .path(this.getLinkPath())
  2316. .add(this.series.group);
  2317. if (!this.series.chart.styledMode) {
  2318. attribs = this.series.pointAttribs(this);
  2319. this.graphic.attr(attribs);
  2320. (this.dataLabels || []).forEach(function (label) {
  2321. if (label) {
  2322. label.attr({
  2323. opacity: attribs.opacity
  2324. });
  2325. }
  2326. });
  2327. }
  2328. }
  2329. },
  2330. /**
  2331. * Redraw link's path.
  2332. * @private
  2333. */
  2334. redrawLink: function () {
  2335. var path = this.getLinkPath(), attribs;
  2336. if (this.graphic) {
  2337. this.shapeArgs = {
  2338. d: path
  2339. };
  2340. if (!this.series.chart.styledMode) {
  2341. attribs = this.series.pointAttribs(this);
  2342. this.graphic.attr(attribs);
  2343. (this.dataLabels || []).forEach(function (label) {
  2344. if (label) {
  2345. label.attr({
  2346. opacity: attribs.opacity
  2347. });
  2348. }
  2349. });
  2350. }
  2351. this.graphic.animate(this.shapeArgs);
  2352. // Required for dataLabels:
  2353. this.plotX = (path[1] + path[4]) / 2;
  2354. this.plotY = (path[2] + path[5]) / 2;
  2355. }
  2356. },
  2357. /**
  2358. * Get mass fraction applied on two nodes connected to each other. By
  2359. * default, when mass is equal to `1`, mass fraction for both nodes
  2360. * equal to 0.5.
  2361. * @private
  2362. * @return {Highcharts.Dictionary<number>}
  2363. * For example `{ fromNode: 0.5, toNode: 0.5 }`
  2364. */
  2365. getMass: function () {
  2366. var m1 = this.fromNode.mass, m2 = this.toNode.mass, sum = m1 + m2;
  2367. return {
  2368. fromNode: 1 - m1 / sum,
  2369. toNode: 1 - m2 / sum
  2370. };
  2371. },
  2372. /**
  2373. * Get link path connecting two nodes.
  2374. * @private
  2375. * @return {Array<Highcharts.SVGPathArray>}
  2376. * Path: `['M', x, y, 'L', x, y]`
  2377. */
  2378. getLinkPath: function () {
  2379. var left = this.fromNode, right = this.toNode;
  2380. // Start always from left to the right node, to prevent rendering
  2381. // labels upside down
  2382. if (left.plotX > right.plotX) {
  2383. left = this.toNode;
  2384. right = this.fromNode;
  2385. }
  2386. return [
  2387. 'M',
  2388. left.plotX,
  2389. left.plotY,
  2390. 'L',
  2391. right.plotX,
  2392. right.plotY
  2393. ];
  2394. /*
  2395. IDEA: different link shapes?
  2396. return [
  2397. 'M',
  2398. from.plotX,
  2399. from.plotY,
  2400. 'Q',
  2401. (to.plotX + from.plotX) / 2,
  2402. (to.plotY + from.plotY) / 2 + 15,
  2403. to.plotX,
  2404. to.plotY
  2405. ];*/
  2406. },
  2407. isValid: function () {
  2408. return !this.isNode || defined(this.id);
  2409. },
  2410. /**
  2411. * Common method for removing points and nodes in networkgraph. To
  2412. * remove `link`, use `series.data[index].remove()`. To remove `node`
  2413. * with all connections, use `series.nodes[index].remove()`.
  2414. * @private
  2415. * @param {boolean} [redraw=true]
  2416. * Whether to redraw the chart or wait for an explicit call. When
  2417. * doing more operations on the chart, for example running
  2418. * `point.remove()` in a loop, it is best practice to set
  2419. * `redraw` to false and call `chart.redraw()` after.
  2420. * @param {boolean|Highcharts.AnimationOptionsObject} [animation=false]
  2421. * Whether to apply animation, and optionally animation
  2422. * configuration.
  2423. * @return {void}
  2424. */
  2425. remove: function (redraw, animation) {
  2426. var point = this, series = point.series, nodesOptions = series.options.nodes || [], index, i = nodesOptions.length;
  2427. // For nodes, remove all connected links:
  2428. if (point.isNode) {
  2429. // Temporary disable series.points array, because
  2430. // Series.removePoint() modifies it
  2431. series.points = [];
  2432. // Remove link from all nodes collections:
  2433. []
  2434. .concat(point.linksFrom)
  2435. .concat(point.linksTo)
  2436. .forEach(function (linkFromTo) {
  2437. // Incoming links
  2438. index = linkFromTo.fromNode.linksFrom.indexOf(linkFromTo);
  2439. if (index > -1) {
  2440. linkFromTo.fromNode.linksFrom.splice(index, 1);
  2441. }
  2442. // Outcoming links
  2443. index = linkFromTo.toNode.linksTo.indexOf(linkFromTo);
  2444. if (index > -1) {
  2445. linkFromTo.toNode.linksTo.splice(index, 1);
  2446. }
  2447. // Remove link from data/points collections
  2448. Series.prototype.removePoint.call(series, series.data.indexOf(linkFromTo), false, false);
  2449. });
  2450. // Restore points array, after links are removed
  2451. series.points = series.data.slice();
  2452. // Proceed with removing node. It's similar to
  2453. // Series.removePoint() method, but doesn't modify other arrays
  2454. series.nodes.splice(series.nodes.indexOf(point), 1);
  2455. // Remove node options from config
  2456. while (i--) {
  2457. if (nodesOptions[i].id === point.options.id) {
  2458. series.options.nodes.splice(i, 1);
  2459. break;
  2460. }
  2461. }
  2462. if (point) {
  2463. point.destroy();
  2464. }
  2465. // Run redraw if requested
  2466. series.isDirty = true;
  2467. series.isDirtyData = true;
  2468. if (redraw) {
  2469. series.chart.redraw(redraw);
  2470. }
  2471. }
  2472. else {
  2473. series.removePoint(series.data.indexOf(point), redraw, animation);
  2474. }
  2475. },
  2476. /**
  2477. * Destroy point. If it's a node, remove all links coming out of this
  2478. * node. Then remove point from the layout.
  2479. * @private
  2480. * @return {void}
  2481. */
  2482. destroy: function () {
  2483. if (this.isNode) {
  2484. this.linksFrom.concat(this.linksTo).forEach(function (link) {
  2485. // Removing multiple nodes at the same time
  2486. // will try to remove link between nodes twice
  2487. if (link.destroyElements) {
  2488. link.destroyElements();
  2489. }
  2490. });
  2491. }
  2492. this.series.layout.removeElementFromCollection(this, this.series.layout[this.isNode ? 'nodes' : 'links']);
  2493. return Point.prototype.destroy.apply(this, arguments);
  2494. }
  2495. });
  2496. /**
  2497. * A `networkgraph` series. If the [type](#series.networkgraph.type) option is
  2498. * not specified, it is inherited from [chart.type](#chart.type).
  2499. *
  2500. * @extends series,plotOptions.networkgraph
  2501. * @excluding boostThreshold, animation, animationLimit, connectEnds,
  2502. * connectNulls, dragDrop, getExtremesFromAll, label, linecap,
  2503. * negativeColor, pointInterval, pointIntervalUnit,
  2504. * pointPlacement, pointStart, softThreshold, stack, stacking,
  2505. * step, threshold, xAxis, yAxis, zoneAxis
  2506. * @product highcharts
  2507. * @requires modules/networkgraph
  2508. * @apioption series.networkgraph
  2509. */
  2510. /**
  2511. * An array of data points for the series. For the `networkgraph` series type,
  2512. * points can be given in the following way:
  2513. *
  2514. * An array of objects with named values. The following snippet shows only a
  2515. * few settings, see the complete options set below. If the total number of
  2516. * data points exceeds the series'
  2517. * [turboThreshold](#series.area.turboThreshold), this option is not available.
  2518. *
  2519. * ```js
  2520. * data: [{
  2521. * from: 'Category1',
  2522. * to: 'Category2'
  2523. * }, {
  2524. * from: 'Category1',
  2525. * to: 'Category3'
  2526. * }]
  2527. * ```
  2528. *
  2529. * @type {Array<Object|Array|Number>}
  2530. * @extends series.line.data
  2531. * @excluding drilldown,marker,x,y,draDrop
  2532. * @sample {highcharts} highcharts/chart/reflow-true/
  2533. * Numerical values
  2534. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  2535. * Arrays of numeric x and y
  2536. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  2537. * Arrays of datetime x and y
  2538. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  2539. * Arrays of point.name and y
  2540. * @sample {highcharts} highcharts/series/data-array-of-objects/
  2541. * Config objects
  2542. * @product highcharts
  2543. * @apioption series.networkgraph.data
  2544. */
  2545. /**
  2546. * @type {Highcharts.SeriesNetworkgraphDataLabelsOptionsObject|Array<Highcharts.SeriesNetworkgraphDataLabelsOptionsObject>}
  2547. * @product highcharts
  2548. * @apioption series.networkgraph.data.dataLabels
  2549. */
  2550. /**
  2551. * The node that the link runs from.
  2552. *
  2553. * @type {string}
  2554. * @product highcharts
  2555. * @apioption series.networkgraph.data.from
  2556. */
  2557. /**
  2558. * The node that the link runs to.
  2559. *
  2560. * @type {string}
  2561. * @product highcharts
  2562. * @apioption series.networkgraph.data.to
  2563. */
  2564. /**
  2565. * A collection of options for the individual nodes. The nodes in a
  2566. * networkgraph diagram are auto-generated instances of `Highcharts.Point`,
  2567. * but options can be applied here and linked by the `id`.
  2568. *
  2569. * @sample highcharts/series-networkgraph/data-options/
  2570. * Networkgraph diagram with node options
  2571. *
  2572. * @type {Array<*>}
  2573. * @product highcharts
  2574. * @apioption series.networkgraph.nodes
  2575. */
  2576. /**
  2577. * The id of the auto-generated node, refering to the `from` or `to` setting of
  2578. * the link.
  2579. *
  2580. * @type {string}
  2581. * @product highcharts
  2582. * @apioption series.networkgraph.nodes.id
  2583. */
  2584. /**
  2585. * The color of the auto generated node.
  2586. *
  2587. * @type {Highcharts.ColorString}
  2588. * @product highcharts
  2589. * @apioption series.networkgraph.nodes.color
  2590. */
  2591. /**
  2592. * The color index of the auto generated node, especially for use in styled
  2593. * mode.
  2594. *
  2595. * @type {number}
  2596. * @product highcharts
  2597. * @apioption series.networkgraph.nodes.colorIndex
  2598. */
  2599. /**
  2600. * The name to display for the node in data labels and tooltips. Use this when
  2601. * the name is different from the `id`. Where the id must be unique for each
  2602. * node, this is not necessary for the name.
  2603. *
  2604. * @sample highcharts/series-networkgraph/data-options/
  2605. * Networkgraph diagram with node options
  2606. *
  2607. * @type {string}
  2608. * @product highcharts
  2609. * @apioption series.networkgraph.nodes.name
  2610. */
  2611. /**
  2612. * Mass of the node. By default, each node has mass equal to it's marker radius
  2613. * . Mass is used to determine how two connected nodes should affect
  2614. * each other:
  2615. *
  2616. * Attractive force is multiplied by the ratio of two connected
  2617. * nodes; if a big node has weights twice as the small one, then the small one
  2618. * will move towards the big one twice faster than the big one to the small one
  2619. * .
  2620. *
  2621. * @sample highcharts/series-networkgraph/ragdoll/
  2622. * Mass determined by marker.radius
  2623. *
  2624. * @type {number}
  2625. * @product highcharts
  2626. * @apioption series.networkgraph.nodes.mass
  2627. */
  2628. /**
  2629. * Individual data label for each node. The options are the same as
  2630. * the ones for [series.networkgraph.dataLabels](#series.networkgraph.dataLabels).
  2631. *
  2632. * @type {Highcharts.SeriesNetworkgraphDataLabelsOptionsObject|Array<Highcharts.SeriesNetworkgraphDataLabelsOptionsObject>}
  2633. *
  2634. * @apioption series.networkgraph.nodes.dataLabels
  2635. */
  2636. ''; // adds doclets above to transpiled file
  2637. });
  2638. _registerModule(_modules, 'masters/modules/networkgraph.src.js', [], function () {
  2639. });
  2640. }));