class SpringSolver { constructor(body, physicsBody, options) { this.body = body; this.physicsBody = physicsBody; this.setOptions(options); } setOptions(options) { this.options = options; 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 } /** * This function calculates the springforces on the nodes, accounting for the support nodes. * * @private */ solve() { let edgeLength, edge; let edgeIndices = this.physicsBody.physicsEdgeIndices; let edges = this.body.edges; let node1, node2, node3; // forces caused by the edges, modelled as springs for (let i = 0; i < edgeIndices.length; i++) { edge = edges[edgeIndices[i]]; if (edge.connected === true && edge.toId !== edge.fromId) { // only calculate forces if nodes are in the same sector if (this.body.nodes[edge.toId] !== undefined && this.body.nodes[edge.fromId] !== undefined) { if (edge.edgeType.via !== undefined) { edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length; node1 = edge.to; node2 = edge.edgeType.via; node3 = edge.from; this._calculateSpringForce(node1, node2, 0.5 * edgeLength); this._calculateSpringForce(node2, node3, 0.5 * edgeLength); } else { // the * 1.5 is here so the edge looks as large as a smooth edge. It does not initially because the smooth edges use // the support nodes which exert a repulsive force on the to and from nodes, making the edge appear larger. edgeLength = edge.options.length === undefined ? this.options.springLength * 1.5: edge.options.length; this._calculateSpringForce(edge.from, edge.to, edgeLength); } } } } } /** * This is the code actually performing the calculation for the function above. * * @param node1 * @param node2 * @param edgeLength * @private */ _calculateSpringForce(node1, node2, edgeLength) { let dx,dy; if (this.overlapAvoidanceFactor < 1) { let nodesWidth = this.overlapAvoidanceFactor * (node1.shape.radius + node2.shape.radius); dx = (node1.x - node2.x) - nodesWidth; dy = (node1.y - node2.y) - nodesWidth; } else { dx = (node1.x - node2.x); dy = (node1.y - node2.y); } let distance = Math.sqrt(dx * dx + dy * dy); distance = distance === 0 ? 0.01 : distance; // the 1/distance is so the fx and fy can be calculated without sine or cosine. let springForce = this.options.springConstant * (edgeLength - distance) / distance; let fx = dx * springForce; let fy = dy * springForce; // handle the case where one node is not part of the physcis if (this.physicsBody.forces[node1.id] !== undefined) { this.physicsBody.forces[node1.id].x += fx; this.physicsBody.forces[node1.id].y += fy; } if (this.physicsBody.forces[node2.id] !== undefined) { this.physicsBody.forces[node2.id].x -= fx; this.physicsBody.forces[node2.id].y -= fy; } } } export default SpringSolver;