WIP new vec class and tests
This commit is contained in:
parent
a022c3321e
commit
0e17c84eb9
204
cpp/src/math.hpp
204
cpp/src/math.hpp
@ -6,6 +6,209 @@
|
||||
#include <initializer_list>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <ranges>
|
||||
#include <numeric>
|
||||
|
||||
|
||||
template <typename T>
|
||||
requires std::floating_point<T>
|
||||
static inline bool equalEpsilon(const T& a, const T& b)
|
||||
{
|
||||
constexpr auto epsilon = [](){
|
||||
if constexpr (std::is_same_v<T, float>) {
|
||||
return T{1e-5};
|
||||
} else {
|
||||
return T{1e-12}; // double, long double
|
||||
}
|
||||
}();
|
||||
if (a == b) {
|
||||
// handle special cases: bit equality, Inf...
|
||||
return true;
|
||||
}
|
||||
return std::abs(a - b) < epsilon;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, size_t N>
|
||||
class vec {
|
||||
public:
|
||||
vec() : m_Array{} {}
|
||||
|
||||
template <typename... ArgsT>
|
||||
requires (std::same_as<ArgsT, T> && ...) && (sizeof...(ArgsT) == N)
|
||||
vec(ArgsT... args) : m_Array{args...} {}
|
||||
|
||||
const T& operator[](size_t index) const
|
||||
{
|
||||
// we leave run-time checks to the underlying std::array
|
||||
return m_Array[index];
|
||||
}
|
||||
|
||||
T& operator[](size_t index)
|
||||
{
|
||||
// we leave run-time checks to the underlying std::array
|
||||
return m_Array[index];
|
||||
}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &os, const vec &obj)
|
||||
{
|
||||
os << "( ";
|
||||
for (const auto& element : obj.m_Array) {
|
||||
os << element << " ";
|
||||
}
|
||||
os << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// binary operators
|
||||
//
|
||||
|
||||
friend bool operator==(const vec& a, const vec& b)
|
||||
{
|
||||
for (const auto& [u, v] : std::views::zip(a.m_Array,b.m_Array)) {
|
||||
if (!equalEpsilon(u, v)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
friend bool operator!=(const vec& a, const vec& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
friend vec operator+(const vec& a, const vec& b)
|
||||
{
|
||||
vec<T,N> c;
|
||||
std::ranges::transform(a.m_Array, b.m_Array, c.m_Array.begin(), std::plus{});
|
||||
return c;
|
||||
}
|
||||
|
||||
friend vec operator-(const vec& a, const vec& b)
|
||||
{
|
||||
vec<T,N> c;
|
||||
std::ranges::transform(a.m_Array, b.m_Array, c.m_Array.begin(), std::minus{});
|
||||
return c;
|
||||
}
|
||||
|
||||
friend vec operator*(const vec& a, const T& scalar)
|
||||
{
|
||||
vec<T,N> c;
|
||||
std::ranges::transform(a.m_Array, std::views::repeat(scalar), c.m_Array.begin(), std::multiplies{});
|
||||
return c;
|
||||
}
|
||||
|
||||
friend vec operator*(const T& scalar, const vec& a) {
|
||||
return a * scalar;
|
||||
}
|
||||
|
||||
friend vec operator/(const vec& a, const T& scalar)
|
||||
{
|
||||
vec<T,N> c;
|
||||
std::ranges::transform(a.m_Array, std::views::repeat(scalar), c.m_Array.begin(), std::divides{});
|
||||
return c;
|
||||
}
|
||||
|
||||
//
|
||||
// compound-assignment operators
|
||||
//
|
||||
|
||||
vec& operator+=(const vec& b)
|
||||
{
|
||||
vec& a = *this;
|
||||
std::ranges::transform(a.m_Array, b.m_Array, a.m_Array.begin(), std::plus{});
|
||||
return a;
|
||||
}
|
||||
|
||||
vec& operator-=(const vec& b)
|
||||
{
|
||||
vec& a = *this;
|
||||
std::ranges::transform(a.m_Array, b.m_Array, a.m_Array.begin(), std::minus{});
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Utility functions
|
||||
//
|
||||
|
||||
T LengthSquared() const
|
||||
{
|
||||
return std::transform_reduce(
|
||||
m_Array.begin(), m_Array.end(),
|
||||
T{0.0},
|
||||
std::plus{},
|
||||
[](T x){
|
||||
return x*x;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
T Length() const
|
||||
{
|
||||
return std::sqrt(LengthSquared());
|
||||
}
|
||||
|
||||
//
|
||||
// In-place vector operations
|
||||
//
|
||||
|
||||
void Normalize()
|
||||
{
|
||||
T length = Length();
|
||||
if (equalEpsilon(length, T{0}))
|
||||
return;
|
||||
std::ranges::transform(m_Array, std::views::repeat(length), m_Array.begin(), std::divides{});
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Methods returning new object
|
||||
//
|
||||
|
||||
vec GetNormalized() const
|
||||
{
|
||||
vec tmp = *this;
|
||||
tmp.Normalize();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
vec GetOrthogonal() const requires (N == 2)
|
||||
{
|
||||
vec tmp = *this;
|
||||
|
||||
std::swap(tmp.m_Array[0], tmp.m_Array[1]);
|
||||
tmp.m_Array[0] *= -1;
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
// const T& x = m_Array[0];
|
||||
// const T& y = m_Array[1];
|
||||
// const T& z = m_Array[2];
|
||||
|
||||
|
||||
private:
|
||||
std::array<T,N> m_Array;
|
||||
};
|
||||
|
||||
|
||||
using vec2 = vec<float, 2>;
|
||||
using vec3 = vec<float, 3>;
|
||||
using vec4 = vec<float, 4>;
|
||||
using dvec2 = vec<double, 2>;
|
||||
using dvec3 = vec<double, 3>;
|
||||
using dvec4 = vec<double, 4>;
|
||||
using ivec2 = vec<std::int32_t, 2>;
|
||||
using ivec3 = vec<std::int32_t, 3>;
|
||||
using ivec4 = vec<std::int32_t, 4>;
|
||||
using uvec2 = vec<std::uint32_t, 2>;
|
||||
using uvec3 = vec<std::uint32_t, 3>;
|
||||
using uvec4 = vec<std::uint32_t, 4>;
|
||||
|
||||
constexpr double EQUALITY_LIMIT = 1e-6;
|
||||
|
||||
@ -132,6 +335,7 @@ public:
|
||||
|
||||
using TilePos = Vec2D<int>;
|
||||
using WorldPos = Vec2D<float>;
|
||||
using WindowPos = Vec2D<float>;
|
||||
|
||||
struct TilePosHash {
|
||||
std::size_t operator()(const TilePos& p) const noexcept {
|
||||
|
@ -5,151 +5,391 @@
|
||||
#include <sstream>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "array.hpp"
|
||||
#include "log.hpp"
|
||||
#include "math.hpp"
|
||||
|
||||
// Vec2D Tests
|
||||
TEST(Vec2D, DefaultConstruction) {
|
||||
Vec2D<int> v;
|
||||
// Default values are uninitialized, but we can test basic functionality
|
||||
v.x = 0;
|
||||
v.y = 0;
|
||||
ASSERT_EQ(v.x, 0);
|
||||
ASSERT_EQ(v.y, 0);
|
||||
|
||||
TEST(vec, DefaultConstruction) {
|
||||
// Test that default-constucted vector
|
||||
// has all elements equal to zero
|
||||
vec3 v1;
|
||||
ASSERT_EQ(v1[0], 0.0);
|
||||
ASSERT_EQ(v1[1], 0.0);
|
||||
ASSERT_EQ(v1[2], 0.0);
|
||||
}
|
||||
|
||||
TEST(Vec2D, InitializerListConstruction) {
|
||||
Vec2D<int> v{3, 4};
|
||||
ASSERT_EQ(v.x, 3);
|
||||
ASSERT_EQ(v.y, 4);
|
||||
|
||||
Vec2D<float> vf{1.5f, 2.5f};
|
||||
ASSERT_FLOAT_EQ(vf.x, 1.5f);
|
||||
ASSERT_FLOAT_EQ(vf.y, 2.5f);
|
||||
|
||||
// Test type conversion
|
||||
Vec2D<float> vd{1, 2}; // int to float
|
||||
ASSERT_FLOAT_EQ(vd.x, 1.0f);
|
||||
ASSERT_FLOAT_EQ(vd.y, 2.0f);
|
||||
TEST(vec, GetElements) {
|
||||
// Test operator[]
|
||||
ivec3 v1{12, 34, 56};
|
||||
ASSERT_EQ(v1[0], 12);
|
||||
ASSERT_EQ(v1[1], 34);
|
||||
ASSERT_EQ(v1[2], 56);
|
||||
}
|
||||
|
||||
TEST(Vec2D, Addition) {
|
||||
Vec2D<int> a{1, 2};
|
||||
Vec2D<int> b{3, 4};
|
||||
|
||||
Vec2D<int> c = a + b;
|
||||
ASSERT_EQ(c.x, 4);
|
||||
ASSERT_EQ(c.y, 6);
|
||||
TEST(vec, equalEpsilon) {
|
||||
// Test equalEpsilon
|
||||
// TODO just an ad-hoc test,
|
||||
// can possibly fail for other machines.
|
||||
// This needs some work
|
||||
vec3 v1{1.0f, 2.0f, 3.0f};
|
||||
vec3 v2{0.999999f, 1.9999999f, 2.9999999f};
|
||||
ASSERT_EQ(v1, v2);
|
||||
}
|
||||
|
||||
TEST(vec, nonEqualEpsilon) {
|
||||
// Test operator!=
|
||||
vec3 v1{1.0f, 2.0f, 3.0f};
|
||||
vec3 v2{2.0f, 4.0f, 6.0f};
|
||||
ASSERT_NE(v1, v2);
|
||||
}
|
||||
|
||||
TEST(vec, LogPrint) {
|
||||
// Test that logger can print the vector of different types
|
||||
// and sizes
|
||||
vec2 v2(1.2f, 3.4f);
|
||||
vec3 v3(1.2f, 3.4f, 5.6f);
|
||||
vec4 v4(1.2f, 3.4f, 5.6f, 7.8f);
|
||||
dvec2 dv2(1.2, 3.4);
|
||||
dvec3 dv3(1.2, 3.4, 5.6);
|
||||
dvec4 dv4(1.2, 3.4, 5.6, 7.8);
|
||||
ivec2 iv2(1, 3);
|
||||
ivec3 iv3(1, 3, 5);
|
||||
ivec4 iv4(1, 3, 5, 7);
|
||||
uvec2 uv2(1u, 3u);
|
||||
uvec3 uv3(1u, 3u, 5u);
|
||||
uvec4 uv4(1u, 3u, 5u, 7u);
|
||||
|
||||
LOG_DEBUG("vec2 ", v2);
|
||||
LOG_DEBUG("vec3 ", v3);
|
||||
LOG_DEBUG("vec4 ", v4);
|
||||
LOG_DEBUG("dvec2 ", dv2);
|
||||
LOG_DEBUG("dvec3 ", dv3);
|
||||
LOG_DEBUG("dvec4 ", dv4);
|
||||
LOG_DEBUG("ivec2 ", iv2);
|
||||
LOG_DEBUG("ivec3 ", iv3);
|
||||
LOG_DEBUG("ivec4 ", iv4);
|
||||
LOG_DEBUG("uvec2 ", uv2);
|
||||
LOG_DEBUG("uvec3 ", uv3);
|
||||
LOG_DEBUG("uvec4 ", uv4);
|
||||
}
|
||||
|
||||
TEST(vec, Add)
|
||||
{
|
||||
// Test operator+ with float vectors
|
||||
vec3 v1{1.0f, 2.0f, 3.0f};
|
||||
vec3 v2{4.0f, 5.0f, 6.0f};
|
||||
vec3 result = v1 + v2;
|
||||
|
||||
ASSERT_FLOAT_EQ(result[0], 5.0f);
|
||||
ASSERT_FLOAT_EQ(result[1], 7.0f);
|
||||
ASSERT_FLOAT_EQ(result[2], 9.0f);
|
||||
|
||||
// Test operator+ with integer vectors
|
||||
ivec3 iv1{1, 2, 3};
|
||||
ivec3 iv2{10, 20, 30};
|
||||
ivec3 iresult = iv1 + iv2;
|
||||
|
||||
ASSERT_EQ(iresult[0], 11);
|
||||
ASSERT_EQ(iresult[1], 22);
|
||||
ASSERT_EQ(iresult[2], 33);
|
||||
|
||||
// Test that original vectors are unchanged
|
||||
ASSERT_EQ(a.x, 1);
|
||||
ASSERT_EQ(a.y, 2);
|
||||
ASSERT_EQ(b.x, 3);
|
||||
ASSERT_EQ(b.y, 4);
|
||||
ASSERT_FLOAT_EQ(v1[0], 1.0f);
|
||||
ASSERT_FLOAT_EQ(v1[1], 2.0f);
|
||||
ASSERT_FLOAT_EQ(v1[2], 3.0f);
|
||||
}
|
||||
|
||||
TEST(Vec2D, AdditionAssignment) {
|
||||
Vec2D<int> a{1, 2};
|
||||
Vec2D<int> b{3, 4};
|
||||
|
||||
a += b;
|
||||
ASSERT_EQ(a.x, 4);
|
||||
ASSERT_EQ(a.y, 6);
|
||||
|
||||
// Test that b is unchanged
|
||||
ASSERT_EQ(b.x, 3);
|
||||
ASSERT_EQ(b.y, 4);
|
||||
TEST(vec, Sub)
|
||||
{
|
||||
// Test operator- with float vectors
|
||||
vec3 v1{5.0f, 7.0f, 9.0f};
|
||||
vec3 v2{1.0f, 2.0f, 3.0f};
|
||||
vec3 result = v1 - v2;
|
||||
|
||||
ASSERT_FLOAT_EQ(result[0], 4.0f);
|
||||
ASSERT_FLOAT_EQ(result[1], 5.0f);
|
||||
ASSERT_FLOAT_EQ(result[2], 6.0f);
|
||||
|
||||
// Test operator- with integer vectors
|
||||
ivec3 iv1{30, 20, 10};
|
||||
ivec3 iv2{5, 3, 1};
|
||||
ivec3 iresult = iv1 - iv2;
|
||||
|
||||
ASSERT_EQ(iresult[0], 25);
|
||||
ASSERT_EQ(iresult[1], 17);
|
||||
ASSERT_EQ(iresult[2], 9);
|
||||
|
||||
// Test that original vectors are unchanged
|
||||
ASSERT_FLOAT_EQ(v1[0], 5.0f);
|
||||
ASSERT_FLOAT_EQ(v1[1], 7.0f);
|
||||
ASSERT_FLOAT_EQ(v1[2], 9.0f);
|
||||
|
||||
// Test subtraction resulting in negative values
|
||||
vec3 v3{1.0f, 2.0f, 3.0f};
|
||||
vec3 v4{4.0f, 5.0f, 6.0f};
|
||||
vec3 negative_result = v3 - v4;
|
||||
|
||||
ASSERT_FLOAT_EQ(negative_result[0], -3.0f);
|
||||
ASSERT_FLOAT_EQ(negative_result[1], -3.0f);
|
||||
ASSERT_FLOAT_EQ(negative_result[2], -3.0f);
|
||||
}
|
||||
|
||||
TEST(Vec2D, ScalarMultiplication) {
|
||||
Vec2D<int> v{2, 3};
|
||||
|
||||
Vec2D<int> result = v * 2.0f;
|
||||
ASSERT_EQ(result.x, 4);
|
||||
ASSERT_EQ(result.y, 6);
|
||||
|
||||
// Test with float vector
|
||||
Vec2D<float> vf{1.5f, 2.5f};
|
||||
Vec2D<float> resultf = vf * 2.0f;
|
||||
ASSERT_FLOAT_EQ(resultf.x, 3.0f);
|
||||
ASSERT_FLOAT_EQ(resultf.y, 5.0f);
|
||||
TEST(vec, ScalarMultiplication)
|
||||
{
|
||||
// Test scalar * vector with float vectors
|
||||
vec3 v1{2.0f, 3.0f, 4.0f};
|
||||
vec3 result = v1 * 2.5f;
|
||||
|
||||
ASSERT_FLOAT_EQ(result[0], 5.0f);
|
||||
ASSERT_FLOAT_EQ(result[1], 7.5f);
|
||||
ASSERT_FLOAT_EQ(result[2], 10.0f);
|
||||
|
||||
// Test scalar * vector with integer vectors
|
||||
ivec3 iv1{3, 5, 7};
|
||||
ivec3 iresult = iv1 * 2;
|
||||
|
||||
ASSERT_EQ(iresult[0], 6);
|
||||
ASSERT_EQ(iresult[1], 10);
|
||||
ASSERT_EQ(iresult[2], 14);
|
||||
|
||||
// Test that original vector is unchanged
|
||||
ASSERT_FLOAT_EQ(v1[0], 2.0f);
|
||||
ASSERT_FLOAT_EQ(v1[1], 3.0f);
|
||||
ASSERT_FLOAT_EQ(v1[2], 4.0f);
|
||||
|
||||
// Test multiplication by zero
|
||||
vec3 v2{1.0f, 2.0f, 3.0f};
|
||||
vec3 zero_result = v2 * 0.0f;
|
||||
|
||||
ASSERT_FLOAT_EQ(zero_result[0], 0.0f);
|
||||
ASSERT_FLOAT_EQ(zero_result[1], 0.0f);
|
||||
ASSERT_FLOAT_EQ(zero_result[2], 0.0f);
|
||||
|
||||
// Test multiplication by negative scalar (and different ordering)
|
||||
vec3 v3{1.0f, -2.0f, 3.0f};
|
||||
vec3 negative_result = -2.0f * v3;
|
||||
|
||||
ASSERT_FLOAT_EQ(negative_result[0], -2.0f);
|
||||
ASSERT_FLOAT_EQ(negative_result[1], 4.0f);
|
||||
ASSERT_FLOAT_EQ(negative_result[2], -6.0f);
|
||||
}
|
||||
|
||||
TEST(Vec2D, Normalization) {
|
||||
Vec2D<float> v{3.0f, 4.0f}; // Length = 5
|
||||
|
||||
v.normalize();
|
||||
ASSERT_FLOAT_EQ(v.x, 0.6f);
|
||||
ASSERT_FLOAT_EQ(v.y, 0.8f);
|
||||
|
||||
// Check that length is approximately 1
|
||||
float length = sqrt(v.x * v.x + v.y * v.y);
|
||||
ASSERT_NEAR(length, 1.0f, 1e-6f);
|
||||
TEST(vec, ScalarDivision)
|
||||
{
|
||||
// Test vector / scalar with float vectors
|
||||
vec3 v1{10.0f, 15.0f, 20.0f};
|
||||
vec3 result = v1 / 2.5f;
|
||||
|
||||
ASSERT_FLOAT_EQ(result[0], 4.0f);
|
||||
ASSERT_FLOAT_EQ(result[1], 6.0f);
|
||||
ASSERT_FLOAT_EQ(result[2], 8.0f);
|
||||
|
||||
// Test vector / scalar with integer vectors
|
||||
ivec3 iv1{12, 18, 24};
|
||||
ivec3 iresult = iv1 / 2;
|
||||
|
||||
ASSERT_EQ(iresult[0], 6);
|
||||
ASSERT_EQ(iresult[1], 9);
|
||||
ASSERT_EQ(iresult[2], 12);
|
||||
|
||||
// Test that original vector is unchanged
|
||||
ASSERT_FLOAT_EQ(v1[0], 10.0f);
|
||||
ASSERT_FLOAT_EQ(v1[1], 15.0f);
|
||||
ASSERT_FLOAT_EQ(v1[2], 20.0f);
|
||||
|
||||
// Test division by negative scalar
|
||||
vec3 v2{6.0f, -9.0f, 12.0f};
|
||||
vec3 negative_result = v2 / -3.0f;
|
||||
|
||||
ASSERT_FLOAT_EQ(negative_result[0], -2.0f);
|
||||
ASSERT_FLOAT_EQ(negative_result[1], 3.0f);
|
||||
ASSERT_FLOAT_EQ(negative_result[2], -4.0f);
|
||||
|
||||
// Test division by fractional scalar
|
||||
vec3 v3{1.0f, 2.0f, 3.0f};
|
||||
vec3 fractional_result = v3 / 0.5f;
|
||||
|
||||
ASSERT_FLOAT_EQ(fractional_result[0], 2.0f);
|
||||
ASSERT_FLOAT_EQ(fractional_result[1], 4.0f);
|
||||
ASSERT_FLOAT_EQ(fractional_result[2], 6.0f);
|
||||
}
|
||||
|
||||
TEST(Vec2D, NormalizedCopy) {
|
||||
Vec2D<float> v{3.0f, 4.0f};
|
||||
Vec2D<float> normalized = v.normalized();
|
||||
TEST(vec, AdditionAssignment)
|
||||
{
|
||||
// Test operator+= with float vectors
|
||||
vec3 v1{1.0f, 2.0f, 3.0f};
|
||||
vec3 v2{4.0f, 5.0f, 6.0f};
|
||||
v1 += v2;
|
||||
|
||||
ASSERT_FLOAT_EQ(v1[0], 5.0f);
|
||||
ASSERT_FLOAT_EQ(v1[1], 7.0f);
|
||||
ASSERT_FLOAT_EQ(v1[2], 9.0f);
|
||||
|
||||
// Test that v2 is unchanged
|
||||
ASSERT_FLOAT_EQ(v2[0], 4.0f);
|
||||
ASSERT_FLOAT_EQ(v2[1], 5.0f);
|
||||
ASSERT_FLOAT_EQ(v2[2], 6.0f);
|
||||
|
||||
// Test operator+= with integer vectors
|
||||
ivec3 iv1{10, 20, 30};
|
||||
ivec3 iv2{1, 2, 3};
|
||||
iv1 += iv2;
|
||||
|
||||
ASSERT_EQ(iv1[0], 11);
|
||||
ASSERT_EQ(iv1[1], 22);
|
||||
ASSERT_EQ(iv1[2], 33);
|
||||
|
||||
// Test chaining
|
||||
vec3 v3{1.0f, 1.0f, 1.0f};
|
||||
vec3 v4{2.0f, 2.0f, 2.0f};
|
||||
vec3 v5{3.0f, 3.0f, 3.0f};
|
||||
v3 += v4 += v5;
|
||||
|
||||
ASSERT_FLOAT_EQ(v3[0], 6.0f);
|
||||
ASSERT_FLOAT_EQ(v3[1], 6.0f);
|
||||
ASSERT_FLOAT_EQ(v3[2], 6.0f);
|
||||
ASSERT_FLOAT_EQ(v4[0], 5.0f);
|
||||
ASSERT_FLOAT_EQ(v4[1], 5.0f);
|
||||
ASSERT_FLOAT_EQ(v4[2], 5.0f);
|
||||
}
|
||||
|
||||
// Original should be unchanged
|
||||
ASSERT_FLOAT_EQ(v.x, 3.0f);
|
||||
ASSERT_FLOAT_EQ(v.y, 4.0f);
|
||||
TEST(vec, SubtractionAssignment)
|
||||
{
|
||||
// Test operator-= with float vectors
|
||||
vec3 v1{10.0f, 15.0f, 20.0f};
|
||||
vec3 v2{3.0f, 5.0f, 7.0f};
|
||||
v1 -= v2;
|
||||
|
||||
ASSERT_FLOAT_EQ(v1[0], 7.0f);
|
||||
ASSERT_FLOAT_EQ(v1[1], 10.0f);
|
||||
ASSERT_FLOAT_EQ(v1[2], 13.0f);
|
||||
|
||||
// Test that v2 is unchanged
|
||||
ASSERT_FLOAT_EQ(v2[0], 3.0f);
|
||||
ASSERT_FLOAT_EQ(v2[1], 5.0f);
|
||||
ASSERT_FLOAT_EQ(v2[2], 7.0f);
|
||||
|
||||
// Test operator-= with integer vectors
|
||||
ivec3 iv1{50, 40, 30};
|
||||
ivec3 iv2{5, 10, 15};
|
||||
iv1 -= iv2;
|
||||
|
||||
ASSERT_EQ(iv1[0], 45);
|
||||
ASSERT_EQ(iv1[1], 30);
|
||||
ASSERT_EQ(iv1[2], 15);
|
||||
|
||||
// Test subtraction resulting in negative values
|
||||
vec3 v3{1.0f, 2.0f, 3.0f};
|
||||
vec3 v4{4.0f, 5.0f, 6.0f};
|
||||
v3 -= v4;
|
||||
|
||||
ASSERT_FLOAT_EQ(v3[0], -3.0f);
|
||||
ASSERT_FLOAT_EQ(v3[1], -3.0f);
|
||||
ASSERT_FLOAT_EQ(v3[2], -3.0f);
|
||||
}
|
||||
|
||||
TEST(vec, LengthSquared)
|
||||
{
|
||||
// Test LengthSquared with float vectors
|
||||
vec3 v1{3.0f, 4.0f, 0.0f};
|
||||
ASSERT_FLOAT_EQ(v1.LengthSquared(), 25.0f); // 3² + 4² + 0² = 25
|
||||
|
||||
vec2 v2{1.0f, 1.0f};
|
||||
ASSERT_FLOAT_EQ(v2.LengthSquared(), 2.0f); // 1² + 1² = 2
|
||||
|
||||
// Test with zero vector
|
||||
vec3 zero{0.0f, 0.0f, 0.0f};
|
||||
ASSERT_FLOAT_EQ(zero.LengthSquared(), 0.0f);
|
||||
}
|
||||
|
||||
TEST(vec, Length)
|
||||
{
|
||||
// Test Length with float vectors
|
||||
vec3 v1{3.0f, 4.0f, 0.0f};
|
||||
ASSERT_FLOAT_EQ(v1.Length(), 5.0f); // sqrt(3² + 4² + 0²) = 5
|
||||
|
||||
vec2 v2{1.0f, 1.0f};
|
||||
ASSERT_NEAR(v2.Length(), 1.414213f, 1e-5f); // sqrt(2) ≈ 1.414213
|
||||
|
||||
// Test with zero vector
|
||||
vec3 zero{0.0f, 0.0f, 0.0f};
|
||||
ASSERT_FLOAT_EQ(zero.Length(), 0.0f);
|
||||
}
|
||||
|
||||
TEST(vec, Normalize)
|
||||
{
|
||||
// Test Normalize with float vectors
|
||||
vec3 v1{3.0f, 4.0f, 0.0f};
|
||||
v1.Normalize();
|
||||
|
||||
ASSERT_FLOAT_EQ(v1[0], 0.6f); // 3/5
|
||||
ASSERT_FLOAT_EQ(v1[1], 0.8f); // 4/5
|
||||
ASSERT_FLOAT_EQ(v1[2], 0.0f);
|
||||
ASSERT_NEAR(v1.Length(), 1.0f, 1e-6f);
|
||||
|
||||
// Test with zero vector (may produce NaN - implementation dependent)
|
||||
vec3 zero{0.0f, 0.0f, 0.0f};
|
||||
zero.Normalize();
|
||||
// Check if result is NaN (which is expected for zero vector normalization)
|
||||
ASSERT_TRUE(zero[0] == 0.0f);
|
||||
ASSERT_TRUE(zero[1] == 0.0f);
|
||||
ASSERT_TRUE(zero[2] == 0.0f);
|
||||
}
|
||||
|
||||
TEST(vec, GetNormalized)
|
||||
{
|
||||
// Test GetNormalized with float vectors
|
||||
const vec3 v1{3.0f, 4.0f, 0.0f};
|
||||
vec3 normalized = v1.GetNormalized();
|
||||
|
||||
// Original vector should be unchanged
|
||||
ASSERT_FLOAT_EQ(v1[0], 3.0f);
|
||||
ASSERT_FLOAT_EQ(v1[1], 4.0f);
|
||||
ASSERT_FLOAT_EQ(v1[2], 0.0f);
|
||||
|
||||
// Normalized copy should be unit length
|
||||
ASSERT_FLOAT_EQ(normalized.x, 0.6f);
|
||||
ASSERT_FLOAT_EQ(normalized.y, 0.8f);
|
||||
|
||||
float length =
|
||||
sqrt(normalized.x * normalized.x + normalized.y * normalized.y);
|
||||
ASSERT_NEAR(length, 1.0f, 1e-6f);
|
||||
ASSERT_FLOAT_EQ(normalized[0], 0.6f); // 3/5
|
||||
ASSERT_FLOAT_EQ(normalized[1], 0.8f); // 4/5
|
||||
ASSERT_FLOAT_EQ(normalized[2], 0.0f);
|
||||
ASSERT_NEAR(normalized.Length(), 1.0f, 1e-6f);
|
||||
|
||||
// Test with zero vector
|
||||
vec3 zero{0.0f, 0.0f, 0.0f};
|
||||
vec3 zero_normalized = zero.GetNormalized();
|
||||
|
||||
ASSERT_FLOAT_EQ(zero_normalized[0], 0.0f);
|
||||
ASSERT_FLOAT_EQ(zero_normalized[1], 0.0f);
|
||||
ASSERT_FLOAT_EQ(zero_normalized[2], 0.0f);
|
||||
|
||||
// Original zero vector should be unchanged
|
||||
ASSERT_FLOAT_EQ(zero[0], 0.0f);
|
||||
ASSERT_FLOAT_EQ(zero[1], 0.0f);
|
||||
ASSERT_FLOAT_EQ(zero[2], 0.0f);
|
||||
}
|
||||
|
||||
TEST(Vec2D, ZeroVectorNormalization) {
|
||||
Vec2D<float> v{0.0f, 0.0f};
|
||||
|
||||
v.normalize();
|
||||
ASSERT_FLOAT_EQ(v.x, 0.0f);
|
||||
ASSERT_FLOAT_EQ(v.y, 0.0f);
|
||||
|
||||
// Test normalized() as well
|
||||
Vec2D<float> v2{0.0f, 0.0f};
|
||||
Vec2D<float> normalized = v2.normalized();
|
||||
ASSERT_FLOAT_EQ(normalized.x, 0.0f);
|
||||
ASSERT_FLOAT_EQ(normalized.y, 0.0f);
|
||||
TEST(vec, GetOrthogonal)
|
||||
{
|
||||
const vec2 v1{5.0f, 1.0f};
|
||||
auto v2 = v1.GetOrthogonal();
|
||||
ASSERT_FLOAT_EQ(v2[0], -1.0f);
|
||||
ASSERT_FLOAT_EQ(v2[1], 5.0f);
|
||||
}
|
||||
|
||||
TEST(Vec2D, VerySmallVectorNormalization) {
|
||||
Vec2D<float> v{1e-7f, 1e-7f}; // Very small vector
|
||||
|
||||
v.normalize();
|
||||
// Should be treated as zero vector
|
||||
ASSERT_FLOAT_EQ(v.x, 0.0f);
|
||||
ASSERT_FLOAT_EQ(v.y, 0.0f);
|
||||
}
|
||||
|
||||
TEST(Vec2D, OutputOperator) {
|
||||
Vec2D<int> v{42, 24};
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << v;
|
||||
ASSERT_EQ(oss.str(), "( 42, 24)");
|
||||
}
|
||||
|
||||
TEST(Vec2D, ChainedOperations) {
|
||||
Vec2D<float> a{1.0f, 2.0f};
|
||||
Vec2D<float> b{3.0f, 4.0f};
|
||||
TEST(vec, ChainedOperations) {
|
||||
vec2 a{1.0f, 2.0f};
|
||||
vec2 b{3.0f, 4.0f};
|
||||
|
||||
// Test chaining: (a + b) * 2.0f
|
||||
Vec2D<float> result = (a + b) * 2.0f;
|
||||
ASSERT_FLOAT_EQ(result.x, 8.0f);
|
||||
ASSERT_FLOAT_EQ(result.y, 12.0f);
|
||||
auto result = (a + b) * 2.0f;
|
||||
ASSERT_FLOAT_EQ(result[0], 8.0f);
|
||||
ASSERT_FLOAT_EQ(result[1], 12.0f);
|
||||
|
||||
// Test chaining with assignment
|
||||
a += b;
|
||||
a = a * 0.5f;
|
||||
ASSERT_FLOAT_EQ(a.x, 2.0f);
|
||||
ASSERT_FLOAT_EQ(a.y, 3.0f);
|
||||
ASSERT_FLOAT_EQ(a[0], 2.0f);
|
||||
ASSERT_FLOAT_EQ(a[1], 3.0f);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user