Skip to content

Commit 468d611

Browse files
Merge pull request #52 from Dynamium-Lab/test-math
Test math
2 parents 8e1c128 + cf54284 commit 468d611

4 files changed

Lines changed: 335 additions & 1 deletion

File tree

blast/blast_math.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,9 @@ inline blast_fn Vec3 operator*(real, Vec3);
6868
inline blast_fn Vec3 operator*(Vec3, real);
6969
inline blast_fn Vec3 operator/(Vec3, real);
7070
inline blast_fn Vec3& operator+=(Vec3&, const Vec3&);
71+
inline blast_fn Vec3& operator-=(Vec3&, const Vec3&);
7172
inline blast_fn Vec3& operator*=(Vec3&, real);
73+
inline blast_fn Vec3& operator/=(Vec3&, real);
7274
inline blast_fn Vec3 cross(Vec3, Vec3);
7375
inline blast_fn Vec3& zero(Vec3&);
7476
inline blast_fn real dot(Vec3, Vec3);

blast/math/Vec3.hpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@
55
namespace blast {
66

77
//------ Vec3 ---------------------
8+
9+
// NOTE: Unsafe operator[]. Use only when i ∈ [0,2]. No runtime check in release builds.
810
inline blast_fn real& Vec3::operator[](int i) {
911
Assert(i < 3);
1012
return *(&x + i); // note: Don't try this at home
1113
}
1214

15+
// NOTE: Unsafe operator[]. Use only when i ∈ [0,2]. No runtime check in release builds.
1316
inline blast_fn real Vec3::operator[](int i) const {
1417
Assert(i < 3);
1518
return *(&x + i); // note: Don't try this at home
@@ -114,10 +117,25 @@ inline blast_fn Vec3& operator+=(Vec3& v1, const Vec3& v2) {
114117
return v1;
115118
}
116119

120+
inline blast_fn Vec3& operator-=(Vec3& v1, const Vec3& v2) {
121+
v1.x -= v2.x;
122+
v1.y -= v2.y;
123+
v1.z -= v2.z;
124+
return v1;
125+
}
126+
117127
inline blast_fn Vec3& operator*=(Vec3& v, real a) {
118128
v.x *= a;
119129
v.y *= a;
120130
v.z *= a;
121131
return v;
122132
}
133+
134+
135+
inline blast_fn Vec3& operator/=(Vec3& v, real a) {
136+
v.x /= a;
137+
v.y /= a;
138+
v.z /= a;
139+
return v;
140+
}
123141
} // namespace blast

tests/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ set(TESTS
8686
# test_optimization_tasks optimization/test_task.cpp
8787
# test_optimization_hierarchical optimization/test_hierarchical_optimization.cpp
8888
# Math
89-
# test_math_matrix math/test_matrix.cpp
89+
test_math_vec3 math/test_vec3.cpp
9090
# World
9191
# test_primitives world/test_primitives.cpp
9292
# Trajectory

tests/math/test_vec3.cpp

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
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

Comments
 (0)