From 1e06a8813f783780d8c29d73ccd8e06108e44ff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 8 Jan 2025 17:31:21 +0100 Subject: [PATCH] handle special case when FFG has a non-manifold mesh and not all umbrellas are selected --- .../CGAL/boost/graph/copy_face_graph.h | 30 +++++- BGL/test/BGL/test_Face_filtered_graph.cpp | 99 +++++++++++++++++++ 2 files changed, 125 insertions(+), 4 deletions(-) diff --git a/BGL/include/CGAL/boost/graph/copy_face_graph.h b/BGL/include/CGAL/boost/graph/copy_face_graph.h index e0d4cbd5b958..bbd7a12708b9 100644 --- a/BGL/include/CGAL/boost/graph/copy_face_graph.h +++ b/BGL/include/CGAL/boost/graph/copy_face_graph.h @@ -178,6 +178,7 @@ void copy_face_graph_impl(const SourceMesh& sm, TargetMesh& tm, } // detect if there are some non-manifold umbrellas and fix missing halfedge target pointers + std::map> nm_umbrella_map; typedef typename std::vector::iterator edge_iterator; for (edge_iterator it=new_edges.begin(); it!=new_edges.end(); ++it) { @@ -199,10 +200,19 @@ void copy_face_graph_impl(const SourceMesh& sm, TargetMesh& tm, // we recover tm_v using the halfedge associated to the target vertex of // the halfedge in sm corresponding to nh_t. This is working because we // set the vertex halfedge pointer to the "same" halfedges. - tm_vertex_descriptor tm_v = - target( get(hs_to_ht, halfedge(target(get(ht_to_hs, nh_t), sm), sm)), tm); - for(tm_halfedge_descriptor ht : halfedges_around_target(nh_t, tm)) - set_target(ht, tm_v, tm); + + sm_vertex_descriptor vs = target(get(ht_to_hs, nh_t), sm); + sm_halfedge_descriptor hs = halfedge(vs, sm); + if (hs == boost::graph_traits::null_halfedge()) + { // special case for Face_filtered_graph with a non-manifold input with not all umbrellas selected + nm_umbrella_map[vs].push_back(nh_t); + } + else + { + tm_vertex_descriptor tm_v = target( get(hs_to_ht, hs), tm); + for(tm_halfedge_descriptor ht : halfedges_around_target(nh_t, tm)) + set_target(ht, tm_v, tm); + } } nh_t = opposite(nh_t, tm); } @@ -210,6 +220,18 @@ void copy_face_graph_impl(const SourceMesh& sm, TargetMesh& tm, break; } } + + for (const auto& vs_and_hts : nm_umbrella_map) + { + sm_vertex_descriptor v_sm = vs_and_hts.first; + tm_vertex_descriptor v_tm = add_vertex(tm); + *v2v++=std::make_pair(v_sm, v_tm); + set_halfedge(v_tm, vs_and_hts.second.front(), tm); + put(tm_vpm, v_tm, conv(get(sm_vpm, v_sm))); + + for (tm_halfedge_descriptor h_tm : vs_and_hts.second) + set_target(h_tm, v_tm, tm); + } } } // end of namespace internal diff --git a/BGL/test/BGL/test_Face_filtered_graph.cpp b/BGL/test/BGL/test_Face_filtered_graph.cpp index d85c55353a4f..1e02729a4baa 100644 --- a/BGL/test/BGL/test_Face_filtered_graph.cpp +++ b/BGL/test/BGL/test_Face_filtered_graph.cpp @@ -498,6 +498,103 @@ void test_invalid_selections() assert(!many_umbrellas_fg.is_selection_valid()); } + +void non_manifoldness_test1() +{ + // works out-of-the-box because Face_filtered_graph handles already non-manifold cycles + SM mesh; + SM::Vertex_index v0=add_vertex(mesh); + SM::Vertex_index v1=add_vertex(mesh); + SM::Vertex_index v2=add_vertex(mesh); + SM::Vertex_index v3=add_vertex(mesh); + SM::Vertex_index v4=add_vertex(mesh); + SM::Vertex_index v5=add_vertex(mesh); + SM::Vertex_index v6=add_vertex(mesh); + + SM::Face_index f0=mesh.add_face(v0,v1,v2); + SM::Face_index f1=mesh.add_face(v0,v3,v4); + SM::Face_index f2=mesh.add_face(v0,v5,v6); + SM::Halfedge_index h = halfedge(f0,mesh); + while(target(h, mesh)!=v0) + h=next(h,mesh); + set_halfedge(v0, h, mesh); + + std::vector selection = {f1, f2}; + CGAL::Face_filtered_graph ffg(mesh, selection); + + SM out; + CGAL::copy_face_graph(ffg, out); + + assert(vertices(out).size()==5); + assert(faces(out).size()==2); +} + +void non_manifoldness_test2() +{ + SM mesh; + SM::Vertex_index v0=add_vertex(mesh); + SM::Vertex_index v0b=add_vertex(mesh); + SM::Vertex_index v0t=add_vertex(mesh); + + SM::Vertex_index v1=add_vertex(mesh); + SM::Vertex_index v2=add_vertex(mesh); + SM::Vertex_index v3=add_vertex(mesh); + + SM::Vertex_index v4=add_vertex(mesh); + SM::Vertex_index v5=add_vertex(mesh); + SM::Vertex_index v6=add_vertex(mesh); + + SM::Vertex_index v7=add_vertex(mesh); + SM::Vertex_index v8=add_vertex(mesh); + SM::Vertex_index v9=add_vertex(mesh); + + SM::Face_index f00=mesh.add_face(v1,v2,v0); + SM::Face_index f01=mesh.add_face(v2,v3,v0); + SM::Face_index f02=mesh.add_face(v3,v1,v0); + + SM::Face_index f10=mesh.add_face(v4,v5,v0b); + SM::Face_index f11=mesh.add_face(v5,v6,v0b); + SM::Face_index f12=mesh.add_face(v6,v4,v0b); + + SM::Face_index f20=mesh.add_face(v7,v8,v0t); + SM::Face_index f21=mesh.add_face(v8,v9,v0t); + SM::Face_index f22=mesh.add_face(v9,v7,v0t); + + assert(f00!=SM::Face_index()); + assert(f01!=SM::Face_index()); + assert(f02!=SM::Face_index()); + assert(f10!=SM::Face_index()); + assert(f11!=SM::Face_index()); + assert(f12!=SM::Face_index()); + assert(f20!=SM::Face_index()); + assert(f21!=SM::Face_index()); + assert(f22!=SM::Face_index()); + + #define UPDATE_V(fX, vX) \ + { SM::Halfedge_index h = halfedge(fX,mesh);\ + while(target(h, mesh)!=vX) h=next(h,mesh);\ + set_target(h, v0, mesh); } + + UPDATE_V(f10, v0b) + UPDATE_V(f11, v0b) + UPDATE_V(f12, v0b) + UPDATE_V(f20, v0t) + UPDATE_V(f21, v0t) + UPDATE_V(f22, v0t) + + remove_vertex(v0b, mesh); + remove_vertex(v0t, mesh); + + std::vector selection = {f10, f11, f12, f20, f21, f22}; + CGAL::Face_filtered_graph ffg(mesh, selection); + + SM out; + CGAL::copy_face_graph(ffg, out); + + assert(vertices(out).size()==7); + assert(faces(out).size()==6); +} + int main() { test_graph_range(poly_data()); @@ -511,6 +608,8 @@ int main() #endif test_invalid_selections(); + non_manifoldness_test1(); + non_manifoldness_test2(); // Make a tetrahedron and test the adapter for a patch that only contains 2 faces typedef CGAL::Face_filtered_graph SM_Adapter;