GCC Code Coverage Report


Directory: ./
File: libs/geometry/glm/mesh.cpp
Date: 2024-04-17 17:28:21
Exec Total Coverage
Lines: 66 66 100.0%
Branches: 9 16 56.2%

Line Branch Exec Source
1 /************************************************************************
2 *
3 * Copyright (C) 2009-2023 IRCAD France
4 * Copyright (C) 2012-2021 IHU Strasbourg
5 *
6 * This file is part of Sight.
7 *
8 * Sight is free software: you can redistribute it and/or modify it under
9 * the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * Sight is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with Sight. If not, see <https://www.gnu.org/licenses/>.
20 *
21 ***********************************************************************/
22
23 #include "geometry/glm/mesh.hpp"
24
25 #include <core/spy_log.hpp>
26
27 #include <glm/glm.hpp>
28
29 #include <string>
30
31 namespace sight::geometry::glm
32 {
33
34 //-----------------------------------------------------------------------------
35
36 4 ::glm::dvec3 to_barycentric_coord(
37 const ::glm::dvec3& _p,
38 const ::glm::dvec3& _a,
39 const ::glm::dvec3& _b,
40 const ::glm::dvec3& _c
41 )
42 {
43 4 ::glm::dvec3 bary_coord;
44
45 4 const ::glm::dvec3 v0 = _b - _a; // AB Vector
46 4 const ::glm::dvec3 v1 = _c - _a; // AC Vector
47 4 const ::glm::dvec3 v2 = _p - _a; // AP Vector
48
49 // Precompute some dot products.
50 4 const double d00 = ::glm::dot(v0, v0);
51 4 const double d01 = ::glm::dot(v0, v1);
52 4 const double d11 = ::glm::dot(v1, v1);
53 4 const double d20 = ::glm::dot(v2, v0);
54 4 const double d21 = ::glm::dot(v2, v1);
55
56 4 const double div = ((d00 * d11) - (d01 * d01));
57
58 // Don't test the case in release to avoid performance issue.
59 4 SIGHT_ASSERT("Degenerate triangle case leads to zero division.", !(div >= -10e-16 && div <= 10e-16));
60
61 // Inverse the denominator to speed up computation of v & w.
62 4 const double invdenom = 1. / div;
63
64 // Barycentric coordinates
65 4 const double v = ((d11 * d20) - (d01 * d21)) * invdenom;
66 4 const double w = ((d00 * d21) - (d01 * d20)) * invdenom;
67 4 const double u = 1. - v - w; // deduce last coordinate from the two others.
68
69 4 bary_coord.x = u;
70 4 bary_coord.y = v;
71 4 bary_coord.z = w;
72
73 4 return bary_coord;
74 }
75
76 //-----------------------------------------------------------------------------
77
78 6 ::glm::dvec4 to_barycentric_coord(
79 const ::glm::dvec3& _p,
80 const ::glm::dvec3& _a,
81 const ::glm::dvec3& _b,
82 const ::glm::dvec3& _c,
83 const ::glm::dvec3& _d
84 )
85 {
86 /*
87 In general, a point with barycentric coordinates (u, v, w,h) is inside (or on) the tetrahedron(ABCD)
88 if and only if 0 ≤ u, v, w, h ≤ 1, or alternatively
89 if and only if 0 ≤ v ≤ 1, 0 ≤ w ≤ 1, 0 ≤ h ≤ 1, and v + w + h ≤ 1.
90
91 The main idea of the barycentric volume coordinate is a proportionality with the ratio of the sub-tetrahedron
92 volumes over the whole volume. Considering one of the four vertexes (_A, _B, _C, _D), the associated
93 barycentric
94 coordinate are equal to the volume of the tetrahedron build with the three other vertexes and P,
95 divided by the total tetrahedron volume.
96
97 As a result, the principle in the present algorithm, is to compute the three tetrahedron (_A,_B,_C,_P)
98 (_A,_B,_D_P) (_A,_C,_D,_P) volume and the (_A,_B,_C,_D) volume. Then the ratio for respectively,
99 _D, _C, _B vertexes are computed, and the last barycentric coordinate is obtained by the formula
100 u + v + w + h = 1
101 */
102
103 6 ::glm::dvec4 bary_coord;
104
105 6 const ::glm::dvec3 vab = _b - _a; // AB Vector
106 6 const ::glm::dvec3 vac = _c - _a; // AC Vector
107 6 const ::glm::dvec3 vad = _d - _a; // AD Vector
108
109 6 const ::glm::dvec3 vap = _p - _a; // AP Vector
110
111 6 const double volume_b = ::glm::dot(vap, ::glm::cross(vac, vad));
112 6 const double volume_c = ::glm::dot(vap, ::glm::cross(vad, vab));
113 6 const double volume_d = ::glm::dot(vap, ::glm::cross(vab, vac));
114
115 6 const double volume_tot = ::glm::dot(vab, ::glm::cross(vac, vad));
116
117 // Don't test the case in release to avoid performance issue.
118 6 SIGHT_ASSERT("Degenerate triangle case leads to zero division.", volume_tot != 0.);
119
120 // Inverse the denominator to speed up computation of v & w.
121 6 const double invdenom = 1. / volume_tot;
122
123 // Barycentric coordinates
124 6 const double v = volume_b * invdenom;
125 6 const double w = volume_c * invdenom;
126 6 const double h = volume_d * invdenom;
127 6 const double u = 1. - v - w - h; // deduce last coordinate from the two others.
128
129 6 bary_coord[0] = u;
130 6 bary_coord[1] = v;
131 6 bary_coord[2] = w;
132 6 bary_coord[3] = h;
133
134 6 return bary_coord;
135 }
136
137 //-----------------------------------------------------------------------------
138
139 3 ::glm::dvec3 from_barycentric_coord(
140 const ::glm::dvec3& _bary_coord,
141 const ::glm::dvec3& _a,
142 const ::glm::dvec3& _b,
143 const ::glm::dvec3& _c
144 )
145 {
146 3 ::glm::dvec3 world_coordinates;
147
148 // Use standard notation for clarity.
149 3 const double u = _bary_coord[0];
150 3 const double v = _bary_coord[1];
151 3 const double w = _bary_coord[2];
152
153 3 [[maybe_unused]] const double sum = u + v + w; // Only used in the following assertion.
154
155 // Don't test in release to avoid performance issue.
156 SIGHT_ASSERT(
157 "Wrong barycentric coordinates.(u + v + w = " + std::to_string(sum) + ")"
158 ,
159 sum<1. + 10e-9 && sum>1. - 10e-9
160 3 );
161
162 3 world_coordinates = u * _a + v * _b + w * _c;
163
164 3 return world_coordinates;
165 }
166
167 //-----------------------------------------------------------------------------
168
169 1 ::glm::dvec3 from_barycentric_coord(
170 const ::glm::dvec4& _bary_coord,
171 const ::glm::dvec3& _a,
172 const ::glm::dvec3& _b,
173 const ::glm::dvec3& _c,
174 const ::glm::dvec3& _d
175 )
176 {
177 /*
178 General formula (if [u, v, w, h] is normalized).
179 x = (u * _A.x + v * _B.x + w * _C.x + h * _D.x)
180 y = (u * _A.y + v * _B.y + w * _C.y + h * _D.y)
181 z = (u * _A.z + v * _B.z + w * _C.z + h * _D.z)
182 */
183
184 // Use standard notation for clarity.
185 1 const double u = _bary_coord[0];
186 1 const double v = _bary_coord[1];
187 1 const double w = _bary_coord[2];
188 1 const double h = _bary_coord[3];
189
190 1 [[maybe_unused]] const double sum = u + v + w + h; // Only used in the following assertion.
191
192 // Don't test in release to avoid performance issue.
193 SIGHT_ASSERT(
194 "Wrong barycentric coordinates.(u + v + w = " + std::to_string(sum) + ")"
195 ,
196 sum<1. + 10e-9 && sum>1. - 10e-9
197 1 );
198
199 1 return u * _a + v * _b + w * _c + h * _d;
200 }
201
202 //------------------------------------------------------------------------------
203
204 5 bool is_inside_tetrahedron(
205 const ::glm::dvec3& _p,
206 const ::glm::dvec3& _a,
207 const ::glm::dvec3& _b,
208 const ::glm::dvec3& _c,
209 const ::glm::dvec3& _d
210 )
211 {
212 /*
213 There are several ways to determine if a point is inside a tetrahedron.
214 The present algorithm make use of the barycentric coordinates.
215 It first computes the barycentric coordinate of the point inside the tetrahedron, and then checks if all of them
216 are
217 in between 0 and 1.
218 */
219 5 const ::glm::dvec4 barycentric_coord = to_barycentric_coord(_p, _a, _b, _c, _d);
220 5 return is_inside_tetrahedron(barycentric_coord);
221 }
222
223 //------------------------------------------------------------------------------
224
225 6 bool is_inside_tetrahedron(const ::glm::dvec4 _barycentric_coord_p_inside_abcd)
226 {
227 /*
228 There are several ways to determine if a point is inside a tetrahedron.
229 The present algorithm make use of the barycentric coordinates.
230 It checks if all of the barycentric coordinates are in between 0 and 1.
231
232 */
233
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 return 0 <= _barycentric_coord_p_inside_abcd[0] && _barycentric_coord_p_inside_abcd[0] <= 1
234
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 && 0 <= _barycentric_coord_p_inside_abcd[1] && _barycentric_coord_p_inside_abcd[1] <= 1
235
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 && 0 <= _barycentric_coord_p_inside_abcd[2] && _barycentric_coord_p_inside_abcd[2] <= 1
236
4/6
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
11 && 0 <= _barycentric_coord_p_inside_abcd[3] && _barycentric_coord_p_inside_abcd[3] <= 1;
237 }
238
239 //-----------------------------------------------------------------------------
240
241 } // namespace sight::geometry::glm
242