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