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.

113 lines
3.4 KiB

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