distToSegment: function(p, v1, v2) { var v = new Point(v2.x,v2.y); v.subtract(v1); var w = new Point(p.x,p.y); w.subtract(v1); var c1 = w.dot(v); var c2 = v.dot(v); if (!c2) return 100000; var b = c1/c2; v.multiply(b); var Pb = new Point(v1.x, v1.y); Pb.add(v); return Point.distance(p, Pb); },
applyFriction: function(collisionInfo) { var edgePoint1 = collisionInfo.edge[0], edgePoint2 = collisionInfo.edge[1], tangentDirection = Point.subtract(edgePoint2.position, edgePoint1.position).normalize(), pointSpeed = collisionInfo.point.getSpeed(), edgeSpeed = edgePoint1.getSpeed().add(edgePoint2.getSpeed()).multiply(0.5); if (!collisionInfo.depth) { return; } var frictionRatio = Math.max(Math.min(collisionInfo.depth / this.FRICTION_RATIO, 1), this.FRICTION_MIN), // Bigger ratio - more friction tangentPointSpeed = tangentDirection.dot(pointSpeed), tangentEdgeSpeed = tangentDirection.dot(edgeSpeed), speedDelta = tangentPointSpeed - tangentEdgeSpeed, deltaToApply = speedDelta * frictionRatio, invMassEdge = 0; if (edgePoint1.mass * edgePoint2.mass > 0) { invMassEdge = 1 / (edgePoint1.mass + edgePoint2.mass); } deltaToApply /= (collisionInfo.point.invMass + invMassEdge); collisionInfo.point.applyForce(Point.multiply(tangentDirection, -deltaToApply * collisionInfo.point.invMass)); edgePoint1.applyForce(Point.multiply(tangentDirection, deltaToApply * edgePoint1.invMass)); edgePoint2.applyForce(Point.multiply(tangentDirection, deltaToApply * edgePoint2.invMass)); },
moveParticles: function(dt) { for (var i = 0; i < this.particles.length; i++) { var particle = this.particles[i]; if (particle.enabled && particle.mass > 0) { var delta = Point.subtract(particle.position, particle.prevPosition); particle.prevPosition = particle.position.clone(); particle.position.addPoint(delta); particle.position.addPoint(Point.multiplyFloat(particle.acceleration, dt * dt)); } } },
processConstraints: function(list) { if (!list) { list = this.constraints; } for (var i = 0; i < list.length; i++) { var constraint = list[i]; if (constraint.enabled) { var point1 = constraint.particle1.position; var point2 = constraint.particle2.position; // delta = x2 - x1 var delta = Point.subtract(point2, point1), deltaLength = delta.getMagnitude(); if (Math.abs(deltaLength) < 0.0000001) { // Ignore same place points to avoid division by zero return; } var diff = (deltaLength - constraint.length) / (deltaLength * (constraint.particle1.invMass + constraint.particle2.invMass)), shouldProcess = false; if (constraint.type == Constraint.TYPE_EQUAL) { shouldProcess = Math.abs(diff) > 0; } else if (constraint.type == Constraint.TYPE_GREATER) { shouldProcess = diff < 0; } else if (constraint.type == Constraint.TYPE_LESS) { shouldProcess = diff > 0; } if (shouldProcess) { delta.multiply(diff * constraint.stiffness); point1.add(Point.multiply(delta, constraint.particle1.invMass)); point2.subtract(Point.multiply(delta, constraint.particle2.invMass)); } } } },
collideConvexes: function(convex1, convex2, collisionInfo, isSecond) { if (collisionInfo.depth === undefined) { collisionInfo.depth = 1000000; // Large value } collisionInfo.convex1 = convex1; collisionInfo.convex2 = convex2; var minDepth = collisionInfo.depth, collisionNormal = null, collisionEdge = null, foundBestNormal = false, interval = null; // Getting collision normal and depth for (var i = 0; i < convex1.particles.length; i++) { var particle1 = convex1.particles[i], particle2 = convex1.particles[(i + 1) % convex1.particles.length], p1 = particle1.position, p2 = particle2.position, normal = new Point(p1.y - p2.y, p2.x - p1.x).normalize(); var projection1 = this.projectConvexToAxis(convex1, normal), projection2 = this.projectConvexToAxis(convex2, normal); interval = this.intervalDistance(projection1, projection2); var distance = interval.distance, centersDirection = Point.subtract(convex1.center, convex2.center), // Projection must be positive (normal looks in the correct direction) // if not - it's probably a parallel edge which must be ignored normalResponseProjection = centersDirection.dot(normal); if (distance > 0) { return false; } else if (normalResponseProjection > 0 && Math.abs(distance) < minDepth) { minDepth = Math.abs(distance); collisionNormal = normal; foundBestNormal = true; collisionEdge = [particle1, particle2]; } } if (foundBestNormal) { var maxDistance = 0, firstPointOnCorrectSide = null; for (i = 0; i < convex2.particles.length; i++) { if (Point.ord(convex2.particles[i].position, collisionEdge[0].position, collisionEdge[1].position) > 0) { var pointToLineDistance = this.distToSegment(convex2.particles[i].position, collisionEdge[0].position, collisionEdge[1].position); if (Math.abs(pointToLineDistance) > maxDistance) { maxDistance = Math.abs(pointToLineDistance); collisionInfo.point = convex2.particles[i]; } firstPointOnCorrectSide = convex2.particles[i]; } } if (!collisionInfo.point) { collisionInfo.point = firstPointOnCorrectSide || convex2.particles[0]; } collisionInfo.axis = collisionNormal; collisionInfo.edge = collisionEdge; collisionInfo.depth = minDepth; } return true; },