Update scale to return new vector and template out the norm functions

This commit is contained in:
Alex Selimov 2025-04-16 12:05:23 -04:00
parent a6c14ce0ab
commit fbc34a0bdd
2 changed files with 61 additions and 23 deletions

View File

@ -2,6 +2,7 @@
#define VEC3_H #define VEC3_H
#include <cmath> #include <cmath>
#include <limits>
template <typename T> struct Vec3 { template <typename T> struct Vec3 {
T x; T x;
T y; T y;
@ -15,11 +16,7 @@ template <typename T> struct Vec3 {
return {x - other.x, y - other.y, z - other.z}; return {x - other.x, y - other.y, z - other.z};
}; };
inline void scale(T scalar) { inline Vec3 scale(T scalar) { return {x * scalar, y * scalar, z * scalar}; };
x *= scalar;
y *= scalar;
z *= scalar;
};
inline T dot(Vec3<T> other) const { inline T dot(Vec3<T> other) const {
return x * other.x + y * other.y + z * other.z; return x * other.x + y * other.y + z * other.z;
@ -30,9 +27,15 @@ template <typename T> struct Vec3 {
x * other.y - y * other.x}; 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<T> normalized() {
// Add epsilon to the norm for stability when the norm is 0
T norm = std::max(norm2(), std::numeric_limits<T>::epsilon());
return {x / norm, y / norm, z / norm};
}
}; };
#endif #endif

View File

@ -69,36 +69,31 @@ TEST_F(Vec3Test, Subtraction) {
// Test vector scaling // Test vector scaling
TEST_F(Vec3Test, Scale) { TEST_F(Vec3Test, Scale) {
// Test integer scaling // Test integer scaling
Vec3<int> int_vec_scaled = int_vec1; Vec3<int> int_vec_scaled = int_vec1.scale(2);
int_vec_scaled.scale(2);
EXPECT_EQ(int_vec_scaled.x, 2); EXPECT_EQ(int_vec_scaled.x, 2);
EXPECT_EQ(int_vec_scaled.y, 4); EXPECT_EQ(int_vec_scaled.y, 4);
EXPECT_EQ(int_vec_scaled.z, 6); EXPECT_EQ(int_vec_scaled.z, 6);
// Test float scaling // Test float scaling
Vec3<float> float_vec_scaled = float_vec1; Vec3<float> float_vec_scaled = float_vec1.scale(2.0f);
float_vec_scaled.scale(2.0f);
EXPECT_FLOAT_EQ(float_vec_scaled.x, 3.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.y, 5.0f);
EXPECT_FLOAT_EQ(float_vec_scaled.z, 7.0f); EXPECT_FLOAT_EQ(float_vec_scaled.z, 7.0f);
// Test double scaling // Test double scaling
Vec3<double> double_vec_scaled = double_vec1; Vec3<double> double_vec_scaled = double_vec1.scale(2.0);
double_vec_scaled.scale(2.0);
EXPECT_DOUBLE_EQ(double_vec_scaled.x, 3.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.y, 5.0);
EXPECT_DOUBLE_EQ(double_vec_scaled.z, 7.0); EXPECT_DOUBLE_EQ(double_vec_scaled.z, 7.0);
// Test scaling by zero // Test scaling by zero
Vec3<float> zero_scaled = float_vec1; Vec3<float> zero_scaled = float_vec1.scale(0.0f);
zero_scaled.scale(0.0f);
EXPECT_FLOAT_EQ(zero_scaled.x, 0.0f); EXPECT_FLOAT_EQ(zero_scaled.x, 0.0f);
EXPECT_FLOAT_EQ(zero_scaled.y, 0.0f); EXPECT_FLOAT_EQ(zero_scaled.y, 0.0f);
EXPECT_FLOAT_EQ(zero_scaled.z, 0.0f); EXPECT_FLOAT_EQ(zero_scaled.z, 0.0f);
// Test scaling by negative number // Test scaling by negative number
Vec3<int> neg_scaled = int_vec1; Vec3<int> neg_scaled = int_vec1.scale(-1);
neg_scaled.scale(-1);
EXPECT_EQ(neg_scaled.x, -1); EXPECT_EQ(neg_scaled.x, -1);
EXPECT_EQ(neg_scaled.y, -2); EXPECT_EQ(neg_scaled.y, -2);
EXPECT_EQ(neg_scaled.z, -3); 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 // Addition with max values may overflow, but we want to test the operation
// works // works
auto max_addition = max_vec + int_vec1; max_vec + int_vec1;
// Test with min values // Test with min values
Vec3<int> min_vec = {std::numeric_limits<int>::min(), Vec3<int> min_vec = {std::numeric_limits<int>::min(),
@ -197,7 +192,7 @@ TEST_F(Vec3Test, EdgeCases) {
// Subtraction with min values may underflow, but we want to test the // Subtraction with min values may underflow, but we want to test the
// operation works // operation works
auto min_subtraction = min_vec - int_vec1; min_vec - int_vec1;
// Test with mixed values // Test with mixed values
Vec3<double> mixed_vec1 = {0.0, -1.0, Vec3<double> 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 EXPECT_DOUBLE_EQ(int_vec1.squared_norm2(), 14.0); // 1*1 + 2*2 + 3*3 = 14
// Test norm2 for integer vector // 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 // Test squared_norm2 for float vector
EXPECT_DOUBLE_EQ( EXPECT_FLOAT_EQ(
float_vec1.squared_norm2(), 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 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 // 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 // Test squared_norm2 for double vector
EXPECT_DOUBLE_EQ(double_vec1.squared_norm2(), EXPECT_DOUBLE_EQ(double_vec1.squared_norm2(),
@ -270,7 +265,7 @@ TEST_F(Vec3Test, NormMethods) {
// Test with negative components // Test with negative components
Vec3<int> neg_vec = {-1, -2, -3}; Vec3<int> neg_vec = {-1, -2, -3};
EXPECT_DOUBLE_EQ(neg_vec.squared_norm2(), 14.0); // (-1)² + (-2)² + (-3)² = 14 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 // Test norm methods with special values
@ -299,6 +294,46 @@ TEST_F(Vec3Test, NormEdgeCases) {
EXPECT_TRUE(std::isnan(nan_vec.norm2())); 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<double> 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 // Main function that runs the tests
int main(int argc, char **argv) { int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv); ::testing::InitGoogleTest(&argc, argv);