From f55d586d9d0b93b8c15b18e57647b0cecb0efc5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Mon, 8 Feb 2021 15:50:48 +0100 Subject: [PATCH 1/5] Re-introduce some kind of zipping as a first step to boundary cycle stitching Zipping was removed when local stitching was introduced, and a common interface between stitching-within-a-cycle and normal-stitching was introduced. However, that common interface does not handle any non-manifold configuration, even if it's a folded cycle with obvious stitching (consecutive equal edges). So, some kind of zipping is re-added. It is cheap anyway (compared to e.g. calling PMP::connected_components on the whole mesh). --- .../Polygon_mesh_processing/stitch_borders.h | 181 +++++++++++++++++- 1 file changed, 174 insertions(+), 7 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_borders.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_borders.h index 4387c52030b..10b3949d45c 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_borders.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_borders.h @@ -787,8 +787,8 @@ template & to_stitch, const CandidateHalfedgeRange& representative_candidates, PolygonMesh& pmesh, - MaintainerVisitor& mv, - const VertexPointMap& vpm) + const VertexPointMap& vpm, + MaintainerVisitor& mv) { typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; @@ -832,7 +832,7 @@ std::size_t stitch_halfedge_range(const std::vector& to_stitch, const VertexPointMap& vpm) { Dummy_cycle_rep_maintainer mv(pmesh); - return stitch_halfedge_range(to_stitch, halfedges(pmesh), pmesh, mv, vpm); + return stitch_halfedge_range(to_stitch, halfedges(pmesh), pmesh, vpm, mv); } //overload to avoid a useless copy @@ -855,6 +855,169 @@ std::size_t stitch_halfedge_range_dispatcher(const HalfedgePairRange& to_stitch_ return stitch_halfedge_range(to_stitch, pmesh, vpm); } +// collect_duplicated_stitchable_boundary_edges() cannot handle any configuration with non-manifoldness. +// However, even if non-manifoldness exists within a loop, it is safe choice to stitch consecutive +// stitchable halfedges +template +std::size_t zip_boundary_cycle(const typename boost::graph_traits::halfedge_descriptor h, + const HalfedgeRange& cycle_halfedges, + PolygonMesh& pmesh, + const VPM vpm, + const HalfedgeKeeper& hd_kpr, + MaintainerVisitor& mv) +{ + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + + std::size_t stitched_boundary_cycles_n = 0; + + // A boundary cycle might need to be stitched starting from different extremities + // + // v11 ------ v10 + // | | + // v0 --- v1(v13) === v2(v12) v5(v9) === v6(v8) --- v7 + // | | + // v3 ------- v4 + // + // As long as we find vertices on the boundary with both incident halfedges being compatible, + // we zip it up as much as possible. + + // not everything is always stitchable + std::set unstitchable_halfedges; + + const halfedge_descriptor null_h = boost::graph_traits::null_halfedge(); + halfedge_descriptor bh = h; + for(;;) // until there is nothing to stitch anymore + { + if(bh == null_h) // the complete boundary cycle is stitched + break; + +#ifdef CGAL_PMP_STITCHING_DEBUG + std::cout << "Walking border from halfedge: " << edge(bh, pmesh) << std::endl; +#endif + + CGAL_assertion(is_border(bh, pmesh)); + + halfedge_descriptor hn = next(bh, pmesh), start_h = null_h; + do + { + halfedge_descriptor hnn = next(hn, pmesh); + CGAL_assertion(get(vpm, target(hn, pmesh)) == get(vpm, source(hnn, pmesh))); + + if(get(vpm, source(hn, pmesh)) == get(vpm, target(hnn, pmesh)) && + !is_degenerate_edge(edge(hn, pmesh), pmesh, parameters::vertex_point_map(vpm))) + { + if(unstitchable_halfedges.count(hn) == 0) + { + start_h = hn; + break; + } + } + + hn = hnn; + } + while(hn != bh); + + if(start_h == null_h) // nothing to be stitched on this boundary cycle + break; + +#ifdef CGAL_PMP_STITCHING_DEBUG_PP + std::cout << "Starting stitching from halfedge: " + << get(vpm, source(edge(start_h, pmesh), pmesh)) << " " + << get(vpm, target(edge(start_h, pmesh), pmesh)) << std::endl; +#endif + + CGAL_assertion(is_border(start_h, pmesh)); + + // Associate as many consecutive halfedge pairs as possible ("zipping") + std::vector > hedges_to_stitch; + + halfedge_descriptor curr_h = start_h; + halfedge_descriptor curr_hn = next(curr_h, pmesh); + for(;;) // while we can expand the zipping range + { + // Don't create an invalid polygon mesh, even if the geometry allows it + if(face(opposite(curr_h, pmesh), pmesh) == face(opposite(curr_hn, pmesh), pmesh)) + { + unstitchable_halfedges.insert(curr_h); + bh = curr_hn; + break; + } + + CGAL_assertion(is_border(curr_h, pmesh)); + CGAL_assertion(is_border(curr_hn, pmesh)); + + if(hd_kpr(curr_h, curr_hn) == curr_h) + hedges_to_stitch.emplace_back(curr_h, curr_hn); + else + hedges_to_stitch.emplace_back(curr_hn, curr_h); + +#ifdef CGAL_PMP_STITCHING_DEBUG_PP + std::cout << "expand zip with:\n" + << edge(curr_h, pmesh) << "\n\t" << source(curr_h, pmesh) << "\t(" << get(vpm, source(curr_h, pmesh)) << ")" + << "\n\t" << target(curr_h, pmesh) << "\t(" << get(vpm, target(curr_h, pmesh)) << ")\n" + << edge(curr_hn, pmesh) << "\n\t" << source(curr_hn, pmesh) << "\t(" << get(vpm, source(curr_hn, pmesh)) << ")" + << "\n\t" << target(curr_hn, pmesh) << "\t(" << get(vpm, target(curr_hn, pmesh)) << ")" << std::endl; +#endif + + // check if we have reached the end of the boundary cycle + if(prev(curr_h, pmesh) == curr_hn || prev(curr_h, pmesh) == next(curr_hn, pmesh)) + { + bh = null_h; + break; + } + + curr_h = prev(curr_h, pmesh); + curr_hn = next(curr_hn, pmesh); + + // check if the next two halfedges are not geometrically compatible + if(get(vpm, source(curr_h, pmesh)) != get(vpm, target(curr_hn, pmesh)) || + is_degenerate_edge(edge(curr_hn, pmesh), pmesh, parameters::vertex_point_map(vpm))) + { + bh = curr_hn; + break; + } + } + + // bh must be a boundary halfedge on the border that will not be impacted by any stitching + CGAL_assertion_code(if(bh != null_h) {) + CGAL_assertion_code( for(const auto& hp : hedges_to_stitch) {) + CGAL_assertion( bh != hp.first && bh != hp.second); + CGAL_assertion_code(}}) + + if(!hedges_to_stitch.empty()) + { +#ifdef CGAL_PMP_STITCHING_DEBUG_PP + std::cout << hedges_to_stitch.size() " halfedge pairs to stitch on border containing:\n" + << edge(h, pmesh) << "\n\t" << source(h, pmesh) << "\t(" << get(vpm, source(h, pmesh)) << ")" + << "\n\t" << target(h, pmesh) << "\t(" << get(vpm, target(h, pmesh)) << ")" << std::endl; +#endif + + mv.remove_representative(bh); + + std::size_t local_stitches = internal::stitch_halfedge_range(hedges_to_stitch, cycle_halfedges, pmesh, vpm, mv); + stitched_boundary_cycles_n += local_stitches; + + if(local_stitches == 0) // refused to stitch this halfedge pair range due to manifold issue + { +#ifdef CGAL_PMP_STITCHING_DEBUG_PP + std::cout << "Failed to stitch this range!" << std::endl; +#endif + + for(const auto& hp : hedges_to_stitch) + { + unstitchable_halfedges.insert(hp.first); + unstitchable_halfedges.insert(hp.second); + } + } + } + } + + return stitched_boundary_cycles_n; +} /// High-level functions @@ -888,6 +1051,8 @@ std::size_t stitch_boundary_cycle(const typename boost::graph_traits to_stitch; internal::collect_duplicated_stitchable_boundary_edges(cycle_halfedges, pmesh, hd_kpr, false /*per cc*/, @@ -895,7 +1060,9 @@ std::size_t stitch_boundary_cycle(const typename boost::graph_traits::%vertex_descriptor` /// as key type and `%Point_3` as value type} -/// \cgalParamDefault{`boost::get(CGAL::vertex_point, pm)`} +/// \cgalParamDefault{`boost::get(CGAL::vertex_point, pmesh)`} /// \cgalParamExtra{If this parameter is omitted, an internal property map for `CGAL::vertex_point_t` /// must be available in `PolygonMesh`.} /// \cgalParamNEnd @@ -988,7 +1155,7 @@ std::size_t stitch_boundary_cycles(const BorderHalfedgeRange& boundary_cycle_rep /// \cgalParamDescription{a property map associating points to the vertices of `pm`} /// \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%vertex_descriptor` /// as key type and `%Point_3` as value type} -/// \cgalParamDefault{`boost::get(CGAL::vertex_point, pm)`} +/// \cgalParamDefault{`boost::get(CGAL::vertex_point, pmesh)`} /// \cgalParamExtra{If this parameter is omitted, an internal property map for `CGAL::vertex_point_t` /// must be available in `PolygonMesh`.} /// \cgalParamNEnd @@ -1147,7 +1314,7 @@ std::size_t stitch_borders(const BorderHalfedgeRange& boundary_cycle_representat internal::collect_duplicated_stitchable_boundary_edges(to_consider, pmesh, hd_kpr, per_cc, std::back_inserter(to_stitch), np); - res += stitch_halfedge_range(to_stitch, to_consider, pmesh, mv, vpm); + res += stitch_halfedge_range(to_stitch, to_consider, pmesh, vpm, mv); const auto& new_representatives = mv.cycle_representatives(); From 1b01b67c86b8a42a51aa82b085441c4fd04a5093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Mon, 8 Feb 2021 15:54:52 +0100 Subject: [PATCH 2/5] Added a test for zipping-based boundary cycle stitching --- .../data_stitching/folded_cycle.off | 59 +++++++++++++++++++ .../test_stitching.cpp | 5 +- 2 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 Polygon_mesh_processing/test/Polygon_mesh_processing/data_stitching/folded_cycle.off diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/data_stitching/folded_cycle.off b/Polygon_mesh_processing/test/Polygon_mesh_processing/data_stitching/folded_cycle.off new file mode 100644 index 00000000000..f2a40878de9 --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/data_stitching/folded_cycle.off @@ -0,0 +1,59 @@ +# Output of a CGAL tool +#CBP +# polyhedral_surface 0 +# halfedges 0 +# triangulated 0 +# non_empty_facets 0 +# terrain 0 +# normalized_to_sphere 0 +# radius 0 +# rounded 0 +# rounded_bits 0 +# ENDCBP + +NOFF +16 16 0 + +# 16 vertices +# ------------------------------------------ + + +-21.8983 -6.38823 63.7562 0.521628 -0.428916 0.73752 +-21.9152 -6.40483 63.7541 0.42945 -0.529738 0.731403 +-21.9057 -6.32693 63.7553 0.99181 0.00120314 0.127719 +-21.8832 -6.80033 63.7582 0.126127 6.70899e-05 -0.992014 +-21.9216 -6.40893 63.551 0.67402 -0.738711 -0.00201436 +-21.9075 -6.25023 63.7552 -0.995002 -0.0234773 -0.0970594 +-21.8983 -6.38823 63.7562 0.117647 -0.000135467 -0.993055 +-21.9021 -6.75613 63.7558 0.117647 -0.000135467 -0.993055 +-21.9057 -6.32693 63.7553 -0.0977085 -0.99516 -0.010509 +-21.8752 -6.32923 63.7592 -0.126943 -0.00144678 0.991909 +-21.9611 -6.89043 63.7501 0.0910592 0.000407086 -0.995845 +-21.9418 -6.23043 63.7515 -0.109893 -0.00197042 0.993942 +-21.927 -6.32803 63.7529 -0.111856 -0.00218345 0.993722 +-21.9001 -6.39073 63.5534 0.765209 -0.643781 0.00114436 +-21.8874 -6.29763 63.7577 0.341741 -0.938826 0.0426494 +-21.882 -6.36043 63.7583 0.551822 -0.379642 0.742539 + +# 16 facets +# ------------------------------------------ + +3 0 4 13 +3 15 0 13 +3 8 10 5 +3 8 1 0 +3 6 2 14 +3 9 14 2 +3 6 7 10 +3 1 4 0 +3 7 6 14 +3 1 8 12 +3 0 15 2 +3 15 9 2 +3 5 11 8 +3 3 7 14 +3 6 10 8 +3 8 11 12 + + +# End of OFF # diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_stitching.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_stitching.cpp index 7ae4b674f9a..7fd2d5d820e 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_stitching.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_stitching.cpp @@ -73,6 +73,7 @@ void test_stitch_boundary_cycles() test_stitch_boundary_cycles("data_stitching/boundary_cycle.off", 4); test_stitch_boundary_cycles("data_stitching/boundary_cycle_2.off", 2); test_stitch_boundary_cycles("data_stitching/complex_hole.off", 3); + test_stitch_boundary_cycles("data_stitching/folded_cycle.off", 2); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -119,9 +120,9 @@ void test_stitch_borders(const char* fname, typedef PMP::internal::Halfedges_keeper_with_marked_edge_priority Keeper; Marked_edges marks = get(Edge_property_tag(), mesh); - int id = 0; + int eid = 0; for(edge_descriptor e : edges(mesh)) - put(marks, e, (unconstrained_edge_ids.count(id++) == 0)); + put(marks, e, (unconstrained_edge_ids.count(eid++) == 0)); Keeper kpr(marks, mesh); From aacd453965695a8a12ee086897161b1aed99d556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Fri, 19 Feb 2021 17:38:34 +0100 Subject: [PATCH 3/5] Do not stitch two halfedges if both are incident to the same degenerate face --- .../CGAL/Polygon_mesh_processing/stitch_borders.h | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_borders.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_borders.h index 10b3949d45c..5c3047c7e7a 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_borders.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_borders.h @@ -332,17 +332,22 @@ void fill_pairs(const Halfedge& he, bool insertion_ok; std::tie(set_it, insertion_ok) = border_halfedge_map.emplace(he, std::make_pair(1,0)); - if(!insertion_ok) // we found already a halfedge with the points + if(!insertion_ok) // there is already a halfedge with the points { ++set_it->second.first; // increase the multiplicity if(set_it->second.first == 2) { + const Halfedge other_he = set_it->first; set_it->second.second = halfedge_pairs.size(); // set the id of the pair in the vector - halfedge_pairs.emplace_back(set_it->first, he); - if(get(vpm, source(he,pmesh)) == get(vpm, target(set_it->first, pmesh)) && - get(vpm, target(he,pmesh)) == get(vpm, source(set_it->first, pmesh))) + halfedge_pairs.emplace_back(other_he, he); + if(get(vpm, source(he,pmesh)) == get(vpm, target(other_he, pmesh)) && + get(vpm, target(he,pmesh)) == get(vpm, source(other_he, pmesh))) { - manifold_halfedge_pairs.push_back(true); + // Even if the halfedges are compatible, refuse to stitch if that would break the graph + if(face(opposite(he, pmesh), pmesh) == face(opposite(other_he, pmesh), pmesh)) + manifold_halfedge_pairs.push_back(false); + else + manifold_halfedge_pairs.push_back(true); } else { From b3d7977fac22fe4021d5bea863e9fd284b10f583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Mon, 22 Feb 2021 16:50:52 +0100 Subject: [PATCH 4/5] Fix boundary cycle zipping not updating its representative --- .../Polygon_mesh_processing/stitch_borders.h | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_borders.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_borders.h index 5c3047c7e7a..9fdb5d884e8 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_borders.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_borders.h @@ -150,6 +150,7 @@ struct Dummy_cycle_rep_maintainer } // Dummies just to fit the API + void add_representative(const halfedge_descriptor) const { } void remove_representative(const halfedge_descriptor) const { } void clear_representatives() const { } @@ -866,19 +867,20 @@ std::size_t stitch_halfedge_range_dispatcher(const HalfedgePairRange& to_stitch_ template -std::size_t zip_boundary_cycle(const typename boost::graph_traits::halfedge_descriptor h, + typename HalfedgeKeeper> +std::size_t zip_boundary_cycle(typename boost::graph_traits::halfedge_descriptor& bh, const HalfedgeRange& cycle_halfedges, PolygonMesh& pmesh, const VPM vpm, - const HalfedgeKeeper& hd_kpr, - MaintainerVisitor& mv) + const HalfedgeKeeper& hd_kpr) { typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; std::size_t stitched_boundary_cycles_n = 0; + // Zipping cannot change the topology of the hole so the maintenance is trivial + internal::Dummy_cycle_rep_maintainer dummy_maintainer(pmesh); + // A boundary cycle might need to be stitched starting from different extremities // // v11 ------ v10 @@ -894,7 +896,6 @@ std::size_t zip_boundary_cycle(const typename boost::graph_traits:: std::set unstitchable_halfedges; const halfedge_descriptor null_h = boost::graph_traits::null_halfedge(); - halfedge_descriptor bh = h; for(;;) // until there is nothing to stitch anymore { if(bh == null_h) // the complete boundary cycle is stitched @@ -1001,9 +1002,8 @@ std::size_t zip_boundary_cycle(const typename boost::graph_traits:: << "\n\t" << target(h, pmesh) << "\t(" << get(vpm, target(h, pmesh)) << ")" << std::endl; #endif - mv.remove_representative(bh); - - std::size_t local_stitches = internal::stitch_halfedge_range(hedges_to_stitch, cycle_halfedges, pmesh, vpm, mv); + std::size_t local_stitches = internal::stitch_halfedge_range(hedges_to_stitch, cycle_halfedges, + pmesh, vpm, dummy_maintainer); stitched_boundary_cycles_n += local_stitches; if(local_stitches == 0) // refused to stitch this halfedge pair range due to manifold issue @@ -1052,20 +1052,36 @@ std::size_t stitch_boundary_cycle(const typename boost::graph_traits()); + halfedge_descriptor bh = h, bh_mem = bh; + std::vector cycle_halfedges; for(halfedge_descriptor h : halfedges_around_face(bh, pmesh)) cycle_halfedges.push_back(h); - std::size_t res = internal::zip_boundary_cycle(bh, cycle_halfedges, pmesh, vpm, hd_kpr, mv); + std::size_t res = internal::zip_boundary_cycle(bh, cycle_halfedges, pmesh, vpm, hd_kpr); + if(bh == boost::graph_traits::null_halfedge()) // stitched everything + { + cycle_reps_maintainer.remove_representative(bh); + return res; + } + + // Re-compute the range if something was stitched + if(res != 0) + { + cycle_reps_maintainer.remove_representative(bh_mem); + cycle_reps_maintainer.add_representative(bh); + + cycle_halfedges.clear(); + for(halfedge_descriptor h : halfedges_around_face(bh, pmesh)) + cycle_halfedges.push_back(h); + } std::vector to_stitch; internal::collect_duplicated_stitchable_boundary_edges(cycle_halfedges, pmesh, hd_kpr, false /*per cc*/, std::back_inserter(to_stitch), np); - mv.remove_representative(bh); - - res += stitch_halfedge_range(to_stitch, cycle_halfedges, pmesh, vpm, mv); + res += stitch_halfedge_range(to_stitch, cycle_halfedges, pmesh, vpm, cycle_reps_maintainer); return res; } From abe1f6e5776cd76fbdaea93a74e842ec7a57deb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Mon, 22 Feb 2021 16:51:35 +0100 Subject: [PATCH 5/5] Misc cleaning --- .../Polygon_mesh_processing/stitch_borders.h | 85 +++++++++---------- .../test_stitching.cpp | 1 - 2 files changed, 42 insertions(+), 44 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_borders.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_borders.h index 9fdb5d884e8..e936df1bff9 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_borders.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_borders.h @@ -215,13 +215,13 @@ public: { typedef typename boost::property_traits::reference Point_ref; - CGAL_assertion(!cycle_halfedges.empty()); - #ifdef CGAL_PMP_STITCHING_DEBUG std::cout << "update_representatives(" << cycle_halfedges.size() << ", " << filtered_stitchable_halfedges.size() << ")" << std::endl; #endif + CGAL_assertion(!cycle_halfedges.empty()); + for(const halfedge_descriptor h : cycle_halfedges) put(m_candidate_halfedges, h, true); @@ -789,12 +789,12 @@ filter_stitchable_pairs(PolygonMesh& pmesh, } template + typename CycleRepMaintainer, typename VertexPointMap> std::size_t stitch_halfedge_range(const std::vector& to_stitch, const CandidateHalfedgeRange& representative_candidates, PolygonMesh& pmesh, const VertexPointMap& vpm, - MaintainerVisitor& mv) + CycleRepMaintainer& cycle_reps_maintainer) { typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; @@ -824,7 +824,7 @@ std::size_t stitch_halfedge_range(const std::vector& to_stitch, const std::vector& to_stitch_filtered = filter_stitchable_pairs(pmesh, to_stitch, to_stitch_local, uf_vertices, uf_handles); - mv.update_representatives(representative_candidates, to_stitch_filtered, vpm); + cycle_reps_maintainer.update_representatives(representative_candidates, to_stitch_filtered, vpm); // Actually stitching run_stitch_borders(pmesh, to_stitch_filtered, vpm, uf_vertices, uf_handles); @@ -837,11 +837,11 @@ std::size_t stitch_halfedge_range(const std::vector& to_stitch, PolygonMesh& pmesh, const VertexPointMap& vpm) { - Dummy_cycle_rep_maintainer mv(pmesh); - return stitch_halfedge_range(to_stitch, halfedges(pmesh), pmesh, vpm, mv); + Dummy_cycle_rep_maintainer cycle_reps_maintainer(pmesh); + return stitch_halfedge_range(to_stitch, halfedges(pmesh), pmesh, vpm, cycle_reps_maintainer); } -//overload to avoid a useless copy +// overload to avoid a useless copy template std::size_t stitch_halfedge_range_dispatcher(const std::vector& to_stitch, PolygonMesh& pmesh, @@ -850,7 +850,7 @@ std::size_t stitch_halfedge_range_dispatcher(const std::vector& to return stitch_halfedge_range(to_stitch, pmesh, vpm); } -//overload to doing the copy +// overload making a copy template std::size_t stitch_halfedge_range_dispatcher(const HalfedgePairRange& to_stitch_const, PolygonMesh& pmesh, @@ -861,7 +861,7 @@ std::size_t stitch_halfedge_range_dispatcher(const HalfedgePairRange& to_stitch_ return stitch_halfedge_range(to_stitch, pmesh, vpm); } -// collect_duplicated_stitchable_boundary_edges() cannot handle any configuration with non-manifoldness. +// collect_duplicated_stitchable_boundary_edges() cannot handle configurations with non-manifoldness. // However, even if non-manifoldness exists within a loop, it is safe choice to stitch consecutive // stitchable halfedges template ::halfed /// High-level functions -template -std::size_t stitch_boundary_cycle(const typename boost::graph_traits::halfedge_descriptor bh, +template +std::size_t stitch_boundary_cycle(const typename boost::graph_traits::halfedge_descriptor h, PolygonMesh& pmesh, - MaintainerVisitor& mv, + CycleRepMaintainer& cycle_reps_maintainer, const CGAL_PMP_NP_CLASS& np) { typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; typedef typename std::pair halfedges_pair; - CGAL_precondition(bh != boost::graph_traits::null_halfedge()); - CGAL_precondition(is_border(bh, pmesh)); + CGAL_precondition(h != boost::graph_traits::null_halfedge()); + CGAL_precondition(is_border(h, pmesh)); CGAL_precondition(is_valid(pmesh)); using parameters::choose_parameter; @@ -1124,8 +1124,8 @@ std::size_t stitch_boundary_cycle(const typename boost::graph_traits mv(pmesh); - return internal::stitch_boundary_cycle(h, pmesh, mv, np); + internal::Dummy_cycle_rep_maintainer dummy_maintainer(pmesh); + return internal::stitch_boundary_cycle(h, pmesh, dummy_maintainer, np); } template @@ -1138,17 +1138,17 @@ std::size_t stitch_boundary_cycle(const typename boost::graph_traits + typename CycleRepMaintainer, typename CGAL_PMP_NP_TEMPLATE_PARAMETERS> std::size_t stitch_boundary_cycles(const BorderHalfedgeRange& boundary_cycle_representatives, PolygonMesh& pmesh, - MaintainerVisitor& mv, + CycleRepMaintainer& cycle_reps_maintainer, const CGAL_PMP_NP_CLASS& np) { typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; std::size_t stitched_boundary_cycles_n = 0; for(const halfedge_descriptor h : boundary_cycle_representatives) - stitched_boundary_cycles_n += stitch_boundary_cycle(h, pmesh, mv, np); + stitched_boundary_cycles_n += stitch_boundary_cycle(h, pmesh, cycle_reps_maintainer, np); return stitched_boundary_cycles_n; } @@ -1194,8 +1194,8 @@ std::size_t stitch_boundary_cycles(const BorderHalfedgeRange& boundary_cycle_rep { // If this API is called, we are not from stitch_borders() (otherwise there would be a maintainer) // so there is only one pass and we don't carea bout maintaining the cycle subset - internal::Dummy_cycle_rep_maintainer mv(pmesh); - return stitch_boundary_cycles(boundary_cycle_representatives, pmesh, mv, np); + internal::Dummy_cycle_rep_maintainer dummy_maintainer(pmesh); + return stitch_boundary_cycles(boundary_cycle_representatives, pmesh, dummy_maintainer, np); } ///\cond SKIP_IN_MANUAL @@ -1290,11 +1290,11 @@ std::size_t stitch_borders(PolygonMesh& pmesh, namespace internal { template std::size_t stitch_borders(const BorderHalfedgeRange& boundary_cycle_representatives, PolygonMesh& pmesh, - MaintainerVisitor& mv, + CycleRepMaintainer& cycle_maintainer, const CGAL_PMP_NP_CLASS& np) { typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; @@ -1318,35 +1318,34 @@ std::size_t stitch_borders(const BorderHalfedgeRange& boundary_cycle_representat bool per_cc = choose_parameter(get_parameter(np, internal_np::apply_per_connected_component), false); #ifdef CGAL_PMP_STITCHING_DEBUG - std::cout << "------- Stitch cycles... (" << boundary_cycle_representatives.size() << " cycle(s))" << std::endl; + std::cout << "------- Stitch cycles (#1)... (" << boundary_cycle_representatives.size() << " cycle(s))" << std::endl; #endif - std::size_t res = stitch_boundary_cycles(boundary_cycle_representatives, pmesh, mv, np); + std::size_t res = stitch_boundary_cycles(boundary_cycle_representatives, pmesh, cycle_maintainer, np); #ifdef CGAL_PMP_STITCHING_DEBUG - std::cout << "------- Stitched " << res << " in boundary cycles" << std::endl; + std::cout << "------- Stitched " << res << " halfedge pairs in boundary cycles" << std::endl; std::cout << "------- Stitch all..." << std::endl; #endif - const auto& to_consider = mv.halfedges_to_consider(); - mv.clear_representatives(); + const auto& to_consider = cycle_maintainer.halfedges_to_consider(); + cycle_maintainer.clear_representatives(); std::vector > to_stitch; internal::collect_duplicated_stitchable_boundary_edges(to_consider, pmesh, hd_kpr, per_cc, std::back_inserter(to_stitch), np); - - res += stitch_halfedge_range(to_stitch, to_consider, pmesh, vpm, mv); - - const auto& new_representatives = mv.cycle_representatives(); + res += stitch_halfedge_range(to_stitch, to_consider, pmesh, vpm, cycle_maintainer); #ifdef CGAL_PMP_STITCHING_DEBUG - std::cout << "------- Stitched " << res << " after cycles & general" << std::endl; - std::cout << "------- Stitch cycles (#2)... (" << new_representatives.size() << " cycles)" << std::endl; + std::cout << "------- Stitched " << res << " halfedge pairs after cycles & general" << std::endl; + std::cout << "------- Stitch cycles (#2)... (" << new_representatives.size() << " cycle(s))" << std::endl; #endif + const auto& new_representatives = cycle_maintainer.cycle_representatives(); + // Don't care about keeping track of the sub-cycles as this is the last pass - internal::Dummy_cycle_rep_maintainer null_mv(pmesh); - res += stitch_boundary_cycles(new_representatives, pmesh, null_mv, np); + internal::Dummy_cycle_rep_maintainer dummy_cycle_maintainer(pmesh); + res += stitch_boundary_cycles(new_representatives, pmesh, dummy_cycle_maintainer, np); #ifdef CGAL_PMP_STITCHING_DEBUG std::cout << "------- Stitched " << res << " (total)" << std::endl; @@ -1416,8 +1415,8 @@ std::size_t stitch_borders(const BorderHalfedgeRange& boundary_cycle_representat ) { // Need to keep track of the cycles since we are working on a subset of all the boundary cycles - internal::Boundary_cycle_rep_maintainer mv(pmesh); - return stitch_borders(boundary_cycle_representatives, pmesh, mv, np); + internal::Boundary_cycle_rep_maintainer cycle_reps_maintainer(pmesh); + return stitch_borders(boundary_cycle_representatives, pmesh, cycle_reps_maintainer, np); } /// \cond SKIP_IN_MANUAL @@ -1430,8 +1429,8 @@ std::size_t stitch_borders(const BorderHalfedgeRange& boundary_cycle_representat >::type* = 0) { // Need to keep track of the cycles since we are working on a subset of all the boundary cycles - internal::Boundary_cycle_rep_maintainer mv(pmesh); - return stitch_borders(boundary_cycle_representatives, pmesh, mv, parameters::all_default()); + internal::Boundary_cycle_rep_maintainer cycle_reps_maintainer(pmesh); + return stitch_borders(boundary_cycle_representatives, pmesh, cycle_reps_maintainer, parameters::all_default()); } template @@ -1444,8 +1443,8 @@ std::size_t stitch_borders(PolygonMesh& pmesh, extract_boundary_cycles(pmesh, std::back_inserter(boundary_cycle_representatives)); // We are working on all boundary cycles, so there is no need to keep track of any subset - internal::Dummy_cycle_rep_maintainer mv(pmesh); - return stitch_borders(boundary_cycle_representatives, pmesh, mv, np); + internal::Dummy_cycle_rep_maintainer dummy_maintainer(pmesh); + return stitch_borders(boundary_cycle_representatives, pmesh, dummy_maintainer, np); } template diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_stitching.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_stitching.cpp index 7fd2d5d820e..049ad81c971 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_stitching.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_stitching.cpp @@ -209,7 +209,6 @@ void test_degenerate() CGAL::make_triangle(Point(0,0,0), Point(1,0,0), Point(0,1,0), tm); std::size_t res = CGAL::Polygon_mesh_processing::stitch_borders(tm); - std::cout << "Stitched: " << res << std::endl; assert(res == 0); }