1 
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
9 // Copyright (c) 2014-2018 Francois Beaune, The appleseedhq Organization
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 // THE SOFTWARE.
28 //
29 
30 // appleseed.foundation headers.
31 #include "foundation/math/aabb.h"
32 #include "foundation/math/intersection/rayaabb.h"
33 #include "foundation/math/ray.h"
34 #include "foundation/math/vector.h"
35 #include "foundation/utility/test.h"
36 
37 // Standard headers.
38 #include <limits>
39 
40 using namespace foundation;
41 using namespace std;
42 
TEST_SUITE(Foundation_Math_Intersection_RayAABB)43 TEST_SUITE(Foundation_Math_Intersection_RayAABB)
44 {
45 #pragma warning (push)
46 #pragma warning (disable : 4723)    // potential division by 0
47 
48     TEST_CASE(Intersect_GivenRayNotPiercingTheAABB_ReturnsFalse)
49     {
50         const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
51         const Ray3d ray(Vector3d(2.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0));
52 
53         const bool hit = intersect(ray, RayInfo3d(ray), aabb);
54 
55         EXPECT_FALSE(hit);
56     }
57 
58     TEST_CASE(Intersect_GivenRayPiercingPosXFaceInTheMiddle_ReturnsTrue)
59     {
60         const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
61         const Ray3d ray(Vector3d(2.0, 0.0, 0.0), Vector3d(-1.0, 0.0, 0.0));
62 
63         const bool hit = intersect(ray, RayInfo3d(ray), aabb);
64 
65         EXPECT_TRUE(hit);
66     }
67 
68     TEST_CASE(Intersect_GivenRayPiercingNegXFaceInTheMiddle_ReturnsTrue)
69     {
70         const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
71         const Ray3d ray(Vector3d(-2.0, 0.0, 0.0), Vector3d(1.0, 0.0, 0.0));
72 
73         const bool hit = intersect(ray, RayInfo3d(ray), aabb);
74 
75         EXPECT_TRUE(hit);
76     }
77 
78     TEST_CASE(Intersect_GivenRayPiercingPosYFaceInTheMiddle_ReturnsTrue)
79     {
80         const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
81         const Ray3d ray(Vector3d(0.0, 2.0, 0.0), Vector3d(0.0, -1.0, 0.0));
82 
83         const bool hit = intersect(ray, RayInfo3d(ray), aabb);
84 
85         EXPECT_TRUE(hit);
86     }
87 
88     TEST_CASE(Intersect_GivenRayPiercingNegYFaceInTheMiddle_ReturnsTrue)
89     {
90         const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
91         const Ray3d ray(Vector3d(0.0, -2.0, 0.0), Vector3d(0.0, 1.0, 0.0));
92 
93         const bool hit = intersect(ray, RayInfo3d(ray), aabb);
94 
95         EXPECT_TRUE(hit);
96     }
97 
98     TEST_CASE(Intersect_GivenRayPiercingPosZFaceInTheMiddle_ReturnsTrue)
99     {
100         const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
101         const Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0));
102 
103         const bool hit = intersect(ray, RayInfo3d(ray), aabb);
104 
105         EXPECT_TRUE(hit);
106     }
107 
108     TEST_CASE(Intersect_GivenRayPiercingNegZFaceInTheMiddle_ReturnsTrue)
109     {
110         const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
111         const Ray3d ray(Vector3d(0.0, 0.0, -2.0), Vector3d(0.0, 0.0, 1.0));
112 
113         const bool hit = intersect(ray, RayInfo3d(ray), aabb);
114 
115         EXPECT_TRUE(hit);
116     }
117 
118     TEST_CASE(Intersect_GivenRayEmbeddedInPosXFace_ReturnsTrue)
119     {
120         const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
121         const Ray3d ray(Vector3d(1.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0));
122 
123         const bool hit = intersect(ray, RayInfo3d(ray), aabb);
124 
125         EXPECT_TRUE(hit);
126     }
127 
128     TEST_CASE(Intersect_GivenRayEmbeddedInNegXFace_ReturnsTrue)
129     {
130         const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
131         const Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0));
132 
133         const bool hit = intersect(ray, RayInfo3d(ray), aabb);
134 
135         EXPECT_TRUE(hit);
136     }
137 
138     TEST_CASE(Intersect_GivenRayEmbeddedInPosYFace_ReturnsTrue)
139     {
140         const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
141         const Ray3d ray(Vector3d(2.0, 1.0, 0.0), Vector3d(-1.0, 0.0, 0.0));
142 
143         const bool hit = intersect(ray, RayInfo3d(ray), aabb);
144 
145         EXPECT_TRUE(hit);
146     }
147 
148     TEST_CASE(Intersect_GivenRayEmbeddedInNegYFace_ReturnsTrue)
149     {
150         const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
151         const Ray3d ray(Vector3d(2.0, 0.0, 0.0), Vector3d(-1.0, 0.0, 0.0));
152 
153         const bool hit = intersect(ray, RayInfo3d(ray), aabb);
154 
155         EXPECT_TRUE(hit);
156     }
157 
158     TEST_CASE(Intersect_GivenRayEmbeddedInPosZFace_ReturnsTrue)
159     {
160         const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
161         const Ray3d ray(Vector3d(0.0, 2.0, 1.0), Vector3d(0.0, -1.0, 0.0));
162 
163         const bool hit = intersect(ray, RayInfo3d(ray), aabb);
164 
165         EXPECT_TRUE(hit);
166     }
167 
168     TEST_CASE(Intersect_GivenRayEmbeddedInNegZFace_ReturnsTrue)
169     {
170         const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
171         const Ray3d ray(Vector3d(0.0, 2.0, 0.0), Vector3d(0.0, -1.0, 0.0));
172 
173         const bool hit = intersect(ray, RayInfo3d(ray), aabb);
174 
175         EXPECT_TRUE(hit);
176     }
177 
178     TEST_CASE(Intersect_GivenRayWithTMinEqualToHitDistance_ReturnsTrue)
179     {
180         const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
181         const Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 3.0, 10.0);
182 
183         const bool hit = intersect(ray, RayInfo3d(ray), aabb);
184 
185         EXPECT_TRUE(hit);
186     }
187 
188     TEST_CASE(Intersect_GivenRayWithTMaxEqualToHitDistance_ReturnsFalse)
189     {
190         const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
191         const Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 0.0, 1.0);
192 
193         const bool hit = intersect(ray, RayInfo3d(ray), aabb);
194 
195         EXPECT_FALSE(hit);
196     }
197 
198     TEST_CASE(Intersect_GivenRayWithTMinLargerThanHitDistance_ReturnsFalse)
199     {
200         const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
201         const Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 3.1, 10.0);
202 
203         const bool hit = intersect(ray, RayInfo3d(ray), aabb);
204 
205         EXPECT_FALSE(hit);
206     }
207 
208     TEST_CASE(Intersect_GivenRayWithTMaxSmallerThanHitDistance_ReturnsFalse)
209     {
210         const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
211         const Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 0.0, 0.9);
212 
213         const bool hit = intersect(ray, RayInfo3d(ray), aabb);
214 
215         EXPECT_FALSE(hit);
216     }
217 
218     TEST_CASE(Intersect_GivenRayNotPiercingTheAABB_ReturnsFalseAndLeavesDistanceUnchanged)
219     {
220         const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
221         const Ray3d ray(Vector3d(2.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0));
222 
223         double distance = 42.0;
224         const bool hit = intersect(ray, RayInfo3d(ray), aabb, distance);
225 
226         EXPECT_FALSE(hit);
227         EXPECT_EQ(42.0, distance);
228     }
229 
230     TEST_CASE(Intersect_GivenRayEmbeddedInPosXFace_ReturnsTrueAndDistanceToHit)
231     {
232         const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
233         const Ray3d ray(Vector3d(1.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0));
234 
235         double distance = 42.0;
236         const bool hit = intersect(ray, RayInfo3d(ray), aabb, distance);
237 
238         ASSERT_TRUE(hit);
239         EXPECT_FEQ(1.0, distance);
240     }
241 
242     TEST_CASE(Intersect_GivenRayEmbeddedInNegXFace_ReturnsTrueAndDistanceToHit)
243     {
244         const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
245         const Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0));
246 
247         double distance = 42.0;
248         const bool hit = intersect(ray, RayInfo3d(ray), aabb, distance);
249 
250         ASSERT_TRUE(hit);
251         EXPECT_FEQ(1.0, distance);
252     }
253 
254     TEST_CASE(Intersect_GivenRayEmbeddedInPosYFace_ReturnsTrueAndDistanceToHit)
255     {
256         const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
257         const Ray3d ray(Vector3d(2.0, 1.0, 0.0), Vector3d(-1.0, 0.0, 0.0));
258 
259         double distance = 42.0;
260         const bool hit = intersect(ray, RayInfo3d(ray), aabb, distance);
261 
262         ASSERT_TRUE(hit);
263         EXPECT_FEQ(1.0, distance);
264     }
265 
266     TEST_CASE(Intersect_GivenRayEmbeddedInNegYFace_ReturnsTrueAndDistanceToHit)
267     {
268         const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
269         const Ray3d ray(Vector3d(2.0, 0.0, 0.0), Vector3d(-1.0, 0.0, 0.0));
270 
271         double distance = 42.0;
272         const bool hit = intersect(ray, RayInfo3d(ray), aabb, distance);
273 
274         ASSERT_TRUE(hit);
275         EXPECT_FEQ(1.0, distance);
276     }
277 
278     TEST_CASE(Intersect_GivenRayEmbeddedInPosZFace_ReturnsTrueAndDistanceToHit)
279     {
280         const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
281         const Ray3d ray(Vector3d(0.0, 2.0, 1.0), Vector3d(0.0, -1.0, 0.0));
282 
283         double distance = 42.0;
284         const bool hit = intersect(ray, RayInfo3d(ray), aabb, distance);
285 
286         ASSERT_TRUE(hit);
287         EXPECT_FEQ(1.0, distance);
288     }
289 
290     TEST_CASE(Intersect_GivenRayEmbeddedInNegZFace_ReturnsTrueAndDistanceToHit)
291     {
292         const AABB3d aabb(Vector3d(0.0), Vector3d(1.0));
293         const Ray3d ray(Vector3d(0.0, 2.0, 0.0), Vector3d(0.0, -1.0, 0.0));
294 
295         double distance = 42.0;
296         const bool hit = intersect(ray, RayInfo3d(ray), aabb, distance);
297 
298         ASSERT_TRUE(hit);
299         EXPECT_FEQ(1.0, distance);
300     }
301 
302     TEST_CASE(Intersect_GivenRayWithTMinEqualToHitDistance_ReturnsTrueAndDistanceToHit)
303     {
304         const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
305         const Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 3.0, 10.0);
306 
307         double distance = 42.0;
308         const bool hit = intersect(ray, RayInfo3d(ray), aabb, distance);
309 
310         EXPECT_TRUE(hit);
311         EXPECT_EQ(3.0, distance);
312     }
313 
314     TEST_CASE(Intersect_GivenRayWithTMaxEqualToHitDistance_ReturnsFalseAndLeavesDistanceUnchanged)
315     {
316         const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
317         const Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 0.0, 1.0);
318 
319         double distance = 42.0;
320         const bool hit = intersect(ray, RayInfo3d(ray), aabb, distance);
321 
322         EXPECT_FALSE(hit);
323         EXPECT_EQ(42.0, distance);
324     }
325 
326     TEST_CASE(Intersect_GivenRayWithTMinLargerThanHitDistance_ReturnsFalseAndLeavesDistanceUnchanged)
327     {
328         const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
329         const Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 3.1, 10.0);
330 
331         double distance = 42.0;
332         const bool hit = intersect(ray, RayInfo3d(ray), aabb, distance);
333 
334         EXPECT_FALSE(hit);
335         EXPECT_EQ(42.0, distance);
336     }
337 
338     TEST_CASE(Intersect_GivenRayWithTMaxSmallerThanHitDistance_ReturnsFalseAndLeavesDistanceUnchanged)
339     {
340         const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
341         const Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 0.0, 0.9);
342 
343         double distance = 42.0;
344         const bool hit = intersect(ray, RayInfo3d(ray), aabb, distance);
345 
346         EXPECT_FALSE(hit);
347         EXPECT_EQ(42.0, distance);
348     }
349 
350     TEST_CASE(Clip_GivenRayNotPiercingTheAABB_ReturnsFalseAndLeavesTMinAndTMaxUnchanged)
351     {
352         const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
353         Ray3d ray(Vector3d(2.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0));
354 
355         const bool hit = clip(ray, RayInfo3d(ray), aabb);
356 
357         EXPECT_FALSE(hit);
358         EXPECT_EQ(0.0, ray.m_tmin);
359         EXPECT_EQ(numeric_limits<double>::max(), ray.m_tmax);
360     }
361 
362     TEST_CASE(Clip_GivenRayPiercingPosZFaceInTheMiddle_ReturnsTrueAndUpdatesTMinAndTMax)
363     {
364         const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
365         Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0));
366 
367         const bool hit = clip(ray, RayInfo3d(ray), aabb);
368 
369         EXPECT_TRUE(hit);
370         EXPECT_FEQ(1.0, ray.m_tmin);
371         EXPECT_FEQ(3.0, ray.m_tmax);
372     }
373 
374     TEST_CASE(Clip_GivenRayWithTMinEqualToHitDistance_ReturnsTrueAndUpdatesTMinAndTMax)
375     {
376         const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
377         Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 3.0, 10.0);
378 
379         const bool hit = clip(ray, RayInfo3d(ray), aabb);
380 
381         EXPECT_TRUE(hit);
382         EXPECT_EQ(3.0, ray.m_tmin);
383         EXPECT_EQ(3.0, ray.m_tmax);
384     }
385 
386     TEST_CASE(Clip_GivenRayWithTMaxEqualToHitDistance_ReturnsFalseAndLeavesTMinAndTMaxUnchanged)
387     {
388         const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
389         Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 0.0, 1.0);
390 
391         const bool hit = clip(ray, RayInfo3d(ray), aabb);
392 
393         EXPECT_FALSE(hit);
394         EXPECT_EQ(0.0, ray.m_tmin);
395         EXPECT_EQ(1.0, ray.m_tmax);
396     }
397 
398     TEST_CASE(Clip_GivenRayWithTMinLargerThanHitDistance_ReturnsFalseAndLeavesTMinAndTMaxUnchanged)
399     {
400         const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
401         Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 3.1, 10.0);
402 
403         const bool hit = clip(ray, RayInfo3d(ray), aabb);
404 
405         EXPECT_FALSE(hit);
406         EXPECT_EQ(3.1, ray.m_tmin);
407         EXPECT_EQ(10.0, ray.m_tmax);
408     }
409 
410     TEST_CASE(Clip_GivenRayWithTMaxSmallerThanHitDistance_ReturnsFalseAndLeavesTMinAndTMaxUnchanged)
411     {
412         const AABB3d aabb(Vector3d(-1.0), Vector3d(1.0));
413         Ray3d ray(Vector3d(0.0, 0.0, 2.0), Vector3d(0.0, 0.0, -1.0), 0.0, 0.9);
414 
415         const bool hit = clip(ray, RayInfo3d(ray), aabb);
416 
417         EXPECT_FALSE(hit);
418         EXPECT_EQ(0.0, ray.m_tmin);
419         EXPECT_EQ(0.9, ray.m_tmax);
420     }
421 
422 #pragma warning (pop)
423 }
424