From fbc34a0bddc2d7d34c73afa0a313e097a1ec5b07 Mon Sep 17 00:00:00 2001 From: Alex Selimov Date: Wed, 16 Apr 2025 12:05:23 -0400 Subject: [PATCH] Update scale to return new vector and template out the norm functions --- include/vec3.h | 17 +++++---- tests/unit_tests/vec_test.cpp | 67 ++++++++++++++++++++++++++--------- 2 files changed, 61 insertions(+), 23 deletions(-) diff --git a/include/vec3.h b/include/vec3.h index ec65c0b..6e37d1e 100644 --- a/include/vec3.h +++ b/include/vec3.h @@ -2,6 +2,7 @@ #define VEC3_H #include +#include template struct Vec3 { T x; T y; @@ -15,11 +16,7 @@ template struct Vec3 { return {x - other.x, y - other.y, z - other.z}; }; - inline void scale(T scalar) { - x *= scalar; - y *= scalar; - z *= scalar; - }; + inline Vec3 scale(T scalar) { return {x * scalar, y * scalar, z * scalar}; }; inline T dot(Vec3 other) const { return x * other.x + y * other.y + z * other.z; @@ -30,9 +27,15 @@ template struct Vec3 { x * other.y - y * other.x}; } - inline double squared_norm2() const { return x * x + y * y + z * z; } + inline T squared_norm2() const { return x * x + y * y + z * z; } - inline double norm2() const { return std::sqrt(squared_norm2()); } + inline T norm2() const { return std::sqrt(squared_norm2()); } + + inline Vec3 normalized() { + // Add epsilon to the norm for stability when the norm is 0 + T norm = std::max(norm2(), std::numeric_limits::epsilon()); + return {x / norm, y / norm, z / norm}; + } }; #endif diff --git a/tests/unit_tests/vec_test.cpp b/tests/unit_tests/vec_test.cpp index 4fa9d95..96f6689 100644 --- a/tests/unit_tests/vec_test.cpp +++ b/tests/unit_tests/vec_test.cpp @@ -69,36 +69,31 @@ TEST_F(Vec3Test, Subtraction) { // Test vector scaling TEST_F(Vec3Test, Scale) { // Test integer scaling - Vec3 int_vec_scaled = int_vec1; - int_vec_scaled.scale(2); + Vec3 int_vec_scaled = int_vec1.scale(2); EXPECT_EQ(int_vec_scaled.x, 2); EXPECT_EQ(int_vec_scaled.y, 4); EXPECT_EQ(int_vec_scaled.z, 6); // Test float scaling - Vec3 float_vec_scaled = float_vec1; - float_vec_scaled.scale(2.0f); + Vec3 float_vec_scaled = float_vec1.scale(2.0f); EXPECT_FLOAT_EQ(float_vec_scaled.x, 3.0f); EXPECT_FLOAT_EQ(float_vec_scaled.y, 5.0f); EXPECT_FLOAT_EQ(float_vec_scaled.z, 7.0f); // Test double scaling - Vec3 double_vec_scaled = double_vec1; - double_vec_scaled.scale(2.0); + Vec3 double_vec_scaled = double_vec1.scale(2.0); EXPECT_DOUBLE_EQ(double_vec_scaled.x, 3.0); EXPECT_DOUBLE_EQ(double_vec_scaled.y, 5.0); EXPECT_DOUBLE_EQ(double_vec_scaled.z, 7.0); // Test scaling by zero - Vec3 zero_scaled = float_vec1; - zero_scaled.scale(0.0f); + Vec3 zero_scaled = float_vec1.scale(0.0f); EXPECT_FLOAT_EQ(zero_scaled.x, 0.0f); EXPECT_FLOAT_EQ(zero_scaled.y, 0.0f); EXPECT_FLOAT_EQ(zero_scaled.z, 0.0f); // Test scaling by negative number - Vec3 neg_scaled = int_vec1; - neg_scaled.scale(-1); + Vec3 neg_scaled = int_vec1.scale(-1); EXPECT_EQ(neg_scaled.x, -1); EXPECT_EQ(neg_scaled.y, -2); EXPECT_EQ(neg_scaled.z, -3); @@ -188,7 +183,7 @@ TEST_F(Vec3Test, EdgeCases) { // Addition with max values may overflow, but we want to test the operation // works - auto max_addition = max_vec + int_vec1; + max_vec + int_vec1; // Test with min values Vec3 min_vec = {std::numeric_limits::min(), @@ -197,7 +192,7 @@ TEST_F(Vec3Test, EdgeCases) { // Subtraction with min values may underflow, but we want to test the // operation works - auto min_subtraction = min_vec - int_vec1; + min_vec - int_vec1; // Test with mixed values Vec3 mixed_vec1 = {0.0, -1.0, @@ -231,15 +226,15 @@ TEST_F(Vec3Test, NormMethods) { EXPECT_DOUBLE_EQ(int_vec1.squared_norm2(), 14.0); // 1*1 + 2*2 + 3*3 = 14 // Test norm2 for integer vector - EXPECT_DOUBLE_EQ(int_vec1.norm2(), std::sqrt(14.0)); // √14 ≈ 3.741657... + EXPECT_EQ(int_vec1.norm2(), (int)std::sqrt(14.0)); // √14 ≈ 3.741657... // Test squared_norm2 for float vector - EXPECT_DOUBLE_EQ( + EXPECT_FLOAT_EQ( float_vec1.squared_norm2(), 20.75); // 1.5*1.5 + 2.5*2.5 + 3.5*3.5 = 2.25 + 6.25 + 12.25 = 20.75 // Test norm2 for float vector - EXPECT_DOUBLE_EQ(float_vec1.norm2(), std::sqrt(20.75)); // √20.75 ≈ 4.5552... + EXPECT_FLOAT_EQ(float_vec1.norm2(), std::sqrt(20.75)); // √20.75 ≈ 4.5552... // Test squared_norm2 for double vector EXPECT_DOUBLE_EQ(double_vec1.squared_norm2(), @@ -270,7 +265,7 @@ TEST_F(Vec3Test, NormMethods) { // Test with negative components Vec3 neg_vec = {-1, -2, -3}; EXPECT_DOUBLE_EQ(neg_vec.squared_norm2(), 14.0); // (-1)² + (-2)² + (-3)² = 14 - EXPECT_DOUBLE_EQ(neg_vec.norm2(), std::sqrt(14.0)); + EXPECT_DOUBLE_EQ(neg_vec.norm2(), (int)std::sqrt(14.0)); } // Test norm methods with special values @@ -299,6 +294,46 @@ TEST_F(Vec3Test, NormEdgeCases) { EXPECT_TRUE(std::isnan(nan_vec.norm2())); } +// Test vector normalization +TEST_F(Vec3Test, Normalized) { + // Test with integer vector + auto int_normalized = int_vec1.normalized(); + int int_norm = std::sqrt(14.0); // sqrt(1^2 + 2^2 + 3^2) = sqrt(14) + EXPECT_EQ(int_normalized.x, (1 / int_norm)); + EXPECT_EQ(int_normalized.y, (2 / int_norm)); + EXPECT_EQ(int_normalized.z, (3 / int_norm)); + + // Verify that the normalized vector has length 1.0 + EXPECT_NEAR(int_normalized.norm2(), 1.0, 1e-10); + + // Test with float vector + auto float_normalized = float_vec1.normalized(); + float float_norm = std::sqrt(1.5f * 1.5f + 2.5f * 2.5f + 3.5f * 3.5f); + EXPECT_FLOAT_EQ(float_normalized.x, 1.5f / float_norm); + EXPECT_FLOAT_EQ(float_normalized.y, 2.5f / float_norm); + EXPECT_FLOAT_EQ(float_normalized.z, 3.5f / float_norm); + + // Verify that the normalized vector has length 1.0 + EXPECT_NEAR(float_normalized.norm2(), 1.0, 1e-6); + + // Test with double vector + auto double_normalized = double_vec1.normalized(); + double double_norm = std::sqrt(1.5 * 1.5 + 2.5 * 2.5 + 3.5 * 3.5); + EXPECT_DOUBLE_EQ(double_normalized.x, 1.5 / double_norm); + EXPECT_DOUBLE_EQ(double_normalized.y, 2.5 / double_norm); + EXPECT_DOUBLE_EQ(double_normalized.z, 3.5 / double_norm); + + // Verify that the normalized vector has length 1.0 + EXPECT_DOUBLE_EQ(double_normalized.norm2(), 1.0); + + // Test with unit vectors (should remain unchanged) + Vec3 unit_x = {1.0, 0.0, 0.0}; + auto normalized_unit_x = unit_x.normalized(); + EXPECT_DOUBLE_EQ(normalized_unit_x.x, 1.0); + EXPECT_DOUBLE_EQ(normalized_unit_x.y, 0.0); + EXPECT_DOUBLE_EQ(normalized_unit_x.z, 0.0); +} + // Main function that runs the tests int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv);