diff --git a/src/main/java/com/thealgorithms/physics/GroundToGroundProjectileMotion.java b/src/main/java/com/thealgorithms/physics/GroundToGroundProjectileMotion.java new file mode 100644 index 000000000000..a8d7ac63a186 --- /dev/null +++ b/src/main/java/com/thealgorithms/physics/GroundToGroundProjectileMotion.java @@ -0,0 +1,90 @@ +package com.thealgorithms.physics; + +/** + * Ground to ground projectile motion calculator + * + * Ground to ground projectile motion is when a projectile's trajectory + * starts at the ground, reaches the apex, then falls back on the ground. + * + * @author [Yash Rajput](https://github.com/the-yash-rajput) + */ +public final class GroundToGroundProjectileMotion { + + private GroundToGroundProjectileMotion() { + throw new AssertionError("No instances."); + } + + /** Standard gravity constant (m/s^2) */ + private static final double GRAVITY = 9.80665; + + /** + * Convert degrees to radians + * + * @param degrees Angle in degrees + * @return Angle in radians + */ + private static double degreesToRadians(double degrees) { + return degrees * (Math.PI / 180.0); + } + + /** + * Calculate the time of flight + * + * @param initialVelocity The starting velocity of the projectile (m/s) + * @param angle The angle that the projectile is launched at in degrees + * @return The time that the projectile is in the air for (seconds) + */ + public static double timeOfFlight(double initialVelocity, double angle) { + return timeOfFlight(initialVelocity, angle, GRAVITY); + } + + /** + * Calculate the time of flight with custom gravity + * + * @param initialVelocity The starting velocity of the projectile (m/s) + * @param angle The angle that the projectile is launched at in degrees + * @param gravity The value used for the gravity constant (m/s^2) + * @return The time that the projectile is in the air for (seconds) + */ + public static double timeOfFlight(double initialVelocity, double angle, double gravity) { + double viy = initialVelocity * Math.sin(degreesToRadians(angle)); + return 2.0 * viy / gravity; + } + + /** + * Calculate the horizontal distance that the projectile travels + * + * @param initialVelocity The starting velocity of the projectile (m/s) + * @param angle The angle that the projectile is launched at in degrees + * @param time The time that the projectile is in the air (seconds) + * @return Horizontal distance that the projectile travels (meters) + */ + public static double horizontalRange(double initialVelocity, double angle, double time) { + double vix = initialVelocity * Math.cos(degreesToRadians(angle)); + return vix * time; + } + + /** + * Calculate the max height of the projectile + * + * @param initialVelocity The starting velocity of the projectile (m/s) + * @param angle The angle that the projectile is launched at in degrees + * @return The max height that the projectile reaches (meters) + */ + public static double maxHeight(double initialVelocity, double angle) { + return maxHeight(initialVelocity, angle, GRAVITY); + } + + /** + * Calculate the max height of the projectile with custom gravity + * + * @param initialVelocity The starting velocity of the projectile (m/s) + * @param angle The angle that the projectile is launched at in degrees + * @param gravity The value used for the gravity constant (m/s^2) + * @return The max height that the projectile reaches (meters) + */ + public static double maxHeight(double initialVelocity, double angle, double gravity) { + double viy = initialVelocity * Math.sin(degreesToRadians(angle)); + return Math.pow(viy, 2) / (2.0 * gravity); + } +} diff --git a/src/test/java/com/thealgorithms/physics/GroundToGroundProjectileMotionTest.java b/src/test/java/com/thealgorithms/physics/GroundToGroundProjectileMotionTest.java new file mode 100644 index 000000000000..9d7e11777983 --- /dev/null +++ b/src/test/java/com/thealgorithms/physics/GroundToGroundProjectileMotionTest.java @@ -0,0 +1,149 @@ +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.DisplayName; +import org.junit.jupiter.api.Test; + +/** + * JUnit test class for GroundToGroundProjectileMotion + * + * Contains unit tests for projectile motion calculations using JUnit 5 + */ +public class GroundToGroundProjectileMotionTest { + + private static final double EPSILON = 0.001; // Tolerance for floating point comparison + + @Test + @DisplayName("Test time of flight calculation") + public void testTimeOfFlight() { + // Arrange + double initialVelocity = 5.0; + double angle = 40.0; + double expectedTimeOfFlight = 0.655; + + // Act + double flightTimeOutput = GroundToGroundProjectileMotion.timeOfFlight(initialVelocity, angle); + flightTimeOutput = Math.round(flightTimeOutput * 1000.0) / 1000.0; + + // Assert + assertEquals(expectedTimeOfFlight, flightTimeOutput, EPSILON, "Time of flight should be " + expectedTimeOfFlight + " seconds"); + + System.out.println("Projectile Flight Time Test"); + System.out.println("Input Initial Velocity: " + initialVelocity + " m/s"); + System.out.println("Input Angle: " + angle + " degrees"); + System.out.println("Expected Output: " + expectedTimeOfFlight + " seconds"); + System.out.println("Actual Output: " + flightTimeOutput + " seconds"); + System.out.println("TEST PASSED\n"); + } + + @Test + @DisplayName("Test horizontal range calculation") + public void testHorizontalRange() { + // Arrange + double initialVelocity = 5.0; + double angle = 40.0; + double flightTime = 0.655; + double expectedHorizontalRange = 2.51; + + // Act + double horizontalRangeOutput = GroundToGroundProjectileMotion.horizontalRange(initialVelocity, angle, flightTime); + horizontalRangeOutput = Math.round(horizontalRangeOutput * 100.0) / 100.0; + + // Assert + assertEquals(expectedHorizontalRange, horizontalRangeOutput, EPSILON, "Horizontal range should be " + expectedHorizontalRange + " meters"); + + System.out.println("Projectile Horizontal Range Test"); + System.out.println("Input Initial Velocity: " + initialVelocity + " m/s"); + System.out.println("Input Angle: " + angle + " degrees"); + System.out.println("Input Time Of Flight: " + flightTime + " seconds"); + System.out.println("Expected Output: " + expectedHorizontalRange + " meters"); + System.out.println("Actual Output: " + horizontalRangeOutput + " meters"); + System.out.println("TEST PASSED\n"); + } + + @Test + @DisplayName("Test max height calculation") + public void testMaxHeight() { + // Arrange + double initialVelocity = 5.0; + double angle = 40.0; + double expectedMaxHeight = 0.527; // Updated to match actual calculation + + // Act + double maxHeightOutput = GroundToGroundProjectileMotion.maxHeight(initialVelocity, angle); + maxHeightOutput = Math.round(maxHeightOutput * 1000.0) / 1000.0; + + // Assert + assertEquals(expectedMaxHeight, maxHeightOutput, EPSILON, "Max height should be " + expectedMaxHeight + " meters"); + + System.out.println("Projectile Max Height Test"); + System.out.println("Input Initial Velocity: " + initialVelocity + " m/s"); + System.out.println("Input Angle: " + angle + " degrees"); + System.out.println("Expected Output: " + expectedMaxHeight + " meters"); + System.out.println("Actual Output: " + maxHeightOutput + " meters"); + System.out.println("TEST PASSED\n"); + } + + @Test + @DisplayName("Test time of flight with custom gravity") + public void testTimeOfFlightWithCustomGravity() { + // Arrange + double initialVelocity = 10.0; + double angle = 45.0; + double customGravity = 1.62; // Moon gravity (m/s^2) + + // Act + double flightTime = GroundToGroundProjectileMotion.timeOfFlight(initialVelocity, angle, customGravity); + + // Assert + assertTrue(flightTime > 0, "Flight time should be positive"); + assertTrue(flightTime > 8.0, "Flight time on moon should be longer than on Earth"); + + System.out.println("Custom Gravity Test (Moon)"); + System.out.println("Input Initial Velocity: " + initialVelocity + " m/s"); + System.out.println("Input Angle: " + angle + " degrees"); + System.out.println("Gravity: " + customGravity + " m/s^2"); + System.out.println("Flight Time: " + flightTime + " seconds"); + System.out.println("TEST PASSED\n"); + } + + @Test + @DisplayName("Test projectile at 90 degrees (straight up)") + public void testVerticalProjectile() { + // Arrange + double initialVelocity = 20.0; + double angle = 90.0; + + // Act + double horizontalRange = GroundToGroundProjectileMotion.horizontalRange(initialVelocity, angle, 1.0); + + // Assert + assertEquals(0.0, horizontalRange, EPSILON, "Horizontal range should be zero for vertical launch"); + + System.out.println("Vertical Projectile Test"); + System.out.println("Input Angle: " + angle + " degrees"); + System.out.println("Horizontal Range: " + horizontalRange + " meters"); + System.out.println("TEST PASSED\n"); + } + + @Test + @DisplayName("Test projectile at 0 degrees (horizontal)") + public void testHorizontalProjectile() { + // Arrange + double initialVelocity = 15.0; + double angle = 0.0; + + // Act + double maxHeight = GroundToGroundProjectileMotion.maxHeight(initialVelocity, angle); + + // Assert + assertEquals(0.0, maxHeight, EPSILON, "Max height should be zero for horizontal launch"); + + System.out.println("Horizontal Projectile Test"); + System.out.println("Input Angle: " + angle + " degrees"); + System.out.println("Max Height: " + maxHeight + " meters"); + System.out.println("TEST PASSED\n"); + } +}