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.0 KiB

  1. /**
  2. * Spring Solver
  3. */
  4. class SpringSolver {
  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. let edgeLength, edge;
  29. let edgeIndices = this.physicsBody.physicsEdgeIndices;
  30. let edges = this.body.edges;
  31. let node1, node2, node3;
  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 && edge.toId !== edge.fromId) {
  36. // only calculate forces if nodes are in the same sector
  37. if (this.body.nodes[edge.toId] !== undefined && this.body.nodes[edge.fromId] !== undefined) {
  38. if (edge.edgeType.via !== undefined) {
  39. edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length;
  40. node1 = edge.to;
  41. node2 = edge.edgeType.via;
  42. node3 = edge.from;
  43. this._calculateSpringForce(node1, node2, 0.5 * edgeLength);
  44. this._calculateSpringForce(node2, node3, 0.5 * edgeLength);
  45. }
  46. else {
  47. // the * 1.5 is here so the edge looks as large as a smooth edge. It does not initially because the smooth edges use
  48. // the support nodes which exert a repulsive force on the to and from nodes, making the edge appear larger.
  49. edgeLength = edge.options.length === undefined ? this.options.springLength * 1.5: edge.options.length;
  50. this._calculateSpringForce(edge.from, edge.to, edgeLength);
  51. }
  52. }
  53. }
  54. }
  55. }
  56. /**
  57. * This is the code actually performing the calculation for the function above.
  58. *
  59. * @param {Node} node1
  60. * @param {Node} node2
  61. * @param {number} edgeLength
  62. * @private
  63. */
  64. _calculateSpringForce(node1, node2, edgeLength) {
  65. let dx = (node1.x - node2.x);
  66. let dy = (node1.y - node2.y);
  67. let distance = Math.max(Math.sqrt(dx * dx + dy * dy),0.01);
  68. // the 1/distance is so the fx and fy can be calculated without sine or cosine.
  69. let springForce = this.options.springConstant * (edgeLength - distance) / distance;
  70. let fx = dx * springForce;
  71. let fy = dy * springForce;
  72. // handle the case where one node is not part of the physcis
  73. if (this.physicsBody.forces[node1.id] !== undefined) {
  74. this.physicsBody.forces[node1.id].x += fx;
  75. this.physicsBody.forces[node1.id].y += fy;
  76. }
  77. if (this.physicsBody.forces[node2.id] !== undefined) {
  78. this.physicsBody.forces[node2.id].x -= fx;
  79. this.physicsBody.forces[node2.id].y -= fy;
  80. }
  81. }
  82. }
  83. export default SpringSolver;