From 95d76709e83dfa32516c2ee313178ecdc1a86bd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 27 Apr 2018 16:20:54 +0200 Subject: [PATCH 01/21] isotropic remeshing can now use a user defined projection functor you can pass it using a named parameter --- .../CGAL/boost/graph/parameters_interface.h | 1 + BGL/test/BGL/test_cgal_bgl_named_params.cpp | 3 ++ .../Isotropic_remeshing/remesh_impl.h | 32 ++++++++++++++++++- .../CGAL/Polygon_mesh_processing/remesh.h | 2 +- 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/BGL/include/CGAL/boost/graph/parameters_interface.h b/BGL/include/CGAL/boost/graph/parameters_interface.h index fd75acf47d3..0d15f20b885 100644 --- a/BGL/include/CGAL/boost/graph/parameters_interface.h +++ b/BGL/include/CGAL/boost/graph/parameters_interface.h @@ -64,6 +64,7 @@ CGAL_add_named_parameter(nb_points_per_distance_unit_t, nb_points_per_distance_u CGAL_add_named_parameter(outward_orientation_t, outward_orientation, outward_orientation) CGAL_add_named_parameter(overlap_test_t, overlap_test, do_overlap_test_of_bounded_sides) CGAL_add_named_parameter(preserve_genus_t, preserve_genus, preserve_genus) +CGAL_add_named_parameter(projection_functor_t, projection_functor, projection_functor) // List of named parameters that we use in the package 'Surface Mesh Simplification' CGAL_add_named_parameter(get_cost_policy_t, get_cost_policy, get_cost) diff --git a/BGL/test/BGL/test_cgal_bgl_named_params.cpp b/BGL/test/BGL/test_cgal_bgl_named_params.cpp index 91cd3c5ff4a..c8cc107ce1a 100644 --- a/BGL/test/BGL/test_cgal_bgl_named_params.cpp +++ b/BGL/test/BGL/test_cgal_bgl_named_params.cpp @@ -86,6 +86,7 @@ void test(const NamedParameters& np) assert(get_param(np, CGAL::internal_np::weight_calculator).v == 39); assert(get_param(np, CGAL::internal_np::preserve_genus).v == 40); assert(get_param(np, CGAL::internal_np::verbosity_level).v == 41); + assert(get_param(np, CGAL::internal_np::projection_functor).v == 42); // Test types @@ -150,6 +151,7 @@ void test(const NamedParameters& np) check_same_type<39>(get_param(np, CGAL::internal_np::weight_calculator)); check_same_type<40>(get_param(np, CGAL::internal_np::preserve_genus)); check_same_type<41>(get_param(np, CGAL::internal_np::verbosity_level)); + check_same_type<42>(get_param(np, CGAL::internal_np::projection_functor)); } int main() @@ -199,6 +201,7 @@ int main() .weight_calculator(A<39>(39)) .preserve_genus(A<40>(40)) .verbosity_level(A<41>(41)) + .projection_functor(A<42>(42)) ); return EXIT_SUCCESS; diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index e5e71f80a5b..33b25825a1c 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -1038,7 +1038,7 @@ namespace internal { // PMP book : // "maps the vertices back to the surface" - void project_to_surface() + void project_to_surface(boost::param_not_found) { //todo : handle the case of boundary vertices #ifdef CGAL_PMP_REMESHING_VERBOSE @@ -1064,6 +1064,36 @@ namespace internal { std::cout << "done." << std::endl; #endif +#ifdef CGAL_DUMP_REMESHING_STEPS + dump("5-project.off"); +#endif + } + + template + void project_to_surface(const ProjectionFunctor& proj) + { + //todo : handle the case of boundary vertices +#ifdef CGAL_PMP_REMESHING_VERBOSE + std::cout << "Project to surface..."; + std::cout.flush(); +#endif + + BOOST_FOREACH(vertex_descriptor v, vertices(mesh_)) + { + if (is_constrained(v) || is_isolated(v) || !is_on_patch(v)) + continue; + //note if v is constrained, it has not moved + put(vpmap_, v, proj(v, vpmap_)); + } + CGAL_assertion(is_valid(mesh_)); + CGAL_assertion(is_triangle_mesh(mesh_)); +#ifdef CGAL_PMP_REMESHING_DEBUG + debug_self_intersections(); +#endif +#ifdef CGAL_PMP_REMESHING_VERBOSE + std::cout << "done." << std::endl; +#endif + #ifdef CGAL_DUMP_REMESHING_STEPS dump("5-project.off"); #endif diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h index 4a7dc4daead..d9f9b439dec 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h @@ -239,8 +239,8 @@ void isotropic_remeshing(const FaceRange& faces } remesher.equalize_valences(); remesher.tangential_relaxation(smoothing_1d, nb_laplacian); - remesher.project_to_surface(); + remesher.project_to_surface(get_param(np, internal_np::projection_functor)); #ifdef CGAL_PMP_REMESHING_VERBOSE std::cout << std::endl; #endif From ab9d668f52fbb3be943b70110487f62bcbf1f9d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Sun, 29 Apr 2018 13:13:58 +0200 Subject: [PATCH 02/21] first solution to correctly re-constrain edges refined --- .../internal/Isotropic_remeshing/remesh_impl.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index 33b25825a1c..80cce01657c 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -287,6 +287,7 @@ namespace internal { typedef typename boost::property_map< PM, CGAL::dynamic_halfedge_property_t >::type Halfedge_status_pmap; + typedef boost::unordered_set Edge_is_constrained_set; public: Incremental_remesher(PolygonMesh& pmesh @@ -314,6 +315,13 @@ namespace internal { BOOST_FOREACH(halfedge_descriptor h, halfedges(mesh_)) put(halfedge_status_pmap_, h, MESH); + /// \todo the following is not working in case the default map is passed (we need to have a way to detect it) + BOOST_FOREACH(edge_descriptor e, edges(pmesh)) + { + if ( get(ecmap_, e) ) + edge_is_constrained_set_.insert(e); + } + CGAL_assertion(CGAL::is_triangle_mesh(mesh_)); } @@ -413,6 +421,7 @@ namespace internal { Point refinement_point = this->midpoint(he); halfedge_descriptor hnew = CGAL::Euler::split_edge(he, mesh_); CGAL_assertion(he == next(hnew, mesh_)); + if (edge_is_constrained_set_.count(edge(he, mesh_))!=0) edge_is_constrained_set_.insert(edge(hnew, mesh_)); ++nb_splits; //move refinement point @@ -511,6 +520,7 @@ namespace internal { Point refinement_point = this->midpoint(he); halfedge_descriptor hnew = CGAL::Euler::split_edge(he, mesh_); CGAL_assertion(he == next(hnew, mesh_)); + if (edge_is_constrained_set_.count(edge(he, mesh_))!=0) edge_is_constrained_set_.insert(edge(hnew, mesh_)); ++nb_splits; //move refinement point @@ -1911,8 +1921,7 @@ private: { BOOST_FOREACH(edge_descriptor e, edges(mesh_)) { - if (is_on_patch_border(halfedge(e, mesh_)) - || is_on_patch_border(opposite(halfedge(e, mesh_), mesh_))) + if (edge_is_constrained_set_.count(e)) put(ecmap_, e, true); else put(ecmap_, e, false); @@ -1929,6 +1938,7 @@ private: Triangle_list input_triangles_; Patch_id_list input_patch_ids_; Halfedge_status_pmap halfedge_status_pmap_; + Edge_is_constrained_set edge_is_constrained_set_; bool protect_constraints_; FacePatchMap patch_ids_map_; EdgeIsConstrainedMap ecmap_; From 54b6eb0fbc56eaba6b3198fa7c6d86868d2e1810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 1 May 2018 13:34:23 +0200 Subject: [PATCH 03/21] handle case when there are no constrained edges --- .../Isotropic_remeshing/remesh_impl.h | 29 +++++++++++-------- .../CGAL/Polygon_mesh_processing/remesh.h | 8 ++--- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index 80cce01657c..b544189d4d5 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -105,7 +105,7 @@ namespace internal { friend void put(No_constraint_pmap& , const key_type& , const bool ) {} }; - template + template struct Border_constraint_pmap { typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; @@ -125,6 +125,8 @@ namespace internal { : border_edges_ptr(new std::set() ) , pmesh_ptr_(NULL) {} + + template Border_constraint_pmap(const PM& pmesh , const FaceRange& faces , const FIMap& fimap) @@ -139,14 +141,14 @@ namespace internal { border_edges_ptr->insert(edge(h, *pmesh_ptr_)); } - friend bool get(const Border_constraint_pmap& map, + friend bool get(const Border_constraint_pmap& map, const edge_descriptor& e) { CGAL_assertion(map.pmesh_ptr_!=NULL); return map.border_edges_ptr->count(e)!=0; } - friend void put(Border_constraint_pmap& map, + friend void put(Border_constraint_pmap& map, const edge_descriptor& e, const bool is) { @@ -315,11 +317,14 @@ namespace internal { BOOST_FOREACH(halfedge_descriptor h, halfedges(mesh_)) put(halfedge_status_pmap_, h, MESH); - /// \todo the following is not working in case the default map is passed (we need to have a way to detect it) - BOOST_FOREACH(edge_descriptor e, edges(pmesh)) + if (!boost::is_same >::value) { - if ( get(ecmap_, e) ) - edge_is_constrained_set_.insert(e); + BOOST_FOREACH(edge_descriptor e, edges(pmesh)) + { + if ( get(ecmap_, e) ) + edge_is_constrained_set_.insert(e); + } } CGAL_assertion(CGAL::is_triangle_mesh(mesh_)); @@ -1518,7 +1523,7 @@ private: } } - internal::Border_constraint_pmap + internal::Border_constraint_pmap border_map(mesh_, face_range, fimap_); //override the border of PATCH //tag PATCH_BORDER,//h belongs to the patch, hopp doesn't @@ -1919,12 +1924,12 @@ private: void update_constraints_property_map() { - BOOST_FOREACH(edge_descriptor e, edges(mesh_)) + if (!edge_is_constrained_set_.empty()) { - if (edge_is_constrained_set_.count(e)) + BOOST_FOREACH(edge_descriptor e, edge_is_constrained_set_) + { put(ecmap_, e, true); - else - put(ecmap_, e, false); + } } } diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h index d9f9b439dec..3a6bfaccf4f 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h @@ -158,14 +158,14 @@ void isotropic_remeshing(const FaceRange& faces typedef typename boost::lookup_named_param_def < internal_np::edge_is_constrained_t, NamedParameters, - internal::Border_constraint_pmap//default + internal::Border_constraint_pmap//default > ::type ECMap; - ECMap ecmap = (boost::is_same >::value) + ECMap ecmap = (boost::is_same >::value) //avoid constructing the Border_constraint_pmap if it's not used ? choose_param(get_param(np, internal_np::edge_is_constrained) - , internal::Border_constraint_pmap(pmesh, faces, fimap)) + , internal::Border_constraint_pmap(pmesh, faces, fimap)) : choose_param(get_param(np, internal_np::edge_is_constrained) - , internal::Border_constraint_pmap()); + , internal::Border_constraint_pmap()); typedef typename boost::lookup_named_param_def < internal_np::vertex_is_constrained_t, From ec4675b451ded1eb75b5eaa6889b1e1029c7f3b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 1 May 2018 13:47:57 +0200 Subject: [PATCH 04/21] do not build the aabb_tree and the cc map if not needed --- .../include/CGAL/Polygon_mesh_processing/remesh.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h index 3a6bfaccf4f..9911de5a29b 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h @@ -145,6 +145,9 @@ void isotropic_remeshing(const FaceRange& faces t.start(); #endif + static const bool need_aabb_tree = + boost::is_default_param(get_param(np, internal_np::projection_functor)); + typedef typename GetGeomTraits::type GT; typedef typename GetVertexPointMap::type VPMap; @@ -183,7 +186,8 @@ void isotropic_remeshing(const FaceRange& faces FPMap fpmap = choose_param( get_param(np, internal_np::face_patch), internal::Connected_components_pmap(pmesh, ecmap, fimap, - boost::is_default_param(get_param(np, internal_np::face_patch)))); + boost::is_default_param(get_param(np, internal_np::face_patch)) && + need_aabb_tree )); double low = 4. / 5. * target_edge_length; double high = 4. / 3. * target_edge_length; @@ -208,7 +212,7 @@ void isotropic_remeshing(const FaceRange& faces #endif typename internal::Incremental_remesher - remesher(pmesh, vpmap, protect, ecmap, vcmap, fpmap, fimap); + remesher(pmesh, vpmap, protect, ecmap, vcmap, fpmap, fimap, need_aabb_tree); remesher.init_remeshing(faces); #ifdef CGAL_PMP_REMESHING_VERBOSE From c3255eee911d1e2634ede4d6de8b1f98e5558990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 1 May 2018 14:15:27 +0200 Subject: [PATCH 05/21] document projection functor named parameter --- .../Polygon_mesh_processing/NamedParameters.txt | 15 +++++++++++---- .../internal/Isotropic_remeshing/remesh_impl.h | 3 +-- .../include/CGAL/Polygon_mesh_processing/remesh.h | 6 ++++++ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt index d9b8e95c9ac..2d13147b949 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt @@ -299,15 +299,22 @@ If this parameter is not provided, the perturbation is not deterministic \cgalNPBegin{outward_orientation} \anchor PMP_outward_orientation Parameter used in orientation functions to choose between an outward or inward orientation. \n -\b Type : `bool` \n -\b Default value is `true` +Type: `bool` \n +Default: `true` \cgalNPBegin{do_overlap_test_of_bounded_sides} \anchor PMP_do_overlap_test_of_bounded_sides Parameter used in intersection test functions to indicate whether overlapping tests of bounded sides of close meshes should be done in addition to surface intersection tests. \n -\b Type : `bool` \n -\b Default value is `false` +Type: `bool` \n +Default: `false` +\cgalNPEnd + +\cgalNPBegin{projection_functor} \anchor PMP_projection_functor +Parameter used in isotropic remeshing function to specify an alternative vertex projection method. +\n +Type: Unary function object with vertex descriptor as argument type. \n +Default: A function object projecting vertices on the input surface. \cgalNPEnd \cgalNPTableEnd diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index b544189d4d5..d92a9e812f6 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -1092,13 +1092,12 @@ namespace internal { std::cout << "Project to surface..."; std::cout.flush(); #endif - BOOST_FOREACH(vertex_descriptor v, vertices(mesh_)) { if (is_constrained(v) || is_isolated(v) || !is_on_patch(v)) continue; //note if v is constrained, it has not moved - put(vpmap_, v, proj(v, vpmap_)); + put(vpmap_, v, proj(v)); } CGAL_assertion(is_valid(mesh_)); CGAL_assertion(is_triangle_mesh(mesh_)); diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h index 9911de5a29b..ed68836e991 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h @@ -107,6 +107,12 @@ namespace Polygon_mesh_processing { * constrained in `edge_is_constrained_map` and boundary edges move along the * constrained polylines they belong to. * \cgalParamEnd +* \cgalParamBegin{projection_functor} +* A function object used to project vertices created. It must have +* `%Point_3 operator()(vertex_descriptor)`, `%Point_3` being the value type +* of the vertex point map. +* If not provided, vertices are projected on the input surface mesh. +* \cgalParamEnd * \cgalNamedParamsEnd * * @sa `split_long_edges()` From 6b601ccf80c2f7d0a47061a1635bdd06897a0ec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 14 May 2018 09:51:26 +0200 Subject: [PATCH 06/21] update after Jane's review --- .../doc/Polygon_mesh_processing/NamedParameters.txt | 2 +- .../include/CGAL/Polygon_mesh_processing/remesh.h | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt index 2d13147b949..3cb3f518a3c 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt @@ -311,7 +311,7 @@ of close meshes should be done in addition to surface intersection tests. \cgalNPEnd \cgalNPBegin{projection_functor} \anchor PMP_projection_functor -Parameter used in isotropic remeshing function to specify an alternative vertex projection method. +Parameter used in `isotropic_remeshing()` to specify an alternative vertex projection method. \n Type: Unary function object with vertex descriptor as argument type. \n Default: A function object projecting vertices on the input surface. diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h index ed68836e991..e6ab4db7a3d 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h @@ -108,8 +108,8 @@ namespace Polygon_mesh_processing { * constrained polylines they belong to. * \cgalParamEnd * \cgalParamBegin{projection_functor} -* A function object used to project vertices created. It must have -* `%Point_3 operator()(vertex_descriptor)`, `%Point_3` being the value type +* A function object used to project input vertices (moved by the smoothing) and created vertices. +* It must have `%Point_3 operator()(vertex_descriptor)`, `%Point_3` being the value type * of the vertex point map. * If not provided, vertices are projected on the input surface mesh. * \cgalParamEnd @@ -249,7 +249,6 @@ void isotropic_remeshing(const FaceRange& faces } remesher.equalize_valences(); remesher.tangential_relaxation(smoothing_1d, nb_laplacian); - remesher.project_to_surface(get_param(np, internal_np::projection_functor)); #ifdef CGAL_PMP_REMESHING_VERBOSE std::cout << std::endl; From ac8fbfd6aa50f85106f4dce1b813b30682f12c0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 14 May 2018 11:46:07 +0200 Subject: [PATCH 07/21] add a condition to make sure the flip does not create a non-manifold edge --- .../Isotropic_remeshing/remesh_impl.h | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index d92a9e812f6..2f0971a0afd 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -655,7 +655,7 @@ namespace internal { std::cout.flush(); #endif - edge_descriptor e = edge(he, mesh_); + const edge_descriptor e = edge(he, mesh_); if (!is_collapse_allowed(e)) continue; //situation could have changed since it was added to the bimap @@ -857,6 +857,7 @@ namespace internal { Patch_id pid = get_patch_id(face(he, mesh_)); + CGAL_assertion( is_flip_topologically_allowed(edge(he, mesh_)) ); CGAL::Euler::flip_edge(he, mesh_); vva -= 1; vvb -= 1; @@ -894,6 +895,7 @@ namespace internal { || !check_normals(target(he, mesh_)) || !check_normals(source(he, mesh_))) { + CGAL_assertion( is_flip_topologically_allowed(edge(he, mesh_)) ); CGAL::Euler::flip_edge(he, mesh_); --nb_flips; @@ -1322,10 +1324,23 @@ private: return true;//we already checked we're not pinching a hole in the patch } + bool is_flip_topologically_allowed(const edge_descriptor& e) const + { + halfedge_descriptor h=halfedge(e, mesh_); + return !halfedge(target(next(h, mesh_), mesh_), + target(next(opposite(h, mesh_), mesh_), mesh_), + mesh_).second; + } + bool is_flip_allowed(const edge_descriptor& e) const { - return is_flip_allowed(halfedge(e, mesh_)) - && is_flip_allowed(opposite(halfedge(e, mesh_), mesh_)); + bool flip_possible = is_flip_allowed(halfedge(e, mesh_)) + && is_flip_allowed(opposite(halfedge(e, mesh_), mesh_)); + + if (!flip_possible) return false; + + // the flip is not possible if the edge already exists + return is_flip_topologically_allowed(e); } bool is_flip_allowed(const halfedge_descriptor& h) const @@ -1631,7 +1646,7 @@ private: short_edges.left.erase(hf); short_edges.left.erase(hfo); - + CGAL_assertion( is_flip_topologically_allowed(edge(hf, mesh_)) ); CGAL::Euler::flip_edge(hf, mesh_); CGAL_assertion_code(++nb_done); From 73db555c5eacc955bfb11abf33740e5084001956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 15 May 2018 10:30:40 +0200 Subject: [PATCH 08/21] unconstrained an edge to be collapsed --- .../internal/Isotropic_remeshing/remesh_impl.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index 2f0971a0afd..c481da89f1e 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -776,6 +776,11 @@ namespace internal { set_constrained(vb, false); } + if (!protect_constraints_) + edge_is_constrained_set_.erase(e); + else + CGAL_assertion( edge_is_constrained_set_.count(e)==0 ); + //perform collapse Point target_point = get(vpmap_, vb); vertex_descriptor vkept = CGAL::Euler::collapse_edge(e, mesh_); @@ -858,6 +863,7 @@ namespace internal { Patch_id pid = get_patch_id(face(he, mesh_)); CGAL_assertion( is_flip_topologically_allowed(edge(he, mesh_)) ); + CGAL_assertion( edge_is_constrained_set_.count(edge(he, mesh_))==0 ); CGAL::Euler::flip_edge(he, mesh_); vva -= 1; vvb -= 1; @@ -896,6 +902,7 @@ namespace internal { || !check_normals(source(he, mesh_))) { CGAL_assertion( is_flip_topologically_allowed(edge(he, mesh_)) ); + CGAL_assertion( edge_is_constrained_set_.count(edge(he, mesh_))==0 ); CGAL::Euler::flip_edge(he, mesh_); --nb_flips; @@ -1647,6 +1654,7 @@ private: short_edges.left.erase(hf); short_edges.left.erase(hfo); CGAL_assertion( is_flip_topologically_allowed(edge(hf, mesh_)) ); + CGAL_assertion( edge_is_constrained_set_.count(edge(hf, mesh_))==0 ); CGAL::Euler::flip_edge(hf, mesh_); CGAL_assertion_code(++nb_done); From fbd982f7d8717889ab4f48276598ef5d29469de5 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 15 May 2018 11:18:15 +0200 Subject: [PATCH 09/21] document undocumented named parameters and add extra info about split edges --- .../include/CGAL/Polygon_mesh_processing/remesh.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h index e6ab4db7a3d..fac3ad8010b 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h @@ -81,6 +81,7 @@ namespace Polygon_mesh_processing { * \cgalParamBegin{edge_is_constrained_map} a property map containing the * constrained-or-not status of each edge of `pmesh`. A constrained edge can be split * or collapsed, but not flipped, nor its endpoints moved by smoothing. +* Sub-edges generated by splitting are set to be constrained. * Note that patch boundary edges (i.e. incident to only one face in the range) * are always considered as constrained edges. * \cgalParamEnd @@ -305,6 +306,12 @@ void isotropic_remeshing( * \cgalParamBegin{vertex_point_map} the property map with the points associated * to the vertices of `pmesh`. Instance of a class model of `ReadWritePropertyMap`. * \cgalParamEnd +* \cgalParamBegin{face_index_map} a property map containing the index of each face of `pmesh` +* \cgalParamEnd +* \cgalParamBegin{edge_is_constrained_map} a property map containing the +* constrained-or-not status of each edge of `pmesh`. A constrained edge can be split, +* and the sub-edges are set to be constrained. +* \cgalParamEnd * \cgalNamedParamsEnd * * @sa `isotropic_remeshing()` From 35125cd9a5cf0ae4b48604877c68a4a43f447e4d Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 15 May 2018 11:23:46 +0200 Subject: [PATCH 10/21] remove todo done by this PR --- .../include/CGAL/Polygon_mesh_processing/remesh.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h index fac3ad8010b..f9c970869f2 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h @@ -118,7 +118,6 @@ namespace Polygon_mesh_processing { * * @sa `split_long_edges()` * -*@todo add possibility to provide a functor that projects to a prescribed surface *@todo Deal with exact constructions Kernel. The only thing that makes sense is to * guarantee that the output vertices are exactly on the input surface. * To do so, we can do every construction in `double`, and use an exact process for From e08c1cdc4deab3df8e8746f92632b486807079ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 15 May 2018 12:21:05 +0200 Subject: [PATCH 11/21] add an option to prevent the collapse of constrained edges --- BGL/include/CGAL/boost/graph/parameters_interface.h | 1 + BGL/test/BGL/test_cgal_bgl_named_params.cpp | 3 +++ .../doc/Polygon_mesh_processing/NamedParameters.txt | 9 +++++++++ .../internal/Isotropic_remeshing/remesh_impl.h | 5 ++++- .../include/CGAL/Polygon_mesh_processing/remesh.h | 9 +++++++-- 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/BGL/include/CGAL/boost/graph/parameters_interface.h b/BGL/include/CGAL/boost/graph/parameters_interface.h index 0d15f20b885..d152be492c8 100644 --- a/BGL/include/CGAL/boost/graph/parameters_interface.h +++ b/BGL/include/CGAL/boost/graph/parameters_interface.h @@ -46,6 +46,7 @@ CGAL_add_named_parameter(sparse_linear_solver_t, sparse_linear_solver, sparse_li CGAL_add_named_parameter(number_of_relaxation_steps_t, number_of_relaxation_steps, number_of_relaxation_steps) CGAL_add_named_parameter(protect_constraints_t, protect_constraints, protect_constraints) CGAL_add_named_parameter(relax_constraints_t, relax_constraints, relax_constraints) +CGAL_add_named_parameter(collapse_constraints_t, collapse_constraints, collapse_constraints) CGAL_add_named_parameter(vertex_is_constrained_t, vertex_is_constrained, vertex_is_constrained_map) CGAL_add_named_parameter(face_patch_t, face_patch, face_patch_map) CGAL_add_named_parameter(random_uniform_sampling_t, random_uniform_sampling, use_random_uniform_sampling) diff --git a/BGL/test/BGL/test_cgal_bgl_named_params.cpp b/BGL/test/BGL/test_cgal_bgl_named_params.cpp index c8cc107ce1a..fd1f631ea6b 100644 --- a/BGL/test/BGL/test_cgal_bgl_named_params.cpp +++ b/BGL/test/BGL/test_cgal_bgl_named_params.cpp @@ -57,6 +57,7 @@ void test(const NamedParameters& np) assert(get_param(np, CGAL::internal_np::number_of_relaxation_steps).v == 16); assert(get_param(np, CGAL::internal_np::protect_constraints).v == 17); assert(get_param(np, CGAL::internal_np::relax_constraints).v == 18); + assert(get_param(np, CGAL::internal_np::collapse_constraints).v == 43); assert(get_param(np, CGAL::internal_np::vertex_is_constrained).v == 19); assert(get_param(np, CGAL::internal_np::face_patch).v == 20); assert(get_param(np, CGAL::internal_np::random_uniform_sampling).v == 21); @@ -122,6 +123,7 @@ void test(const NamedParameters& np) check_same_type<16>(get_param(np, CGAL::internal_np::number_of_relaxation_steps)); check_same_type<17>(get_param(np, CGAL::internal_np::protect_constraints)); check_same_type<18>(get_param(np, CGAL::internal_np::relax_constraints)); + check_same_type<43>(get_param(np, CGAL::internal_np::collapse_constraints)); check_same_type<19>(get_param(np, CGAL::internal_np::vertex_is_constrained)); check_same_type<20>(get_param(np, CGAL::internal_np::face_patch)); check_same_type<21>(get_param(np, CGAL::internal_np::random_uniform_sampling)); @@ -178,6 +180,7 @@ int main() .number_of_relaxation_steps(A<16>(16)) .protect_constraints(A<17>(17)) .relax_constraints(A<18>(18)) + .collapse_constraints(A<43>(43)) .vertex_is_constrained_map(A<19>(19)) .face_patch_map(A<20>(20)) .use_random_uniform_sampling(A<21>(21)) diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt index 3cb3f518a3c..62ffc20975e 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt @@ -159,6 +159,15 @@ during the remeshing process.\n Default: `false` \cgalNPEnd +\cgalNPBegin{collapse_constraints} \anchor PMP_collapse_constraints +enables the collapse of constraints listed by \ref PMP_edge_is_constrained_map +"edge_is_constrained_map" and boundary edges +in `isotropic_remeshing()`. If `false`, constraint edges cannot be collapsed +during the remeshing process.\n +Type: `bool` \n +Default: `true` +\cgalNPEnd + \cgalNPBegin{relax_constraints} \anchor PMP_relax_constraints enables the tangential relaxation step in `isotropic_remeshing()` to be performed on vertices that are endpoints of constraints listed diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index c481da89f1e..562432e4899 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -295,6 +295,7 @@ namespace internal { Incremental_remesher(PolygonMesh& pmesh , VertexPointMap& vpmap , const bool protect_constraints + , const bool collapse_constraints , EdgeIsConstrainedMap ecmap , VertexIsConstrainedMap vcmap , FacePatchMap fpmap @@ -307,6 +308,7 @@ namespace internal { , input_triangles_() , input_patch_ids_() , protect_constraints_(protect_constraints) + , collapse_constraints_(collapse_constraints) , patch_ids_map_(fpmap) , ecmap_(ecmap) , vcmap_(vcmap) @@ -1267,7 +1269,7 @@ private: if (is_on_mesh(he) && is_on_mesh(hopp)) return false; - if (protect_constraints_ && is_constrained(e)) + if ( (protect_constraints_ || !collapse_constraints_) && is_constrained(e)) return false; if (is_on_patch(he)) //hopp is also on patch { @@ -1967,6 +1969,7 @@ private: Halfedge_status_pmap halfedge_status_pmap_; Edge_is_constrained_set edge_is_constrained_set_; bool protect_constraints_; + bool collapse_constraints_; FacePatchMap patch_ids_map_; EdgeIsConstrainedMap ecmap_; VertexIsConstrainedMap vcmap_; diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h index f9c970869f2..7b49d5d7744 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h @@ -97,6 +97,10 @@ namespace Polygon_mesh_processing { * good quality results. It can even fail to terminate because of cascading vertex * insertions. * \cgalParamEnd +* \cgalParamBegin{collapse_constraints} If `true`, the edges set as constrained +* in `edge_is_constrained_map` (or by default the boundary edges) +* are collapsed during remeshing. This value is ignored if `protect_constraints` is true; +* \cgalParamEnd * \cgalParamBegin{face_patch_map} a property map with the patch id's associated to the faces of `faces`. Instance of a class model of `ReadWritePropertyMap`. It gets updated during the remeshing process while new faces are created. @@ -217,8 +221,9 @@ void isotropic_remeshing(const FaceRange& faces t.reset(); t.start(); #endif + bool collapse_constraints = choose_param(get_param(np, internal_np::collapse_constraints), true); typename internal::Incremental_remesher - remesher(pmesh, vpmap, protect, ecmap, vcmap, fpmap, fimap, need_aabb_tree); + remesher(pmesh, vpmap, protect, collapse_constraints, ecmap, vcmap, fpmap, fimap, need_aabb_tree); remesher.init_remeshing(faces); #ifdef CGAL_PMP_REMESHING_VERBOSE @@ -352,7 +357,7 @@ void split_long_edges(const EdgeRange& edges internal::Connected_components_pmap, FIMap > - remesher(pmesh, vpmap, false/*protect constraints*/ + remesher(pmesh, vpmap, false/*protect constraints*/, false /*collapse_constraints*/ , ecmap , internal::No_constraint_pmap() , internal::Connected_components_pmap(pmesh, ecmap, fimap, false) From 76fd482ccea6eae46436888cd233d5f24f038eeb Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 15 May 2018 12:52:32 +0200 Subject: [PATCH 12/21] make collapse_constraints a parameter of collapse_short_edges instead of a remesher global parameter --- .../Isotropic_remeshing/remesh_impl.h | 27 ++++++++++--------- .../CGAL/Polygon_mesh_processing/remesh.h | 8 +++--- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index 562432e4899..d5b8a2765c4 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -295,7 +295,6 @@ namespace internal { Incremental_remesher(PolygonMesh& pmesh , VertexPointMap& vpmap , const bool protect_constraints - , const bool collapse_constraints , EdgeIsConstrainedMap ecmap , VertexIsConstrainedMap vcmap , FacePatchMap fpmap @@ -308,7 +307,6 @@ namespace internal { , input_triangles_() , input_patch_ids_() , protect_constraints_(protect_constraints) - , collapse_constraints_(collapse_constraints) , patch_ids_map_(fpmap) , ecmap_(ecmap) , vcmap_(vcmap) @@ -614,7 +612,9 @@ namespace internal { // "collapses and thus removes all edges that are shorter than a // threshold `low`. [...] testing before each collapse whether the collapse // would produce an edge that is longer than `high`" - void collapse_short_edges(const double& low, const double& high) + void collapse_short_edges(const double& low, + const double& high, + const bool collapse_constraints) { typedef boost::bimap< boost::bimaps::set_of, @@ -636,7 +636,7 @@ namespace internal { BOOST_FOREACH(edge_descriptor e, edges(mesh_)) { double sqlen = sqlength(e); - if( (sqlen < sq_low) && is_collapse_allowed(e) ) + if ((sqlen < sq_low) && is_collapse_allowed(e, collapse_constraints)) short_edges.insert(short_edge(halfedge(e, mesh_), sqlen)); } #ifdef CGAL_PMP_REMESHING_VERBOSE_PROGRESS @@ -658,7 +658,7 @@ namespace internal { #endif const edge_descriptor e = edge(he, mesh_); - if (!is_collapse_allowed(e)) + if (!is_collapse_allowed(e, collapse_constraints)) continue; //situation could have changed since it was added to the bimap //handle the boundary case : @@ -708,7 +708,7 @@ namespace internal { continue;//both directions invert a face } CGAL_assertion(collapse_does_not_invert_face(he)); - CGAL_assertion(is_collapse_allowed(e)); + CGAL_assertion(is_collapse_allowed(e, collapse_constraints)); if (degree(va, mesh_) < 3 || degree(vb, mesh_) < 3 @@ -793,7 +793,7 @@ namespace internal { if (constrained_case)//we have made sure that collapse goes to constrained vertex set_constrained(vkept, true); - fix_degenerate_faces(vkept, short_edges, sq_low); + fix_degenerate_faces(vkept, short_edges, sq_low, collapse_constraints); #ifdef CGAL_PMP_REMESHING_DEBUG debug_status_map(); @@ -804,7 +804,7 @@ namespace internal { BOOST_FOREACH(halfedge_descriptor ht, halfedges_around_target(vkept, mesh_)) { double sqlen = sqlength(ht); - if( (sqlen < sq_low) && is_collapse_allowed(edge(ht, mesh_)) ) + if ((sqlen < sq_low) && is_collapse_allowed(edge(ht, mesh_), collapse_constraints)) short_edges.insert(short_edge(ht, sqlen)); } }//end if(collapse_ok) @@ -1261,7 +1261,8 @@ private: } } - bool is_collapse_allowed(const edge_descriptor& e) const + bool is_collapse_allowed(const edge_descriptor& e + , const bool collapse_constraints) const { halfedge_descriptor he = halfedge(e, mesh_); halfedge_descriptor hopp = opposite(he, mesh_); @@ -1269,7 +1270,7 @@ private: if (is_on_mesh(he) && is_on_mesh(hopp)) return false; - if ( (protect_constraints_ || !collapse_constraints_) && is_constrained(e)) + if ( (protect_constraints_ || !collapse_constraints) && is_constrained(e)) return false; if (is_on_patch(he)) //hopp is also on patch { @@ -1611,7 +1612,8 @@ private: template void fix_degenerate_faces(const vertex_descriptor& v, Bimap& short_edges, - const double& sq_low) + const double& sq_low, + const bool collapse_constraints) { CGAL_assertion_code(std::size_t nb_done = 0); boost::unordered_set degenerate_faces; @@ -1673,7 +1675,7 @@ private: #endif //insert new edges in 'short_edges' - if (is_collapse_allowed(edge(hf, mesh_))) + if (is_collapse_allowed(edge(hf, mesh_), collapse_constraints)) { double sqlen = sqlength(hf); if (sqlen < sq_low) @@ -1969,7 +1971,6 @@ private: Halfedge_status_pmap halfedge_status_pmap_; Edge_is_constrained_set edge_is_constrained_set_; bool protect_constraints_; - bool collapse_constraints_; FacePatchMap patch_ids_map_; EdgeIsConstrainedMap ecmap_; VertexIsConstrainedMap vcmap_; diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h index 7b49d5d7744..459685381a5 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h @@ -221,9 +221,8 @@ void isotropic_remeshing(const FaceRange& faces t.reset(); t.start(); #endif - bool collapse_constraints = choose_param(get_param(np, internal_np::collapse_constraints), true); typename internal::Incremental_remesher - remesher(pmesh, vpmap, protect, collapse_constraints, ecmap, vcmap, fpmap, fimap, need_aabb_tree); + remesher(pmesh, vpmap, protect, ecmap, vcmap, fpmap, fimap, need_aabb_tree); remesher.init_remeshing(faces); #ifdef CGAL_PMP_REMESHING_VERBOSE @@ -231,6 +230,7 @@ void isotropic_remeshing(const FaceRange& faces std::cout << " done ("<< t.time() <<" sec)." << std::endl; #endif + bool collapse_constraints = choose_param(get_param(np, internal_np::collapse_constraints), true); unsigned int nb_iterations = choose_param(get_param(np, internal_np::number_of_iterations), 1); bool smoothing_1d = choose_param(get_param(np, internal_np::relax_constraints), false); unsigned int nb_laplacian = choose_param(get_param(np, internal_np::number_of_relaxation_steps), 1); @@ -250,7 +250,7 @@ void isotropic_remeshing(const FaceRange& faces if (target_edge_length>0) { remesher.split_long_edges(high); - remesher.collapse_short_edges(low, high); + remesher.collapse_short_edges(low, high, collapse_constraints); } remesher.equalize_valences(); remesher.tangential_relaxation(smoothing_1d, nb_laplacian); @@ -357,7 +357,7 @@ void split_long_edges(const EdgeRange& edges internal::Connected_components_pmap, FIMap > - remesher(pmesh, vpmap, false/*protect constraints*/, false /*collapse_constraints*/ + remesher(pmesh, vpmap, false/*protect constraints*/ , ecmap , internal::No_constraint_pmap() , internal::Connected_components_pmap(pmesh, ecmap, fimap, false) From 43c6fd36352489be24969720d869f699aae5f0a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 18 May 2018 10:59:26 +0200 Subject: [PATCH 13/21] make the projection optional --- .../include/CGAL/Polygon_mesh_processing/remesh.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h index 459685381a5..3b47eff317a 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h @@ -112,6 +112,9 @@ namespace Polygon_mesh_processing { * constrained in `edge_is_constrained_map` and boundary edges move along the * constrained polylines they belong to. * \cgalParamEnd +* \cgalParamBegin{do_project} a boolean that sets whether vertices should be reprojected +* on the input surface after creation or displacement. +* \cgalParamEnd * \cgalParamBegin{projection_functor} * A function object used to project input vertices (moved by the smoothing) and created vertices. * It must have `%Point_3 operator()(vertex_descriptor)`, `%Point_3` being the value type @@ -254,7 +257,8 @@ void isotropic_remeshing(const FaceRange& faces } remesher.equalize_valences(); remesher.tangential_relaxation(smoothing_1d, nb_laplacian); - remesher.project_to_surface(get_param(np, internal_np::projection_functor)); + if ( choose_param(get_param(np, internal_np::do_project), true) ) + remesher.project_to_surface(get_param(np, internal_np::projection_functor)); #ifdef CGAL_PMP_REMESHING_VERBOSE std::cout << std::endl; #endif From 42de73320af733d9d75118f266e39a78351ea620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 18 May 2018 11:07:18 +0200 Subject: [PATCH 14/21] only constrain edges split that were constrained before --- .../internal/Isotropic_remeshing/remesh_impl.h | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index d5b8a2765c4..da49aba8396 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -383,6 +383,7 @@ namespace internal { // split edges of edge_range that have their length > high + // Note: only used to split a range of edges provided as input template void split_long_edges(const EdgeRange& edge_range, const double& high) @@ -404,12 +405,7 @@ namespace internal { { double sqlen = sqlength(e); if (sqlen > sq_high) - { long_edges.insert(long_edge(halfedge(e, mesh_), sqlen)); - put(ecmap_, e, false); - } - else - put(ecmap_, e, true); } //split long edges @@ -425,6 +421,8 @@ namespace internal { //split edge Point refinement_point = this->midpoint(he); halfedge_descriptor hnew = CGAL::Euler::split_edge(he, mesh_); + // propagate the constrained status + put(ecmap_, edge(hnew, mesh_), get(ecmap_, edge(he, mesh_))); CGAL_assertion(he == next(hnew, mesh_)); if (edge_is_constrained_set_.count(edge(he, mesh_))!=0) edge_is_constrained_set_.insert(edge(hnew, mesh_)); ++nb_splits; @@ -444,11 +442,6 @@ namespace internal { long_edges.insert(long_edge(hnew, sqlen_new)); long_edges.insert(long_edge(next(hnew, mesh_), sqlen_new)); } - else - { - put(ecmap_, edge(hnew, mesh_), true); - put(ecmap_, edge(next(hnew, mesh_), mesh_), true); - } //insert new edges to keep triangular faces, and update long_edges if (!is_border(hnew, mesh_)) From 90faf5d0602c46622b2cb521761ee11ffb8e3b3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 18 May 2018 14:10:31 +0200 Subject: [PATCH 15/21] simplify initialization of CC --- .../Isotropic_remeshing/remesh_impl.h | 89 ++++++++++++------- .../CGAL/Polygon_mesh_processing/remesh.h | 22 ++--- Property_map/include/CGAL/property_map.h | 10 ++- 3 files changed, 75 insertions(+), 46 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index da49aba8396..f9db30f6ae2 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -162,15 +162,13 @@ namespace internal { template struct Connected_components_pmap { typedef typename boost::graph_traits::face_descriptor face_descriptor; typedef std::size_t Patch_id; typedef FaceIndexMap FIMap; - typedef EdgeIsConstrainedMap ECMap; - typedef Connected_components_pmap CCMap; + typedef Connected_components_pmap CCMap; typedef CGAL::dynamic_face_property_t Face_property_tag; typedef typename boost::property_map::type Patch_ids_map; @@ -185,7 +183,9 @@ namespace internal { //note pmesh is a non-const ref because properties are added and removed //modify the mesh data structure, but not the mesh itself - Connected_components_pmap(PM& pmesh + template + Connected_components_pmap(const FaceRange& face_range + , PM& pmesh , EdgeIsConstrainedMap ecmap , FIMap fimap , const bool do_init = true) @@ -196,15 +196,32 @@ namespace internal { #ifdef CGAL_PMP_REMESHING_VERBOSE std::cout << "Compute connected components property map." << std::endl; #endif - nb_cc - = PMP::connected_components(pmesh, - patch_ids_map, - PMP::parameters::edge_is_constrained_map(ecmap) - .face_index_map(fimap)); + if (boost::size(face_range) == boost::size(faces(pmesh))) + { + // applied on the whole mesh + nb_cc + = PMP::connected_components(pmesh, + patch_ids_map, + PMP::parameters::edge_is_constrained_map(ecmap) + .face_index_map(fimap)); + } + else + { + // applied on a subset of the mesh + nb_cc + = PMP::connected_components(pmesh, + patch_ids_map, + PMP::parameters::edge_is_constrained_map( + make_OR_property_map(ecmap + , internal::Border_constraint_pmap(pmesh, face_range, fimap) ) ) + .face_index_map(fimap)); + } if(nb_cc == 1){ patch_ids_map = Patch_ids_map(); } } + else + nb_cc=0; // default value } @@ -318,7 +335,7 @@ namespace internal { put(halfedge_status_pmap_, h, MESH); if (!boost::is_same >::value) + No_constraint_pmap >::value) { BOOST_FOREACH(edge_descriptor e, edges(pmesh)) { @@ -1517,8 +1534,9 @@ private: template void tag_halfedges_status(const FaceRange& face_range) { - //tag MESH, //h and hopp belong to the mesh, not the patch - //tag MESH_BORDER //h belongs to the mesh, face(hopp, pmesh) == null_face() + //init halfedges as: + // - MESH, //h and hopp belong to the mesh, not the patch + // - MESH_BORDER //h belongs to the mesh, face(hopp, pmesh) == null_face() BOOST_FOREACH(halfedge_descriptor h, halfedges(mesh_)) { //being part of the border of the mesh is predominant @@ -1540,34 +1558,41 @@ private: } } - internal::Border_constraint_pmap - border_map(mesh_, face_range, fimap_); - //override the border of PATCH - //tag PATCH_BORDER,//h belongs to the patch, hopp doesn't - BOOST_FOREACH(edge_descriptor e, edges(mesh_)) + // tag patch border halfedges + BOOST_FOREACH(halfedge_descriptor h, halfedges(mesh_)) { - if (get(ecmap_, e) - || get(border_map, e) - || get_patch_id(face(halfedge(e, mesh_), mesh_)) - != get_patch_id(face(opposite(halfedge(e, mesh_), mesh_), mesh_))) + if (status(h)==PATCH && status(opposite(h, mesh_))!=PATCH) { - //deal with h and hopp for borders that are sharp edges to be preserved - halfedge_descriptor h = halfedge(e, mesh_); - if (status(h) == PATCH){ - set_status(h, PATCH_BORDER); - has_border_ = true; - } + set_status(h, PATCH_BORDER); + has_border_ = true; + } + } - halfedge_descriptor hopp = opposite(h, mesh_); - if (status(hopp) == PATCH){ - set_status(hopp, PATCH_BORDER); - has_border_ = true; + // update status using constrained edge map + if (!boost::is_same >::value) + { + BOOST_FOREACH(edge_descriptor e, edges(mesh_)) + { + if (get(ecmap_, e)) + { + //deal with h and hopp for borders that are sharp edges to be preserved + halfedge_descriptor h = halfedge(e, mesh_); + if (status(h) == PATCH){ + set_status(h, PATCH_BORDER); + has_border_ = true; + } + + halfedge_descriptor hopp = opposite(h, mesh_); + if (status(hopp) == PATCH){ + set_status(hopp, PATCH_BORDER); + has_border_ = true; + } } } } } - // for a Surface_mesh::Property_map we make MESH the default value Halfedge_status status(const halfedge_descriptor& h) const { diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h index 3b47eff317a..d6c4e808ff4 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h @@ -147,6 +147,7 @@ void isotropic_remeshing(const FaceRange& faces typedef PolygonMesh PM; typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; using boost::get_param; using boost::choose_param; @@ -174,14 +175,10 @@ void isotropic_remeshing(const FaceRange& faces typedef typename boost::lookup_named_param_def < internal_np::edge_is_constrained_t, NamedParameters, - internal::Border_constraint_pmap//default + internal::No_constraint_pmap//default > ::type ECMap; - ECMap ecmap = (boost::is_same >::value) - //avoid constructing the Border_constraint_pmap if it's not used - ? choose_param(get_param(np, internal_np::edge_is_constrained) - , internal::Border_constraint_pmap(pmesh, faces, fimap)) - : choose_param(get_param(np, internal_np::edge_is_constrained) - , internal::Border_constraint_pmap()); + ECMap ecmap = choose_param(get_param(np, internal_np::edge_is_constrained) + , internal::No_constraint_pmap()); typedef typename boost::lookup_named_param_def < internal_np::vertex_is_constrained_t, @@ -194,13 +191,12 @@ void isotropic_remeshing(const FaceRange& faces typedef typename boost::lookup_named_param_def < internal_np::face_patch_t, NamedParameters, - internal::Connected_components_pmap//default + internal::Connected_components_pmap//default > ::type FPMap; FPMap fpmap = choose_param( get_param(np, internal_np::face_patch), - internal::Connected_components_pmap(pmesh, ecmap, fimap, - boost::is_default_param(get_param(np, internal_np::face_patch)) && - need_aabb_tree )); + internal::Connected_components_pmap(faces, pmesh, ecmap, fimap, + boost::is_default_param(get_param(np, internal_np::face_patch)) && need_aabb_tree) ); double low = 4. / 5. * target_edge_length; double high = 4. / 3. * target_edge_length; @@ -358,13 +354,13 @@ void split_long_edges(const EdgeRange& edges typename internal::Incremental_remesher, - internal::Connected_components_pmap, + internal::Connected_components_pmap, FIMap > remesher(pmesh, vpmap, false/*protect constraints*/ , ecmap , internal::No_constraint_pmap() - , internal::Connected_components_pmap(pmesh, ecmap, fimap, false) + , internal::Connected_components_pmap(faces(pmesh), pmesh, ecmap, fimap, false) , fimap , false/*need aabb_tree*/); diff --git a/Property_map/include/CGAL/property_map.h b/Property_map/include/CGAL/property_map.h index e8612db429c..6e1c1b55a23 100644 --- a/Property_map/include/CGAL/property_map.h +++ b/Property_map/include/CGAL/property_map.h @@ -82,6 +82,8 @@ class OR_property_map { PM2 pm2; public: + OR_property_map() {} // required by boost::connected_components + OR_property_map(PM1 pm1, PM2 pm2) : pm1(pm1),pm2(pm2) {} @@ -100,9 +102,15 @@ class OR_property_map { put(pm.pm1,k, v); put(pm.pm2,k, v); } - }; +template +OR_property_map +make_OR_property_map(const PM1& pm1, const PM2& pm2) +{ + return OR_property_map(pm1, pm2); +} + // A property map that uses the result of a property map as key. template struct Property_map_binder{ From be42fa9147c8dc3634611673dc33af0ebf41e6d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 18 May 2018 17:03:26 +0200 Subject: [PATCH 16/21] update ecmap_ during the remeshing --- .../Isotropic_remeshing/remesh_impl.h | 50 ++++++------------- .../CGAL/Polygon_mesh_processing/remesh.h | 2 - 2 files changed, 14 insertions(+), 38 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index f9db30f6ae2..f927d676e89 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -306,7 +306,6 @@ namespace internal { typedef typename boost::property_map< PM, CGAL::dynamic_halfedge_property_t >::type Halfedge_status_pmap; - typedef boost::unordered_set Edge_is_constrained_set; public: Incremental_remesher(PolygonMesh& pmesh @@ -331,19 +330,6 @@ namespace internal { { halfedge_status_pmap_ = get(CGAL::dynamic_halfedge_property_t(), pmesh); - BOOST_FOREACH(halfedge_descriptor h, halfedges(mesh_)) - put(halfedge_status_pmap_, h, MESH); - - if (!boost::is_same >::value) - { - BOOST_FOREACH(edge_descriptor e, edges(pmesh)) - { - if ( get(ecmap_, e) ) - edge_is_constrained_set_.insert(e); - } - } - CGAL_assertion(CGAL::is_triangle_mesh(mesh_)); } @@ -441,7 +427,6 @@ namespace internal { // propagate the constrained status put(ecmap_, edge(hnew, mesh_), get(ecmap_, edge(he, mesh_))); CGAL_assertion(he == next(hnew, mesh_)); - if (edge_is_constrained_set_.count(edge(he, mesh_))!=0) edge_is_constrained_set_.insert(edge(hnew, mesh_)); ++nb_splits; //move refinement point @@ -463,14 +448,18 @@ namespace internal { //insert new edges to keep triangular faces, and update long_edges if (!is_border(hnew, mesh_)) { - CGAL::Euler::split_face(hnew, next(next(hnew, mesh_), mesh_), mesh_); + halfedge_descriptor hnew2 = + CGAL::Euler::split_face(hnew, next(next(hnew, mesh_), mesh_), mesh_); + put(ecmap_, edge(hnew2, mesh_), false); } //do it again on the other side if we're not on boundary halfedge_descriptor hnew_opp = opposite(hnew, mesh_); if (!is_border(hnew_opp, mesh_)) { - CGAL::Euler::split_face(prev(hnew_opp, mesh_), next(hnew_opp, mesh_), mesh_); + halfedge_descriptor hnew2 = + CGAL::Euler::split_face(prev(hnew_opp, mesh_), next(hnew_opp, mesh_), mesh_); + put(ecmap_, edge(hnew2, mesh_), false); } } #ifdef CGAL_PMP_REMESHING_VERBOSE @@ -535,7 +524,7 @@ namespace internal { Point refinement_point = this->midpoint(he); halfedge_descriptor hnew = CGAL::Euler::split_edge(he, mesh_); CGAL_assertion(he == next(hnew, mesh_)); - if (edge_is_constrained_set_.count(edge(he, mesh_))!=0) edge_is_constrained_set_.insert(edge(hnew, mesh_)); + put(ecmap_, edge(hnew, mesh_), get(ecmap_, edge(he, mesh_)) ); ++nb_splits; //move refinement point @@ -565,6 +554,7 @@ namespace internal { halfedge_descriptor hnew2 = CGAL::Euler::split_face(hnew, next(next(hnew, mesh_), mesh_), mesh_); + put(ecmap_, edge(hnew2, mesh_), false); Halfedge_status snew = (is_on_patch(hnew) || is_on_patch_border(hnew)) ? PATCH : MESH; @@ -587,6 +577,7 @@ namespace internal { halfedge_descriptor hnew2 = CGAL::Euler::split_face(prev(hnew_opp, mesh_), next(hnew_opp, mesh_), mesh_); + put(ecmap_, edge(hnew2, mesh_), false); Halfedge_status snew = (is_on_patch(hnew_opp) || is_on_patch_border(hnew_opp)) ? PATCH : MESH; @@ -789,9 +780,9 @@ namespace internal { } if (!protect_constraints_) - edge_is_constrained_set_.erase(e); + put(ecmap_, e, false); else - CGAL_assertion( edge_is_constrained_set_.count(e)==0 ); + CGAL_assertion( !get(ecmap_, e) ); //perform collapse Point target_point = get(vpmap_, vb); @@ -875,7 +866,7 @@ namespace internal { Patch_id pid = get_patch_id(face(he, mesh_)); CGAL_assertion( is_flip_topologically_allowed(edge(he, mesh_)) ); - CGAL_assertion( edge_is_constrained_set_.count(edge(he, mesh_))==0 ); + CGAL_assertion( !get(ecmap_, edge(he, mesh_)) ); CGAL::Euler::flip_edge(he, mesh_); vva -= 1; vvb -= 1; @@ -914,7 +905,7 @@ namespace internal { || !check_normals(source(he, mesh_))) { CGAL_assertion( is_flip_topologically_allowed(edge(he, mesh_)) ); - CGAL_assertion( edge_is_constrained_set_.count(edge(he, mesh_))==0 ); + CGAL_assertion( !get(ecmap_, edge(he, mesh_)) ); CGAL::Euler::flip_edge(he, mesh_); --nb_flips; @@ -1593,7 +1584,6 @@ private: } } - // for a Surface_mesh::Property_map we make MESH the default value Halfedge_status status(const halfedge_descriptor& h) const { return get(halfedge_status_pmap_,h); @@ -1676,7 +1666,7 @@ private: short_edges.left.erase(hf); short_edges.left.erase(hfo); CGAL_assertion( is_flip_topologically_allowed(edge(hf, mesh_)) ); - CGAL_assertion( edge_is_constrained_set_.count(edge(hf, mesh_))==0 ); + CGAL_assertion( !get(ecmap_, edge(hf, mesh_)) ); CGAL::Euler::flip_edge(hf, mesh_); CGAL_assertion_code(++nb_done); @@ -1966,17 +1956,6 @@ private: return input_patch_ids_; } - void update_constraints_property_map() - { - if (!edge_is_constrained_set_.empty()) - { - BOOST_FOREACH(edge_descriptor e, edge_is_constrained_set_) - { - put(ecmap_, e, true); - } - } - } - private: PolygonMesh& mesh_; VertexPointMap& vpmap_; @@ -1987,7 +1966,6 @@ private: Triangle_list input_triangles_; Patch_id_list input_patch_ids_; Halfedge_status_pmap halfedge_status_pmap_; - Edge_is_constrained_set edge_is_constrained_set_; bool protect_constraints_; FacePatchMap patch_ids_map_; EdgeIsConstrainedMap ecmap_; diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h index d6c4e808ff4..aeaec296b1f 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h @@ -260,8 +260,6 @@ void isotropic_remeshing(const FaceRange& faces #endif } - remesher.update_constraints_property_map(); - #ifdef CGAL_PMP_REMESHING_VERBOSE t.stop(); std::cout << "Remeshing done (size = " << target_edge_length; From f03f2176dd2f52c0ea879e1f8fa6f4634aa2cb3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 23 May 2018 09:07:13 +0200 Subject: [PATCH 17/21] do no collapse constrained edges At the same time I removed halfedge_and_opp_removed since we don't know prior to the collapse which edges are removed. The only thing done by the function is to reset the status. This is not an issue to remove it because halfedge_added is called each time a new edge is created, thus a previous status cannot be recovered by error. --- .../internal/Isotropic_remeshing/remesh_impl.h | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index f927d676e89..d3804bc8613 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -764,12 +764,6 @@ namespace internal { if (!mesh_border_case_opp) merge_status(en_p, s_epo_p, s_ep_p); - halfedge_and_opp_removed(he); - if (!mesh_border_case) - halfedge_and_opp_removed(prev(he, mesh_)); - if (!mesh_border_case_opp) - halfedge_and_opp_removed(prev(opposite(he, mesh_), mesh_)); - //constrained case bool constrained_case = is_constrained(va) || is_constrained(vb); if (constrained_case) @@ -786,7 +780,7 @@ namespace internal { //perform collapse Point target_point = get(vpmap_, vb); - vertex_descriptor vkept = CGAL::Euler::collapse_edge(e, mesh_); + vertex_descriptor vkept = CGAL::Euler::collapse_edge(e, mesh_, ecmap_); put(vpmap_, vkept, target_point); ++nb_collapses; @@ -1826,12 +1820,6 @@ private: set_status(h, s); } - void halfedge_and_opp_removed(const halfedge_descriptor& h) - { - set_status(h,MESH); - set_status(opposite(h, mesh_),MESH); - } - std::size_t nb_valid_halfedges() const { return static_cast( From 117b1d810aa12bf6d8707025ffd6f4359adc2828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 7 Jun 2018 15:10:41 +0200 Subject: [PATCH 18/21] work around issue with boost::size in boost 1.55 improve at the same time the runtime in cases we could also have a lazy version for non-random access iterators --- .../internal/Isotropic_remeshing/remesh_impl.h | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index b993732b91b..4af0bc2b91e 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -175,6 +175,20 @@ namespace internal { Patch_ids_map patch_ids_map; std::size_t nb_cc; + template + bool same_range(const Range& r1, const Range& r2) + { + return boost::begin(r1)==boost::begin(r2) && + boost::end(r1)==boost::end(r2); + } + + template + bool same_range(const Range1& r1, const Range2& r2) + { + return std::distance(boost::begin(r1), boost::end(r1)) == + std::distance(boost::begin(r2), boost::end(r2)); + } + public: typedef face_descriptor key_type; typedef Patch_id value_type; @@ -196,7 +210,7 @@ namespace internal { #ifdef CGAL_PMP_REMESHING_VERBOSE std::cout << "Compute connected components property map." << std::endl; #endif - if (boost::size(face_range) == boost::size(faces(pmesh))) + if ( same_range(face_range, (faces(pmesh))) ) { // applied on the whole mesh nb_cc From 895dbaad73ea49dc6db34dbd729f7688a39217bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 7 Jun 2018 17:35:29 +0200 Subject: [PATCH 19/21] fix merge of status after the usage of ecm in collapse_edge ep and epo are no longer guarantee to be the halfedges to be removed --- .../Isotropic_remeshing/remesh_impl.h | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index 4af0bc2b91e..3cf1016a633 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -761,20 +761,16 @@ namespace internal { bool mesh_border_case = is_on_border(he); bool mesh_border_case_opp = is_on_border(he_opp); halfedge_descriptor ep_p = prev(he_opp, mesh_); - halfedge_descriptor epo_p = opposite(ep_p, mesh_); halfedge_descriptor en = next(he, mesh_); + halfedge_descriptor ep = prev(he, mesh_); halfedge_descriptor en_p = next(he_opp, mesh_); - Halfedge_status s_ep_p = status(ep_p); - Halfedge_status s_epo_p = status(epo_p); - Halfedge_status s_ep = status(prev(he, mesh_)); - Halfedge_status s_epo = status(opposite(prev(he, mesh_), mesh_)); // merge halfedge_status to keep the more important on both sides //do it before collapse is performed to be sure everything is valid if (!mesh_border_case) - merge_status(en, s_epo, s_ep); + merge_and_update_status(en, ep); if (!mesh_border_case_opp) - merge_status(en_p, s_epo_p, s_ep_p); + merge_and_update_status(en_p, ep_p); if (!protect_constraints_) put(ecmap_, e, false); @@ -1550,17 +1546,16 @@ private: put(halfedge_status_pmap_,h,s); } - void merge_status(const halfedge_descriptor& en, - const Halfedge_status& s_epo, - const Halfedge_status& s_ep) + void merge_and_update_status(halfedge_descriptor en, + halfedge_descriptor ep) { - //get missing data + halfedge_descriptor eno = opposite(en, mesh_); + halfedge_descriptor epo = opposite(ep, mesh_); Halfedge_status s_eno = status(eno); + Halfedge_status s_epo = status(epo); - if (s_epo == MESH_BORDER && s_eno == MESH_BORDER) - return; - + Halfedge_status s_ep = status(ep); if(s_epo == MESH_BORDER || s_ep == MESH_BORDER || s_epo == PATCH_BORDER @@ -1569,6 +1564,18 @@ private: set_status(en, s_epo); set_status(eno, s_ep); } + else + { + Halfedge_status s_en = status(en); + if(s_eno == MESH_BORDER + || s_en == MESH_BORDER + || s_eno == PATCH_BORDER + || s_en == PATCH_BORDER) + { + set_status(ep, s_epo); + set_status(epo, s_ep); + } + } // else keep current status for en and eno } From f7241be70ae2a4c9e323f7768cfd14bec0cc5662 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 18 Jun 2018 10:08:52 +0200 Subject: [PATCH 20/21] fix test on the shortness of protect edges since ecm was updated to only contain real constrained edges (and no additional patch border edges), we use connected component patch map to identify them --- .../internal/Isotropic_remeshing/remesh_impl.h | 13 +++++++------ .../include/CGAL/Polygon_mesh_processing/remesh.h | 12 +++++++++--- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index 3cf1016a633..bfbe181b40e 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -230,9 +230,6 @@ namespace internal { , internal::Border_constraint_pmap(pmesh, face_range, fimap) ) ) .face_index_map(fimap)); } - if(nb_cc == 1){ - patch_ids_map = Patch_ids_map(); - } } else nb_cc=0; // default value @@ -254,10 +251,12 @@ namespace internal { template + typename VertexPointMap, + typename FacePatchMap> bool constraints_are_short_enough(const PM& pmesh, EdgeConstraintMap ecmap, VertexPointMap vpmap, + const FacePatchMap& fpm, const double& high) { double sqh = high*high; @@ -265,9 +264,11 @@ namespace internal { typedef typename boost::graph_traits::edge_descriptor edge_descriptor; BOOST_FOREACH(edge_descriptor e, edges(pmesh)) { - if (get(ecmap, e)) + halfedge_descriptor h = halfedge(e, pmesh); + if ( is_border(e, pmesh) || + get(ecmap, e) || + get(fpm, face(h,pmesh))!=get(fpm, face(opposite(h,pmesh),pmesh)) ) { - halfedge_descriptor h = halfedge(e, pmesh); if (sqh < CGAL::squared_distance(get(vpmap, source(h, pmesh)), get(vpmap, target(h, pmesh)))) { diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h index aeaec296b1f..e1b61cfb3e8 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h @@ -188,6 +188,7 @@ void isotropic_remeshing(const FaceRange& faces VCMap vcmap = choose_param(get_param(np, internal_np::vertex_is_constrained), internal::No_constraint_pmap()); + bool protect = choose_param(get_param(np, internal_np::protect_constraints), false); typedef typename boost::lookup_named_param_def < internal_np::face_patch_t, NamedParameters, @@ -196,21 +197,26 @@ void isotropic_remeshing(const FaceRange& faces FPMap fpmap = choose_param( get_param(np, internal_np::face_patch), internal::Connected_components_pmap(faces, pmesh, ecmap, fimap, - boost::is_default_param(get_param(np, internal_np::face_patch)) && need_aabb_tree) ); + boost::is_default_param(get_param(np, internal_np::face_patch)) && (need_aabb_tree +#if !defined(CGAL_NO_PRECONDITIONS) + || protect // face patch map is used to identify patch border edges to check protected edges are short enough +#endif + ) ) ); double low = 4. / 5. * target_edge_length; double high = 4. / 3. * target_edge_length; - bool protect = choose_param(get_param(np, internal_np::protect_constraints), false); +#if !defined(CGAL_NO_PRECONDITIONS) if(protect) { std::string msg("Isotropic remeshing : protect_constraints cannot be set to"); msg.append(" true with constraints larger than 4/3 * target_edge_length."); msg.append(" Remeshing aborted."); CGAL_precondition_msg( - internal::constraints_are_short_enough(pmesh, ecmap, vpmap, high), + internal::constraints_are_short_enough(pmesh, ecmap, vpmap, fpmap, high), msg.c_str()); } +#endif #ifdef CGAL_PMP_REMESHING_VERBOSE t.stop(); From 78e4b6935996b86d559539f76fec865a46cbdfad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 19 Jun 2018 09:57:50 +0200 Subject: [PATCH 21/21] update plugin using internal function with updated signature --- .../demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp index 12b3e251c11..45ce18f78fc 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp @@ -400,6 +400,7 @@ public Q_SLOTS: *selection_item->polyhedron(), selection_item->constrained_edges_pmap(), get(CGAL::vertex_point, *selection_item->polyhedron()), + CGAL::Static_property_map(1), 4. / 3. * target_length)) { QApplication::restoreOverrideCursor();