Skip to content

Commit a88279a

Browse files
committed
Feat: Added reflect(Vector4f normal) to Vector4f
1 parent 00134df commit a88279a

File tree

2 files changed

+180
-0
lines changed

2 files changed

+180
-0
lines changed

src/main/java/math/Vector4f.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,45 @@ public Vector4f projectOnto(Vector4f other) {
365365
return new Vector4f(other).multiplyLocal(scalar);
366366
}
367367

368+
/**
369+
* Reflects this vector around the given normal vector.
370+
*
371+
* <p>Reflection is the process of "bouncing" a vector off a surface defined by the normal vector,
372+
* such that the angle of incidence equals the angle of reflection. This is commonly used in
373+
* physics simulations, computer graphics, and geometric calculations.
374+
*
375+
* <p>The reflection formula is:
376+
*
377+
* <pre>
378+
* R = V - 2 * (V ⋅ N) * N
379+
* </pre>
380+
*
381+
* where:
382+
*
383+
* <ul>
384+
* <li><b>R</b>: Reflected vector
385+
* <li><b>V</b>: Original vector (this vector)
386+
* <li><b>N</b>: Normal vector (should be normalized to ensure accurate results)
387+
* </ul>
388+
*
389+
* This method computes the reflection of the current vector based on the specified normal and
390+
* returns the resulting vector.
391+
*
392+
* @param normal the normal vector around which this vector will be reflected. Must not be {@code
393+
* null} or a zero vector.
394+
* @return a new {@code Vector4f} instance representing the reflected vector.
395+
* @throws IllegalArgumentException if the {@code normal} vector is {@code null} or a zero vector.
396+
*/
397+
public Vector4f reflect(Vector4f normal) {
398+
if (normal == null) {
399+
throw new IllegalArgumentException("Normal cannot be null.");
400+
}
401+
if (normal.isZero()) {
402+
throw new IllegalArgumentException("Normal cannot be zero vertor.");
403+
}
404+
return this.subtract(normal.multiply(2 * this.dot(normal)));
405+
}
406+
368407
/**
369408
* Returns the x component of the vector.
370409
*

src/test/java/math/Vector4fTest.java

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2116,6 +2116,147 @@ public void testProjectOntoReturnsNewInstanceAndLeavesOriginalUntouched() {
21162116
assertEquals(0.0f, vector2.getW(), "The original vector2's W component should be unchanged.");
21172117
}
21182118

2119+
// ----------------------------------------------------------------------------------------------
2120+
// Reflect
2121+
// ----------------------------------------------------------------------------------------------
2122+
2123+
@Test
2124+
public void testReflectXAxis() {
2125+
Vector4f v1 = new Vector4f(1, 2, 3, 4);
2126+
Vector4f normal = new Vector4f(1, 0, 0, 0); // Normal of the x-axis
2127+
Vector4f expected = new Vector4f(-1, 2, 3, 4);
2128+
assertEquals(expected, v1.reflect(normal));
2129+
}
2130+
2131+
@Test
2132+
public void testReflectYAxis() {
2133+
Vector4f v1 = new Vector4f(1, 2, 3, 4);
2134+
Vector4f normal = new Vector4f(0, 1, 0, 0); // Normal of the y-axis
2135+
Vector4f expected = new Vector4f(1, -2, 3, 4);
2136+
assertEquals(expected, v1.reflect(normal));
2137+
}
2138+
2139+
@Test
2140+
public void testReflectZAxis() {
2141+
Vector4f v1 = new Vector4f(1, 2, 3, 4);
2142+
Vector4f normal = new Vector4f(0, 0, 1, 0); // Normal of the z-axis
2143+
Vector4f expected = new Vector4f(1, 2, -3, 4);
2144+
assertEquals(expected, v1.reflect(normal));
2145+
}
2146+
2147+
@Test
2148+
public void testReflectArbitraryNormal() {
2149+
// Setup
2150+
Vector4f v1 = new Vector4f(1, 1, 1, 1);
2151+
Vector4f normal = new Vector4f(1, 1, 1, 0).normalize();
2152+
2153+
// Expected reflection result calculated manually:
2154+
// Reflection formula: v' = v - 2 * (v ⋅ n) * n
2155+
// Dot product (v ⋅ n)
2156+
float dot =
2157+
v1.getX() * normal.getX()
2158+
+ v1.getY() * normal.getY()
2159+
+ v1.getZ() * normal.getZ()
2160+
+ v1.getW() * normal.getW();
2161+
2162+
// Scale the normal by 2 * dot
2163+
Vector4f scaledNormal =
2164+
new Vector4f(
2165+
2 * dot * normal.getX(),
2166+
2 * dot * normal.getY(),
2167+
2 * dot * normal.getZ(),
2168+
2 * dot * normal.getW());
2169+
2170+
// Subtract scaledNormal from v1 to get the reflected vector
2171+
Vector4f expectedReflection =
2172+
new Vector4f(
2173+
v1.getX() - scaledNormal.getX(),
2174+
v1.getY() - scaledNormal.getY(),
2175+
v1.getZ() - scaledNormal.getZ(),
2176+
v1.getW() - scaledNormal.getW());
2177+
2178+
// Actual reflection
2179+
Vector4f reflected = v1.reflect(normal);
2180+
2181+
// Assert equality
2182+
assertEquals(expectedReflection.getX(), reflected.getX(), 1e-6);
2183+
assertEquals(expectedReflection.getY(), reflected.getY(), 1e-6);
2184+
assertEquals(expectedReflection.getZ(), reflected.getZ(), 1e-6);
2185+
assertEquals(expectedReflection.getW(), reflected.getW(), 1e-6);
2186+
}
2187+
2188+
@Test
2189+
public void testReflectAlongItself() {
2190+
Vector4f v1 = new Vector4f(1, 0, 0, 0);
2191+
Vector4f normal = new Vector4f(1, 0, 0, 0);
2192+
assertEquals(v1.negate(), v1.reflect(normal));
2193+
}
2194+
2195+
@Test
2196+
public void testReflectAlongZeroVector() {
2197+
Vector4f v1 = new Vector4f(1, 2, 3, 4);
2198+
Vector4f normal = new Vector4f(0, 0, 0, 0);
2199+
assertThrows(IllegalArgumentException.class, () -> v1.reflect(normal));
2200+
}
2201+
2202+
@Test
2203+
public void testReflectAlongNullVectorThrowsIllegalArgumentException() {
2204+
Vector4f v1 = new Vector4f(1, 2, 3, 4);
2205+
assertThrows(IllegalArgumentException.class, () -> v1.reflect(null));
2206+
}
2207+
2208+
@Test
2209+
public void testReflectParallelToNormal() {
2210+
// Vector and normal are parallel
2211+
Vector4f v1 = new Vector4f(2, 0, 0, 0);
2212+
Vector4f normal = new Vector4f(1, 0, 0, 0).normalize();
2213+
2214+
// Perform reflection
2215+
Vector4f reflected = v1.reflect(normal);
2216+
2217+
// Expected result: negation of the vector
2218+
Vector4f expected = new Vector4f(-2, 0, 0, 0);
2219+
2220+
// Assert the reflected vector is correct
2221+
assertEquals(
2222+
expected, reflected, "The reflected vector is incorrect when v1 is parallel to normal.");
2223+
2224+
// Assert v1 and normal remain unchanged
2225+
Vector4f originalV1 = new Vector4f(2, 0, 0, 0);
2226+
Vector4f originalNormal = new Vector4f(1, 0, 0, 0).normalize();
2227+
assertEquals(originalV1, v1, "The original vector should remain unchanged.");
2228+
assertEquals(originalNormal, normal, "The normal vector should remain unchanged.");
2229+
}
2230+
2231+
@Test
2232+
public void testReflectCreatesNewInstanceAndDoesNotChangeOriginalVectorAndInputNormal() {
2233+
// Original vector
2234+
Vector4f v1 = new Vector4f(3, -2, 1, 0);
2235+
// Normal vector
2236+
Vector4f normal = new Vector4f(0, 1, 0, 0).normalize();
2237+
2238+
// Store copies of the original vectors for later comparison
2239+
Vector4f originalV1 = new Vector4f(v1.getX(), v1.getY(), v1.getZ(), v1.getW());
2240+
Vector4f originalNormal =
2241+
new Vector4f(normal.getX(), normal.getY(), normal.getZ(), normal.getW());
2242+
2243+
// Perform reflection
2244+
Vector4f reflected = v1.reflect(normal);
2245+
2246+
// Assert that the returned instance is a new object
2247+
assertNotSame("The method should return a new Vector4f instance.", v1, reflected);
2248+
2249+
// Assert the original vector remains unchanged
2250+
assertEquals(originalV1, v1, "The original vector should remain unchanged.");
2251+
2252+
// Assert the normal vector remains unchanged
2253+
assertEquals(originalNormal, normal, "The normal vector should remain unchanged.");
2254+
2255+
// Optional: Assert that the reflection result is correct
2256+
Vector4f expected = new Vector4f(3, 2, 1, 0); // Reflection around Y-axis
2257+
assertEquals(expected, reflected, "The reflected vector is incorrect.");
2258+
}
2259+
21192260
// ----------------------------------------------------------------------------------------------
21202261
// Set Values
21212262
// ----------------------------------------------------------------------------------------------

0 commit comments

Comments
 (0)