/** * Created by Alex on 2/25/2015. */ class HierarchicalSpringSolver { constructor(body, physicsBody, options) { this.body = body; this.physicsBody = physicsBody; this.setOptions(options); } setOptions(options) { this.options = options; } /** * This function calculates the springforces on the nodes, accounting for the support nodes. * * @private */ solve() { var edgeLength, edge, edgeId; var dx, dy, fx, fy, springForce, distance; var edges = this.body.edges; var nodeIndices = this.physicsBody.calculationNodeIndices; var forces = this.physicsBody.forces; // initialize the spring force counters for (let i = 0; i < nodeIndices.length; i++) { let nodeId = nodeIndices[i]; forces[nodeId].springFx = 0; forces[nodeId].springFy = 0; } // forces caused by the edges, modelled as springs for (edgeId in edges) { if (edges.hasOwnProperty(edgeId)) { edge = edges[edgeId]; if (edge.connected === true) { edgeLength = edge.properties.length === undefined ? this.options.springLength : edge.properties.length; dx = (edge.from.x - edge.to.x); dy = (edge.from.y - edge.to.y); 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. springForce = this.options.springConstant * (edgeLength - distance) / distance; fx = dx * springForce; fy = dy * springForce; if (edge.to.level != edge.from.level) { forces[edge.toId].springFx -= fx; forces[edge.toId].springFy -= fy; forces[edge.fromId].springFx += fx; forces[edge.fromId].springFy += fy; } else { let factor = 0.5; forces[edge.toId].x -= factor*fx; forces[edge.toId].y -= factor*fy; forces[edge.fromId].x += factor*fx; forces[edge.fromId].y += factor*fy; } } } } // normalize spring forces var springForce = 1; var springFx, springFy; for (let i = 0; i < nodeIndices.length; i++) { let nodeId = nodeIndices[i]; springFx = Math.min(springForce,Math.max(-springForce,forces[nodeId].springFx)); springFy = Math.min(springForce,Math.max(-springForce,forces[nodeId].springFy)); forces[nodeId].x += springFx; forces[nodeId].y += springFy; } // retain energy balance var totalFx = 0; var totalFy = 0; for (let i = 0; i < nodeIndices.length; i++) { let nodeId = nodeIndices[i]; totalFx += forces[nodeId].x; totalFy += forces[nodeId].y; } var correctionFx = totalFx / nodeIndices.length; var correctionFy = totalFy / nodeIndices.length; for (let i = 0; i < nodeIndices.length; i++) { let nodeId = nodeIndices[i]; forces[nodeId].x -= correctionFx; forces[nodeId].y -= correctionFy; } } } export {HierarchicalSpringSolver};