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.

102 lines
3.0 KiB

  1. /**
  2. * Created by Alex on 2/25/2015.
  3. */
  4. class HierarchicalSpringSolver {
  5. constructor(body, physicsBody, options) {
  6. this.body = body;
  7. this.physicsBody = physicsBody;
  8. this.options = options;
  9. }
  10. /**
  11. * This function calculates the springforces on the nodes, accounting for the support nodes.
  12. *
  13. * @private
  14. */
  15. solve() {
  16. var edgeLength, edge, edgeId;
  17. var dx, dy, fx, fy, springForce, distance;
  18. var edges = this.body.edges;
  19. var nodes = this.physicsBody.calculationNodes;
  20. var nodeIndices = this.physicsBody.calculationNodeIndices;
  21. // initialize the spring force counters
  22. for (let i = 0; i < nodeIndices.length; i++) {
  23. let node1 = nodes[nodeIndices[i]];
  24. node1.springFx = 0;
  25. node1.springFy = 0;
  26. }
  27. // forces caused by the edges, modelled as springs
  28. for (edgeId in edges) {
  29. if (edges.hasOwnProperty(edgeId)) {
  30. edge = edges[edgeId];
  31. if (edge.connected === true) {
  32. // only calculate forces if nodes are in the same sector
  33. if (this.body.nodes[edge.toId] !== undefined && this.body.nodes[edge.fromId] !== undefined) {
  34. edgeLength = edge.properties.length === undefined ? this.options.springLength : edge.properties.length;
  35. dx = (edge.from.x - edge.to.x);
  36. dy = (edge.from.y - edge.to.y);
  37. distance = Math.sqrt(dx * dx + dy * dy);
  38. distance = distance == 0 ? 0.01 : distance;
  39. // the 1/distance is so the fx and fy can be calculated without sine or cosine.
  40. springForce = this.options.springConstant * (edgeLength - distance) / distance;
  41. fx = dx * springForce;
  42. fy = dy * springForce;
  43. if (edge.to.level != edge.from.level) {
  44. edge.to.springFx -= fx;
  45. edge.to.springFy -= fy;
  46. edge.from.springFx += fx;
  47. edge.from.springFy += fy;
  48. }
  49. else {
  50. let factor = 0.5;
  51. edge.to.fx -= factor*fx;
  52. edge.to.fy -= factor*fy;
  53. edge.from.fx += factor*fx;
  54. edge.from.fy += factor*fy;
  55. }
  56. }
  57. }
  58. }
  59. }
  60. // normalize spring forces
  61. var springForce = 1;
  62. var springFx, springFy;
  63. for (let i = 0; i < nodeIndices.length; i++) {
  64. var node = nodes[nodeIndices[i]];
  65. springFx = Math.min(springForce,Math.max(-springForce,node.springFx));
  66. springFy = Math.min(springForce,Math.max(-springForce,node.springFy));
  67. node.fx += springFx;
  68. node.fy += springFy;
  69. }
  70. // retain energy balance
  71. var totalFx = 0;
  72. var totalFy = 0;
  73. for (let i = 0; i < nodeIndices.length; i++) {
  74. var node = nodes[nodeIndices[i]];
  75. totalFx += node.fx;
  76. totalFy += node.fy;
  77. }
  78. var correctionFx = totalFx / nodeIndices.length;
  79. var correctionFy = totalFy / nodeIndices.length;
  80. for (let i = 0; i < nodeIndices.length; i++) {
  81. var node = nodes[nodeIndices[i]];
  82. node.fx -= correctionFx;
  83. node.fy -= correctionFy;
  84. }
  85. }
  86. }
  87. export {HierarchicalSpringSolver};