File: mesh.cpp

package info (click to toggle)
sight 25.1.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 43,252 kB
  • sloc: cpp: 310,629; xml: 17,622; ansic: 9,960; python: 1,379; sh: 144; makefile: 33
file content (240 lines) | stat: -rw-r--r-- 8,302 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
/************************************************************************
 *
 * Copyright (C) 2025 IRCAD France
 *
 * This file is part of Sight.
 *
 * Sight is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Sight is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with Sight. If not, see <https://www.gnu.org/licenses/>.
 *
 ***********************************************************************/

#include "geometry/__/mesh.hpp"

#include <core/spy_log.hpp>

#include <glm/glm.hpp>

#include <string>

namespace sight::geometry
{

//-----------------------------------------------------------------------------

::glm::dvec3 to_barycentric_coord(
    const ::glm::dvec3& _p,
    const ::glm::dvec3& _a,
    const ::glm::dvec3& _b,
    const ::glm::dvec3& _c
)
{
    ::glm::dvec3 bary_coord;

    const ::glm::dvec3 v0 = _b - _a; // AB Vector
    const ::glm::dvec3 v1 = _c - _a; // AC Vector
    const ::glm::dvec3 v2 = _p - _a; // AP Vector

    // Precompute some dot products.
    const double d00 = ::glm::dot(v0, v0);
    const double d01 = ::glm::dot(v0, v1);
    const double d11 = ::glm::dot(v1, v1);
    const double d20 = ::glm::dot(v2, v0);
    const double d21 = ::glm::dot(v2, v1);

    const double div = ((d00 * d11) - (d01 * d01));

    // Don't test the case in release to avoid performance issue.
    SIGHT_ASSERT("Degenerate triangle case leads to zero division.", !(div >= -10e-16 && div <= 10e-16));

    // Inverse the denominator to speed up computation of v & w.
    const double invdenom = 1. / div;

    // Barycentric coordinates
    const double v = ((d11 * d20) - (d01 * d21)) * invdenom;
    const double w = ((d00 * d21) - (d01 * d20)) * invdenom;
    const double u = 1. - v - w; // deduce last coordinate from the two others.

    bary_coord.x = u;
    bary_coord.y = v;
    bary_coord.z = w;

    return bary_coord;
}

//-----------------------------------------------------------------------------

::glm::dvec4 to_barycentric_coord(
    const ::glm::dvec3& _p,
    const ::glm::dvec3& _a,
    const ::glm::dvec3& _b,
    const ::glm::dvec3& _c,
    const ::glm::dvec3& _d
)
{
    /*
       In general, a point with barycentric coordinates (u, v, w,h) is inside (or on) the tetrahedron(ABCD)
          if and only if 0 ≤ u, v, w, h ≤ 1, or alternatively
          if and only if 0 ≤ v ≤ 1, 0 ≤ w ≤ 1, 0 ≤ h ≤ 1, and v + w + h ≤ 1.

       The main idea of the barycentric volume coordinate is a proportionality with the ratio of the sub-tetrahedron
          volumes over the whole volume. Considering one of the four vertexes (_A, _B, _C, _D), the associated
             barycentric
          coordinate are equal to the volume of the tetrahedron build with the three other vertexes and P,
          divided by the total tetrahedron volume.

       As a result, the principle in the present algorithm, is to compute the three tetrahedron (_A,_B,_C,_P)
          (_A,_B,_D_P) (_A,_C,_D,_P) volume and the (_A,_B,_C,_D) volume. Then the ratio for respectively,
          _D, _C, _B vertexes are computed, and the last barycentric coordinate is obtained by the formula
          u + v + w + h = 1
     */

    ::glm::dvec4 bary_coord;

    const ::glm::dvec3 vab = _b - _a; // AB Vector
    const ::glm::dvec3 vac = _c - _a; // AC Vector
    const ::glm::dvec3 vad = _d - _a; // AD Vector

    const ::glm::dvec3 vap = _p - _a; // AP Vector

    const double volume_b = ::glm::dot(vap, ::glm::cross(vac, vad));
    const double volume_c = ::glm::dot(vap, ::glm::cross(vad, vab));
    const double volume_d = ::glm::dot(vap, ::glm::cross(vab, vac));

    const double volume_tot = ::glm::dot(vab, ::glm::cross(vac, vad));

    // Don't test the case in release to avoid performance issue.
    SIGHT_ASSERT("Degenerate triangle case leads to zero division.", volume_tot != 0.);

    // Inverse the denominator to speed up computation of v & w.
    const double invdenom = 1. / volume_tot;

    // Barycentric coordinates
    const double v = volume_b * invdenom;
    const double w = volume_c * invdenom;
    const double h = volume_d * invdenom;
    const double u = 1. - v - w - h; // deduce last coordinate from the two others.

    bary_coord[0] = u;
    bary_coord[1] = v;
    bary_coord[2] = w;
    bary_coord[3] = h;

    return bary_coord;
}

//-----------------------------------------------------------------------------

::glm::dvec3 from_barycentric_coord(
    const ::glm::dvec3& _bary_coord,
    const ::glm::dvec3& _a,
    const ::glm::dvec3& _b,
    const ::glm::dvec3& _c
)
{
    ::glm::dvec3 world_coordinates;

    // Use standard notation for clarity.
    const double u = _bary_coord[0];
    const double v = _bary_coord[1];
    const double w = _bary_coord[2];

    [[maybe_unused]] const double sum = u + v + w; // Only used in the following assertion.

    // Don't test in release to avoid performance issue.
    SIGHT_ASSERT(
        "Wrong barycentric coordinates.(u + v + w = " + std::to_string(sum) + ")"
        ,
        sum<1. + 10e-9 && sum>1. - 10e-9
    );

    world_coordinates = u * _a + v * _b + w * _c;

    return world_coordinates;
}

//-----------------------------------------------------------------------------

::glm::dvec3 from_barycentric_coord(
    const ::glm::dvec4& _bary_coord,
    const ::glm::dvec3& _a,
    const ::glm::dvec3& _b,
    const ::glm::dvec3& _c,
    const ::glm::dvec3& _d
)
{
    /*
       General formula (if [u, v, w, h] is normalized).
       x = (u * _A.x + v * _B.x + w * _C.x + h * _D.x)
       y = (u * _A.y + v * _B.y + w * _C.y + h * _D.y)
       z = (u * _A.z + v * _B.z + w * _C.z + h * _D.z)
     */

    // Use standard notation for clarity.
    const double u = _bary_coord[0];
    const double v = _bary_coord[1];
    const double w = _bary_coord[2];
    const double h = _bary_coord[3];

    [[maybe_unused]] const double sum = u + v + w + h; // Only used in the following assertion.

    // Don't test in release to avoid performance issue.
    SIGHT_ASSERT(
        "Wrong barycentric coordinates.(u + v + w = " + std::to_string(sum) + ")"
        ,
        sum<1. + 10e-9 && sum>1. - 10e-9
    );

    return u * _a + v * _b + w * _c + h * _d;
}

//------------------------------------------------------------------------------

bool is_inside_tetrahedron(
    const ::glm::dvec3& _p,
    const ::glm::dvec3& _a,
    const ::glm::dvec3& _b,
    const ::glm::dvec3& _c,
    const ::glm::dvec3& _d
)
{
    /*
       There are several ways to determine if a point is inside a tetrahedron.
       The present algorithm make use of the barycentric coordinates.
       It first computes the barycentric coordinate of the point inside the tetrahedron, and then checks if all of them
          are
          in between 0 and 1.
     */
    const ::glm::dvec4 barycentric_coord = to_barycentric_coord(_p, _a, _b, _c, _d);
    return is_inside_tetrahedron(barycentric_coord);
}

//------------------------------------------------------------------------------

bool is_inside_tetrahedron(const ::glm::dvec4 _barycentric_coord_p_inside_abcd)
{
    /*
       There are several ways to determine if a point is inside a tetrahedron.
       The present algorithm make use of the barycentric coordinates.
       It checks if all of the barycentric coordinates are in between 0 and 1.

     */
    return 0 <= _barycentric_coord_p_inside_abcd[0] && _barycentric_coord_p_inside_abcd[0] <= 1
           && 0 <= _barycentric_coord_p_inside_abcd[1] && _barycentric_coord_p_inside_abcd[1] <= 1
           && 0 <= _barycentric_coord_p_inside_abcd[2] && _barycentric_coord_p_inside_abcd[2] <= 1
           && 0 <= _barycentric_coord_p_inside_abcd[3] && _barycentric_coord_p_inside_abcd[3] <= 1;
}

//-----------------------------------------------------------------------------

} // namespace sight::geometry