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 |