Skip to content

Commit 152af67

Browse files
committed
feat(SurfaceIntersections): Added an inspector to check the self intersections of surfaces.
1 parent aa1d6f9 commit 152af67

File tree

15 files changed

+792
-11
lines changed

15 files changed

+792
-11
lines changed

bindings/python/src/inspector/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ add_geode_python_binding(
3535
"degeneration/solid_degeneration.h"
3636
"degeneration/surface_degeneration.h"
3737
"degeneration/component_meshes_degeneration.h"
38+
"intersections/surface_intersections.h"
3839
"manifold/surface_edge_manifold.h"
3940
"manifold/surface_vertex_manifold.h"
4041
"manifold/solid_vertex_manifold.h"

bindings/python/src/inspector/inspector.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
#include "degeneration/solid_degeneration.h"
4343
#include "degeneration/surface_degeneration.h"
4444

45+
#include "intersections/surface_intersections.h"
46+
4547
#include "manifold/brep_meshes_manifold.h"
4648
#include "manifold/section_meshes_manifold.h"
4749
#include "manifold/solid_edge_manifold.h"
@@ -92,6 +94,7 @@ PYBIND11_MODULE( opengeode_inspector_py_inspector, module )
9294
geode::define_solid_degeneration( module );
9395
geode::define_surface_degeneration( module );
9496
geode::define_models_meshes_degeneration( module );
97+
geode::define_surface_intersections( module );
9598
geode::define_surface_edge_manifold( module );
9699
geode::define_surface_vertex_manifold( module );
97100
geode::define_solid_edge_manifold( module );
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright (c) 2019 - 2023 Geode-solutions
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
*
22+
*/
23+
24+
#include <geode/mesh/core/surface_mesh.h>
25+
26+
#include <geode/inspector/criterion/intersections/surface_intersections.h>
27+
28+
#define PYTHON_SURFACE_INTERSECTIONS( dimension ) \
29+
const auto name##dimension = \
30+
"SurfaceMeshIntersections" + std::to_string( dimension ) + "D"; \
31+
pybind11::class_< SurfaceMeshIntersections##dimension##D >( \
32+
module, name##dimension.c_str() ) \
33+
.def( pybind11::init< const SurfaceMesh< dimension >& >() ) \
34+
.def( pybind11::init< const SurfaceMesh< dimension >&, bool >() ) \
35+
.def( "mesh_has_self_intersections", \
36+
&SurfaceMeshIntersections##dimension##D:: \
37+
mesh_has_self_intersections ) \
38+
.def( "nb_intersecting_elements_pair", \
39+
&SurfaceMeshIntersections##dimension##D:: \
40+
nb_intersecting_elements_pair ) \
41+
.def( "intersecting_elements", \
42+
&SurfaceMeshIntersections##dimension##D::intersecting_elements )
43+
44+
namespace geode
45+
{
46+
void define_surface_intersections( pybind11::module& module )
47+
{
48+
PYTHON_SURFACE_INTERSECTIONS( 2 );
49+
PYTHON_SURFACE_INTERSECTIONS( 3 );
50+
}
51+
} // namespace geode

bindings/python/src/inspector/surface_inspector.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
SurfaceMeshAdjacency##dimension##D, \
3333
SurfaceMeshColocation##dimension##D, \
3434
SurfaceMeshDegeneration##dimension##D, \
35+
SurfaceMeshIntersections##dimension##D, \
3536
SurfaceMeshEdgeManifold##dimension##D, \
3637
SurfaceMeshVertexManifold##dimension##D >( \
3738
module, name##dimension.c_str() ) \

bindings/python/tests/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@ add_geode_python_test(
7878
${PROJECT_NAME}::py_inspector
7979
)
8080

81+
add_geode_python_test(
82+
SOURCE "test-py-surface-intersections.py"
83+
DEPENDENCIES
84+
${PROJECT_NAME}::py_inspector
85+
)
86+
8187
add_geode_python_test(
8288
SOURCE "test-py-surface-manifold.py"
8389
DEPENDENCIES

bindings/python/tests/test-py-surface-adjacency.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import opengeode as geode
3030
import opengeode_inspector_py_inspector as inspector
3131

32-
3332
def check_adjacency2D():
3433
surface = geode.TriangulatedSurface2D.create()
3534
builder = geode.TriangulatedSurfaceBuilder2D.create(surface)
@@ -54,7 +53,6 @@ def check_adjacency2D():
5453
raise ValueError(
5554
"[Test] Surface edges adjacencies are shown wrong whereas they are not.")
5655

57-
5856
def check_non_adjacency_no_bijection2D():
5957
surface = geode.TriangulatedSurface2D.create()
6058
builder = geode.TriangulatedSurfaceBuilder2D.create(surface)
@@ -82,7 +80,6 @@ def check_non_adjacency_no_bijection2D():
8280
if not adjacency_inspector.polygon_edges_with_wrong_adjacency()[0] == polygon_edge:
8381
raise ValueError("[Test] Surface edges show wrong adjacency problems.")
8482

85-
8683
def check_non_adjacency_wrong_edge2D():
8784
surface = geode.TriangulatedSurface2D.create()
8885
builder = geode.TriangulatedSurfaceBuilder2D.create(surface)
@@ -112,7 +109,6 @@ def check_non_adjacency_wrong_edge2D():
112109
raise ValueError(
113110
"[Test] Surface shows wrong second edge with adjacency problems.")
114111

115-
116112
def check_non_adjacency_inversed_triangle2D():
117113
surface = geode.TriangulatedSurface2D.create()
118114
builder = geode.TriangulatedSurfaceBuilder2D.create(surface)
@@ -142,7 +138,6 @@ def check_non_adjacency_inversed_triangle2D():
142138
raise ValueError(
143139
"[Test] Surface shows wrong second edge with adjacency problems due to an inversed triangle..")
144140

145-
146141
def check_adjacency3D():
147142
surface = geode.TriangulatedSurface3D.create()
148143
builder = geode.TriangulatedSurfaceBuilder3D.create(surface)
@@ -167,7 +162,6 @@ def check_adjacency3D():
167162
raise ValueError(
168163
"[Test] 3D Surface edges adjacencies are shown wrong whereas they are not.")
169164

170-
171165
def check_non_adjacency_no_bijection3D():
172166
surface = geode.TriangulatedSurface3D.create()
173167
builder = geode.TriangulatedSurfaceBuilder3D.create(surface)
@@ -196,7 +190,6 @@ def check_non_adjacency_no_bijection3D():
196190
raise ValueError(
197191
"[Test] 3D Surface edges show wrong adjacency problems.")
198192

199-
200193
def check_non_adjacency_wrong_edge3D():
201194
surface = geode.TriangulatedSurface3D.create()
202195
builder = geode.TriangulatedSurfaceBuilder3D.create(surface)
@@ -226,7 +219,6 @@ def check_non_adjacency_wrong_edge3D():
226219
raise ValueError(
227220
"[Test] Surface shows wrong second edge with adjacency problems.")
228221

229-
230222
def check_non_adjacency_inversed_triangle3D():
231223
surface = geode.TriangulatedSurface3D.create()
232224
builder = geode.TriangulatedSurfaceBuilder3D.create(surface)
@@ -256,7 +248,6 @@ def check_non_adjacency_inversed_triangle3D():
256248
raise ValueError(
257249
"[Test] 3D Surface shows wrong second edge with adjacency problems due to an inversed triangle..")
258250

259-
260251
if __name__ == '__main__':
261252
inspector.OpenGeodeInspectorInspector.initialize()
262253
check_adjacency2D()
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright (c) 2019 - 2023 Geode-solutions
3+
#
4+
# Permission is hereby granted, free of charge, to any person obtaining a copy
5+
# of this software and associated documentation files (the "Software"), to deal
6+
# in the Software without restriction, including without limitation the rights
7+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
# copies of the Software, and to permit persons to whom the Software is
9+
# furnished to do so, subject to the following conditions:
10+
#
11+
# The above copyright notice and this permission notice shall be included in
12+
# all copies or substantial portions of the Software.
13+
#
14+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
# SOFTWARE.
21+
22+
import os
23+
import sys
24+
import platform
25+
if sys.version_info >= (3, 8, 0) and platform.system() == "Windows":
26+
for path in [x.strip() for x in os.environ['PATH'].split(';') if x]:
27+
os.add_dll_directory(path)
28+
29+
import opengeode as geode
30+
import opengeode_inspector_py_inspector as inspector
31+
32+
def check_intersections2D():
33+
surface = geode.TriangulatedSurface2D.create()
34+
builder = geode.TriangulatedSurfaceBuilder2D.create( surface )
35+
builder.create_vertices( 5 )
36+
builder.set_point( 0, geode.Point2D( [ 0., 0. ] ) )
37+
builder.set_point( 1, geode.Point2D( [ 3., 0. ] ) )
38+
builder.set_point( 2, geode.Point2D( [ 0., 4. ] ) )
39+
builder.set_point( 3, geode.Point2D( [ 3., 4. ] ) )
40+
builder.set_point( 4, geode.Point2D( [ -1., 2. ] ) )
41+
builder.create_triangle( [ 0, 1, 2 ] )
42+
builder.create_triangle( [ 0, 1, 3 ] )
43+
builder.create_triangle( [ 1, 3, 4 ] )
44+
builder.set_polygon_adjacent( geode.PolygonEdge( 0, 0 ), 1 )
45+
builder.set_polygon_adjacent( geode.PolygonEdge( 1, 0 ), 0 )
46+
builder.set_polygon_adjacent( geode.PolygonEdge( 1, 1 ), 2 )
47+
builder.set_polygon_adjacent( geode.PolygonEdge( 2, 0 ), 1 )
48+
49+
intersections_inspector = inspector.SurfaceMeshIntersections2D( surface )
50+
if not intersections_inspector.mesh_has_self_intersections():
51+
raise ValueError( "[Test] 2D Surface should have intersections." )
52+
if not intersections_inspector.nb_intersecting_elements_pair() == 3:
53+
raise ValueError( "[Test] 2D Surface should have 3 intersecting elements pair." )
54+
triangles_inter = intersections_inspector.intersecting_elements()
55+
if len( triangles_inter ) != 3 or triangles_inter[0][0] != 2 or triangles_inter[0][1] != 0 or triangles_inter[1][0] != 2 or triangles_inter[1][1] != 1 or triangles_inter[2][0] != 0 or triangles_inter[2][1] != 1:
56+
raise ValueError( "[Test] 2D Surface has wrong intersecting elements pairs." )
57+
58+
def check_intersections3D():
59+
surface = geode.TriangulatedSurface3D.create()
60+
builder = geode.TriangulatedSurfaceBuilder3D.create( surface )
61+
builder.create_vertices( 7 )
62+
builder.set_point( 0, geode.Point3D( [ 0., 0., 0. ] ) )
63+
builder.set_point( 1, geode.Point3D( [ 0., 0., 3. ] ) )
64+
builder.set_point( 2, geode.Point3D( [ 0., 2., 0. ] ) )
65+
builder.set_point( 3, geode.Point3D( [ 2., 0., 0. ] ) )
66+
builder.set_point( 4, geode.Point3D( [ 5., 0., 1.5 ] ) )
67+
builder.set_point( 5, geode.Point3D( [ 2., 0.5, 2. ] ) )
68+
builder.set_point( 6, geode.Point3D( [ 0., 0., 1.5 ] ) )
69+
builder.create_triangle( [ 0, 1, 2 ] )
70+
builder.create_triangle( [ 0, 2, 3 ] )
71+
builder.create_triangle( [ 3, 5, 2 ] )
72+
builder.create_triangle( [ 5, 2, 4 ] )
73+
builder.create_triangle( [ 4, 2, 6 ] )
74+
builder.set_polygon_adjacent( geode.PolygonEdge( 0, 2 ), 1 )
75+
builder.set_polygon_adjacent( geode.PolygonEdge( 1, 0 ), 0 )
76+
builder.set_polygon_adjacent( geode.PolygonEdge( 1, 1 ), 2 )
77+
builder.set_polygon_adjacent( geode.PolygonEdge( 2, 2 ), 1 )
78+
builder.set_polygon_adjacent( geode.PolygonEdge( 2, 1 ), 3 )
79+
builder.set_polygon_adjacent( geode.PolygonEdge( 3, 0 ), 2 )
80+
81+
intersections_inspector = inspector.SurfaceMeshIntersections3D(surface)
82+
if not intersections_inspector.mesh_has_self_intersections():
83+
raise ValueError( "[Test] 3D Surface should have intersections." )
84+
nb_intersections = intersections_inspector.nb_intersecting_elements_pair()
85+
if not nb_intersections == 2:
86+
raise ValueError( "[Test] 3D Surface should have 2 intersecting elements pair." )
87+
triangles_inter = intersections_inspector.intersecting_elements()
88+
if len( triangles_inter ) != 2 or triangles_inter[0][0] != 0 or triangles_inter[0][1] != 4 or triangles_inter[1][0] != 2 or triangles_inter[1][1] != 4:
89+
raise ValueError( "[Test] 3D Surface has wrong intersecting elements pairs." )
90+
91+
if __name__ == '__main__':
92+
inspector.OpenGeodeInspectorInspector.initialize()
93+
check_intersections2D()
94+
check_intersections3D()
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright (c) 2019 - 2023 Geode-solutions
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
*
22+
*/
23+
24+
#pragma once
25+
26+
#include <absl/types/span.h>
27+
28+
#include <geode/basic/pimpl.h>
29+
30+
#include <geode/inspector/common.h>
31+
32+
namespace geode
33+
{
34+
FORWARD_DECLARATION_DIMENSION_CLASS( SurfaceMesh );
35+
struct ComponentMeshElement;
36+
} // namespace geode
37+
38+
namespace geode
39+
{
40+
/*!
41+
* Class for inspecting the intersections of SurfaceMeshes
42+
*/
43+
template < index_t dimension >
44+
class opengeode_inspector_inspector_api SurfaceMeshIntersections
45+
{
46+
OPENGEODE_DISABLE_COPY( SurfaceMeshIntersections );
47+
48+
public:
49+
SurfaceMeshIntersections( const SurfaceMesh< dimension >& mesh );
50+
51+
SurfaceMeshIntersections(
52+
const SurfaceMesh< dimension >& mesh, bool verbose );
53+
54+
~SurfaceMeshIntersections();
55+
56+
bool mesh_has_self_intersections() const;
57+
58+
index_t nb_intersecting_elements_pair() const;
59+
60+
std::vector< std::array< index_t, 2 > > intersecting_elements() const;
61+
62+
private:
63+
IMPLEMENTATION_MEMBER( impl_ );
64+
};
65+
ALIAS_2D_AND_3D( SurfaceMeshIntersections );
66+
} // namespace geode

include/geode/inspector/surface_inspector.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <geode/inspector/criterion/adjacency/surface_adjacency.h>
2828
#include <geode/inspector/criterion/colocation/surface_colocation.h>
2929
#include <geode/inspector/criterion/degeneration/surface_degeneration.h>
30+
#include <geode/inspector/criterion/intersections/surface_intersections.h>
3031
#include <geode/inspector/criterion/manifold/surface_edge_manifold.h>
3132
#include <geode/inspector/criterion/manifold/surface_vertex_manifold.h>
3233
#include <geode/inspector/mixin/add_inspectors.h>
@@ -38,6 +39,7 @@ namespace geode
3839
* @extends SurfaceMeshAdjacency
3940
* @extends SurfaceMeshColocation
4041
* @extends SurfaceMeshDegeneration
42+
* @extends SurfaceMeshIntersections
4143
* @extends SurfaceMeshEdgeManifold
4244
* @extends SurfaceMeshVertexManifold
4345
*/
@@ -47,6 +49,7 @@ namespace geode
4749
SurfaceMeshAdjacency< dimension >,
4850
SurfaceMeshColocation< dimension >,
4951
SurfaceMeshDegeneration< dimension >,
52+
SurfaceMeshIntersections< dimension >,
5053
SurfaceMeshEdgeManifold< dimension >,
5154
SurfaceMeshVertexManifold< dimension > >
5255
{

src/bin/geode-inspector-surface.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ ABSL_FLAG( bool, colocation, true, "Toggle colocation criterion" );
4949
ABSL_FLAG( bool, degeneration, true, "Toggle degeneration criterion" );
5050
ABSL_FLAG( bool, manifold_vertex, true, "Toggle manifold vertex criterion" );
5151
ABSL_FLAG( bool, manifold_edge, true, "Toggle manifold edge criterion" );
52+
ABSL_FLAG( bool,
53+
intersection,
54+
true,
55+
"Toggle self intersection criterion (only for triangulated surfaces)" );
5256
ABSL_FLAG( bool,
5357
verbose,
5458
false,
@@ -59,7 +63,7 @@ template < geode::index_t dimension >
5963
void inspect_surface( const geode::SurfaceMesh< dimension >& surface )
6064
{
6165
const auto verbose = absl::GetFlag( FLAGS_verbose );
62-
absl::InlinedVector< async::task< void >, 6 > tasks;
66+
absl::InlinedVector< async::task< void >, 7 > tasks;
6367
const geode::SurfaceMeshInspector< dimension > inspector{ surface,
6468
verbose };
6569
if( absl::GetFlag( FLAGS_adjacency ) )
@@ -105,6 +109,15 @@ void inspect_surface( const geode::SurfaceMesh< dimension >& surface )
105109
geode::Logger::info( nb, " non manifold edges" );
106110
} ) );
107111
}
112+
if( absl::GetFlag( FLAGS_intersection )
113+
&& surface.type_name()
114+
== geode::TriangulatedSurface< dimension >::type_name_static() )
115+
{
116+
tasks.emplace_back( async::spawn( [&inspector] {
117+
const auto nb = inspector.nb_intersecting_elements_pair();
118+
geode::Logger::info( nb, " pairs of intersecting triangles" );
119+
} ) );
120+
}
108121
for( auto& task : async::when_all( tasks.begin(), tasks.end() ).get() )
109122
{
110123
task.get();

0 commit comments

Comments
 (0)