Line data Source code
1 : #include "catch.hpp"
2 : #include "Vector.h"
3 : #include "Matrix.h"
4 :
5 41 : SCENARIO( "Manipulating a vector", "[Vector]" ) {
6 41 : GIVEN( "A set vector" ) {
7 1 : Vector v;
8 1 : v.setVector(1.0f, 2.0f, 3.0f);
9 :
10 2 : WHEN( "the normalize method is called" ) {
11 1 : v.normalize();
12 :
13 2 : THEN( "the vector is normalized" ) {
14 1 : REQUIRE(v.x == Approx(0.26726f));
15 1 : REQUIRE(v.y == Approx(0.53452f));
16 1 : REQUIRE(v.z == Approx(0.80178f));
17 1 : }
18 1 : }
19 1 : }
20 :
21 42 : GIVEN( "Default and parameterized constructors" ) {
22 2 : Vector a;
23 2 : Vector b(3.0f, 4.0f, 0.0f);
24 :
25 3 : THEN( "default is zero" ) {
26 1 : REQUIRE(a.x == 0.0f);
27 1 : REQUIRE(a.y == 0.0f);
28 1 : REQUIRE(a.z == 0.0f);
29 1 : }
30 :
31 3 : THEN( "constructor sets components" ) {
32 1 : REQUIRE(b.x == 3.0f);
33 1 : REQUIRE(b.y == 4.0f);
34 1 : REQUIRE(b.z == 0.0f);
35 1 : }
36 2 : }
37 :
38 42 : GIVEN( "Vectors for dot and cross product" ) {
39 2 : Vector u;
40 2 : u.setVector(1.0f, 2.0f, 3.0f);
41 2 : Vector v;
42 2 : v.setVector(4.0f, 5.0f, 6.0f);
43 :
44 3 : THEN( "dotProduct matches hand calculation" ) {
45 1 : REQUIRE(u.dotProduct(v) == Approx(32.0f));
46 1 : }
47 :
48 3 : WHEN( "crossProduct stores u x v" ) {
49 1 : Vector w;
50 1 : w.crossProduct(u, v);
51 2 : THEN( "result is orthogonal to both" ) {
52 1 : REQUIRE(w.x == Approx(-3.0f));
53 1 : REQUIRE(w.y == Approx(6.0f));
54 1 : REQUIRE(w.z == Approx(-3.0f));
55 1 : }
56 1 : }
57 2 : }
58 :
59 43 : GIVEN( "Vectors for distance, length, and scale" ) {
60 3 : Vector p(0.0f, 0.0f, 0.0f);
61 3 : Vector q(2.0f, 0.0f, 0.0f);
62 :
63 4 : THEN( "getDistance is Euclidean" ) {
64 1 : REQUIRE(p.getDistance(q) == Approx(2.0f));
65 1 : }
66 :
67 4 : THEN( "getLength uses current components" ) {
68 1 : REQUIRE(q.getLength() == Approx(2.0f));
69 1 : }
70 :
71 4 : WHEN( "scale multiplies components" ) {
72 1 : q.scale(3.0f);
73 2 : THEN( "components scale uniformly" ) {
74 1 : REQUIRE(q.x == Approx(6.0f));
75 1 : REQUIRE(q.y == Approx(0.0f));
76 1 : REQUIRE(q.z == Approx(0.0f));
77 1 : }
78 1 : }
79 3 : }
80 :
81 42 : GIVEN( "getScaler and getSum" ) {
82 2 : Vector a(1.0f, 2.0f, 3.0f);
83 2 : Vector b(4.0f, 5.0f, 6.0f);
84 :
85 3 : THEN( "getScaler is component-wise multiply then sum" ) {
86 1 : REQUIRE(a.getScaler(b) == Approx(32.0f));
87 1 : }
88 :
89 3 : THEN( "getSum adds components" ) {
90 1 : REQUIRE(a.getSum() == Approx(6.0f));
91 1 : }
92 2 : }
93 :
94 43 : GIVEN( "Operator assignments and arithmetic" ) {
95 3 : Vector a(1.0f, 2.0f, 3.0f);
96 3 : Vector b(10.0f, 20.0f, 30.0f);
97 :
98 4 : WHEN( "operator=" ) {
99 1 : a = b;
100 2 : THEN( "values copy" ) {
101 1 : REQUIRE(a.x == 10.0f);
102 1 : REQUIRE(a.y == 20.0f);
103 1 : REQUIRE(a.z == 30.0f);
104 1 : }
105 1 : }
106 :
107 4 : WHEN( "operator+ and operator*" ) {
108 1 : Vector c = a + b;
109 1 : Vector d = a * 2.0f;
110 2 : THEN( "addition and scalar multiply" ) {
111 1 : REQUIRE(c.x == Approx(11.0f));
112 1 : REQUIRE(c.y == Approx(22.0f));
113 1 : REQUIRE(c.z == Approx(33.0f));
114 1 : REQUIRE(d.x == Approx(2.0f));
115 1 : REQUIRE(d.y == Approx(4.0f));
116 1 : REQUIRE(d.z == Approx(6.0f));
117 1 : }
118 1 : }
119 :
120 4 : WHEN( "operator+= and operator-=" ) {
121 1 : a += b;
122 1 : a -= Vector(1.0f, 2.0f, 3.0f);
123 2 : THEN( "in-place add and subtract" ) {
124 1 : REQUIRE(a.x == Approx(10.0f));
125 1 : REQUIRE(a.y == Approx(20.0f));
126 1 : REQUIRE(a.z == Approx(30.0f));
127 1 : }
128 1 : }
129 3 : }
130 :
131 41 : GIVEN( "Normalizing a zero vector" ) {
132 1 : Vector z(0.0f, 0.0f, 0.0f);
133 1 : z.normalize();
134 2 : THEN( "components stay zero" ) {
135 1 : REQUIRE(z.x == 0.0f);
136 1 : REQUIRE(z.y == 0.0f);
137 1 : REQUIRE(z.z == 0.0f);
138 1 : }
139 1 : }
140 :
141 41 : GIVEN( "calcNorm from three triangle corners" ) {
142 1 : Vector p1(0.0f, 0.0f, 0.0f);
143 1 : Vector p2(1.0f, 0.0f, 0.0f);
144 1 : Vector p3(0.0f, 1.0f, 0.0f);
145 1 : Vector n;
146 1 : n.calcNorm(p1, p2, p3);
147 2 : THEN( "normal points along +Z for XY triangle" ) {
148 1 : REQUIRE(n.x == Approx(0.0f));
149 1 : REQUIRE(n.y == Approx(0.0f));
150 1 : REQUIRE(n.z == Approx(1.0f));
151 1 : }
152 1 : }
153 :
154 41 : GIVEN( "transformVector with translation" ) {
155 1 : Matrix m;
156 1 : m.setTranslation(1.0f, 2.0f, 3.0f);
157 1 : Vector v(10.0f, 20.0f, 30.0f);
158 1 : Vector out;
159 1 : out.transformVector(m, v);
160 2 : THEN( "affine part accumulates translation" ) {
161 1 : REQUIRE(out.x == Approx(11.0f));
162 1 : REQUIRE(out.y == Approx(22.0f));
163 1 : REQUIRE(out.z == Approx(33.0f));
164 1 : }
165 1 : }
166 :
167 41 : GIVEN( "average of two vectors" ) {
168 1 : Vector a(1.0f, 0.0f, 0.0f);
169 1 : Vector b(0.0f, 1.0f, 0.0f);
170 1 : Vector mid;
171 1 : mid.average(a, b);
172 2 : THEN( "result is normalized sum" ) {
173 1 : const float invSqrt2 = 0.70710678118f;
174 1 : REQUIRE(mid.x == Approx(invSqrt2));
175 1 : REQUIRE(mid.y == Approx(invSqrt2));
176 1 : REQUIRE(mid.z == Approx(0.0f));
177 1 : }
178 1 : }
179 :
180 : // intersectBox: `this` is the ray direction; rayPosition is the ray origin.
181 : // collisionBox[0] = min corner, collisionBox[1] = max corner (expanded by extend).
182 : // Returns the entry intersection point in hitPoint (4-arg overload).
183 : // Returns false when no slab intersection is found for the ray direction.
184 63 : GIVEN( "intersectBox with a unit box at the origin" ) {
185 46 : Vector box[2];
186 23 : box[0].setVector(-1.0f, -1.0f, -1.0f);
187 23 : box[1].setVector( 1.0f, 1.0f, 1.0f);
188 :
189 25 : WHEN( "an X-directed ray approaches the box from the left" ) {
190 2 : Vector dir(1.0f, 0.0f, 0.0f);
191 2 : Vector origin(-5.0f, 0.0f, 0.0f);
192 :
193 3 : THEN( "the hit test returns true" ) {
194 1 : REQUIRE(dir.intersectBox(origin, box, 0.0f));
195 1 : }
196 :
197 3 : THEN( "hitPoint is placed at the entry (near) face" ) {
198 1 : Vector hitPoint;
199 1 : dir.intersectBox(origin, box, 0.0f, hitPoint);
200 1 : REQUIRE(hitPoint.x == Approx(-1.0f));
201 1 : REQUIRE(hitPoint.y == Approx( 0.0f));
202 1 : REQUIRE(hitPoint.z == Approx( 0.0f));
203 1 : }
204 2 : }
205 :
206 25 : WHEN( "an X-directed ray approaches the box from the right" ) {
207 2 : Vector dir(-1.0f, 0.0f, 0.0f);
208 2 : Vector origin(5.0f, 0.0f, 0.0f);
209 :
210 3 : THEN( "the hit test returns true" ) {
211 1 : REQUIRE(dir.intersectBox(origin, box, 0.0f));
212 1 : }
213 :
214 3 : THEN( "hitPoint is placed at the entry (near) face" ) {
215 1 : Vector hitPoint;
216 1 : dir.intersectBox(origin, box, 0.0f, hitPoint);
217 1 : REQUIRE(hitPoint.x == Approx( 1.0f));
218 1 : REQUIRE(hitPoint.y == Approx( 0.0f));
219 1 : REQUIRE(hitPoint.z == Approx( 0.0f));
220 1 : }
221 2 : }
222 :
223 24 : WHEN( "an X-directed ray passes above the box" ) {
224 1 : Vector dir(1.0f, 0.0f, 0.0f);
225 1 : Vector origin(-5.0f, 2.0f, 0.0f);
226 :
227 2 : THEN( "the hit test returns false" ) {
228 1 : REQUIRE_FALSE(dir.intersectBox(origin, box, 0.0f));
229 1 : }
230 1 : }
231 :
232 25 : WHEN( "a Y-directed ray approaches the box from below" ) {
233 2 : Vector dir(0.0f, 1.0f, 0.0f);
234 2 : Vector origin(0.0f, -5.0f, 0.0f);
235 :
236 3 : THEN( "the hit test returns true" ) {
237 1 : REQUIRE(dir.intersectBox(origin, box, 0.0f));
238 1 : }
239 :
240 3 : THEN( "hitPoint is placed at the entry (near) face" ) {
241 1 : Vector hitPoint;
242 1 : dir.intersectBox(origin, box, 0.0f, hitPoint);
243 1 : REQUIRE(hitPoint.x == Approx( 0.0f));
244 1 : REQUIRE(hitPoint.y == Approx(-1.0f));
245 1 : REQUIRE(hitPoint.z == Approx( 0.0f));
246 1 : }
247 2 : }
248 :
249 25 : WHEN( "a Y-directed ray approaches the box from above" ) {
250 2 : Vector dir(0.0f, -1.0f, 0.0f);
251 2 : Vector origin(0.0f, 5.0f, 0.0f);
252 :
253 3 : THEN( "the hit test returns true" ) {
254 1 : REQUIRE(dir.intersectBox(origin, box, 0.0f));
255 1 : }
256 :
257 3 : THEN( "hitPoint is placed at the entry (near) face" ) {
258 1 : Vector hitPoint;
259 1 : dir.intersectBox(origin, box, 0.0f, hitPoint);
260 1 : REQUIRE(hitPoint.x == Approx( 0.0f));
261 1 : REQUIRE(hitPoint.y == Approx( 1.0f));
262 1 : REQUIRE(hitPoint.z == Approx( 0.0f));
263 1 : }
264 2 : }
265 :
266 24 : WHEN( "a Y-directed ray passes beside the box in Z" ) {
267 1 : Vector dir(0.0f, 1.0f, 0.0f);
268 1 : Vector origin(0.0f, -5.0f, 2.0f);
269 :
270 2 : THEN( "the hit test returns false" ) {
271 1 : REQUIRE_FALSE(dir.intersectBox(origin, box, 0.0f));
272 1 : }
273 1 : }
274 :
275 25 : WHEN( "a Z-directed ray approaches the box from the front" ) {
276 2 : Vector dir(0.0f, 0.0f, 1.0f);
277 2 : Vector origin(0.0f, 0.0f, -5.0f);
278 :
279 3 : THEN( "the hit test returns true" ) {
280 1 : REQUIRE(dir.intersectBox(origin, box, 0.0f));
281 1 : }
282 :
283 3 : THEN( "hitPoint is placed at the entry (near) face" ) {
284 1 : Vector hitPoint;
285 1 : dir.intersectBox(origin, box, 0.0f, hitPoint);
286 1 : REQUIRE(hitPoint.x == Approx( 0.0f));
287 1 : REQUIRE(hitPoint.y == Approx( 0.0f));
288 1 : REQUIRE(hitPoint.z == Approx(-1.0f));
289 1 : }
290 2 : }
291 :
292 25 : WHEN( "a Z-directed ray approaches the box from behind" ) {
293 2 : Vector dir(0.0f, 0.0f, -1.0f);
294 2 : Vector origin(0.0f, 0.0f, 5.0f);
295 :
296 3 : THEN( "the hit test returns true" ) {
297 1 : REQUIRE(dir.intersectBox(origin, box, 0.0f));
298 1 : }
299 :
300 3 : THEN( "hitPoint is placed at the entry (near) face" ) {
301 1 : Vector hitPoint;
302 1 : dir.intersectBox(origin, box, 0.0f, hitPoint);
303 1 : REQUIRE(hitPoint.x == Approx( 0.0f));
304 1 : REQUIRE(hitPoint.y == Approx( 0.0f));
305 1 : REQUIRE(hitPoint.z == Approx( 1.0f));
306 1 : }
307 2 : }
308 :
309 24 : WHEN( "the ray direction is zero" ) {
310 1 : Vector dir(0.0f, 0.0f, 0.0f);
311 1 : Vector origin(0.0f, 0.0f, 0.0f);
312 :
313 2 : THEN( "the hit test returns false" ) {
314 1 : REQUIRE_FALSE(dir.intersectBox(origin, box, 0.0f));
315 1 : }
316 1 : }
317 :
318 : // A ray that starts past the box and points further away has a negative
319 : // tNear to the near face. It should not register as a hit.
320 24 : WHEN( "the ray origin is past the box and the ray points away" ) {
321 1 : Vector dir(1.0f, 0.0f, 0.0f);
322 1 : Vector origin(5.0f, 0.0f, 0.0f);
323 :
324 2 : THEN( "the hit test returns false" ) {
325 1 : REQUIRE_FALSE(dir.intersectBox(origin, box, 0.0f));
326 1 : }
327 1 : }
328 :
329 25 : WHEN( "a ray passes just outside the Y bound of the box" ) {
330 2 : Vector dir(1.0f, 0.0f, 0.0f);
331 2 : Vector origin(-5.0f, 1.5f, 0.0f);
332 :
333 3 : THEN( "with no extension it misses" ) {
334 1 : REQUIRE_FALSE(dir.intersectBox(origin, box, 0.0f));
335 1 : }
336 :
337 3 : THEN( "with enough extension to cover the gap it hits" ) {
338 1 : REQUIRE(dir.intersectBox(origin, box, 1.0f));
339 1 : }
340 2 : }
341 :
342 : // A ray parallel to an axis whose origin lies outside the box on that axis
343 : // must miss, even though the parallel axis slab test is skipped entirely.
344 : // The hitPoint bounds check on the skipped axis catches this correctly.
345 24 : WHEN( "a Y-directed ray has its origin outside the box on X" ) {
346 1 : Vector dir(0.0f, 1.0f, 0.0f);
347 1 : Vector origin(50.0f, -5.0f, 0.0f);
348 :
349 2 : THEN( "the hit test returns false" ) {
350 1 : REQUIRE_FALSE(dir.intersectBox(origin, box, 0.0f));
351 1 : }
352 1 : }
353 :
354 24 : WHEN( "a Z-directed ray has its origin outside the box on X" ) {
355 1 : Vector dir(0.0f, 0.0f, 1.0f);
356 1 : Vector origin(50.0f, 0.0f, -5.0f);
357 :
358 2 : THEN( "the hit test returns false" ) {
359 1 : REQUIRE_FALSE(dir.intersectBox(origin, box, 0.0f));
360 1 : }
361 1 : }
362 :
363 : // A ray whose origin is already inside the box trivially intersects it.
364 : // hitPoint is set to the ray origin.
365 25 : WHEN( "an X-directed ray starts inside the box" ) {
366 2 : Vector dir(1.0f, 0.0f, 0.0f);
367 2 : Vector origin(0.0f, 0.0f, 0.0f);
368 :
369 3 : THEN( "the hit test returns true" ) {
370 1 : REQUIRE(dir.intersectBox(origin, box, 0.0f));
371 1 : }
372 :
373 3 : THEN( "hitPoint is set to the ray origin" ) {
374 1 : Vector hitPoint;
375 1 : dir.intersectBox(origin, box, 0.0f, hitPoint);
376 1 : REQUIRE(hitPoint.x == Approx(0.0f));
377 1 : REQUIRE(hitPoint.y == Approx(0.0f));
378 1 : REQUIRE(hitPoint.z == Approx(0.0f));
379 1 : }
380 2 : }
381 :
382 24 : WHEN( "a Y-directed ray starts inside the box" ) {
383 1 : Vector dir(0.0f, 1.0f, 0.0f);
384 1 : Vector origin(0.0f, 0.0f, 0.0f);
385 :
386 2 : THEN( "the hit test returns true" ) {
387 1 : REQUIRE(dir.intersectBox(origin, box, 0.0f));
388 1 : }
389 1 : }
390 23 : }
391 40 : }
|