Skip to content

Commit c450232

Browse files
committed
fix(Geometry): implements a new computation of aspect ratio for tetrahedron
1 parent a1ef2cd commit c450232

File tree

3 files changed

+203
-5
lines changed

3 files changed

+203
-5
lines changed

include/geode/geometry/quality.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,7 @@ namespace geode
3636
const Tetrahedron& tetra );
3737
[[nodiscard]] double opengeode_geometry_api
3838
tetrahedron_volume_to_edge_ratio( const Tetrahedron& tetra );
39+
40+
[[nodiscard]] double opengeode_geometry_api
41+
tetrahedron_collapse_aspect_ratio( const Tetrahedron& tetra );
3942
} // namespace geode

src/geode/geometry/quality.cpp

Lines changed: 152 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,115 @@
2121
*
2222
*/
2323

24-
// Implementation inspired from https://github.com/sandialabs/verdict
25-
2624
#include <geode/geometry/quality.hpp>
2725

2826
#include <limits>
2927

3028
#include <geode/geometry/basic_objects/tetrahedron.hpp>
29+
#include <geode/geometry/basic_objects/triangle.hpp>
3130
#include <geode/geometry/mensuration.hpp>
31+
#include <geode/geometry/square_matrix.hpp>
3232
#include <geode/geometry/vector.hpp>
3333

34+
namespace
35+
{
36+
37+
std::array< geode::local_index_t, 3 > lu_decomposition(
38+
geode::SquareMatrix3D& matrix )
39+
{
40+
std::array< geode::local_index_t, 3 > indices{ 0, 0, 0 };
41+
double inverse_biggest[3];
42+
for( const auto i : geode::LRange{ 3 } )
43+
{
44+
double biggest{ 0.0 };
45+
for( const auto j : geode::LRange{ 3 } )
46+
{
47+
const auto t = std::fabs( matrix.value( i, j ) );
48+
if( biggest < t )
49+
biggest = t;
50+
}
51+
if( biggest == 0.0 )
52+
{
53+
return indices;
54+
}
55+
inverse_biggest[i] = 1.0 / biggest;
56+
indices[i] = i;
57+
}
58+
geode::local_index_t pivot_id{ 0 };
59+
for( const auto k : geode::LRange{ 2 } )
60+
{
61+
double biggest{ 0.0 };
62+
for( const auto i : geode::LRange{ k, 3 } )
63+
{
64+
const double t = std::fabs( matrix.value( indices[i], k ) )
65+
* inverse_biggest[indices[i]];
66+
if( biggest < t )
67+
{
68+
biggest = t;
69+
pivot_id = i;
70+
}
71+
}
72+
if( biggest == 0.0 )
73+
{
74+
return indices;
75+
}
76+
if( pivot_id != k )
77+
{
78+
const int j = indices[k];
79+
indices[k] = indices[pivot_id];
80+
indices[pivot_id] = j;
81+
}
82+
const auto pivot = matrix.value( indices[k], k );
83+
for( const auto i : geode::LRange{ k + 1, 3 } )
84+
{
85+
const double m = matrix.value( indices[i], k ) / pivot;
86+
matrix.set_value( indices[i], k, m );
87+
if( m != 0.0 )
88+
{
89+
for( const auto j : geode::LRange{ k + 1, 3 } )
90+
{
91+
matrix.set_value( indices[i], j,
92+
matrix.value( indices[i], j )
93+
- m * matrix.value( indices[k], j ) );
94+
}
95+
}
96+
}
97+
}
98+
return indices;
99+
}
100+
std::array< double, 3 > lu_solving( const geode::SquareMatrix3D& lu_matrix,
101+
const std::array< double, 3 >& rhs,
102+
const std::array< geode::local_index_t, 3 >& indices )
103+
{
104+
std::array< double, 3 > result{ 0., 0., 0. };
105+
for( const auto i : geode::LRange{ 3 } )
106+
{
107+
double s{ 0.0 };
108+
for( const auto j : geode::LRange{ 0, i } )
109+
{
110+
s += lu_matrix.value( indices[i], j ) * result[j];
111+
}
112+
result[i] = rhs[indices[i]] - s;
113+
}
114+
for( const auto i :
115+
geode::LReverseRange{ 3 } ) // for( int i = 2; i >= 0; --i )
116+
{
117+
double s{ 0.0 };
118+
for( const auto j : geode::LRange{ i + 1, 3 } )
119+
{
120+
s += lu_matrix.value( indices[i], j ) * result[j];
121+
}
122+
result[i] = ( result[i] - s ) / lu_matrix.value( indices[i], i );
123+
}
124+
125+
return result;
126+
}
127+
128+
} // namespace
129+
34130
namespace geode
35131
{
132+
// Implementation inspired from https://github.com/sandialabs/verdict
36133
double tetrahedron_aspect_ratio( const Tetrahedron& tetra )
37134
{
38135
const auto& vertices = tetra.vertices();
@@ -87,4 +184,57 @@ namespace geode
87184
const auto l_rms = std::sqrt( sq_len / 6 );
88185
return 6 * std::sqrt( 2 ) * signed_volume / ( l_rms * l_rms * l_rms );
89186
}
187+
188+
double tetrahedron_collapse_aspect_ratio( const Tetrahedron& tetra )
189+
{
190+
if( geode::tetrahedron_volume( tetra ) < geode::GLOBAL_EPSILON )
191+
{
192+
return std::numeric_limits< double >::max();
193+
}
194+
195+
const auto& vertices = tetra.vertices();
196+
const Vector3D edge_ab{ vertices[0], vertices[1] };
197+
const Vector3D edge_ac{ vertices[0], vertices[2] };
198+
const Vector3D edge_ad{ vertices[0], vertices[3] };
199+
const Vector3D edge_bc{ vertices[1], vertices[2] };
200+
const Vector3D edge_bd{ vertices[1], vertices[3] };
201+
const Vector3D edge_cd{ vertices[2], vertices[3] };
202+
const auto edge_ab_l2 = edge_ab.length2();
203+
const auto edge_bc_l2 = edge_bc.length2();
204+
const auto edge_ac_l2 = edge_ac.length2();
205+
const auto edge_ad_l2 = edge_ad.length2();
206+
const auto edge_bd_l2 = edge_bd.length2();
207+
const auto edge_cd_l2 = edge_cd.length2();
208+
const auto longest_edge_length = std::sqrt( std::max( { edge_ab_l2,
209+
edge_bc_l2, edge_ac_l2, edge_ad_l2, edge_bd_l2, edge_cd_l2 } ) );
210+
211+
geode::SquareMatrix3D matrix{ { edge_ad, edge_bd, edge_cd } };
212+
const auto row_indices = lu_decomposition( matrix );
213+
214+
std::array< Vector3D, 4 > N;
215+
for( const auto j : geode::LRange{ 3 } )
216+
{
217+
std::array< double, 3 > b{ 0., 0., 0. };
218+
b[j] = 1.0; // Positive means the inside direction
219+
N[j] = Vector3D{ lu_solving( matrix, b, row_indices ) };
220+
}
221+
N[3] = ( N[0] + N[1] + N[2] ) * -1.;
222+
223+
std::array< double, 4 >
224+
H; // H[i] = inverse of the height of its corresponding face
225+
for( const auto i : geode::LRange{ 4 } )
226+
{
227+
H[i] = N[i].length();
228+
}
229+
230+
auto heightinv = H[0];
231+
for( const auto i : geode::LRange{ 1, 4 } )
232+
{ // Get the biggest H[i] (corresponding to the smallest height)
233+
if( H[i] > heightinv )
234+
{
235+
heightinv = H[i];
236+
}
237+
}
238+
return ( longest_edge_length * heightinv ) / std::sqrt( 3. / 2. );
239+
}
90240
} // namespace geode

