Skip to content

Commit b598f45

Browse files
Adding ElasticCollision2D
1 parent 3519a91 commit b598f45

File tree

2 files changed

+149
-0
lines changed

2 files changed

+149
-0
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.thealgorithms.physics;
2+
3+
public final class ElasticCollision2D {
4+
5+
private ElasticCollision2D() {
6+
// utility class
7+
}
8+
9+
public static class Body {
10+
public double x, y;
11+
public double vx, vy;
12+
public double mass, radius;
13+
14+
public Body(double x, double y, double vx, double vy, double mass, double radius) {
15+
this.x = x;
16+
this.y = y;
17+
this.vx = vx;
18+
this.vy = vy;
19+
this.mass = mass;
20+
this.radius = radius;
21+
}
22+
}
23+
24+
/**
25+
* Resolve instantaneous elastic collision between two circular bodies.
26+
*
27+
* @param a first body
28+
* @param b second body
29+
*/
30+
public static void resolveCollision(Body a, Body b) {
31+
double dx = b.x - a.x;
32+
double dy = b.y - a.y;
33+
double dist = Math.hypot(dx, dy);
34+
if (dist == 0) return; // overlapping
35+
36+
double nx = dx / dist;
37+
double ny = dy / dist;
38+
39+
// relative velocity along normal
40+
double rv = (b.vx - a.vx) * nx + (b.vy - a.vy) * ny;
41+
if (rv > 0) return; // moving apart
42+
43+
// Correct 2D impulse with masses
44+
double m1 = a.mass;
45+
double m2 = b.mass;
46+
47+
double j = -(1 + 1.0) * rv / (1.0 / m1 + 1.0 / m2);
48+
49+
// impulse vector along normal
50+
double impulseX = j * nx;
51+
double impulseY = j * ny;
52+
53+
a.vx -= impulseX / m1;
54+
a.vy -= impulseY / m1;
55+
b.vx += impulseX / m2;
56+
b.vy += impulseY / m2;
57+
}
58+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package com.thealgorithms.physics;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
5+
6+
import org.junit.jupiter.api.Test;
7+
8+
class ElasticCollision2DTest {
9+
10+
@Test
11+
void testEqualMassHeadOnCollision() {
12+
ElasticCollision2D.Body a = new ElasticCollision2D.Body(0, 0, 1, 0, 1.0, 0.5);
13+
ElasticCollision2D.Body b = new ElasticCollision2D.Body(1, 0, -1, 0, 1.0, 0.5);
14+
15+
ElasticCollision2D.resolveCollision(a, b);
16+
17+
assertEquals(-1.0, a.vx, 1e-6);
18+
assertEquals(0.0, a.vy, 1e-6);
19+
assertEquals(1.0, b.vx, 1e-6);
20+
assertEquals(0.0, b.vy, 1e-6);
21+
}
22+
23+
@Test
24+
void testUnequalMassHeadOnCollision() {
25+
ElasticCollision2D.Body a = new ElasticCollision2D.Body(0, 0, 2, 0, 2.0, 0.5);
26+
ElasticCollision2D.Body b = new ElasticCollision2D.Body(1, 0, -1, 0, 1.0, 0.5);
27+
28+
ElasticCollision2D.resolveCollision(a, b);
29+
30+
// 1D head-on collision results
31+
assertEquals(0.0, a.vx, 1e-6);
32+
assertEquals(0.0, a.vy, 1e-6);
33+
assertEquals(3.0, b.vx, 1e-6);
34+
assertEquals(0.0, b.vy, 1e-6);
35+
}
36+
37+
@Test
38+
void testMovingApartNoCollision() {
39+
ElasticCollision2D.Body a = new ElasticCollision2D.Body(0, 0, -1, 0, 1.0, 0.5);
40+
ElasticCollision2D.Body b = new ElasticCollision2D.Body(1, 0, 1, 0, 1.0, 0.5);
41+
42+
ElasticCollision2D.resolveCollision(a, b);
43+
44+
assertEquals(-1.0, a.vx, 1e-6);
45+
assertEquals(0.0, a.vy, 1e-6);
46+
assertEquals(1.0, b.vx, 1e-6);
47+
assertEquals(0.0, b.vy, 1e-6);
48+
}
49+
50+
@Test
51+
void testGlancingCollision() {
52+
ElasticCollision2D.Body a = new ElasticCollision2D.Body(0, 0, 1, 1, 1.0, 0.5);
53+
ElasticCollision2D.Body b = new ElasticCollision2D.Body(1, 1, -1, -0.5, 1.0, 0.5);
54+
55+
ElasticCollision2D.resolveCollision(a, b);
56+
57+
// Ensure relative velocity along normal is reversed
58+
double nx = (b.x - a.x) / Math.hypot(b.x - a.x, b.y - a.y);
59+
double ny = (b.y - a.y) / Math.hypot(b.x - a.x, b.y - a.y);
60+
double relVelAfter = (b.vx - a.vx) * nx + (b.vy - a.vy) * ny;
61+
62+
assertTrue(relVelAfter > 0);
63+
}
64+
65+
@Test
66+
void testOverlappingBodies() {
67+
ElasticCollision2D.Body a = new ElasticCollision2D.Body(0, 0, 1, 0, 1.0, 0.5);
68+
ElasticCollision2D.Body b = new ElasticCollision2D.Body(0, 0, -1, 0, 1.0, 0.5);
69+
70+
ElasticCollision2D.resolveCollision(a, b);
71+
72+
// Should not crash, velocities may remain unchanged
73+
assertEquals(1.0, a.vx, 1e-6);
74+
assertEquals(0.0, a.vy, 1e-6);
75+
assertEquals(-1.0, b.vx, 1e-6);
76+
assertEquals(0.0, b.vy, 1e-6);
77+
}
78+
79+
@Test
80+
void testStationaryBodyHit() {
81+
ElasticCollision2D.Body a = new ElasticCollision2D.Body(0, 0, 2, 0, 1.0, 0.5);
82+
ElasticCollision2D.Body b = new ElasticCollision2D.Body(1, 0, 0, 0, 1.0, 0.5);
83+
84+
ElasticCollision2D.resolveCollision(a, b);
85+
86+
assertEquals(0.0, a.vx, 1e-6);
87+
assertEquals(0.0, a.vy, 1e-6);
88+
assertEquals(2.0, b.vx, 1e-6);
89+
assertEquals(0.0, b.vy, 1e-6);
90+
}
91+
}

0 commit comments

Comments
 (0)