Skip to content

Commit a7f0bab

Browse files
Adding ElasticCollision2D (#6802)
* Adding ElasticCollision2D * Fixing build issues.
1 parent b312567 commit a7f0bab

File tree

2 files changed

+164
-0
lines changed

2 files changed

+164
-0
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package com.thealgorithms.physics;
2+
3+
/**
4+
* 2D Elastic collision between two circular bodies
5+
* Based on principles of conservation of momentum and kinetic energy.
6+
*
7+
* @author [Yash Rajput](https://github.com/the-yash-rajput)
8+
*/
9+
public final class ElasticCollision2D {
10+
11+
private ElasticCollision2D() {
12+
throw new AssertionError("No instances. Utility class");
13+
}
14+
15+
public static class Body {
16+
public double x;
17+
public double y;
18+
public double vx;
19+
public double vy;
20+
public double mass;
21+
public double radius;
22+
23+
public Body(double x, double y, double vx, double vy, double mass, double radius) {
24+
this.x = x;
25+
this.y = y;
26+
this.vx = vx;
27+
this.vy = vy;
28+
this.mass = mass;
29+
this.radius = radius;
30+
}
31+
}
32+
33+
/**
34+
* Resolve instantaneous elastic collision between two circular bodies.
35+
*
36+
* @param a first body
37+
* @param b second body
38+
*/
39+
public static void resolveCollision(Body a, Body b) {
40+
double dx = b.x - a.x;
41+
double dy = b.y - a.y;
42+
double dist = Math.hypot(dx, dy);
43+
44+
if (dist == 0) {
45+
return; // overlapping
46+
}
47+
48+
double nx = dx / dist;
49+
double ny = dy / dist;
50+
51+
// relative velocity along normal
52+
double rv = (b.vx - a.vx) * nx + (b.vy - a.vy) * ny;
53+
54+
if (rv > 0) {
55+
return; // moving apart
56+
}
57+
58+
// impulse with masses
59+
double m1 = a.mass;
60+
double m2 = b.mass;
61+
62+
double j = -(1 + 1.0) * rv / (1.0 / m1 + 1.0 / m2);
63+
64+
// impulse vector
65+
double impulseX = j * nx;
66+
double impulseY = j * ny;
67+
68+
a.vx -= impulseX / m1;
69+
a.vy -= impulseY / m1;
70+
b.vx += impulseX / m2;
71+
b.vy += impulseY / m2;
72+
}
73+
}
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)