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.

118 lines
3.5 KiB

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