PackedBubbleComposition.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. /* *
  2. *
  3. * (c) 2010-2021 Grzegorz Blachlinski, Sebastian Bochan
  4. *
  5. * License: www.highcharts.com/license
  6. *
  7. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  8. *
  9. * */
  10. 'use strict';
  11. import Chart from '../../Core/Chart/Chart.js';
  12. import H from '../../Core/Globals.js';
  13. import '../../Series/Networkgraph/Layouts.js';
  14. var Reingold = H.layouts['reingold-fruchterman'];
  15. import U from '../../Core/Utilities.js';
  16. var addEvent = U.addEvent, extendClass = U.extendClass, pick = U.pick;
  17. /* *
  18. *
  19. * Composition
  20. *
  21. * */
  22. Chart.prototype.getSelectedParentNodes = function () {
  23. var chart = this, series = chart.series, selectedParentsNodes = [];
  24. series.forEach(function (series) {
  25. if (series.parentNode && series.parentNode.selected) {
  26. selectedParentsNodes.push(series.parentNode);
  27. }
  28. });
  29. return selectedParentsNodes;
  30. };
  31. H.networkgraphIntegrations.packedbubble = {
  32. repulsiveForceFunction: function (d, k, node, repNode) {
  33. return Math.min(d, (node.marker.radius + repNode.marker.radius) / 2);
  34. },
  35. barycenter: function () {
  36. var layout = this, gravitationalConstant = layout.options.gravitationalConstant, box = layout.box, nodes = layout.nodes, centerX, centerY;
  37. nodes.forEach(function (node) {
  38. if (layout.options.splitSeries && !node.isParentNode) {
  39. centerX = node.series.parentNode.plotX;
  40. centerY = node.series.parentNode.plotY;
  41. }
  42. else {
  43. centerX = box.width / 2;
  44. centerY = box.height / 2;
  45. }
  46. if (!node.fixedPosition) {
  47. node.plotX -=
  48. (node.plotX - centerX) *
  49. gravitationalConstant /
  50. (node.mass * Math.sqrt(nodes.length));
  51. node.plotY -=
  52. (node.plotY - centerY) *
  53. gravitationalConstant /
  54. (node.mass * Math.sqrt(nodes.length));
  55. }
  56. });
  57. },
  58. repulsive: function (node, force, distanceXY, repNode) {
  59. var factor = (force * this.diffTemperature / node.mass /
  60. node.degree), x = distanceXY.x * factor, y = distanceXY.y * factor;
  61. if (!node.fixedPosition) {
  62. node.plotX += x;
  63. node.plotY += y;
  64. }
  65. if (!repNode.fixedPosition) {
  66. repNode.plotX -= x;
  67. repNode.plotY -= y;
  68. }
  69. },
  70. integrate: H.networkgraphIntegrations.verlet.integrate,
  71. getK: H.noop
  72. };
  73. H.layouts.packedbubble = extendClass(Reingold, {
  74. beforeStep: function () {
  75. if (this.options.marker) {
  76. this.series.forEach(function (series) {
  77. if (series) {
  78. series.calculateParentRadius();
  79. }
  80. });
  81. }
  82. },
  83. setCircularPositions: function () {
  84. var layout = this, box = layout.box, nodes = layout.nodes, nodesLength = nodes.length + 1, angle = 2 * Math.PI / nodesLength, centerX, centerY, radius = layout.options.initialPositionRadius;
  85. nodes.forEach(function (node, index) {
  86. if (layout.options.splitSeries &&
  87. !node.isParentNode) {
  88. centerX = node.series.parentNode.plotX;
  89. centerY = node.series.parentNode.plotY;
  90. }
  91. else {
  92. centerX = box.width / 2;
  93. centerY = box.height / 2;
  94. }
  95. node.plotX = node.prevX = pick(node.plotX, centerX +
  96. radius * Math.cos(node.index || index * angle));
  97. node.plotY = node.prevY = pick(node.plotY, centerY +
  98. radius * Math.sin(node.index || index * angle));
  99. node.dispX = 0;
  100. node.dispY = 0;
  101. });
  102. },
  103. repulsiveForces: function () {
  104. var layout = this, force, distanceR, distanceXY, bubblePadding = layout.options.bubblePadding;
  105. layout.nodes.forEach(function (node) {
  106. node.degree = node.mass;
  107. node.neighbours = 0;
  108. layout.nodes.forEach(function (repNode) {
  109. force = 0;
  110. if (
  111. // Node can not repulse itself:
  112. node !== repNode &&
  113. // Only close nodes affect each other:
  114. // Not dragged:
  115. !node.fixedPosition &&
  116. (layout.options.seriesInteraction ||
  117. node.series === repNode.series)) {
  118. distanceXY = layout.getDistXY(node, repNode);
  119. distanceR = (layout.vectorLength(distanceXY) -
  120. (node.marker.radius +
  121. repNode.marker.radius +
  122. bubblePadding));
  123. // TODO padding configurable
  124. if (distanceR < 0) {
  125. node.degree += 0.01;
  126. node.neighbours++;
  127. force = layout.repulsiveForce(-distanceR / Math.sqrt(node.neighbours), layout.k, node, repNode);
  128. }
  129. layout.force('repulsive', node, force * repNode.mass, distanceXY, repNode, distanceR);
  130. }
  131. });
  132. });
  133. },
  134. applyLimitBox: function (node) {
  135. var layout = this, distanceXY, distanceR, factor = 0.01;
  136. // parentNodeLimit should be used together
  137. // with seriesInteraction: false
  138. if (layout.options.splitSeries &&
  139. !node.isParentNode &&
  140. layout.options.parentNodeLimit) {
  141. distanceXY = layout.getDistXY(node, node.series.parentNode);
  142. distanceR = (node.series.parentNodeRadius -
  143. node.marker.radius -
  144. layout.vectorLength(distanceXY));
  145. if (distanceR < 0 &&
  146. distanceR > -2 * node.marker.radius) {
  147. node.plotX -= distanceXY.x * factor;
  148. node.plotY -= distanceXY.y * factor;
  149. }
  150. }
  151. Reingold.prototype.applyLimitBox.apply(this, arguments);
  152. }
  153. });
  154. // Remove accumulated data points to redistribute all of them again
  155. // (i.e after hiding series by legend)
  156. addEvent(Chart, 'beforeRedraw', function () {
  157. // eslint-disable-next-line no-invalid-this
  158. if (this.allDataPoints) {
  159. // eslint-disable-next-line no-invalid-this
  160. delete this.allDataPoints;
  161. }
  162. });