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.

93 lines
3.2 KiB

  1. class SpringSolver {
  2. constructor(body, physicsBody, options) {
  3. this.body = body;
  4. this.physicsBody = physicsBody;
  5. this.setOptions(options);
  6. }
  7. setOptions(options) {
  8. this.options = options;
  9. this.overlapAvoidanceFactor = 1 - Math.max(0, Math.min(1,this.options.avoidOverlap)); // if 1 then min distance = 0.5, if 0.5 then min distance = 0.5 + 0.5*node.shape.radius
  10. }
  11. /**
  12. * This function calculates the springforces on the nodes, accounting for the support nodes.
  13. *
  14. * @private
  15. */
  16. solve() {
  17. let edgeLength, edge;
  18. let edgeIndices = this.physicsBody.physicsEdgeIndices;
  19. let edges = this.body.edges;
  20. let node1, node2, node3;
  21. // forces caused by the edges, modelled as springs
  22. for (let i = 0; i < edgeIndices.length; i++) {
  23. edge = edges[edgeIndices[i]];
  24. if (edge.connected === true && edge.toId !== edge.fromId) {
  25. // only calculate forces if nodes are in the same sector
  26. if (this.body.nodes[edge.toId] !== undefined && this.body.nodes[edge.fromId] !== undefined) {
  27. if (edge.edgeType.via !== undefined) {
  28. edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length;
  29. node1 = edge.to;
  30. node2 = edge.edgeType.via;
  31. node3 = edge.from;
  32. this._calculateSpringForce(node1, node2, 0.5 * edgeLength);
  33. this._calculateSpringForce(node2, node3, 0.5 * edgeLength);
  34. }
  35. else {
  36. // the * 1.5 is here so the edge looks as large as a smooth edge. It does not initially because the smooth edges use
  37. // the support nodes which exert a repulsive force on the to and from nodes, making the edge appear larger.
  38. edgeLength = edge.options.length === undefined ? this.options.springLength * 1.5: edge.options.length;
  39. this._calculateSpringForce(edge.from, edge.to, edgeLength);
  40. }
  41. }
  42. }
  43. }
  44. }
  45. /**
  46. * This is the code actually performing the calculation for the function above.
  47. *
  48. * @param node1
  49. * @param node2
  50. * @param edgeLength
  51. * @private
  52. */
  53. _calculateSpringForce(node1, node2, edgeLength) {
  54. let dx,dy;
  55. if (this.overlapAvoidanceFactor < 1) {
  56. let nodesWidth = this.overlapAvoidanceFactor * (node1.shape.radius + node2.shape.radius);
  57. dx = (node1.x - node2.x) - nodesWidth;
  58. dy = (node1.y - node2.y) - nodesWidth;
  59. }
  60. else {
  61. dx = (node1.x - node2.x);
  62. dy = (node1.y - node2.y);
  63. }
  64. let distance = Math.sqrt(dx * dx + dy * dy);
  65. distance = distance === 0 ? 0.01 : distance;
  66. // the 1/distance is so the fx and fy can be calculated without sine or cosine.
  67. let springForce = this.options.springConstant * (edgeLength - distance) / distance;
  68. let fx = dx * springForce;
  69. let fy = dy * springForce;
  70. // handle the case where one node is not part of the physcis
  71. if (this.physicsBody.forces[node1.id] !== undefined) {
  72. this.physicsBody.forces[node1.id].x += fx;
  73. this.physicsBody.forces[node1.id].y += fy;
  74. }
  75. if (this.physicsBody.forces[node2.id] !== undefined) {
  76. this.physicsBody.forces[node2.id].x -= fx;
  77. this.physicsBody.forces[node2.id].y -= fy;
  78. }
  79. }
  80. }
  81. export default SpringSolver;