diff --git a/src/geode/mesh/core/surface_mesh.cpp b/src/geode/mesh/core/surface_mesh.cpp index 275d6ab12..299bb61c6 100644 --- a/src/geode/mesh/core/surface_mesh.cpp +++ b/src/geode/mesh/core/surface_mesh.cpp @@ -107,6 +107,42 @@ namespace "edge" ); } + template < geode::index_t dimension > + bool not_same_orientation( const geode::SurfaceMesh< dimension >& mesh, + const geode::PolygonEdge& current_edge, + const geode::PolygonEdge& adj_edge ) + { + const auto cur_v0 = mesh.polygon_edge_vertex( current_edge, 0 ); + const auto cur_v1 = mesh.polygon_edge_vertex( current_edge, 1 ); + const auto adj_v0 = mesh.polygon_edge_vertex( adj_edge, 0 ); + const auto adj_v1 = mesh.polygon_edge_vertex( adj_edge, 1 ); + return cur_v0 == adj_v0 && cur_v1 == adj_v1; + } + + template < geode::index_t dimension > + std::optional< geode::PolygonVertex > next_polygon_vertex_around( + const geode::SurfaceMesh< dimension >& mesh, + const geode::PolygonVertex& cur_polygon_vertex, + bool& vertex_is_next ) + { + const auto exit_vertex = + vertex_is_next ? cur_polygon_vertex + : mesh.previous_polygon_vertex( cur_polygon_vertex ); + const geode::PolygonEdge exit_edge{ exit_vertex }; + const auto adj_edge = mesh.polygon_adjacent_edge( exit_edge ); + if( !adj_edge ) + { + return std::nullopt; + } + if( not_same_orientation( mesh, exit_edge, adj_edge.value() ) ) + { + vertex_is_next = !vertex_is_next; + } + return vertex_is_next ? geode::PolygonVertex{ mesh.next_polygon_vertex( + geode::PolygonVertex{ adj_edge.value() } ) } + : geode::PolygonVertex{ adj_edge.value() }; + } + template < geode::index_t dimension > geode::internal::PolygonsAroundVertexImpl compute_polygons_around_vertex( const geode::SurfaceMesh< dimension >& mesh, @@ -125,6 +161,7 @@ namespace constexpr geode::index_t MAX_SAFETY_COUNT{ 1000 }; geode::internal::PolygonsAroundVertexImpl result; auto cur_polygon_vertex = first_polygon; + bool vertex_is_next{ false }; do { OPENGEODE_ASSERT( @@ -134,18 +171,11 @@ namespace vertex_id, " / ", cur_polygon_vertex->string(), " ", mesh.polygon_vertex( cur_polygon_vertex.value() ) ); result.polygons.push_back( cur_polygon_vertex.value() ); - const auto prev_vertex = - mesh.previous_polygon_vertex( cur_polygon_vertex.value() ); - auto adj_edge = - mesh.polygon_adjacent_edge( geode::PolygonEdge{ prev_vertex } ); safety_count++; - if( adj_edge ) - { - cur_polygon_vertex = geode::PolygonVertex{ adj_edge.value() }; - } - else + cur_polygon_vertex = next_polygon_vertex_around( + mesh, cur_polygon_vertex.value(), vertex_is_next ); + if( !cur_polygon_vertex ) { - cur_polygon_vertex = std::nullopt; break; } } while( cur_polygon_vertex != first_polygon @@ -154,35 +184,27 @@ namespace result.vertex_is_on_border = cur_polygon_vertex != first_polygon; if( result.vertex_is_on_border ) { - auto adj_edge = mesh.polygon_adjacent_edge( - geode::PolygonEdge{ first_polygon.value() } ); - if( adj_edge ) - { - cur_polygon_vertex = geode::PolygonVertex{ adj_edge.value() }; - } - else - { - cur_polygon_vertex = std::nullopt; - } - while( cur_polygon_vertex && safety_count < MAX_SAFETY_COUNT ) + vertex_is_next = true; + cur_polygon_vertex = next_polygon_vertex_around( + mesh, first_polygon.value(), vertex_is_next ); + } + else + { + cur_polygon_vertex = std::nullopt; + } + while( cur_polygon_vertex && safety_count < MAX_SAFETY_COUNT ) + { + OPENGEODE_ASSERT( + mesh.polygon_vertex( cur_polygon_vertex.value() ) == vertex_id, + "[SurfaceMesh::polygons_around_vertex] Wrong polygon " + "around vertex" ); + result.polygons.push_back( cur_polygon_vertex.value() ); + safety_count++; + cur_polygon_vertex = next_polygon_vertex_around( + mesh, cur_polygon_vertex.value(), vertex_is_next ); + if( !cur_polygon_vertex ) { - const geode::PolygonVertex next_vertex{ mesh.next_polygon_edge( - geode::PolygonEdge{ cur_polygon_vertex.value() } ) }; - OPENGEODE_ASSERT( - mesh.polygon_vertex( next_vertex ) == vertex_id, - "[SurfaceMesh::polygons_around_vertex] Wrong polygon " - "around vertex" ); - result.polygons.push_back( next_vertex ); - safety_count++; - adj_edge = mesh.polygon_adjacent_edge( - geode::PolygonEdge{ next_vertex } ); - if( adj_edge ) - { - cur_polygon_vertex = - geode::PolygonVertex{ adj_edge.value() }; - continue; - } - cur_polygon_vertex = std::nullopt; + break; } } OPENGEODE_EXCEPTION( safety_count < MAX_SAFETY_COUNT, diff --git a/src/geode/mesh/helpers/detail/surface_merger.cpp b/src/geode/mesh/helpers/detail/surface_merger.cpp index 4af911c8a..062f9c343 100644 --- a/src/geode/mesh/helpers/detail/surface_merger.cpp +++ b/src/geode/mesh/helpers/detail/surface_merger.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -131,7 +132,15 @@ namespace geode void clean_surface( SurfaceMeshMerger< dimension >& merger ) { separate_surfaces( merger ); - repair_polygon_orientations( merger.mesh(), merger.builder() ); + try + { + repair_polygon_orientations( + merger.mesh(), merger.builder() ); + } + catch( const OpenGeodeException& e ) + { + Logger::warn( e.what() ); + } } void create_polygons( SurfaceMeshMerger< dimension >& merger ) diff --git a/src/geode/mesh/helpers/repair_polygon_orientations.cpp b/src/geode/mesh/helpers/repair_polygon_orientations.cpp index 8b30758a3..3e7bb3013 100644 --- a/src/geode/mesh/helpers/repair_polygon_orientations.cpp +++ b/src/geode/mesh/helpers/repair_polygon_orientations.cpp @@ -67,6 +67,13 @@ namespace } return get_bad_oriented_polygons(); } + catch( geode::OpenGeodeDataException& e ) + { + const auto msg = absl::StrCat( "Surface ", + mesh_.name().value_or( mesh_.id().string() ), ": ", + e.what() ); + throw geode::OpenGeodeDataException( msg ); + } catch( geode::OpenGeodeException& e ) { const auto msg = absl::StrCat( "Surface ", diff --git a/tests/data/moebius_strip.og_tsf3d b/tests/data/moebius_strip.og_tsf3d new file mode 100644 index 000000000..ffe602ed4 Binary files /dev/null and b/tests/data/moebius_strip.og_tsf3d differ diff --git a/tests/mesh/CMakeLists.txt b/tests/mesh/CMakeLists.txt index 785031fdf..f072eb2e8 100644 --- a/tests/mesh/CMakeLists.txt +++ b/tests/mesh/CMakeLists.txt @@ -289,3 +289,9 @@ add_geode_test( ${PROJECT_NAME}::geometry ${PROJECT_NAME}::mesh ) +add_geode_test( + SOURCE "test-moebius-strip.cpp" + DEPENDENCIES + ${PROJECT_NAME}::basic + ${PROJECT_NAME}::mesh +) \ No newline at end of file diff --git a/tests/mesh/test-moebius-strip.cpp b/tests/mesh/test-moebius-strip.cpp new file mode 100644 index 000000000..3754220fa --- /dev/null +++ b/tests/mesh/test-moebius-strip.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2019 - 2026 Geode-solutions + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#include + +#include +#include + +#include +#include +#include + +void test_polygons_around_vertex() +{ + const auto moebius_strip = geode::load_triangulated_surface< 3 >( + absl::StrCat( geode::DATA_PATH, "moebius_strip.og_tsf3d" ) ); + + const geode::index_t vertex_id_0{ 0 }; + const auto polygons_around_0 = + moebius_strip->polygons_around_vertex( vertex_id_0 ); + OPENGEODE_EXCEPTION( polygons_around_0.size() == 2, + "[Test] Wrong computation of polygons around vertex 0 : should be 2 " + "polygons, get ", + polygons_around_0.size() ); + OPENGEODE_EXCEPTION( polygons_around_0[0].polygon_id == 159 + && polygons_around_0[0].vertex_id == 2, + "[TEST] Wrong polygons around vertex 0" ); + OPENGEODE_EXCEPTION( polygons_around_0[1].polygon_id == 0 + && polygons_around_0[1].vertex_id == 0, + "[TEST] Wrong polygons around vertex 0" ); + + const geode::index_t vertex_id_1{ 1 }; + const auto polygons_around_1 = + moebius_strip->polygons_around_vertex( vertex_id_1 ); + OPENGEODE_EXCEPTION( polygons_around_1.size() == 6, + "[Test] Wrong computation of polygons around vertex 1 : should be 6 " + "polygons, get ", + polygons_around_1.size() ); + OPENGEODE_EXCEPTION( polygons_around_1[0].polygon_id == 159 + && polygons_around_1[0].vertex_id == 1, + "[TEST] Wrong polygons around vertex 1" ); + OPENGEODE_EXCEPTION( polygons_around_1[1].polygon_id == 158 + && polygons_around_1[1].vertex_id == 1, + "[TEST] Wrong polygons around vertex 1" ); + OPENGEODE_EXCEPTION( polygons_around_1[2].polygon_id == 157 + && polygons_around_1[2].vertex_id == 2, + "[TEST] Wrong polygons around vertex 1" ); + OPENGEODE_EXCEPTION( polygons_around_1[3].polygon_id == 2 + && polygons_around_1[3].vertex_id == 0, + "[TEST] Wrong polygons around vertex 1" ); + OPENGEODE_EXCEPTION( polygons_around_1[4].polygon_id == 1 + && polygons_around_1[4].vertex_id == 0, + "[TEST] Wrong polygons around vertex 1" ); + OPENGEODE_EXCEPTION( polygons_around_1[5].polygon_id == 0 + && polygons_around_1[5].vertex_id == 2, + "[TEST] Wrong polygons around vertex 1" ); + + const geode::index_t vertex_id_99{ 99 }; + const auto polygons_around_99 = + moebius_strip->polygons_around_vertex( vertex_id_99 ); + OPENGEODE_EXCEPTION( polygons_around_99.size() == 4, + "[Test] Wrong computation of polygons around vertex 99 : should be 4 " + "polygons, get ", + polygons_around_99.size() ); + OPENGEODE_EXCEPTION( polygons_around_99[0].polygon_id == 153 + && polygons_around_99[0].vertex_id == 1, + "[TEST] Wrong polygons around vertex 99" ); + OPENGEODE_EXCEPTION( polygons_around_99[1].polygon_id == 152 + && polygons_around_99[1].vertex_id == 1, + "[TEST] Wrong polygons around vertex 99" ); + OPENGEODE_EXCEPTION( polygons_around_99[2].polygon_id == 6 + && polygons_around_99[2].vertex_id == 2, + "[TEST] Wrong polygons around vertex 99" ); + OPENGEODE_EXCEPTION( polygons_around_99[3].polygon_id == 7 + && polygons_around_99[3].vertex_id == 0, + "[TEST] Wrong polygons around vertex 99" ); +} + +void test() +{ + geode::OpenGeodeMeshLibrary::initialize(); + geode::Logger::set_level( geode::Logger::LEVEL::info ); + test_polygons_around_vertex(); +} + +OPENGEODE_TEST( "moebius-strip" ) \ No newline at end of file