From b598f45faec301cc61299aafaadb4aa5f355498c Mon Sep 17 00:00:00 2001 From: the-yash-rajput Date: Thu, 16 Oct 2025 21:29:55 +0530 Subject: [PATCH 1/2] Adding ElasticCollision2D --- .../physics/ElasticCollision2D.java | 58 ++++++++++++ .../physics/ElasticCollision2DTest.java | 91 +++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 src/main/java/com/thealgorithms/physics/ElasticCollision2D.java create mode 100644 src/test/java/com/thealgorithms/physics/ElasticCollision2DTest.java diff --git a/src/main/java/com/thealgorithms/physics/ElasticCollision2D.java b/src/main/java/com/thealgorithms/physics/ElasticCollision2D.java new file mode 100644 index 000000000000..9048a5dd4527 --- /dev/null +++ b/src/main/java/com/thealgorithms/physics/ElasticCollision2D.java @@ -0,0 +1,58 @@ +package com.thealgorithms.physics; + +public final class ElasticCollision2D { + + private ElasticCollision2D() { + // utility class + } + + public static class Body { + public double x, y; + public double vx, vy; + public double mass, radius; + + public Body(double x, double y, double vx, double vy, double mass, double radius) { + this.x = x; + this.y = y; + this.vx = vx; + this.vy = vy; + this.mass = mass; + this.radius = radius; + } + } + + /** + * Resolve instantaneous elastic collision between two circular bodies. + * + * @param a first body + * @param b second body + */ + public static void resolveCollision(Body a, Body b) { + double dx = b.x - a.x; + double dy = b.y - a.y; + double dist = Math.hypot(dx, dy); + if (dist == 0) return; // overlapping + + double nx = dx / dist; + double ny = dy / dist; + + // relative velocity along normal + double rv = (b.vx - a.vx) * nx + (b.vy - a.vy) * ny; + if (rv > 0) return; // moving apart + + // Correct 2D impulse with masses + double m1 = a.mass; + double m2 = b.mass; + + double j = -(1 + 1.0) * rv / (1.0 / m1 + 1.0 / m2); + + // impulse vector along normal + double impulseX = j * nx; + double impulseY = j * ny; + + a.vx -= impulseX / m1; + a.vy -= impulseY / m1; + b.vx += impulseX / m2; + b.vy += impulseY / m2; + } +} diff --git a/src/test/java/com/thealgorithms/physics/ElasticCollision2DTest.java b/src/test/java/com/thealgorithms/physics/ElasticCollision2DTest.java new file mode 100644 index 000000000000..480e5da23db1 --- /dev/null +++ b/src/test/java/com/thealgorithms/physics/ElasticCollision2DTest.java @@ -0,0 +1,91 @@ +package com.thealgorithms.physics; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class ElasticCollision2DTest { + + @Test + void testEqualMassHeadOnCollision() { + ElasticCollision2D.Body a = new ElasticCollision2D.Body(0, 0, 1, 0, 1.0, 0.5); + ElasticCollision2D.Body b = new ElasticCollision2D.Body(1, 0, -1, 0, 1.0, 0.5); + + ElasticCollision2D.resolveCollision(a, b); + + assertEquals(-1.0, a.vx, 1e-6); + assertEquals(0.0, a.vy, 1e-6); + assertEquals(1.0, b.vx, 1e-6); + assertEquals(0.0, b.vy, 1e-6); + } + + @Test + void testUnequalMassHeadOnCollision() { + ElasticCollision2D.Body a = new ElasticCollision2D.Body(0, 0, 2, 0, 2.0, 0.5); + ElasticCollision2D.Body b = new ElasticCollision2D.Body(1, 0, -1, 0, 1.0, 0.5); + + ElasticCollision2D.resolveCollision(a, b); + + // 1D head-on collision results + assertEquals(0.0, a.vx, 1e-6); + assertEquals(0.0, a.vy, 1e-6); + assertEquals(3.0, b.vx, 1e-6); + assertEquals(0.0, b.vy, 1e-6); + } + + @Test + void testMovingApartNoCollision() { + ElasticCollision2D.Body a = new ElasticCollision2D.Body(0, 0, -1, 0, 1.0, 0.5); + ElasticCollision2D.Body b = new ElasticCollision2D.Body(1, 0, 1, 0, 1.0, 0.5); + + ElasticCollision2D.resolveCollision(a, b); + + assertEquals(-1.0, a.vx, 1e-6); + assertEquals(0.0, a.vy, 1e-6); + assertEquals(1.0, b.vx, 1e-6); + assertEquals(0.0, b.vy, 1e-6); + } + + @Test + void testGlancingCollision() { + ElasticCollision2D.Body a = new ElasticCollision2D.Body(0, 0, 1, 1, 1.0, 0.5); + ElasticCollision2D.Body b = new ElasticCollision2D.Body(1, 1, -1, -0.5, 1.0, 0.5); + + ElasticCollision2D.resolveCollision(a, b); + + // Ensure relative velocity along normal is reversed + double nx = (b.x - a.x) / Math.hypot(b.x - a.x, b.y - a.y); + double ny = (b.y - a.y) / Math.hypot(b.x - a.x, b.y - a.y); + double relVelAfter = (b.vx - a.vx) * nx + (b.vy - a.vy) * ny; + + assertTrue(relVelAfter > 0); + } + + @Test + void testOverlappingBodies() { + ElasticCollision2D.Body a = new ElasticCollision2D.Body(0, 0, 1, 0, 1.0, 0.5); + ElasticCollision2D.Body b = new ElasticCollision2D.Body(0, 0, -1, 0, 1.0, 0.5); + + ElasticCollision2D.resolveCollision(a, b); + + // Should not crash, velocities may remain unchanged + assertEquals(1.0, a.vx, 1e-6); + assertEquals(0.0, a.vy, 1e-6); + assertEquals(-1.0, b.vx, 1e-6); + assertEquals(0.0, b.vy, 1e-6); + } + + @Test + void testStationaryBodyHit() { + ElasticCollision2D.Body a = new ElasticCollision2D.Body(0, 0, 2, 0, 1.0, 0.5); + ElasticCollision2D.Body b = new ElasticCollision2D.Body(1, 0, 0, 0, 1.0, 0.5); + + ElasticCollision2D.resolveCollision(a, b); + + assertEquals(0.0, a.vx, 1e-6); + assertEquals(0.0, a.vy, 1e-6); + assertEquals(2.0, b.vx, 1e-6); + assertEquals(0.0, b.vy, 1e-6); + } +} From e34d6542f7da46585b4f1dcd7e46e62ca6d258ce Mon Sep 17 00:00:00 2001 From: the-yash-rajput Date: Thu, 16 Oct 2025 21:34:42 +0530 Subject: [PATCH 2/2] Fixing build issues. --- .../physics/ElasticCollision2D.java | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/thealgorithms/physics/ElasticCollision2D.java b/src/main/java/com/thealgorithms/physics/ElasticCollision2D.java index 9048a5dd4527..399c3f1e041f 100644 --- a/src/main/java/com/thealgorithms/physics/ElasticCollision2D.java +++ b/src/main/java/com/thealgorithms/physics/ElasticCollision2D.java @@ -1,15 +1,24 @@ package com.thealgorithms.physics; +/** + * 2D Elastic collision between two circular bodies + * Based on principles of conservation of momentum and kinetic energy. + * + * @author [Yash Rajput](https://github.com/the-yash-rajput) + */ public final class ElasticCollision2D { private ElasticCollision2D() { - // utility class + throw new AssertionError("No instances. Utility class"); } public static class Body { - public double x, y; - public double vx, vy; - public double mass, radius; + public double x; + public double y; + public double vx; + public double vy; + public double mass; + public double radius; public Body(double x, double y, double vx, double vy, double mass, double radius) { this.x = x; @@ -31,22 +40,28 @@ public static void resolveCollision(Body a, Body b) { double dx = b.x - a.x; double dy = b.y - a.y; double dist = Math.hypot(dx, dy); - if (dist == 0) return; // overlapping + + if (dist == 0) { + return; // overlapping + } double nx = dx / dist; double ny = dy / dist; // relative velocity along normal double rv = (b.vx - a.vx) * nx + (b.vy - a.vy) * ny; - if (rv > 0) return; // moving apart - // Correct 2D impulse with masses + if (rv > 0) { + return; // moving apart + } + + // impulse with masses double m1 = a.mass; double m2 = b.mass; double j = -(1 + 1.0) * rv / (1.0 / m1 + 1.0 / m2); - // impulse vector along normal + // impulse vector double impulseX = j * nx; double impulseY = j * ny;