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