-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcollisions.js
134 lines (108 loc) · 4.07 KB
/
collisions.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
class Vec2
{
constructor(x, y)
{
this.x = x;
this.y = y;
}
add(vec)
{
//component-wise addition
return new Vec2(this.x + vec.x, this.y + vec.y);
}
sub(vec)
{
//component-wise subtraction
return new Vec2(this.x - vec.x, this.y - vec.y);
}
mul(scalar)
{
//multiply vector by scalar
return new Vec2(this.x * scalar, this.y * scalar);
}
dot(vec)
{
return this.x * vec.x + this.y * vec.y;
}
cross(vec)
{
return this.x * vec.y - this.y * vec.x;
}
length()
{
return Math.sqrt(this.dot(this));
}
normalize()
{
return new Vec2(this.x / this.length(), this.y / this.length());
}
perp()
{
return new Vec2(-this.y, this.x);
}
}
class Ball
{
constructor(x, y, r)
{
this.pos = new Vec2(x, y);
this.vel = new Vec2(0, 0);
this.radius = r;
this.mass = 1;
}
getPosition() { return this.pos; }
getVelocity() { return this.vel; }
getRadius() { return this.radius; }
getMass() { return this.mass; }
setPosition(pos) { this.pos = pos; }
setVelocity(vel) { this.vel = vel; }
}
function staticResolution(ball, other, ballDist)
{
//manually move balls away from each other in equal measure so they dont overlap, no real physics
//scalar distance needed to move to not be overlapping anymore
let displacement = -0.5 * (ballDist - ball.getRadius() - other.getRadius());
//unit vector of direction to move in
let direction = ball.getPosition().sub(other.getPosition()).normalize();
//because we did ball.sub(other), direction points from other to ball.
//so ball wants to move the way direction is pointing, and other wants to move the opposite way
ball.setPosition(ball.getPosition().add(direction.mul(displacement)));
other.setPosition(other.getPosition().sub(direction.mul(displacement)));
}
function dynamicResolution(ball, other)
{
//get unit vector pointing from ball to other
let normal = other.getPosition().sub(ball.getPosition()).normalize();
//unit vector perpendicular to normal (tangent to circles at point of collision)
let tangent = normal.perp();
//how much does ball's velocity project onto the normal (i.e. how fast is it moving in the direction normal is pointing)
let ballNormSpeed = ball.getVelocity().dot(normal);
let otherNormSpeed = other.getVelocity().dot(normal);
//same for tangent direction
let ballTanSpeed = ball.getVelocity().dot(tangent);
let otherTanSpeed = other.getVelocity().dot(tangent);
//calculate speed along normal direction after collision for each ball. speed in tangent direction will remain unchanged
let ballNormSpeedAfter = (ballNormSpeed * (ball.getMass() - other.getMass()) + 2 * other.getMass() * otherNormSpeed) / (ball.getMass() + other.getMass());
let otherNormSpeedAfter = (otherNormSpeed * (other.getMass() - ball.getMass()) + 2 * ball.getMass() * ballNormSpeed) / (ball.getMass() + other.getMass());
//if all balls have the same mass, can use this calculation instead
/*
let ballNormSpeedAfter = otherNormSpeed;
let otherNormSpeedAfter = ballNormSpeed;
*/
let ballVelocityAfter = normal.mul(ballNormSpeedAfter).add(tangent.mul(ballTanSpeed));
ball.setVelocity(ballVelocityAfter);
let otherVelocityAfter = normal.mul(otherNormSpeedAfter).add(tangent.mul(otherTanSpeed));
other.setVelocity(otherVelocityAfter);
}
function handleCollision(ball, other)
{
//do the balls overlap? if not, nothing more to do
let ballDist = ball.getPosition().sub(other.getPosition()).length();
if (ballDist > ball.getRadius() + other.getRadius()) return; //not overlapping
//balls are overlapping
//first, handle static collision resolution to prevent balls getting stuck inside each other
//save one whole sqrt by passing ballDist
if (inpStatic.checked) staticResolution(ball, other, ballDist);
//then, calculate dynamic physics (bounces)
if (inpDynamic.checked) dynamicResolution(ball, other);
}