tests/geometry/test-quality.cpp

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525

2626
#include <limits>
2727

28+
#include <geode/geometry/basic_objects/segment.hpp>
2829
#include <geode/geometry/basic_objects/tetrahedron.hpp>
30+
#include <geode/geometry/basic_objects/triangle.hpp>
2931
#include <geode/geometry/distance.hpp>
3032
#include <geode/geometry/point.hpp>
3133
#include <geode/geometry/quality.hpp>
@@ -43,8 +45,14 @@ void test_perfect_tetrahedron()
4345
bary + geode::Point3D{ { 0, 0, std::sqrt( 1 - length * length ) } };
4446
const geode::Tetrahedron tetra{ point0, point1, point2, point3 };
4547
const auto quality = geode::tetrahedron_aspect_ratio( tetra );
46-
OPENGEODE_EXCEPTION( quality - 1 < geode::GLOBAL_EPSILON,
48+
const auto quality2 = geode::tetrahedron_volume_to_edge_ratio( tetra );
49+
const auto quality3 = geode::tetrahedron_collapse_aspect_ratio( tetra );
50+
OPENGEODE_EXCEPTION( std::fabs( quality - 1 ) < geode::GLOBAL_EPSILON,
4751
"[Test] Wrong aspect ratio for perfect tetrahedron ", quality );
52+
OPENGEODE_EXCEPTION( std::fabs( quality2 - 1 ) < geode::GLOBAL_EPSILON,
53+
"[Test] Wrong aspect ratio 2 for perfect tetrahedron ", quality2 );
54+
OPENGEODE_EXCEPTION( std::fabs( quality3 - 1 ) < geode::GLOBAL_EPSILON,
55+
"[Test] Wrong aspect ratio 3 for perfect tetrahedron ", quality3 );
4856
}
4957

