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.

119 lines
3.5 KiB

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