update = new Timeline(new KeyFrame(Duration.millis(1000), e -> {
// Remove Collisions
vecCollidingPairs.clear();
// Static Collision
for (Ball ball : Ball.balls)
{
ball.update();
}
for (Ball ball : Ball.balls)
{
// Against Edges
for (Capsule edge : Capsule.capsules)
{
double x1 = edge.getEndX() - edge.getStartX();
double y1 = edge.getEndY() - edge.getStartY();
double x2 = ball.getX() - edge.getStartX();
double y2 = ball.getY() - edge.getStartY();
double edgeLength = Math.pow(x1, 2) + Math.pow(y1, 2);
double t = Math.max(0, Math.min(edgeLength, (x1 * x2 + y1 * y2))) / edgeLength;
double closestPointX = edge.getStartX() + t * x1;
double closestPointY = edge.getStartY() + t * y1;
double distEdge = Math.sqrt((ball.getX() - closestPointX) * (ball.getX() - closestPointX) + (ball.getY() - closestPointY) * (ball.getY() - closestPointY));
if (distEdge <= (ball.getRadius() + edge.getRadius()))
{
// Static collision has occured
Ball fakeball = new Ball(false);
fakeball.setRadius(edge.getRadius());
fakeball.setMass(ball.getMass() * 1.0);
fakeball.setX(closestPointX);
fakeball.setY(closestPointY);
fakeball.setVel(-ball.getVelX(), -ball.getVelY());
vecFakeBalls.add(fakeball);
vecCollidingPairs.add(new BallPairs(ball, fakeball));
double overl = 1.0 * (distEdge - ball.getRadius() - fakeball.getRadius());
// Displace Current Ball away from collision
ball.setX(ball.getX() - (overl * (ball.getX() - fakeball.getX()) / distEdge));
ball.setY(ball.getY() - (overl * (ball.getY() - fakeball.getY()) / distEdge));
}
}
// Against Other Balls
for (Ball target : Ball.balls)
{
if (ball.getId() != target.getId())
{
if (Utils.isHit(ball, target))
{
// Collision has occured
vecCollidingPairs.add(new BallPairs(ball, target));
// Distance between ball centers
double distance = Utils.distance(ball, target);
double overlap = 0.5 * (distance - ball.getRadius() - target.getRadius());
// Displace Current Ball
ball.setX(ball.getX() - ((overlap * (ball.getX() - target.getX()) / distance)));
ball.setY(ball.getY() - ((overlap * (ball.getY() - target.getY()) / distance)));
// Displace Targer Ball
target.setX(target.getX() + ((overlap * (ball.getX() - target.getX()) / distance)));
target.setY(target.getY() + ((overlap * (ball.getY() - target.getY()) / distance)));
}
}
}
}
// Now work out dynamic collisions
for (BallPairs c : vecCollidingPairs)
{
Ball b1 = c.getB1();
Ball b2 = c.getB2();
// Distance between balls
double dist = Utils.distance(b1, b2);
// Normal
double nx = (b2.getX() - b1.getX()) / dist;
double ny = (b2.getY() - b1.getY()) / dist;
// Tangent
double tx = -ny;
double ty = nx;
// Dot Product Tangent
double dpTan1 = b1.getVelX() * tx + b1.getVelY() * ty;
double dpTan2 = b2.getVelX() * tx + b2.getVelY() * ty;
// Dot Product Normal
double dpNorm1 = b1.getVelX() * nx + b1.getVelY() * ny;
double dpNorm2 = b2.getVelX() * nx + b2.getVelY() * ny;
// Conservation of momentum in 1D
double m1 = (dpNorm1 * (b1.getMass() - b2.getMass()) + 2.0 * b2.getMass() * dpNorm2) / (b1.getMass() + b2.getMass());
double m2 = (dpNorm2 * (b2.getMass() - b1.getMass()) + 2.0 * b1.getMass() * dpNorm1) / (b1.getMass() + b2.getMass());
// Update ball velocities
b1.setVel(tx * dpTan1 + nx * m1, ty * dpTan1 + ny * m1);
b2.setVel(tx * dpTan2 + nx * m2, ty * dpTan2 + ny * m2);
}
// Remove fake balls
vecFakeBalls.clear();
}));