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
|