|
| 1 | +// |
| 2 | +// Created by nikos on 2025-10-15. |
| 3 | +// |
| 4 | +#define CATCH_CONFIG_MAIN |
| 5 | + |
| 6 | +#include <blast> |
| 7 | +#include "catch2/catch.hpp" |
| 8 | + |
| 9 | +// --------------------------------------------------------------------------- |
| 10 | +// Construction |
| 11 | +// --------------------------------------------------------------------------- |
| 12 | + |
| 13 | +TEST_CASE("Vec3 - default constructor zeroes all components", "[Math][Vec3]") { |
| 14 | + using namespace blast; |
| 15 | + Vec3 v; |
| 16 | + CHECK(v.x == 0.0); |
| 17 | + CHECK(v.y == 0.0); |
| 18 | + CHECK(v.z == 0.0); |
| 19 | +} |
| 20 | + |
| 21 | +TEST_CASE("Vec3 - parameterized constructor stores x, y, z", "[Math][Vec3]") { |
| 22 | + using namespace blast; |
| 23 | + Vec3 v(1.0, 2.0, 3.0); |
| 24 | + CHECK(v.x == 1.0); |
| 25 | + CHECK(v.y == 2.0); |
| 26 | + CHECK(v.z == 3.0); |
| 27 | +} |
| 28 | + |
| 29 | +TEST_CASE("Vec3 - copy constructor and assignment produce independent copies", "[Math][Vec3]") { |
| 30 | + using namespace blast; |
| 31 | + Vec3 v(1.0, 2.0, 3.0); |
| 32 | + |
| 33 | + Vec3 v_copy(v); |
| 34 | + CHECK(v_copy.x == v.x); |
| 35 | + CHECK(v_copy.y == v.y); |
| 36 | + CHECK(v_copy.z == v.z); |
| 37 | + |
| 38 | + Vec3 v_assign = v; |
| 39 | + CHECK(v_assign.x == v.x); |
| 40 | + CHECK(v_assign.y == v.y); |
| 41 | + CHECK(v_assign.z == v.z); |
| 42 | + |
| 43 | + // Mutating the copy must not affect the original |
| 44 | + v_copy.x = 99.0; |
| 45 | + CHECK(v.x == 1.0); |
| 46 | +} |
| 47 | + |
| 48 | +// --------------------------------------------------------------------------- |
| 49 | +// operator[] |
| 50 | +// NOTE: unsafe — no bounds check in release builds. |
| 51 | +// --------------------------------------------------------------------------- |
| 52 | + |
| 53 | +TEST_CASE("Vec3 - operator[] maps indices 0,1,2 to x,y,z (mutable)", "[Math][Vec3]") { |
| 54 | + using namespace blast; |
| 55 | + Vec3 v(1.0, 2.0, 3.0); |
| 56 | + CHECK(v[0] == v.x); |
| 57 | + CHECK(v[1] == v.y); |
| 58 | + CHECK(v[2] == v.z); |
| 59 | +} |
| 60 | + |
| 61 | +TEST_CASE("Vec3 - operator[] maps indices 0,1,2 to x,y,z (const)", "[Math][Vec3]") { |
| 62 | + using namespace blast; |
| 63 | + const Vec3 v(4.0, 5.0, 6.0); |
| 64 | + CHECK(v[0] == 4.0); |
| 65 | + CHECK(v[1] == 5.0); |
| 66 | + CHECK(v[2] == 6.0); |
| 67 | +} |
| 68 | + |
| 69 | +// --------------------------------------------------------------------------- |
| 70 | +// Equality (operator== delegates to is_close) |
| 71 | +// --------------------------------------------------------------------------- |
| 72 | + |
| 73 | +TEST_CASE("Vec3 - operator== treats vectors within epsilon as equal", "[Math][Vec3]") { |
| 74 | + using namespace blast; |
| 75 | + Vec3 a(1.0, 2.0, 3.0); |
| 76 | + Vec3 b(1.0 + 1e-7, 2.0, 3.0); // within default epsilon |
| 77 | + CHECK(a == b); |
| 78 | +} |
| 79 | + |
| 80 | +TEST_CASE("Vec3 - operator== treats vectors outside epsilon as not equal", "[Math][Vec3]") { |
| 81 | + using namespace blast; |
| 82 | + Vec3 a(1.0, 2.0, 3.0); |
| 83 | + Vec3 c(1.0 + 1e-4, 2.0, 3.0); // outside default epsilon |
| 84 | + CHECK_FALSE(a == c); |
| 85 | +} |
| 86 | + |
| 87 | +// --------------------------------------------------------------------------- |
| 88 | +// Arithmetic operators |
| 89 | +// --------------------------------------------------------------------------- |
| 90 | + |
| 91 | +TEST_CASE("Vec3 - unary minus negates all components", "[Math][Vec3]") { |
| 92 | + using namespace blast; |
| 93 | + CHECK(-Vec3(1.0, 2.0, 3.0) == Vec3(-1.0, -2.0, -3.0)); |
| 94 | + CHECK(-Vec3(0.0, 0.0, 0.0) == Vec3(0.0, 0.0, 0.0)); |
| 95 | +} |
| 96 | + |
| 97 | +TEST_CASE("Vec3 - binary + and - are correct", "[Math][Vec3]") { |
| 98 | + using namespace blast; |
| 99 | + Vec3 v1(1.0, 2.0, 3.0); |
| 100 | + Vec3 v2(2.0, 4.0, 6.0); |
| 101 | + |
| 102 | + CHECK(v1 + v2 == Vec3(3.0, 6.0, 9.0)); |
| 103 | + CHECK(v2 - v1 == v1); |
| 104 | +} |
| 105 | + |
| 106 | +TEST_CASE("Vec3 - scalar multiplication and division are correct", "[Math][Vec3]") { |
| 107 | + using namespace blast; |
| 108 | + Vec3 v1(1.0, 2.0, 3.0); |
| 109 | + Vec3 v2(2.0, 4.0, 6.0); |
| 110 | + real c = 2.0; |
| 111 | + |
| 112 | + CHECK(c * v1 == v2); // left scalar multiply |
| 113 | + CHECK(v1 * c == v2); // right scalar multiply |
| 114 | + CHECK(v2 / c == v1); |
| 115 | +} |
| 116 | + |
| 117 | +TEST_CASE("Vec3 - compound assignment operators are correct", "[Math][Vec3]") { |
| 118 | + using namespace blast; |
| 119 | + Vec3 v1(1.0, 2.0, 3.0); |
| 120 | + Vec3 v2(2.0, 4.0, 6.0); |
| 121 | + real c = 2.0; |
| 122 | + |
| 123 | + Vec3 v_add(1.0, 2.0, 3.0); |
| 124 | + v_add += v1; |
| 125 | + CHECK(v_add == v2); |
| 126 | + |
| 127 | + Vec3 v_sub(2.0, 4.0, 6.0); |
| 128 | + v_sub -= v1; |
| 129 | + CHECK(v_sub == v1); |
| 130 | + |
| 131 | + Vec3 v_mul(1.0, 2.0, 3.0); |
| 132 | + v_mul *= c; |
| 133 | + CHECK(v_mul == v2); |
| 134 | + |
| 135 | + Vec3 v_div(2.0, 4.0, 6.0); |
| 136 | + v_div /= c; |
| 137 | + CHECK(v_div == v1); |
| 138 | +} |
| 139 | + |
| 140 | +// --------------------------------------------------------------------------- |
| 141 | +// Cross product |
| 142 | +// --------------------------------------------------------------------------- |
| 143 | + |
| 144 | +TEST_CASE("Vec3 - cross product of a vector with itself is zero", "[Math][Vec3]") { |
| 145 | + using namespace blast; |
| 146 | + Vec3 v(1.0, 2.0, 3.0); |
| 147 | + CHECK(cross(v, v) == Vec3(0.0, 0.0, 0.0)); |
| 148 | +} |
| 149 | + |
| 150 | +TEST_CASE("Vec3 - cross product gives correct result for known inputs", "[Math][Vec3]") { |
| 151 | + using namespace blast; |
| 152 | + Vec3 a(1.0, 2.0, 3.0); |
| 153 | + Vec3 b(2.0, 3.0, 4.0); |
| 154 | + CHECK(cross(a, b) == Vec3(-1.0, 2.0, -1.0)); |
| 155 | +} |
| 156 | + |
| 157 | +TEST_CASE("Vec3 - cross product is anticommutative", "[Math][Vec3]") { |
| 158 | + using namespace blast; |
| 159 | + Vec3 a(1.0, 2.0, 3.0); |
| 160 | + Vec3 b(2.0, 3.0, 4.0); |
| 161 | + CHECK(cross(a, b) == -cross(b, a)); |
| 162 | +} |
| 163 | + |
| 164 | +TEST_CASE("Vec3 - cross product result is perpendicular to both inputs", "[Math][Vec3]") { |
| 165 | + using namespace blast; |
| 166 | + Vec3 a(1.0, 2.0, 3.0); |
| 167 | + Vec3 b(4.0, 5.0, 6.0); |
| 168 | + Vec3 c = cross(a, b); |
| 169 | + CHECK(std::abs(dot(c, a)) < 1e-9); |
| 170 | + CHECK(std::abs(dot(c, b)) < 1e-9); |
| 171 | +} |
| 172 | + |
| 173 | +// --------------------------------------------------------------------------- |
| 174 | +// Dot product |
| 175 | +// --------------------------------------------------------------------------- |
| 176 | + |
| 177 | +TEST_CASE("Vec3 - dot product of a vector with itself equals squared norm", "[Math][Vec3]") { |
| 178 | + using namespace blast; |
| 179 | + Vec3 v(1.0, 2.0, 3.0); |
| 180 | + CHECK(dot(v, v) == 14.0); // 1 + 4 + 9 |
| 181 | +} |
| 182 | + |
| 183 | +TEST_CASE("Vec3 - dot product of orthogonal vectors is zero", "[Math][Vec3]") { |
| 184 | + using namespace blast; |
| 185 | + CHECK(dot(Vec3(1, 0, 0), Vec3(0, 1, 0)) == 0.0); |
| 186 | + CHECK(dot(Vec3(0, 1, 0), Vec3(0, 0, 1)) == 0.0); |
| 187 | + CHECK(dot(Vec3(1, 0, 0), Vec3(0, 0, 1)) == 0.0); |
| 188 | +} |
| 189 | + |
| 190 | +TEST_CASE("Vec3 - dot product gives correct result for known inputs", "[Math][Vec3]") { |
| 191 | + using namespace blast; |
| 192 | + // [1,2,3] · [4,5,6] = 4 + 10 + 18 = 32 |
| 193 | + CHECK(dot(Vec3(1, 2, 3), Vec3(4, 5, 6)) == 32.0); |
| 194 | +} |
| 195 | + |
| 196 | +TEST_CASE("Vec3 - dot product is commutative", "[Math][Vec3]") { |
| 197 | + using namespace blast; |
| 198 | + Vec3 a(1.0, 2.0, 3.0); |
| 199 | + Vec3 b(4.0, 5.0, 6.0); |
| 200 | + CHECK(dot(a, b) == dot(b, a)); |
| 201 | +} |
| 202 | + |
| 203 | +// --------------------------------------------------------------------------- |
| 204 | +// Norm |
| 205 | +// --------------------------------------------------------------------------- |
| 206 | + |
| 207 | +TEST_CASE("Vec3 - norm gives correct result for known input", "[Math][Vec3]") { |
| 208 | + using namespace blast; |
| 209 | + CHECK(std::abs(norm(Vec3(1.0, 2.0, 3.0)) - 3.7416573868) < 1e-6); |
| 210 | +} |
| 211 | + |
| 212 | +TEST_CASE("Vec3 - norm of zero vector is zero", "[Math][Vec3]") { |
| 213 | + using namespace blast; |
| 214 | + CHECK(norm(Vec3(0.0, 0.0, 0.0)) == 0.0); |
| 215 | +} |
| 216 | + |
| 217 | +TEST_CASE("Vec3 - norm of unit axis vectors is one", "[Math][Vec3]") { |
| 218 | + using namespace blast; |
| 219 | + CHECK(std::abs(norm(Vec3(1, 0, 0)) - 1.0) < 1e-9); |
| 220 | + CHECK(std::abs(norm(Vec3(0, 1, 0)) - 1.0) < 1e-9); |
| 221 | + CHECK(std::abs(norm(Vec3(0, 0, 1)) - 1.0) < 1e-9); |
| 222 | +} |
| 223 | + |
| 224 | +// --------------------------------------------------------------------------- |
| 225 | +// zero() |
| 226 | +// --------------------------------------------------------------------------- |
| 227 | + |
| 228 | +TEST_CASE("Vec3 - zero() sets all components to 0.0", "[Math][Vec3]") { |
| 229 | + using namespace blast; |
| 230 | + Vec3 v(1.0, 2.0, 3.0); |
| 231 | + zero(v); |
| 232 | + CHECK(v.x == 0.0); |
| 233 | + CHECK(v.y == 0.0); |
| 234 | + CHECK(v.z == 0.0); |
| 235 | +} |
| 236 | + |
| 237 | +TEST_CASE("Vec3 - zero() returns a reference to the same object", "[Math][Vec3]") { |
| 238 | + using namespace blast; |
| 239 | + Vec3 v(1.0, 2.0, 3.0); |
| 240 | + Vec3& ref = zero(v); |
| 241 | + CHECK(&ref == &v); |
| 242 | +} |
| 243 | + |
| 244 | +// --------------------------------------------------------------------------- |
| 245 | +// constant() |
| 246 | +// --------------------------------------------------------------------------- |
| 247 | + |
| 248 | +TEST_CASE("Vec3 - constant() sets all components to the given value", "[Math][Vec3]") { |
| 249 | + using namespace blast; |
| 250 | + Vec3 v; |
| 251 | + |
| 252 | + constant(v, 2.0); |
| 253 | + CHECK(v.x == 2.0); |
| 254 | + CHECK(v.y == 2.0); |
| 255 | + CHECK(v.z == 2.0); |
| 256 | + |
| 257 | + constant(v, 0.0); |
| 258 | + CHECK(v.x == 0.0); |
| 259 | + CHECK(v.y == 0.0); |
| 260 | + CHECK(v.z == 0.0); |
| 261 | + |
| 262 | + constant(v, -5.0); |
| 263 | + CHECK(v.x == -5.0); |
| 264 | + CHECK(v.y == -5.0); |
| 265 | + CHECK(v.z == -5.0); |
| 266 | +} |
| 267 | + |
| 268 | +TEST_CASE("Vec3 - constant() returns a reference to the same object", "[Math][Vec3]") { |
| 269 | + using namespace blast; |
| 270 | + Vec3 v; |
| 271 | + Vec3& ref = constant(v, 7.0); |
| 272 | + CHECK(&ref == &v); |
| 273 | +} |
| 274 | + |
| 275 | +// --------------------------------------------------------------------------- |
| 276 | +// is_small() |
| 277 | +// --------------------------------------------------------------------------- |
| 278 | + |
| 279 | +TEST_CASE("Vec3 - is_small() returns true when all components are within epsilon", "[Math][Vec3]") { |
| 280 | + using namespace blast; |
| 281 | + CHECK(is_small(Vec3(1e-6, 1e-6, 1e-6)) == true); |
| 282 | +} |
| 283 | + |
| 284 | +TEST_CASE("Vec3 - is_small() returns false when any component exceeds epsilon", "[Math][Vec3]") { |
| 285 | + using namespace blast; |
| 286 | + CHECK(is_small(Vec3(1e-5, 1e-6, 1e-6)) == false); // x exceeds |
| 287 | + CHECK(is_small(Vec3(1e-6, 1e-5, 1e-6)) == false); // y exceeds |
| 288 | + CHECK(is_small(Vec3(1e-6, 1e-6, 1e-5)) == false); // z exceeds |
| 289 | +} |
| 290 | + |
| 291 | +// --------------------------------------------------------------------------- |
| 292 | +// is_close() |
| 293 | +// --------------------------------------------------------------------------- |
| 294 | + |
| 295 | +TEST_CASE("Vec3 - is_close() returns true when vectors are within epsilon", "[Math][Vec3]") { |
| 296 | + using namespace blast; |
| 297 | + CHECK(is_close(Vec3(1e-6, 1e-6, 1e-6), Vec3(0.0, 0.0, 0.0)) == true); |
| 298 | + |
| 299 | + Vec3 p(1.0, 2.0, 3.0); |
| 300 | + Vec3 q(1.0 + 1e-7, 2.0 + 1e-7, 3.0 + 1e-7); |
| 301 | + CHECK(is_close(p, q) == true); |
| 302 | +} |
| 303 | + |
| 304 | +TEST_CASE("Vec3 - is_close() returns false when any component exceeds epsilon", "[Math][Vec3]") { |
| 305 | + using namespace blast; |
| 306 | + Vec3 z(0.0, 0.0, 0.0); |
| 307 | + CHECK(is_close(Vec3(1e-5, 1e-6, 1e-6), z) == false); // x exceeds |
| 308 | + CHECK(is_close(Vec3(1e-6, 1e-5, 1e-6), z) == false); // y exceeds |
| 309 | + CHECK(is_close(Vec3(1e-6, 1e-6, 1e-5), z) == false); // z exceeds |
| 310 | + |
| 311 | + Vec3 p(1.0, 2.0, 3.0); |
| 312 | + Vec3 r(1.0 + 1e-4, 2.0, 3.0); |
| 313 | + CHECK(is_close(p, r) == false); |
| 314 | +} |
0 commit comments