1 // Copyright (c) 2017-2021, Lawrence Livermore National Security, LLC and
2 // other Axom Project Developers. See the top-level LICENSE file for details.
3 //
4 // SPDX-License-Identifier: (BSD-3-Clause)
5 
6 #include "axom/config.hpp"
7 #include "axom/primal/operators/detail/intersect_ray_impl.hpp"
8 
9 #include "axom/core/numerics/Matrix.hpp"     // for numerics::Matrix
10 #include "axom/core/numerics/matvecops.hpp"  // for matrix-vector operators
11 
12 #include "gtest/gtest.h"
13 
14 // C/C++ includes
15 #include <cmath>
16 
17 // namespace aliases
18 namespace primal = axom::primal;
19 namespace numerics = axom::numerics;
20 
21 //------------------------------------------------------------------------------
22 // UNIT TESTS
23 //------------------------------------------------------------------------------
TEST(primal_ray_intersect,ray_aabb_intersection_2D)24 TEST(primal_ray_intersect, ray_aabb_intersection_2D)
25 {
26   constexpr int N = 3;
27   constexpr double LO = 0.0f;
28   constexpr double HI = 1.0f;
29 
30   using namespace primal::detail;
31 
32 // Defines the test box:
33 #define TEST_BOX2D LO, HI, LO, HI
34 
35   double x[N];
36   numerics::linspace(LO, HI, x, N);
37 
38   // test BOTTOM (-y)
39   {
40     const double n0[] = {0.0, 1.0};
41     const double y0 = -1.0f;
42     for(int i = 0; i < N; ++i)
43     {
44       double tmin = 0.0;
45       double tmax = 0.0;
46 
47       const double sx = x[i];
48       const double sy = y0;
49       const double nx = n0[0];
50       const double ny = n0[1];
51       EXPECT_TRUE(intersect_ray(sx, nx, sy, ny, TEST_BOX2D, tmin, tmax));
52       EXPECT_FALSE(intersect_ray(sx, -nx, sy, -ny, TEST_BOX2D, tmin, tmax));
53     }
54   }
55 
56   // test RIGHT (+x)
57   {
58     const double n0[] = {-1.0, 0.0};
59     const double x0 = 2.0f;
60     for(int i = 0; i < N; ++i)
61     {
62       double tmin = 0.0;
63       double tmax = 0.0;
64 
65       const double sx = x0;
66       const double sy = x[i];
67       const double nx = n0[0];
68       const double ny = n0[1];
69       EXPECT_TRUE(intersect_ray(sx, nx, sy, ny, TEST_BOX2D, tmin, tmax));
70       EXPECT_FALSE(intersect_ray(sx, -nx, sy, -ny, TEST_BOX2D, tmin, tmax));
71     }
72   }
73 
74   // test TOP (+y)
75   {
76     const double n0[] = {0.0, -1.0};
77     const double y0 = 2.0f;
78     for(int i = 0; i < N; ++i)
79     {
80       double tmin = 0.0;
81       double tmax = 0.0;
82 
83       const double sx = x[i];
84       const double sy = y0;
85       const double nx = n0[0];
86       const double ny = n0[1];
87       EXPECT_TRUE(intersect_ray(sx, nx, sy, ny, TEST_BOX2D, tmin, tmax));
88       EXPECT_FALSE(intersect_ray(sx, -nx, sy, -ny, TEST_BOX2D, tmin, tmax));
89     }
90   }
91 
92   // test LEFT (-x)
93   {
94     const double n0[] = {1.0, 0.0};
95     const double x0 = -1.0f;
96     for(int i = 0; i < N; ++i)
97     {
98       double tmin = 0.0;
99       double tmax = 0.0;
100 
101       const double sx = x0;
102       const double sy = x[i];
103       const double nx = n0[0];
104       const double ny = n0[1];
105       EXPECT_TRUE(intersect_ray(sx, nx, sy, ny, TEST_BOX2D, tmin, tmax));
106       EXPECT_FALSE(intersect_ray(sx, -nx, sy, -ny, TEST_BOX2D, tmin, tmax));
107     }
108   }
109 
110   // Test a bunch of rays emitted from the box center
111   constexpr int NUM_ANGLES = 20;
112   double angles[NUM_ANGLES];
113   numerics::linspace(0.0, 360.0, angles, NUM_ANGLES);
114 
115   numerics::Matrix<double> rotation_matrix(2, 2);
116 
117   constexpr double PI_OVER_180 = M_PI / 180.0;
118   const double xc[] = {0.5, 0.5};
119   const double e1[] = {1.0, 0.0};
120   for(int i = 0; i < NUM_ANGLES; ++i)
121   {
122     const double t = angles[i] * PI_OVER_180;
123     const double cost = cos(t);
124     const double sint = sin(t);
125 
126     rotation_matrix(0, 0) = cost;
127     rotation_matrix(0, 1) = -sint;
128     rotation_matrix(1, 0) = sint;
129     rotation_matrix(1, 1) = cost;
130 
131     double n[2];
132     numerics::matrix_vector_multiply(rotation_matrix, e1, n);
133 
134     double tmin = 0.0;
135     double tmax = 0.0;
136     const double x0 = xc[0];
137     const double y0 = xc[1];
138     const double nx = n[0];
139     const double ny = n[1];
140     EXPECT_TRUE(intersect_ray(x0, nx, y0, ny, TEST_BOX2D, tmin, tmax));
141     EXPECT_TRUE(intersect_ray(x0, -nx, y0, -ny, TEST_BOX2D, tmin, tmax));
142   }
143 
144 #undef TEST_BOX2D
145 }
146 
147 //------------------------------------------------------------------------------
TEST(primal_ray_intersect,ray_aabb_intersection_3D)148 TEST(primal_ray_intersect, ray_aabb_intersection_3D)
149 {
150   constexpr int N = 3;
151   constexpr double LO = 0.0f;
152   constexpr double HI = 1.0f;
153 
154   using namespace primal::detail;
155 
156 // Defines the test box:
157 #define TEST_BOX3D LO, HI, LO, HI, LO, HI
158 
159   double x[N];
160   numerics::linspace(LO, HI, x, N);
161 
162   // test BOTTOM (-z)
163   {
164     const double n0[] = {0.0, 0.0, 1.0};
165     const double z0 = -1.0f;
166 
167     for(int i = 0; i < N; ++i)
168     {
169       for(int j = 0; j < N; ++j)
170       {
171         double tmin = 0.0;
172         double tmax = 0.0;
173 
174         const double sx = x[i];
175         const double sy = x[j];
176         const double sz = z0;
177         const double nx = n0[0];
178         const double ny = n0[1];
179         const double nz = n0[2];
180         EXPECT_TRUE(intersect_ray(sx, nx, sy, ny, sz, nz, TEST_BOX3D, tmin, tmax));
181         EXPECT_FALSE(
182           intersect_ray(sx, -nx, sy, -ny, sz, -nz, TEST_BOX3D, tmin, tmax));
183       }  // END for all j
184     }    // END for all i
185   }
186 
187   // test TOP (+z)
188   {
189     const double n0[] = {0.0, 0.0, -1.0};
190     const double z0 = 2.0f;
191 
192     for(int i = 0; i < N; ++i)
193     {
194       for(int j = 0; j < N; ++j)
195       {
196         double tmin = 0.0;
197         double tmax = 0.0;
198 
199         const double sx = x[i];
200         const double sy = x[j];
201         const double sz = z0;
202         const double nx = n0[0];
203         const double ny = n0[1];
204         const double nz = n0[2];
205         EXPECT_TRUE(intersect_ray(sx, nx, sy, ny, sz, nz, TEST_BOX3D, tmin, tmax));
206         EXPECT_FALSE(
207           intersect_ray(sx, -nx, sy, -ny, sz, -nz, TEST_BOX3D, tmin, tmax));
208       }  // END for all j
209     }    // END for all i
210   }
211 
212   // test LEFT (-y)
213   {
214     const double n0[] = {0.0, 1.0, 0.0};
215     const double y0 = -1.0f;
216 
217     for(int i = 0; i < N; ++i)
218     {
219       for(int j = 0; j < N; ++j)
220       {
221         double tmin = 0.0;
222         double tmax = 0.0;
223 
224         const double sx = x[i];
225         const double sy = y0;
226         const double sz = x[j];
227         const double nx = n0[0];
228         const double ny = n0[1];
229         const double nz = n0[2];
230         EXPECT_TRUE(intersect_ray(sx, nx, sy, ny, sz, nz, TEST_BOX3D, tmin, tmax));
231         EXPECT_FALSE(
232           intersect_ray(sx, -nx, sy, -ny, sz, -nz, TEST_BOX3D, tmin, tmax));
233       }  // END for all j
234     }    // END for all i
235   }
236 
237   // test RIGHT (+y)
238   {
239     const double n0[] = {0.0, -1.0, 0.0};
240     const double y0 = 2.0f;
241 
242     for(int i = 0; i < N; ++i)
243     {
244       for(int j = 0; j < N; ++j)
245       {
246         double tmin = 0.0;
247         double tmax = 0.0;
248 
249         const double sx = x[i];
250         const double sy = y0;
251         const double sz = x[j];
252         const double nx = n0[0];
253         const double ny = n0[1];
254         const double nz = n0[2];
255         EXPECT_TRUE(intersect_ray(sx, nx, sy, ny, sz, nz, TEST_BOX3D, tmin, tmax));
256         EXPECT_FALSE(
257           intersect_ray(sx, -nx, sy, -ny, sz, -nz, TEST_BOX3D, tmin, tmax));
258       }  // END for all j
259     }    // END for all i
260   }
261 
262   // test BACK (-x)
263   {
264     const double n0[] = {1.0, 0.0, 0.0};
265     const double x0 = -1.0f;
266 
267     for(int i = 0; i < N; ++i)
268     {
269       for(int j = 0; j < N; ++j)
270       {
271         double tmin = 0.0;
272         double tmax = 0.0;
273 
274         const double sx = x0;
275         const double sy = x[i];
276         const double sz = x[j];
277         const double nx = n0[0];
278         const double ny = n0[1];
279         const double nz = n0[2];
280         EXPECT_TRUE(intersect_ray(sx, nx, sy, ny, sz, nz, TEST_BOX3D, tmin, tmax));
281         EXPECT_FALSE(
282           intersect_ray(sx, -nx, sy, -ny, sz, -nz, TEST_BOX3D, tmin, tmax));
283       }  // END for all j
284     }    // END for all i
285   }
286 
287   // test FRONT (+x)
288   {
289     const double n0[] = {-1.0, 0.0, 0.0};
290     const double x0 = 2.0f;
291 
292     for(int i = 0; i < N; ++i)
293     {
294       for(int j = 0; j < N; ++j)
295       {
296         double tmin = 0.0;
297         double tmax = 0.0;
298 
299         const double sx = x0;
300         const double sy = x[i];
301         const double sz = x[j];
302         const double nx = n0[0];
303         const double ny = n0[1];
304         const double nz = n0[2];
305         EXPECT_TRUE(intersect_ray(sx, nx, sy, ny, sz, nz, TEST_BOX3D, tmin, tmax));
306         EXPECT_FALSE(
307           intersect_ray(sx, -nx, sy, -ny, sz, -nz, TEST_BOX3D, tmin, tmax));
308       }  // END for all j
309     }    // END for all i
310   }
311 
312   // Test a bunch of rays emitted from the box center
313   constexpr int NUM_ANGLES = 20;
314   double angles[NUM_ANGLES];
315   numerics::linspace(0.0, 360.0, angles, NUM_ANGLES);
316 
317   numerics::Matrix<double> Rx(3, 3);
318   Rx(0, 0) = 1.0;
319   numerics::Matrix<double> Ry(3, 3);
320   Ry(1, 1) = 1.0;
321   numerics::Matrix<double> Rz(3, 3);
322   Rz(2, 2) = 1.0;
323 
324   constexpr double PI_OVER_180 = M_PI / 180.0;
325   const double xc[] = {0.5, 0.5, 0.5};
326   const double e1[] = {1.0, 0.0, 0.0};
327   const double e2[] = {0.0, 1.0, 0.0};
328   const double e3[] = {0.0, 0.0, 1.0};
329   double n[3];
330   for(int i = 0; i < NUM_ANGLES; ++i)
331   {
332     const double t = angles[i] * PI_OVER_180;
333     const double cost = cos(t);
334     const double sint = sin(t);
335 
336     double tmin = 0.0;
337     double tmax = 0.0;
338 
339     double nx = 0.0;
340     double ny = 0.0;
341     double nz = 0.0;
342 
343     const double x0 = xc[0];
344     const double y0 = xc[1];
345     const double z0 = xc[2];
346 
347     // Update Rx
348     Rx(1, 1) = cost;
349     Rx(1, 2) = -sint;
350     Rx(2, 1) = sint;
351     Rx(2, 2) = cost;
352 
353     numerics::matrix_vector_multiply(Rx, e1, n);
354     nx = n[0];
355     ny = n[1];
356     nz = n[2];
357     EXPECT_TRUE(intersect_ray(x0, nx, y0, ny, z0, nz, TEST_BOX3D, tmin, tmax));
358 
359     // Update Ry
360     Ry(0, 0) = cost;
361     Ry(0, 2) = sint;
362     Ry(2, 0) = -sint;
363     Ry(2, 2) = cost;
364 
365     numerics::matrix_vector_multiply(Ry, e2, n);
366     nx = n[0];
367     ny = n[1];
368     nz = n[2];
369     EXPECT_TRUE(intersect_ray(x0, nx, y0, ny, z0, nz, TEST_BOX3D, tmin, tmax));
370 
371     // Update Rz
372     Rz(0, 0) = cost;
373     Rz(0, 1) = -sint;
374     Rz(1, 0) = sint;
375     Rz(1, 1) = cost;
376 
377     numerics::matrix_vector_multiply(Rz, e3, n);
378     nx = n[0];
379     ny = n[1];
380     nz = n[2];
381     EXPECT_TRUE(intersect_ray(x0, nx, y0, ny, z0, nz, TEST_BOX3D, tmin, tmax));
382   }
383 
384 #undef TEST_BOX3D
385 }
386 
387 //------------------------------------------------------------------------------
388 #include "axom/slic/core/SimpleLogger.hpp"
389 using axom::slic::SimpleLogger;
390 
main(int argc,char * argv[])391 int main(int argc, char* argv[])
392 {
393   int result = 0;
394 
395   ::testing::InitGoogleTest(&argc, argv);
396 
397   SimpleLogger logger;  // create & initialize test logger,
398 
399   // finalized when exiting main scope
400 
401   result = RUN_ALL_TESTS();
402 
403   return result;
404 }
405