diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Snapping/snap.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Snapping/snap.h index e41f296fe10..19e06467087 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Snapping/snap.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Snapping/snap.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -59,11 +60,25 @@ namespace Polygon_mesh_processing { namespace internal { +template +VertexOutputIterator border_vertices(const PolygonMesh& pmesh, VertexOutputIterator out) +{ + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + + BOOST_FOREACH(vertex_descriptor vd, vertices(pmesh)) + if(is_border(vd, pmesh)) + *out++ = vd; + + return out; +} + // Assigns at each vertex the length of its shortest incident edge as 'epsilon' value -template -void compute_tolerance_at_vertices(ToleranceMap& tol_vm, +void compute_tolerance_at_vertices(const VertexRange& vrange, + ToleranceMap& tol_vm, PolygonMesh& smesh, const SourceNamedParameters& snp) { @@ -71,7 +86,6 @@ void compute_tolerance_at_vertices(ToleranceMap& tol_vm, using boost::choose_param; typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; typedef typename GetVertexPointMap::type SVPM; typedef typename GetGeomTraits::type GT; @@ -82,14 +96,8 @@ void compute_tolerance_at_vertices(ToleranceMap& tol_vm, SVPM svpm = choose_param(get_param(snp, internal_np::vertex_point), get_property_map(vertex_point, smesh)); - std::vector border_vertices; - border_halfedges(smesh, std::back_inserter(border_vertices)); - - BOOST_FOREACH(halfedge_descriptor hd, border_vertices) + BOOST_FOREACH(vertex_descriptor vd, vrange) { - CGAL_assertion(CGAL::is_border(hd, smesh)); - vertex_descriptor vd = target(hd, smesh); - CGAL::Halfedge_around_target_iterator hit, hend; boost::tie(hit, hend) = CGAL::halfedges_around_target(vd, smesh); CGAL_assertion(hit != hend); @@ -160,7 +168,7 @@ struct Vertex_proximity_report if(vb2 != vb) { typename CGAL::cpp11::unordered_map::iterator dist_it = - m_sq_distance_to_snapped_point.find(va); + m_sq_distance_to_snapped_point.find(va); CGAL_assertion(dist_it != m_sq_distance_to_snapped_point.end()); const FT sq_dist_to_prev_best = dist_it->second; @@ -192,19 +200,30 @@ private: // \ingroup PMP_repairing_grp // -// Attempts to snap the border vertices of the source mesh onto the target mesh. -// A border vertex of the source mesh is snapped to a vertex of the target mesh +// Attempts to snap the vertices in `source_vrange` onto the vertices in `target_vrange`. +// A vertex of the source range is only snapped to a vertex of the target mesh // if its distance to the target mesh vertex is smaller than a user-chosen bound. -// If any source target vertex can be projected onto multiple vertices of the target -// mesh, the snapping process is aborted and no vertex is snapped. +// If any source vertex can be snapped onto multiple vertices of the target +// range, the closest one is chosen. +// If multiple vertices within the source range are to be snapped to the same target vertex, +// then the snapping is not performed for these vertices. // // @tparam PolygonMesh a model of `FaceListGraph` and `MutableFaceGraph` +// @tparam VertexRange a model of `Range` with value type `boost::graph_traits::%vertex_descriptor` +// @tparam ToleranceMap a model of `ReadablePropertyMap` with key type `boost::graph_traits::%vertex_descriptor` +// and value type `GetGeomTraits::type::FT` // @tparam SourceNamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" // @tparam TargetNamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" // -// @param smesh the source mesh whose border vertices might be moved. -// @param tmesh the target mesh whose vertices are taken as potential new position for the border -// vertices of the source mesh. +// @param source_vrange a range of vertices of the source mesh whose positions can be changed. +// the vertices must be border vertices of `smesh`. +// @param smesh the source mesh whose border vertices might be moved +// @param target_vrange a range of vertices of the target mesh which are potential new positions +// for the vertices in the source range +// @param tmesh the target mesh to which the vertices in `target_vrange` belong +// @param tol_vm a tolerance map associating to each vertex of the source range a tolerance radius: +// potential projection targets are sought in a box centered on that vertex, and whose +// side has length twice the tolerance // @param snp optional \ref pmp_namedparameters "Named Parameters" related to the source mesh, // amongst those described below: // @@ -237,21 +256,30 @@ private: // \cgalParamEnd // \cgalNamedParamsEnd // -// \return the number of snapped vertices +// @pre if `smesh` and `tmesh` are the same mesh, the ranges must be disjoint // -template -std::size_t snap_border(PolygonMesh& smesh, - const PolygonMesh& tmesh, - const ToleranceMap& tol_vm, - const SourceNamedParameters& snp, - const TargetNamedParameters& tnp) +std::size_t snap_vertex_range_onto_vertex_range(const SourceVertexRange& source_vrange, + PolygonMesh& smesh, + const TargetVertexRange& target_vrange, + const PolygonMesh& tmesh, + const ToleranceMap& tol_vm, + const SourceNamedParameters& snp, + const TargetNamedParameters& tnp) { using boost::get_param; using boost::choose_param; + if(is_empty_range(source_vrange.begin(), source_vrange.end()) || + is_empty_range(target_vrange.begin(), target_vrange.end())) + return 0; + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; typedef CGAL::Box_intersection_d::Box_with_info_d Box; @@ -272,15 +300,10 @@ std::size_t snap_border(PolygonMesh& smesh, TVPM tvpm = choose_param(get_param(tnp, internal_np::vertex_point), get_const_property_map(vertex_point, tmesh)); - // Extract border vertices - std::vector border_vertices; - border_halfedges(smesh, std::back_inserter(border_vertices)); - // Try to snap vertices std::vector boxes; - BOOST_FOREACH(halfedge_descriptor hd, border_vertices) + BOOST_FOREACH(vertex_descriptor vd, source_vrange) { - vertex_descriptor vd = target(hd, smesh); const FT eps = get(tol_vm, vd); const typename GT::Vector_3 eps_v = gt.construct_vector_3_object()(eps, eps, eps); @@ -293,7 +316,7 @@ std::size_t snap_border(PolygonMesh& smesh, } std::vector target_boxes; - BOOST_FOREACH(vertex_descriptor vd, vertices(tmesh)) + BOOST_FOREACH(vertex_descriptor vd, target_vrange) { const Point& p = get(tvpm, vd); target_boxes.push_back(Box(gt.construct_bbox_3_object()(p), vd)); @@ -301,7 +324,7 @@ std::size_t snap_border(PolygonMesh& smesh, // the correspondence map, multiset of targets because the mapping is not necessarily surjective typedef boost::bimap, - boost::bimaps::multiset_of > Vertex_correspondence_map; + boost::bimaps::multiset_of > Vertex_correspondence_map; typedef typename Vertex_correspondence_map::right_iterator VCM_right_it; @@ -331,7 +354,7 @@ std::size_t snap_border(PolygonMesh& smesh, const vertex_descriptor vt = vmc_it->first; // Check that the next iterator is not also the same target vertex, otherwise that means that - // we have multiple source vertices projecting to the same target vertex + // we have multiple source vertices snapping to the same target vertex // In this case, ignore all those mappings. if(vmc_it == last) @@ -371,37 +394,14 @@ std::size_t snap_border(PolygonMesh& smesh, return counter; } -template -std::size_t snap_border(PolygonMesh& smesh, - const PolygonMesh& tmesh, - const double epsilon, - const SourceNamedParameters& snp, - const TargetNamedParameters& tnp) -{ - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename GetGeomTraits::type::FT FT; - - Constant_property_map constant_tolerance_map(epsilon); - - return snap_border(smesh, tmesh, constant_tolerance_map, snp, tnp); -} - -template -std::size_t snap_border(PolygonMesh& smesh, - const PolygonMesh& tmesh, - const double epsilon) -{ - return snap_border(smesh, tmesh, epsilon, - CGAL::parameters::all_default(), CGAL::parameters::all_default()); -} - -template -std::size_t snap_border(PolygonMesh& smesh, - const PolygonMesh& tmesh, - const SourceNamedParameters& snp, - const TargetNamedParameters& tnp) +std::size_t snap_vertex_range_onto_vertex_range(const SourceVertexRange& source_vrange, + PolygonMesh& smesh, + const TargetVertexRange& target_vrange, + const PolygonMesh& tmesh, + const SourceNamedParameters& snp, + const TargetNamedParameters& tnp) { typedef typename GetGeomTraits::type GT; typedef typename GT::FT FT; @@ -409,16 +409,104 @@ std::size_t snap_border(PolygonMesh& smesh, typedef typename boost::property_map::type Tolerance_map; Tolerance_map tol_vm = get(Vertex_property_tag(), smesh); - compute_tolerance_at_vertices(tol_vm, smesh, snp); + compute_tolerance_at_vertices(source_vrange, tol_vm, smesh, snp); - return snap_border(smesh, tmesh, tol_vm, snp, tnp); + return snap_vertex_range_onto_vertex_range(source_vrange, smesh, target_vrange, tmesh, tol_vm, snp, tnp); +} + +template +std::size_t snap_vertex_range_onto_vertex_range(const SourceVertexRange& source_vrange, + PolygonMesh& smesh, + const TargetVertexRange& target_vrange, + const PolygonMesh& tmesh, + const ToleranceMap& tol_vm) +{ + return snap_vertex_range_onto_vertex_range(source_vrange, smesh, target_vrange, tmesh, tol_vm, + CGAL::parameters::all_default(), + CGAL::parameters::all_default()); +} + +template +std::size_t snap_vertex_range_onto_vertex_range(const SourceVertexRange& source_vrange, + PolygonMesh& smesh, + const TargetVertexRange& target_vrange, + const PolygonMesh& tmesh) +{ + return snap_vertex_range_onto_vertex_range(source_vrange, smesh, target_vrange, tmesh, + CGAL::parameters::all_default(), + CGAL::parameters::all_default()); } template -std::size_t snap_border(PolygonMesh& smesh, - const PolygonMesh& tmesh) +std::size_t snap_vertex_range_onto_vertex_range(PolygonMesh& smesh, const PolygonMesh& tmesh) { - return snap_border(smesh, tmesh, CGAL::parameters::all_default(), CGAL::parameters::all_default()); + return snap_vertex_range_onto_vertex_range(vertices(smesh), smesh, vertices(tmesh), tmesh); +} + +template +std::size_t snap_border_vertices_onto_vertex_range(PolygonMesh& smesh, + const VertexRange& target_vrange, + const PolygonMesh& tmesh, + const ToleranceMap& tol_vm) +{ + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + + std::vector border_vertices_range; + border_vertices(smesh, std::back_inserter(border_vertices_range)); + + return snap_vertex_range_onto_vertex_range(border_vertices_range, smesh, target_vrange, tmesh, tol_vm); +} + +template +std::size_t snap_border_vertices_onto_vertex_range(PolygonMesh& smesh, + const VertexRange& target_vrange, + const PolygonMesh& tmesh) +{ + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + + std::vector border_vertices_range; + border_vertices(smesh, std::back_inserter(border_vertices_range)); + + return snap_vertex_range_onto_vertex_range(border_vertices_range, smesh, target_vrange, tmesh); +} + +// \ingroup PMP_repairing_grp +// +// Attempts to snap the border vertices of the source mesh onto the vertices of the target mesh. +// +// A vertex of the source range is only snapped to a vertex of the target mesh +// if its distance to the target mesh vertex is smaller than a user-chosen bound. +// If any source vertex can be snapped onto multiple vertices of the target +// range, the closest one is chosen. +// If multiple vertices within the source range are to be snapped to the same target vertex, +// then the snapping is not performed for these vertices. +// +// @tparam PolygonMesh a model of `FaceListGraph` and `MutableFaceGraph` +// @tparam ToleranceMap a model of `ReadablePropertyMap` with key type `boost::graph_traits::%vertex_descriptor` +// and value type the number type associated with the traits of the mesh. +// +// @param smesh the source mesh whose border vertices might be moved +// @param tmesh the target mesh whose vertices are potential projection targets +// @param tol_vm a tolerance map associating to each vertex of the source range a tolerance radius: +// potential projection targets are sought in a box centered on that vertex, and whose +// side has length twice the tolerance +// +// @pre `smesh` and `tmesh` are different meshes +// +// \return the number of snapped vertices +// +template +std::size_t snap_border_vertices_onto_vertex_range(PolygonMesh& smesh, + const PolygonMesh& tmesh, + const ToleranceMap& tol_vm) +{ + return snap_border_vertices_onto_vertex_range(smesh, vertices(tmesh), tmesh, tol_vm); +} + +template +std::size_t snap_border_vertices_onto_vertex_range(PolygonMesh& smesh, const PolygonMesh& tmesh) +{ + return snap_border_vertices_onto_vertex_range(smesh, vertices(tmesh), tmesh); } } // end namespace internal diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_snap.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_snap.cpp index 1f212785e95..0802c763ed6 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_snap.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_snap.cpp @@ -28,12 +28,11 @@ void test() { typedef typename Kernel::FT FT; typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; Mesh fg_source, fg_target; // empty meshes - std::size_t res = PMP::internal::snap_border(fg_source, fg_target); + std::size_t res = PMP::internal::snap_border_vertices_onto_vertex_range(fg_source, fg_target); assert(res == 0); std::ifstream source_input("data_snapping/border_snapping_source.off"); @@ -43,15 +42,15 @@ void test() return; } - std::vector border_vertices; - PMP::border_halfedges(fg_source, std::back_inserter(border_vertices)); - std::cout << border_vertices.size() << " border vertices" << std::endl; + std::vector border_vertices_range; + PMP::internal::border_vertices(fg_source, std::back_inserter(border_vertices_range)); + std::cout << border_vertices_range.size() << " border vertices" << std::endl; // one empty mesh - res = PMP::internal::snap_border(fg_source, fg_target); + res = PMP::internal::snap_border_vertices_onto_vertex_range(fg_source, fg_target); assert(res == 0); - res = PMP::internal::snap_border(fg_target, fg_source); + res = PMP::internal::snap_border_vertices_onto_vertex_range(fg_target, vertices(fg_source), fg_source); assert(res == 0); std::ifstream target_input("data_snapping/border_snapping_target.off"); @@ -65,25 +64,37 @@ void test() // this epsilon value is too small, nothing happens std::cout << "*********************** EPS = 0.000000001 *************** " << std::endl; - res = PMP::internal::snap_border(fg_source_cpy, fg_target, 0.000000001); + CGAL::Constant_property_map tol_map_small(0.000000001); + res = PMP::internal::snap_border_vertices_onto_vertex_range(fg_source_cpy, fg_target, tol_map_small); + res = PMP::internal::snap_border_vertices_onto_vertex_range(fg_source_cpy, vertices(fg_target), fg_target, tol_map_small); std::cout << "Moved: " << res << " vertices" << std::endl; assert(res == 0); // this epsilon value is too big to get a 1-to-1 snapping std::cout << "*********************** EPS = 0.1 *************** " << std::endl; + CGAL::Constant_property_map tol_map_big(0.1); fg_source_cpy = fg_source; - res = PMP::internal::snap_border(fg_source_cpy, fg_target, 0.1, - params::geom_traits(Kernel()), params::all_default()); + + border_vertices_range.clear(); + PMP::internal::border_vertices(fg_source_cpy, std::back_inserter(border_vertices_range)); + + res = PMP::internal::snap_vertex_range_onto_vertex_range(border_vertices_range, fg_source_cpy, + vertices(fg_target), fg_target, tol_map_big, + params::geom_traits(Kernel()), params::all_default()); std::cout << "Moved: " << res << " vertices" << std::endl; assert(res == 2); // this is a good value of 'epsilon', but not all expected vertices are projected // because the sampling of the border of the source mesh is not uniform std::cout << "*********************** EPS = 0.001 *************** " << std::endl; + CGAL::Constant_property_map tol_map_good(0.001); fg_source_cpy = fg_source; - CGAL::Constant_property_map tol_map(0.001); - res = PMP::internal::snap_border(fg_source_cpy, fg_target, tol_map, - params::all_default(), params::all_default()); + + border_vertices_range.clear(); + PMP::internal::border_vertices(fg_source_cpy, std::back_inserter(border_vertices_range)); + + res = PMP::internal::snap_vertex_range_onto_vertex_range(border_vertices_range, fg_source_cpy, + vertices(fg_target), fg_target, tol_map_good); std::cout << "Moved: " << res << " vertices" << std::endl; assert(res == 76); @@ -93,7 +104,7 @@ void test() // this one automatically computes an epsilon bound at each vertex std::cout << "*********************** EPS = LOCALLY COMPUTED *************** " << std::endl; fg_source_cpy = fg_source; - res = PMP::internal::snap_border(fg_source_cpy, fg_target); + res = PMP::internal::snap_border_vertices_onto_vertex_range(fg_source_cpy, fg_target); std::cout << "Moved: " << res << " vertices" << std::endl; assert(res == 77);