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 "gtest/gtest.h"
7 
8 #include "axom/config.hpp"
9 #include "axom/slic.hpp"
10 
11 #include "axom/primal/geometry/Point.hpp"
12 #include "axom/primal/geometry/Tetrahedron.hpp"
13 
14 #include "axom/fmt.hpp"
15 
16 #include <cmath>
17 
18 using namespace axom;
19 
20 /// Test fixture for testing primal::Tetrahedron
21 class TetrahedronTest : public ::testing::Test
22 {
23 public:
24   static const int DIM = 3;
25 
26   typedef double CoordType;
27   typedef primal::Point<CoordType, DIM> QPoint;
28   typedef primal::Tetrahedron<CoordType, DIM> QTet;
29 
30 protected:
SetUp()31   virtual void SetUp()
32   {
33     EPS = 1e-12;
34 
35     // Define coordinates for first tetrahedron
36     qData0[0] = QPoint::make_point(0, 0, 0);
37     qData0[1] = QPoint::make_point(1, 0, 0);
38     qData0[2] = QPoint::make_point(1, 1, 0);
39     qData0[3] = QPoint::make_point(1, 1, 1);
40 
41     // Define coordinates for second tetrahedron
42     qData1[0] = QPoint::make_point(1, 0, 0);
43     qData1[1] = QPoint::make_point(0, 1, 0);
44     qData1[2] = QPoint::make_point(0, 0, 1);
45     qData1[3] = QPoint::make_point(0, 0, 0);
46   }
47 
48   QPoint qData0[4];
49   QPoint qData1[4];
50   double EPS;
51 };
52 
53 //------------------------------------------------------------------------------
TEST_F(TetrahedronTest,defaultConstructor)54 TEST_F(TetrahedronTest, defaultConstructor)
55 {
56   typedef TetrahedronTest::QPoint QPoint;
57   typedef TetrahedronTest::QTet QTet;
58 
59   const QTet tet;
60 
61   // Test ostream operator
62   SLIC_INFO("Empty tetrahedron coordinates: " << tet);
63 
64   // Check indirection operator
65   EXPECT_EQ(QPoint::zero(), tet[0]);
66   EXPECT_EQ(QPoint::zero(), tet[1]);
67   EXPECT_EQ(QPoint::zero(), tet[2]);
68   EXPECT_EQ(QPoint::zero(), tet[3]);
69 }
70 
TEST_F(TetrahedronTest,constructFromPoints)71 TEST_F(TetrahedronTest, constructFromPoints)
72 {
73   typedef TetrahedronTest::QPoint QPoint;
74   typedef TetrahedronTest::QTet QTet;
75 
76   // Access the test data
77   const QPoint* pt = this->qData0;
78 
79   QTet tet(pt[0], pt[1], pt[2], pt[3]);
80 
81   // Test ostream operator
82   SLIC_INFO("Tetrahedron coordinates: " << tet);
83 
84   // Check indirection operator
85   EXPECT_EQ(pt[0], tet[0]);
86   EXPECT_EQ(pt[1], tet[1]);
87   EXPECT_EQ(pt[2], tet[2]);
88   EXPECT_EQ(pt[3], tet[3]);
89 }
90 
TEST_F(TetrahedronTest,volume)91 TEST_F(TetrahedronTest, volume)
92 {
93   typedef TetrahedronTest::QPoint QPoint;
94   typedef TetrahedronTest::QTet QTet;
95 
96   // Access the test data
97   const QPoint* pt = this->qData0;
98   QTet tet(pt[0], pt[1], pt[2], pt[3]);
99 
100   double expVolume = 1. / 6.;
101   EXPECT_EQ(expVolume, tet.signedVolume());
102   EXPECT_EQ(tet.signedVolume(), tet.volume());
103 }
104 
TEST_F(TetrahedronTest,degenerate)105 TEST_F(TetrahedronTest, degenerate)
106 {
107   typedef TetrahedronTest::QPoint QPoint;
108   typedef TetrahedronTest::QTet QTet;
109 
110   // Access the test data
111   const QPoint* pt = this->qData0;
112   QTet tet(pt[0], pt[1], pt[2], pt[3]);
113 
114   EXPECT_FALSE(tet.degenerate());
115 
116   // Make the tet degenerate by identifying two vertices
117   tet[0] = tet[1];
118   EXPECT_TRUE(tet.degenerate());
119 }
120 
TEST_F(TetrahedronTest,barycentric)121 TEST_F(TetrahedronTest, barycentric)
122 {
123   typedef TetrahedronTest::CoordType CoordType;
124   typedef TetrahedronTest::QPoint QPoint;
125   typedef TetrahedronTest::QTet QTet;
126 
127   typedef primal::Point<CoordType, 4> RPoint;
128   typedef std::vector<std::pair<QPoint, RPoint>> TestVec;
129 
130   const QPoint* pt = this->qData1;
131   QTet tet(pt[0], pt[1], pt[2], pt[3]);
132 
133   TestVec testData;
134 
135   // Test the four vertices
136   const CoordType coord_list0[] = {1., 0., 0., 0.};
137   const CoordType coord_list1[] = {0., 1., 0., 0.};
138   const CoordType coord_list2[] = {0., 0., 1., 0.};
139   const CoordType coord_list3[] = {0., 0., 0., 1.};
140   testData.push_back(std::make_pair(pt[0], RPoint(coord_list0, 4)));
141   testData.push_back(std::make_pair(pt[1], RPoint(coord_list1, 4)));
142   testData.push_back(std::make_pair(pt[2], RPoint(coord_list2, 4)));
143   testData.push_back(std::make_pair(pt[3], RPoint(coord_list3, 4)));
144 
145   // Test some of the edge midpoints
146   const CoordType coord_list4[] = {0.5, 0.5, 0., 0.};
147   const CoordType coord_list5[] = {0., 0.5, 0.5, 0.};
148   const CoordType coord_list6[] = {0., 0., 0.5, 0.5};
149   testData.push_back(std::make_pair(QPoint(0.5 * (pt[0].array() + pt[1].array())),
150                                     RPoint(coord_list4, 4)));
151   testData.push_back(std::make_pair(QPoint(0.5 * (pt[1].array() + pt[2].array())),
152                                     RPoint(coord_list5, 4)));
153   testData.push_back(std::make_pair(QPoint(0.5 * (pt[2].array() + pt[3].array())),
154                                     RPoint(coord_list6, 4)));
155 
156   // Test the centroid
157   const CoordType coord_list7[] = {1. / 4., 1. / 4., 1. / 4., 1. / 4.};
158   testData.push_back(std::make_pair(
159     QPoint(1. / 4. *
160            (pt[0].array() + pt[1].array() + pt[2].array() + pt[3].array())),
161     RPoint(coord_list7, 4)));
162 
163   // Test a point outside the tetrahedron
164   const CoordType coord_list8[] = {-0.4, 1.2, 0.2, 0.};
165   testData.push_back(std::make_pair(
166     QPoint(-0.4 * pt[0].array() + 1.2 * pt[1].array() + 0.2 * pt[2].array()),
167     RPoint(coord_list8, 4)));
168 
169   // Now run the actual tests
170   for(TestVec::const_iterator it = testData.begin(); it != testData.end(); ++it)
171   {
172     const QPoint& query = it->first;
173     const RPoint& expBary = it->second;
174     RPoint bary = tet.physToBarycentric(query);
175 
176     SLIC_DEBUG(fmt::format(
177       "Computed barycentric coordinates for tetrahedron {} and point {} are {}",
178       tet,
179       query,
180       bary));
181 
182     EXPECT_NEAR(bary[0], expBary[0], this->EPS);
183     EXPECT_NEAR(bary[1], expBary[1], this->EPS);
184     EXPECT_NEAR(bary[2], expBary[2], this->EPS);
185     EXPECT_NEAR(bary[3], expBary[3], this->EPS);
186   }
187 }
188 
189 //----------------------------------------------------------------------
190 //----------------------------------------------------------------------
191 using axom::slic::SimpleLogger;
192 
main(int argc,char * argv[])193 int main(int argc, char* argv[])
194 {
195   ::testing::InitGoogleTest(&argc, argv);
196 
197   SimpleLogger logger;  // create & initialize test logger,
198   axom::slic::setLoggingMsgLevel(axom::slic::message::Info);
199 
200   int result = RUN_ALL_TESTS();
201   return result;
202 }
203