5058
void test_regular_tetrahedron()
@@ -55,8 +63,15 @@ void test_regular_tetrahedron()
5563
const geode::Point3D point3{ { 0, 0, 1 } };
5664
const geode::Tetrahedron tetra{ point0, point1, point2, point3 };
5765
const auto quality = geode::tetrahedron_aspect_ratio( tetra );
58-
OPENGEODE_EXCEPTION( quality - 1.36603 < geode::GLOBAL_EPSILON,
66+
const auto quality2 = geode::tetrahedron_volume_to_edge_ratio( tetra );
67+
const auto quality3 = geode::tetrahedron_collapse_aspect_ratio( tetra );
68+
OPENGEODE_EXCEPTION(
69+
std::fabs( quality - 1.366026 ) < geode::GLOBAL_EPSILON,
5970
"[Test] Wrong aspect ratio for regular tetrahedron ", quality );
71+
OPENGEODE_EXCEPTION( std::fabs( quality2 - 0.7698 ) < geode::GLOBAL_EPSILON,
72+
"[Test] Wrong aspect ratio 2 for regular tetrahedron ", quality2 );
73+
OPENGEODE_EXCEPTION( std::fabs( quality3 - 2 ) < geode::GLOBAL_EPSILON,
74+
"[Test] Wrong aspect ratio 3 for regular tetrahedron ", quality3 );
6075
}
6176

6277
void test_sliver_tetrahedron()
@@ -67,15 +82,45 @@ void test_sliver_tetrahedron()
6782
const geode::Point3D point3{ { 1, 1, 0 } };
6883
const geode::Tetrahedron tetra{ point0, point1, point2, point3 };
6984
const auto quality = geode::tetrahedron_aspect_ratio( tetra );
85+
const auto quality2 = geode::tetrahedron_volume_to_edge_ratio( tetra );
86+
const auto quality3 = geode::tetrahedron_collapse_aspect_ratio( tetra );
7087
OPENGEODE_EXCEPTION( quality == std::numeric_limits< double >::max(),
71-
"[Test] Wrong aspect ratio for regular tetrahedron ", quality );
88+
"[Test] Wrong aspect ratio for sliver tetrahedron ", quality );
89+
OPENGEODE_EXCEPTION( std::fabs( quality2 ) < geode::GLOBAL_EPSILON,
90+
"[Test] Wrong aspect ratio 2 for sliver tetrahedron ", quality2 );
91+
OPENGEODE_EXCEPTION( quality3 == std::numeric_limits< double >::max(),
92+
"[Test] Wrong aspect ratio 3 for sliver tetrahedron ", quality3 );
93+
}
94+
95+
void test_other_tetrahedron()
96+
{
97+
const geode::Point3D point0{ { 0, 0, 0 } };
98+
const geode::Point3D point1{ { 4, 0, 0 } };
99+
const geode::Point3D point2{ { 2, 1, 0 } };
100+
const geode::Point3D point3{ { 2, 1, 1 } };
101+
const geode::Tetrahedron tetra{ point0, point1, point2, point3 };
102+
const auto quality = geode::tetrahedron_aspect_ratio( tetra );
103+
const auto quality2 = geode::tetrahedron_volume_to_edge_ratio( tetra );
104+
const auto quality3 = geode::tetrahedron_collapse_aspect_ratio( tetra );
105+
106+
OPENGEODE_EXCEPTION(
107+
std::fabs( quality - 2.884068 ) < geode::GLOBAL_EPSILON,
108+
"[Test] Wrong aspect ratio for random tetrahedron ", quality );
109+
OPENGEODE_EXCEPTION(
110+
std::fabs( quality2 - 0.341354 ) < geode::GLOBAL_EPSILON,
111+
"[Test] Wrong aspect ratio 2 for random tetrahedron ", quality2 );
112+
OPENGEODE_EXCEPTION(
113+
std::fabs( quality3 - 4.618802 ) < geode::GLOBAL_EPSILON,
114+
"[Test] Wrong aspect ratio 3 for random tetrahedron ", quality3 );
72115
}
73116

74117
void test()
75118
{
119+
geode::Logger::set_level( geode::Logger::LEVEL::debug );
76120
test_perfect_tetrahedron();
77121
test_regular_tetrahedron();
78122
test_sliver_tetrahedron();
123+
test_other_tetrahedron();
79124
}
80125

81126
OPENGEODE_TEST( "quality" )

0 commit comments

Comments
 (0)