vis.js is a dynamic, browser-based visualization library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

153 lines
4.4 KiB

  1. /**
  2. * Calculate the forces the nodes apply on eachother based on a repulsion field.
  3. * This field is linearly approximated.
  4. *
  5. * @private
  6. */
  7. exports._calculateNodeForces = function () {
  8. var dx, dy, distance, fx, fy,
  9. repulsingForce, node1, node2, i, j;
  10. var nodes = this.calculationNodes;
  11. var nodeIndices = this.calculationNodeIndices;
  12. // repulsing forces between nodes
  13. var nodeDistance = this.constants.physics.hierarchicalRepulsion.nodeDistance;
  14. // we loop from i over all but the last entree in the array
  15. // j loops from i+1 to the last. This way we do not double count any of the indices, nor i == j
  16. for (i = 0; i < nodeIndices.length - 1; i++) {
  17. node1 = nodes[nodeIndices[i]];
  18. for (j = i + 1; j < nodeIndices.length; j++) {
  19. node2 = nodes[nodeIndices[j]];
  20. // nodes only affect nodes on their level
  21. if (node1.level == node2.level) {
  22. dx = node2.x - node1.x;
  23. dy = node2.y - node1.y;
  24. distance = Math.sqrt(dx * dx + dy * dy);
  25. var steepness = 0.05;
  26. if (distance < nodeDistance) {
  27. repulsingForce = -Math.pow(steepness*distance,2) + Math.pow(steepness*nodeDistance,2);
  28. }
  29. else {
  30. repulsingForce = 0;
  31. }
  32. // normalize force with
  33. if (distance == 0) {
  34. distance = 0.01;
  35. }
  36. else {
  37. repulsingForce = repulsingForce / distance;
  38. }
  39. fx = dx * repulsingForce;
  40. fy = dy * repulsingForce;
  41. node1.fx -= fx;
  42. node1.fy -= fy;
  43. node2.fx += fx;
  44. node2.fy += fy;
  45. }
  46. }
  47. }
  48. };
  49. /**
  50. * this function calculates the effects of the springs in the case of unsmooth curves.
  51. *
  52. * @private
  53. */
  54. exports._calculateHierarchicalSpringForces = function () {
  55. var edgeLength, edge, edgeId;
  56. var dx, dy, fx, fy, springForce, distance;
  57. var edges = this.edges;
  58. var nodes = this.calculationNodes;
  59. var nodeIndices = this.calculationNodeIndices;
  60. for (var i = 0; i < nodeIndices.length; i++) {
  61. var node1 = nodes[nodeIndices[i]];
  62. node1.springFx = 0;
  63. node1.springFy = 0;
  64. }
  65. // forces caused by the edges, modelled as springs
  66. for (edgeId in edges) {
  67. if (edges.hasOwnProperty(edgeId)) {
  68. edge = edges[edgeId];
  69. if (edge.connected) {
  70. // only calculate forces if nodes are in the same sector
  71. if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) {
  72. edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength;
  73. // this implies that the edges between big clusters are longer
  74. edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth;
  75. dx = (edge.from.x - edge.to.x);
  76. dy = (edge.from.y - edge.to.y);
  77. distance = Math.sqrt(dx * dx + dy * dy);
  78. if (distance == 0) {
  79. distance = 0.01;
  80. }
  81. // the 1/distance is so the fx and fy can be calculated without sine or cosine.
  82. springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance;
  83. fx = dx * springForce;
  84. fy = dy * springForce;
  85. if (edge.to.level != edge.from.level) {
  86. edge.to.springFx -= fx;
  87. edge.to.springFy -= fy;
  88. edge.from.springFx += fx;
  89. edge.from.springFy += fy;
  90. }
  91. else {
  92. var factor = 0.5;
  93. edge.to.fx -= factor*fx;
  94. edge.to.fy -= factor*fy;
  95. edge.from.fx += factor*fx;
  96. edge.from.fy += factor*fy;
  97. }
  98. }
  99. }
  100. }
  101. }
  102. // normalize spring forces
  103. var springForce = 1;
  104. var springFx, springFy;
  105. for (i = 0; i < nodeIndices.length; i++) {
  106. var node = nodes[nodeIndices[i]];
  107. springFx = Math.min(springForce,Math.max(-springForce,node.springFx));
  108. springFy = Math.min(springForce,Math.max(-springForce,node.springFy));
  109. node.fx += springFx;
  110. node.fy += springFy;
  111. }
  112. // retain energy balance
  113. var totalFx = 0;
  114. var totalFy = 0;
  115. for (i = 0; i < nodeIndices.length; i++) {
  116. var node = nodes[nodeIndices[i]];
  117. totalFx += node.fx;
  118. totalFy += node.fy;
  119. }
  120. var correctionFx = totalFx / nodeIndices.length;
  121. var correctionFy = totalFy / nodeIndices.length;
  122. for (i = 0; i < nodeIndices.length; i++) {
  123. var node = nodes[nodeIndices[i]];
  124. node.fx -= correctionFx;
  125. node.fy -= correctionFy;
  126. }
  127. };