From e43221ae8bcc56fa5b94e68d1335a05c25e11fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 16 Jun 2020 15:15:48 +0200 Subject: [PATCH 01/14] add an option to not refine one of the two meshes --- .../CGAL/boost/graph/parameters_interface.h | 1 + BGL/test/BGL/test_cgal_bgl_named_params.cpp | 3 +++ .../CGAL/Polygon_mesh_processing/corefinement.h | 15 ++++++++++++++- .../internal/Corefinement/Visitor.h | 7 ++++++- 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/BGL/include/CGAL/boost/graph/parameters_interface.h b/BGL/include/CGAL/boost/graph/parameters_interface.h index 0ca726f6e31..ba367732370 100644 --- a/BGL/include/CGAL/boost/graph/parameters_interface.h +++ b/BGL/include/CGAL/boost/graph/parameters_interface.h @@ -101,6 +101,7 @@ CGAL_add_named_parameter(area_threshold_t, area_threshold, area_threshold) CGAL_add_named_parameter(halfedges_keeper_t, halfedges_keeper, halfedges_keeper) CGAL_add_named_parameter(volume_threshold_t, volume_threshold, volume_threshold) CGAL_add_named_parameter(dry_run_t, dry_run, dry_run) +CGAL_add_named_parameter(do_not_modify_t, do_not_modify, do_not_modify) // 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 1d65c3cd1c2..f2669e50313 100644 --- a/BGL/test/BGL/test_cgal_bgl_named_params.cpp +++ b/BGL/test/BGL/test_cgal_bgl_named_params.cpp @@ -97,6 +97,7 @@ void test(const NamedParameters& np) assert(get_parameter(np, CGAL::internal_np::do_lock_mesh).v == 61); assert(get_parameter(np, CGAL::internal_np::halfedges_keeper).v == 62); assert(get_parameter(np, CGAL::internal_np::do_simplify_border).v == 64); + assert(get_parameter(np, CGAL::internal_np::do_not_modify).v == 65); assert(get_parameter(np, CGAL::internal_np::maximum_number_of_faces).v == 78910); // Named parameters that we use in the package 'Surface Mesh Simplification' @@ -184,6 +185,7 @@ void test(const NamedParameters& np) check_same_type<54>(get_parameter(np, CGAL::internal_np::use_area_smoothing)); check_same_type<55>(get_parameter(np, CGAL::internal_np::use_Delaunay_flips)); check_same_type<56>(get_parameter(np, CGAL::internal_np::use_safety_constraints)); + check_same_type<65>(get_parameter(np, CGAL::internal_np::do_not_modify)); check_same_type<12340>(get_parameter(np, CGAL::internal_np::do_self_intersection_tests)); check_same_type<12341>(get_parameter(np, CGAL::internal_np::do_orientation_tests)); @@ -353,6 +355,7 @@ int main() .halfedges_keeper(A<62>(62)) .use_convex_hull(A<63>(63)) .do_simplify_border(A<64>(64)) + .do_not_modify(A<65>(65)) .point_map(A<9000>(9000)) .query_point_map(A<9001>(9001)) .normal_map(A<9002>(9002)) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h index e0e8c27de2f..85c02424b55 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h @@ -626,6 +626,9 @@ corefine_and_compute_difference( TriangleMesh& tm1, * checked for self-intersection and `CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception` * will be thrown if at least one is found (`np1` only). * \cgalParamEnd + * \cgalParamBegin{do_not_modify} if `true`, the corresponding mesh will not be updated. The default value is `false`. + * Obviously if this parameter is set to `true` for both meshes nothing will be done. + * \cgalParamEnd * \cgalNamedParamsEnd * */ @@ -641,9 +644,19 @@ corefine( TriangleMesh& tm1, using parameters::choose_parameter; using parameters::get_parameter; + if (choose_parameter(get_parameter(np1, internal_np::do_not_modify), false)) + { + if (choose_parameter(get_parameter(np2, internal_np::do_not_modify), false)) + return; + return corefine(tm2,tm1, np2, np1); + } + const bool throw_on_self_intersection = choose_parameter(get_parameter(np1, internal_np::throw_on_self_intersection), false); + const bool modify_tm2 = + !choose_parameter(get_parameter(np2, internal_np::do_not_modify), false); + // Vertex point maps typedef typename GetVertexPointMap::type Vpm; @@ -700,7 +713,7 @@ corefine( TriangleMesh& tm1, Ob ob; Ecm ecm(tm1,tm2,ecm1,ecm2); Corefinement::Intersection_of_triangle_meshes - functor(tm1, tm2, vpm1, vpm2, Algo_visitor(uv,ob,ecm)); + functor(tm1, tm2, vpm1, vpm2, Algo_visitor(uv,ob,ecm,modify_tm2)); functor(CGAL::Emptyset_iterator(), throw_on_self_intersection, true); } diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Visitor.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Visitor.h index d668782b25c..e81be0cfa60 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Visitor.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Visitor.h @@ -170,6 +170,7 @@ private: OutputBuilder& output_builder; EdgeMarkMapBind marks_on_edges; bool input_with_coplanar_faces; + bool refine_tm2; template void call_put(Ecm_bind& ecm, @@ -200,12 +201,13 @@ private: // visitor public functions public: Surface_intersection_visitor_for_corefinement( - UserVisitor& uv, OutputBuilder& o, const EdgeMarkMapBind& emm) + UserVisitor& uv, OutputBuilder& o, const EdgeMarkMapBind& emm, bool refine_tm2=true) : number_coplanar_vertices(0) , user_visitor(uv) , output_builder(o) , marks_on_edges(emm) , input_with_coplanar_faces(false) + , refine_tm2(refine_tm2) {} template @@ -624,6 +626,7 @@ public: ++it) { TriangleMesh& tm=*it->first; + if (!refine_tm2 && tm2_ptr==&tm) continue; // Face_boundaries& face_boundaries=mesh_to_face_boundaries[&tm]; Node_to_target_of_hedge_map& nodes_to_hedge=it->second; @@ -693,6 +696,7 @@ public: it=on_edge.begin(); it!=on_edge.end(); ++it) { TriangleMesh& tm=*it->first; + if (!refine_tm2 && tm2_ptr==&tm) continue; const VertexPointMap& vpm=vpms[&tm]; On_edge_map& on_edge_map=it->second; On_face_map& on_face_map=on_face[&tm]; @@ -788,6 +792,7 @@ public: it=on_face.begin(); it!=on_face.end(); ++it) { TriangleMesh& tm=*it->first; + if (!refine_tm2 && tm2_ptr==&tm) continue; const VertexPointMap& vpm=vpms[&tm]; On_face_map& on_face_map=it->second; Face_boundaries& face_boundaries=mesh_to_face_boundaries[&tm]; From 9eb7a9da85171349703cb5d768226b6c726fb465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 16 Jun 2020 15:25:42 +0200 Subject: [PATCH 02/14] avoid filling the maps --- .../Polygon_mesh_processing/corefinement.h | 11 ++-- .../internal/Corefinement/Visitor.h | 65 ++++++++++--------- 2 files changed, 40 insertions(+), 36 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h index 85c02424b55..53c8703f8c7 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h @@ -644,19 +644,20 @@ corefine( TriangleMesh& tm1, using parameters::choose_parameter; using parameters::get_parameter; + TriangleMesh* const_mesh_ptr=nullptr; if (choose_parameter(get_parameter(np1, internal_np::do_not_modify), false)) { if (choose_parameter(get_parameter(np2, internal_np::do_not_modify), false)) return; - return corefine(tm2,tm1, np2, np1); + const_mesh_ptr=&tm1; } + else + if (choose_parameter(get_parameter(np2, internal_np::do_not_modify), false)) + const_mesh_ptr=&tm2; const bool throw_on_self_intersection = choose_parameter(get_parameter(np1, internal_np::throw_on_self_intersection), false); - const bool modify_tm2 = - !choose_parameter(get_parameter(np2, internal_np::do_not_modify), false); - // Vertex point maps typedef typename GetVertexPointMap::type Vpm; @@ -713,7 +714,7 @@ corefine( TriangleMesh& tm1, Ob ob; Ecm ecm(tm1,tm2,ecm1,ecm2); Corefinement::Intersection_of_triangle_meshes - functor(tm1, tm2, vpm1, vpm2, Algo_visitor(uv,ob,ecm,modify_tm2)); + functor(tm1, tm2, vpm1, vpm2, Algo_visitor(uv,ob,ecm,const_mesh_ptr)); functor(CGAL::Emptyset_iterator(), throw_on_self_intersection, true); } diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Visitor.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Visitor.h index e81be0cfa60..0f71e8df6f9 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Visitor.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Visitor.h @@ -170,7 +170,7 @@ private: OutputBuilder& output_builder; EdgeMarkMapBind marks_on_edges; bool input_with_coplanar_faces; - bool refine_tm2; + TriangleMesh* const_mesh_ptr; template void call_put(Ecm_bind& ecm, @@ -201,13 +201,13 @@ private: // visitor public functions public: Surface_intersection_visitor_for_corefinement( - UserVisitor& uv, OutputBuilder& o, const EdgeMarkMapBind& emm, bool refine_tm2=true) + UserVisitor& uv, OutputBuilder& o, const EdgeMarkMapBind& emm, TriangleMesh* const_mesh_ptr=nullptr) : number_coplanar_vertices(0) , user_visitor(uv) , output_builder(o) , marks_on_edges(emm) , input_with_coplanar_faces(false) - , refine_tm2(refine_tm2) + , const_mesh_ptr(const_mesh_ptr) {} template @@ -334,33 +334,36 @@ public: //forward to the visitor // user_visitor.new_node_added(node_id, type, h_1, h_2, is_target_coplanar, is_source_coplanar); // NODE_VISITOR_TAG - switch(type) - { - case ON_FACE: //Face intersected by an edge - on_face[tm2_ptr][face(h_2,tm2)].push_back(node_id); - break; - case ON_EDGE: //Edge intersected by an edge + if (tm2_ptr!=const_mesh_ptr) + switch(type) { - on_edge[tm2_ptr][edge(h_2,tm2)].push_back(node_id); - // check_node_on_non_manifold_edge(node_id,h_2,tm2); + case ON_FACE: //Face intersected by an edge + on_face[tm2_ptr][face(h_2,tm2)].push_back(node_id); + break; + case ON_EDGE: //Edge intersected by an edge + { + on_edge[tm2_ptr][edge(h_2,tm2)].push_back(node_id); + // check_node_on_non_manifold_edge(node_id,h_2,tm2); + } + break; + case ON_VERTEX: + { + //grab original vertex that is on commom intersection + mesh_to_vertices_on_inter[tm2_ptr].insert(std::make_pair(node_id,h_2)); + Node_id_to_vertex& node_id_to_vertex=mesh_to_node_id_to_vertex[tm2_ptr]; + if (node_id_to_vertex.size()<=node_id) + node_id_to_vertex.resize(node_id+1,Graph_traits::null_vertex()); + node_id_to_vertex[node_id]=target(h_2,tm2); + all_incident_faces_got_a_node_as_vertex(h_2,node_id,*tm2_ptr); + // check_node_on_non_manifold_vertex(node_id,h_2,tm2); + output_builder.set_vertex_id(target(h_2, tm2), node_id, tm2); + } + break; + default: + return; } - break; - case ON_VERTEX: - { - //grab original vertex that is on commom intersection - mesh_to_vertices_on_inter[tm2_ptr].insert(std::make_pair(node_id,h_2)); - Node_id_to_vertex& node_id_to_vertex=mesh_to_node_id_to_vertex[tm2_ptr]; - if (node_id_to_vertex.size()<=node_id) - node_id_to_vertex.resize(node_id+1,Graph_traits::null_vertex()); - node_id_to_vertex[node_id]=target(h_2,tm2); - all_incident_faces_got_a_node_as_vertex(h_2,node_id,*tm2_ptr); - // check_node_on_non_manifold_vertex(node_id,h_2,tm2); - output_builder.set_vertex_id(target(h_2, tm2), node_id, tm2); - } - break; - default: - return; - } + + if (tm1_ptr==const_mesh_ptr) return; CGAL_assertion(!is_target_coplanar || !is_source_coplanar); //coplanar edge are not forwarded @@ -626,7 +629,7 @@ public: ++it) { TriangleMesh& tm=*it->first; - if (!refine_tm2 && tm2_ptr==&tm) continue; + CGAL_assertion(&tm!=const_mesh_ptr); // Face_boundaries& face_boundaries=mesh_to_face_boundaries[&tm]; Node_to_target_of_hedge_map& nodes_to_hedge=it->second; @@ -696,7 +699,7 @@ public: it=on_edge.begin(); it!=on_edge.end(); ++it) { TriangleMesh& tm=*it->first; - if (!refine_tm2 && tm2_ptr==&tm) continue; + CGAL_assertion(&tm!=const_mesh_ptr); const VertexPointMap& vpm=vpms[&tm]; On_edge_map& on_edge_map=it->second; On_face_map& on_face_map=on_face[&tm]; @@ -792,7 +795,7 @@ public: it=on_face.begin(); it!=on_face.end(); ++it) { TriangleMesh& tm=*it->first; - if (!refine_tm2 && tm2_ptr==&tm) continue; + CGAL_assertion(&tm!=const_mesh_ptr); const VertexPointMap& vpm=vpms[&tm]; On_face_map& on_face_map=it->second; Face_boundaries& face_boundaries=mesh_to_face_boundaries[&tm]; From fd72d53d5f037b5fa43816bd462ab88aa56c47de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 16 Jun 2020 16:15:09 +0200 Subject: [PATCH 03/14] test do_not_modify --- .../Polygon_mesh_processing/test_corefine.cpp | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_corefine.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_corefine.cpp index 5804b67a88f..5c7ca780315 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_corefine.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_corefine.cpp @@ -71,11 +71,71 @@ void test(const char* f1, const char* f2) assert(P.is_valid()); assert(Q.is_valid()); } + +void test_no_modifications(const char* f1, const char* f2) +{ + std::cout << "Corefining " << f1 + << " and " << f2 << "\n"; + + std::cout << " with Surface_mesh\n"; + Surface_mesh sm1, sm2; + std::ifstream input(f1); + assert(input); + input >> sm1; + input.close(); + input.open(f2); + assert(input); + input >> sm2; + input.close(); + My_visitor sm_v; + + std::size_t nb_v_before1 = vertices(sm1).size(); + std::size_t nb_v_before2 = vertices(sm2).size(); + + CGAL::Polygon_mesh_processing::corefine(sm1, sm2, + CGAL::parameters::visitor(sm_v), + CGAL::parameters::do_not_modify(true)); + + std::size_t nb_v_after1 = vertices(sm1).size(); + std::size_t nb_v_after2 = vertices(sm2).size(); + + assert(sm1.is_valid()); + assert(sm2.is_valid()); + assert(nb_v_after2==nb_v_before2); + + assert((*(sm_v.i) != 0) == (nb_v_before1!=nb_v_after1)); + + std::cout << " with Polyhedron_3\n"; + Polyhedron_3 P, Q; + input.open(f1); + assert(input); + input >> P; + input.close(); + input.open(f2); + assert(input); + input >> Q; + My_visitor sm_p; + + nb_v_before1 = vertices(P).size(); + nb_v_before2 = vertices(Q).size(); + CGAL::Polygon_mesh_processing::corefine(P, Q, + CGAL::parameters::visitor(sm_p).do_not_modify(true)); + nb_v_after1 = vertices(P).size(); + nb_v_after2 = vertices(Q).size(); + + assert(nb_v_after1==nb_v_before1); + assert((*(sm_p.i) != 0) == (nb_v_before2!=nb_v_after2)); + + assert(P.is_valid()); + assert(Q.is_valid()); +} + int main(int argc, char** argv) { for(int i=0; i< (argc-1)/2;++i) { test(argv[2*i+1], argv[2*(i+1)]); test(argv[2*(i+1)], argv[2*i+1]); + test_no_modifications(argv[2*(i+1)], argv[2*i+1]); } } From 50591915347fed2ef405f99ce2279486561c874d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 16 Jun 2020 16:30:02 +0200 Subject: [PATCH 04/14] remove tabs + update doc --- BGL/test/BGL/test_cgal_bgl_named_params.cpp | 2 +- .../Polygon_mesh_processing/corefinement.h | 2 + .../internal/Corefinement/Visitor.h | 48 +++++++++---------- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/BGL/test/BGL/test_cgal_bgl_named_params.cpp b/BGL/test/BGL/test_cgal_bgl_named_params.cpp index f2669e50313..04fcec337e6 100644 --- a/BGL/test/BGL/test_cgal_bgl_named_params.cpp +++ b/BGL/test/BGL/test_cgal_bgl_named_params.cpp @@ -355,7 +355,7 @@ int main() .halfedges_keeper(A<62>(62)) .use_convex_hull(A<63>(63)) .do_simplify_border(A<64>(64)) - .do_not_modify(A<65>(65)) + .do_not_modify(A<65>(65)) .point_map(A<9000>(9000)) .query_point_map(A<9001>(9001)) .normal_map(A<9002>(9002)) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h index 53c8703f8c7..568187c2dcc 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h @@ -628,6 +628,8 @@ corefine_and_compute_difference( TriangleMesh& tm1, * \cgalParamEnd * \cgalParamBegin{do_not_modify} if `true`, the corresponding mesh will not be updated. The default value is `false`. * Obviously if this parameter is set to `true` for both meshes nothing will be done. + * An interesting property of this option is that it removes the self-intersection free + * pre-condition on the other mesh that is modified. * \cgalParamEnd * \cgalNamedParamsEnd * diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Visitor.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Visitor.h index 0f71e8df6f9..f52022861ac 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Visitor.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Visitor.h @@ -337,30 +337,30 @@ public: if (tm2_ptr!=const_mesh_ptr) switch(type) { - case ON_FACE: //Face intersected by an edge - on_face[tm2_ptr][face(h_2,tm2)].push_back(node_id); - break; - case ON_EDGE: //Edge intersected by an edge - { - on_edge[tm2_ptr][edge(h_2,tm2)].push_back(node_id); - // check_node_on_non_manifold_edge(node_id,h_2,tm2); - } - break; - case ON_VERTEX: - { - //grab original vertex that is on commom intersection - mesh_to_vertices_on_inter[tm2_ptr].insert(std::make_pair(node_id,h_2)); - Node_id_to_vertex& node_id_to_vertex=mesh_to_node_id_to_vertex[tm2_ptr]; - if (node_id_to_vertex.size()<=node_id) - node_id_to_vertex.resize(node_id+1,Graph_traits::null_vertex()); - node_id_to_vertex[node_id]=target(h_2,tm2); - all_incident_faces_got_a_node_as_vertex(h_2,node_id,*tm2_ptr); - // check_node_on_non_manifold_vertex(node_id,h_2,tm2); - output_builder.set_vertex_id(target(h_2, tm2), node_id, tm2); - } - break; - default: - return; + case ON_FACE: //Face intersected by an edge + on_face[tm2_ptr][face(h_2,tm2)].push_back(node_id); + break; + case ON_EDGE: //Edge intersected by an edge + { + on_edge[tm2_ptr][edge(h_2,tm2)].push_back(node_id); + // check_node_on_non_manifold_edge(node_id,h_2,tm2); + } + break; + case ON_VERTEX: + { + //grab original vertex that is on commom intersection + mesh_to_vertices_on_inter[tm2_ptr].insert(std::make_pair(node_id,h_2)); + Node_id_to_vertex& node_id_to_vertex=mesh_to_node_id_to_vertex[tm2_ptr]; + if (node_id_to_vertex.size()<=node_id) + node_id_to_vertex.resize(node_id+1,Graph_traits::null_vertex()); + node_id_to_vertex[node_id]=target(h_2,tm2); + all_incident_faces_got_a_node_as_vertex(h_2,node_id,*tm2_ptr); + // check_node_on_non_manifold_vertex(node_id,h_2,tm2); + output_builder.set_vertex_id(target(h_2, tm2), node_id, tm2); + } + break; + default: + return; } if (tm1_ptr==const_mesh_ptr) return; From b9a12bf901729484c2575987ad2599dff32384d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 16 Jun 2020 16:36:10 +0200 Subject: [PATCH 05/14] update doc --- .../include/CGAL/Polygon_mesh_processing/corefinement.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h index 568187c2dcc..2a138d88bbc 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h @@ -628,8 +628,8 @@ corefine_and_compute_difference( TriangleMesh& tm1, * \cgalParamEnd * \cgalParamBegin{do_not_modify} if `true`, the corresponding mesh will not be updated. The default value is `false`. * Obviously if this parameter is set to `true` for both meshes nothing will be done. - * An interesting property of this option is that it removes the self-intersection free - * pre-condition on the other mesh that is modified. + * An interesting property of this option is that if set to `true` for one mesh, + * it removes the self-intersection free pre-condition on the other mesh that is modified. * \cgalParamEnd * \cgalNamedParamsEnd * From 802fa469d55dd7de706c532564c50a68403ec568 Mon Sep 17 00:00:00 2001 From: Sebastien Loriot Date: Wed, 17 Jun 2020 08:10:32 +0200 Subject: [PATCH 06/14] Apply suggestions from code review Co-authored-by: Mael --- .../include/CGAL/Polygon_mesh_processing/corefinement.h | 6 ++++-- .../Polygon_mesh_processing/internal/Corefinement/Visitor.h | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h index 2a138d88bbc..209f6eaf2b4 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h @@ -628,8 +628,8 @@ corefine_and_compute_difference( TriangleMesh& tm1, * \cgalParamEnd * \cgalParamBegin{do_not_modify} if `true`, the corresponding mesh will not be updated. The default value is `false`. * Obviously if this parameter is set to `true` for both meshes nothing will be done. - * An interesting property of this option is that if set to `true` for one mesh, - * it removes the self-intersection free pre-condition on the other mesh that is modified. + * An interesting property of this option is that when set to `true` for one mesh, + * the other mesh is no longer required to be without self-intersection. * \cgalParamEnd * \cgalNamedParamsEnd * @@ -654,8 +654,10 @@ corefine( TriangleMesh& tm1, const_mesh_ptr=&tm1; } else + { if (choose_parameter(get_parameter(np2, internal_np::do_not_modify), false)) const_mesh_ptr=&tm2; + } const bool throw_on_self_intersection = choose_parameter(get_parameter(np1, internal_np::throw_on_self_intersection), false); diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Visitor.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Visitor.h index f52022861ac..62a021acfbd 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Visitor.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Visitor.h @@ -335,6 +335,7 @@ public: //forward to the visitor // user_visitor.new_node_added(node_id, type, h_1, h_2, is_target_coplanar, is_source_coplanar); // NODE_VISITOR_TAG if (tm2_ptr!=const_mesh_ptr) + { switch(type) { case ON_FACE: //Face intersected by an edge @@ -362,6 +363,7 @@ public: default: return; } + } if (tm1_ptr==const_mesh_ptr) return; From ab12e31bec16ddfb923ffbacf668d04a908cccdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 17 Jun 2020 08:33:50 +0200 Subject: [PATCH 07/14] please doc --- .../doc/Polygon_mesh_processing/NamedParameters.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt index 5fe5a41c38f..6b8001b1025 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt @@ -369,6 +369,13 @@ and make the operation impossible with the current version of the code. Default: `false` \cgalNPEnd +\cgalNPBegin{do_not_modify} \anchor PMP_do_not_modify +Parameter used in `corefine()` function to prevent the refinement of one of the two input meshes. +\n +Type: `bool` \n +Default: `false` +\cgalNPEnd + \cgalNPBegin{clip_volume} \anchor PMP_clip_volume Parameter used in `clip()` functions to clip a volume rather than a surface. \n From 035b8786b9574c6f4d8cdfd97b8e206a7141a674 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Mon, 22 Jun 2020 09:53:13 +0200 Subject: [PATCH 08/14] remove tws --- .../test/Polygon_mesh_processing/test_corefine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_corefine.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_corefine.cpp index 5c7ca780315..10dd9ff76d7 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_corefine.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_corefine.cpp @@ -91,7 +91,7 @@ void test_no_modifications(const char* f1, const char* f2) std::size_t nb_v_before1 = vertices(sm1).size(); std::size_t nb_v_before2 = vertices(sm2).size(); - + CGAL::Polygon_mesh_processing::corefine(sm1, sm2, CGAL::parameters::visitor(sm_v), CGAL::parameters::do_not_modify(true)); @@ -102,7 +102,7 @@ void test_no_modifications(const char* f1, const char* f2) assert(sm1.is_valid()); assert(sm2.is_valid()); assert(nb_v_after2==nb_v_before2); - + assert((*(sm_v.i) != 0) == (nb_v_before1!=nb_v_after1)); std::cout << " with Polyhedron_3\n"; From fd875cf71a1d25dcfc546662f7b0765b1aede504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 1 Jul 2020 16:07:09 +0200 Subject: [PATCH 09/14] add do_no_modify option to split --- .../include/CGAL/Polygon_mesh_processing/clip.h | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h index d059c3eb33d..1b054befbf0 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h @@ -432,19 +432,19 @@ void split_along_edges(TriangleMesh& tm, * \cgalParamBegin{face_index_map} * a property map containing a unique index for each face of `tm` (`clipper`). * \cgalParamEnd - * \cgalParamBegin{visitor} a class model of `PMPCorefinementVisitor` + * \cgalParamBegin{visitor} (`np_tm` only) a class model of `PMPCorefinementVisitor` * that is used to track the creation of new faces. * \cgalParamEnd - * \cgalParamBegin{throw_on_self_intersection} if `true`, + * \cgalParamBegin{throw_on_self_intersection} (`np_tm` only) if `true`, * the set of triangles closed to the intersection of `tm` and `clipper` will be * checked for self-intersections and `CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception` * will be thrown if at least one is found. Default value is `false`. * \cgalParamEnd - * \cgalParamBegin{clip_volume} if `true` and `tm` is closed, the clipping will be done on + * \cgalParamBegin{clip_volume} (`np_tm` only) if `true` and `tm` is closed, the clipping will be done on * the volume \link coref_def_subsec bounded \endlink by `tm` rather than on its surface * (i.e., `tm` will be kept closed). Default value is `false`. * \cgalParamEnd - * \cgalParamBegin{use_compact_clipper} if `false`, the parts of `tm` coplanar with `clipper` + * \cgalParamBegin{use_compact_clipper} (`np_tm` only) if `false`, the parts of `tm` coplanar with `clipper` * will not be part of the output. Default value is `true`. * \cgalParamEnd * \cgalNamedParamsEnd @@ -638,6 +638,10 @@ bool clip(TriangleMesh& tm, * checked for self-intersections and `CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception` * will be thrown if at least one is found. * \cgalParamEnd + * \cgalParamBegin{do_not_modify} (`np_s` only) if `true`, `splitter` will not be modified. The default value is `false`. + * An interesting property of this option is that when set to `true`, + * `tm` is no longer required to be without self-intersection. + * \cgalParamEnd * \cgalNamedParamsEnd */ template Date: Wed, 1 Jul 2020 21:03:31 +0200 Subject: [PATCH 10/14] remove useless test (done in the main call) --- .../Side_of_triangle_mesh/Point_inside_vertical_ray_cast.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Side_of_triangle_mesh/Point_inside_vertical_ray_cast.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Side_of_triangle_mesh/Point_inside_vertical_ray_cast.h index 2c81aa7c824..e282e092fa1 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Side_of_triangle_mesh/Point_inside_vertical_ray_cast.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Side_of_triangle_mesh/Point_inside_vertical_ray_cast.h @@ -75,13 +75,6 @@ public: { typename Traits::Bounding_box bbox = m_helper.get_tree_bbox(tree); - if( point.x() < bbox.xmin() || point.x() > bbox.xmax() - || point.y() < bbox.ymin() || point.y() > bbox.ymax() - || point.z() < bbox.zmin() || point.z() > bbox.zmax() ) - { - return ON_UNBOUNDED_SIDE; - } - //the direction of the vertical ray depends on the position of the point in the bbox //in order to limit the expected number of nodes visited. Ray query = ray_functor(point, vector_functor(0,0,(2*point.z() < bbox.zmax()+bbox.zmin()?-1:1))); From d64faf31995d4ea4558f07960ff56696865a8407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 1 Jul 2020 21:03:55 +0200 Subject: [PATCH 11/14] add non-documented operator to do queries using points from another kernel --- .../Ray_3_Triangle_3_traversal_traits.h | 54 +++++++++++++ .../include/CGAL/Side_of_triangle_mesh.h | 75 +++++++++++++++++++ 2 files changed, 129 insertions(+) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Side_of_triangle_mesh/Ray_3_Triangle_3_traversal_traits.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Side_of_triangle_mesh/Ray_3_Triangle_3_traversal_traits.h index 285017791a5..130c2732ea1 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Side_of_triangle_mesh/Ray_3_Triangle_3_traversal_traits.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Side_of_triangle_mesh/Ray_3_Triangle_3_traversal_traits.h @@ -216,6 +216,60 @@ public: } }; +//special case when ray query is from another Kernel K1 is the kernel compatible with the AABB-tree +template +class K2_Ray_3_K1_Triangle_3_traversal_traits +{ + //the status indicates whether the query point is strictly inside the polyhedron, and the number of intersected triangles if yes + std::pair& m_status; + bool m_stop; + const AABBTraits& m_aabb_traits; + typedef typename AABBTraits::Primitive Primitive; + typedef CGAL::AABB_node Node; + Helper m_helper; + CGAL::Cartesian_converter to_K2; + +public: + K2_Ray_3_K1_Triangle_3_traversal_traits(std::pair& status, + const AABBTraits& aabb_traits, + const Helper& h) + :m_status(status), m_stop(false), m_aabb_traits(aabb_traits), m_helper(h) + {m_status.first=true;} + + bool go_further() const { return !m_stop; } + + template + void intersection(const Query& query, const Primitive& primitive) + { + Intersections::internal::r3t3_do_intersect_endpoint_position_visitor visitor; + std::pair res= + Intersections::internal::do_intersect(to_K2(m_helper.get_primitive_datum(primitive, m_aabb_traits)), + query, K2(), visitor); + + if (res.first){ + switch (res.second){ + case Intersections::internal::R3T3_intersection::CROSS_FACET: + ++m_status.second; + break; + case Intersections::internal::R3T3_intersection::ENDPOINT_IN_TRIANGLE: + m_status.first=false; + m_stop=true; + break; + default: + m_status.first=boost::logic::indeterminate; + m_stop=true; + } + } + } + + template + bool do_intersect(const Query& query, const Node& node) const + { + return CGAL::do_intersect(query, m_helper.get_node_bbox(node)); + } +}; + + }// namespace internal }// namespace CGAL diff --git a/Polygon_mesh_processing/include/CGAL/Side_of_triangle_mesh.h b/Polygon_mesh_processing/include/CGAL/Side_of_triangle_mesh.h index 20ec968d50a..f0c23942f60 100644 --- a/Polygon_mesh_processing/include/CGAL/Side_of_triangle_mesh.h +++ b/Polygon_mesh_processing/include/CGAL/Side_of_triangle_mesh.h @@ -242,6 +242,81 @@ public: } } +#ifndef DOXYGEN_RUNNING + template + Bounded_side operator()(const typename K2::Point_3& point, const K2& k2) const + { + if(point.x() < box.xmin() + || point.x() > box.xmax() + || point.y() < box.ymin() + || point.y() > box.ymax() + || point.z() < box.zmin() + || point.z() > box.zmax()) + { + return CGAL::ON_UNBOUNDED_SIDE; + } + +#ifdef CGAL_HAS_THREADS + AABB_tree_* tree_ptr = + const_cast(atomic_tree_ptr.load(std::memory_order_acquire)); +#endif + // Lazily build the tree only when needed + if (tree_ptr==nullptr) + { +#ifdef CGAL_HAS_THREADS + CGAL_SCOPED_LOCK(tree_mutex); + tree_ptr = const_cast(atomic_tree_ptr.load(std::memory_order_relaxed)); +#endif + CGAL_assertion(tm_ptr != nullptr && opt_vpm!=boost::none); + if (tree_ptr==nullptr) + { + tree_ptr = new AABB_tree(faces(*tm_ptr).first, + faces(*tm_ptr).second, + *tm_ptr, *opt_vpm); + const_cast(tree_ptr)->build(); +#ifdef CGAL_HAS_THREADS + atomic_tree_ptr.store(tree_ptr, std::memory_order_release); +#endif + } + } + + typedef typename Kernel_traits::Kernel K1; + typedef typename AABB_tree::AABB_traits AABB_traits; + typedef internal::Default_tree_helper Helper; + Helper helper; + typename AABB_traits::Bounding_box bbox = helper.get_tree_bbox(*tree_ptr); + + static const unsigned int seed = 1340818006; + CGAL::Random rg(seed); // seed some value for make it easy to debug + Random_points_on_sphere_3 random_point(1.,rg); + + typename K2::Construct_ray_3 ray = k2.construct_ray_3_object(); + typename K2::Construct_vector_3 vector = k2.construct_vector_3_object(); + + do { //retry with a random ray + typename K2::Ray_3 query = ray(point, vector(CGAL::ORIGIN,*random_point++)); + + std::pair + status( boost::logic::tribool(boost::logic::indeterminate), 0); + + internal::K2_Ray_3_K1_Triangle_3_traversal_traits + traversal_traits(status, tree_ptr->traits(), helper); + + tree_ptr->traversal(query, traversal_traits); + + if ( !boost::logic::indeterminate(status.first) ) + { + if (status.first) + return (status.second&1) == 1 ? ON_BOUNDED_SIDE : ON_UNBOUNDED_SIDE; + //otherwise the point is on the facet + return ON_BOUNDARY; + } + } while (true); + return ON_BOUNDARY; // should never be reached + } + +#endif + }; } // namespace CGAL From 1246e9e375dc8e1dd6f2739f9d85038405cf4fa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 1 Jul 2020 21:04:52 +0200 Subject: [PATCH 12/14] add a generic clip version that handles self-intersections --- .../CGAL/Polygon_mesh_processing/clip.h | 110 +++++++++++++++++- 1 file changed, 108 insertions(+), 2 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h index 1b054befbf0..ad1201317fe 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -400,6 +401,97 @@ void split_along_edges(TriangleMesh& tm, CGAL_assertion(is_valid_polygon_mesh(tm)); } +template +void +generic_clip_impl( + TriangleMesh& tm1, + TriangleMesh& tm2, + const NamedParameters1& np1, + const NamedParameters2& np2) +{ + using parameters::choose_parameter; + using parameters::get_parameter; + +// Vertex point maps + //for input meshes + typedef typename GetVertexPointMap::type Vpm; + typedef typename GetVertexPointMap::type Vpm2; + CGAL_USE_TYPE(Vpm2); + CGAL_assertion_code( + static const bool same_vpm = (boost::is_same::value); ) + CGAL_static_assertion(same_vpm); + + Vpm vpm1 = choose_parameter(get_parameter(np1, internal_np::vertex_point), + get_property_map(boost::vertex_point, tm1)); + + Vpm vpm2 = choose_parameter(get_parameter(np2, internal_np::vertex_point), + get_property_map(boost::vertex_point, tm2)); + + if (&tm1==&tm2) + { + // TODO mark all edges + return; + } + + // handle case of empty meshes (isolated vertices are ignored) + if (faces(tm1).empty()) + return; + +// Edge is-constrained maps + //for input meshes + typedef typename internal_np::Lookup_named_param_def < + internal_np::edge_is_constrained_t, + NamedParameters1, + Corefinement::No_mark//default + > ::type User_ecm1; + + // User and internal edge is-constrained map + typedef typename boost::template property_map >::type Algo_ecm1; + typedef Corefinement::No_mark Ecm2; + typedef OR_property_map Ecm1; + typedef Corefinement::Ecm_bind Ecm_in; + + Algo_ecm1 algo_ecm1 = get(CGAL::dynamic_edge_property_t(), tm1); + Ecm1 ecm1 = Ecm1(algo_ecm1, choose_parameter(get_parameter(np1, internal_np::edge_is_constrained))); + Ecm2 ecm2; + + // Face index point maps + typedef typename CGAL::GetInitializedFaceIndexMap::type FaceIndexMap1; + FaceIndexMap1 fid_map1 = get_initialized_face_index_map(tm1, np1); + + const bool use_compact_clipper = + choose_parameter(get_parameter(np1, internal_np::use_compact_clipper), true); + + + // User visitor + typedef typename internal_np::Lookup_named_param_def < + internal_np::graph_visitor_t, + NamedParameters1, + Corefinement::Default_visitor//default + > ::type User_visitor; + User_visitor uv(choose_parameter(get_parameter(np1, internal_np::graph_visitor))); + + // surface intersection algorithm call + typedef Corefinement::Generic_clip_output_builder Ob; + + typedef Corefinement::Surface_intersection_visitor_for_corefinement< + TriangleMesh, Vpm, Ob, Ecm_in, User_visitor> Algo_visitor; + Ecm_in ecm_in(tm1,tm2,ecm1,ecm2); + Ob ob(tm1, tm2, vpm1, vpm2, algo_ecm1, fid_map1, use_compact_clipper); + + Corefinement::Intersection_of_triangle_meshes + functor(tm1, tm2, vpm1, vpm2, Algo_visitor(uv,ob,ecm_in,&tm2)); + functor(CGAL::Emptyset_iterator(), false, true); +} + } // end of internal namespace /** @@ -447,6 +539,12 @@ void split_along_edges(TriangleMesh& tm, * \cgalParamBegin{use_compact_clipper} (`np_tm` only) if `false`, the parts of `tm` coplanar with `clipper` * will not be part of the output. Default value is `true`. * \cgalParamEnd + * \cgalParamBegin{do_not_modify} (`np_c` only) if `true`, `clipper` will not be modified. The default value is `false`. + * An interesting property of this option is that when set to `true`, + * `tm` is no longer required to be without self-intersection. + * Setting this option to `true` will automatically set `throw_on_self_intersection` to `false` + * and `clip_volume` to `false`. + * \cgalParamEnd * \cgalNamedParamsEnd * * @return `true` if the output surface mesh is manifold. @@ -461,6 +559,14 @@ clip(TriangleMesh& tm, const NamedParameters1& np_tm, const NamedParameters2& np_c) { + if (parameters::choose_parameter(parameters::get_parameter(np_c, internal_np::do_not_modify), false)) + { + CGAL_assertion(is_closed(clipper)); + + internal::generic_clip_impl(tm, clipper, np_tm, np_c); + return true; + } + const bool clip_volume = parameters::choose_parameter(parameters::get_parameter(np_tm, internal_np::clip_volume), false); @@ -630,10 +736,10 @@ bool clip(TriangleMesh& tm, * If this parameter is omitted, an internal property map for * `CGAL::vertex_point_t` must be available in `TriangleMesh` * \cgalParamEnd - * \cgalParamBegin{visitor} a class model of `PMPCorefinementVisitor` + * \cgalParamBegin{visitor} (`np_s` only) a class model of `PMPCorefinementVisitor` * that is used to track the creation of new faces. * \cgalParamEnd - * \cgalParamBegin{throw_on_self_intersection} if `true`, + * \cgalParamBegin{throw_on_self_intersection} (`np_s` only) if `true`, * the set of triangles closed to the intersection of `tm` and `splitter` will be * checked for self-intersections and `CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception` * will be thrown if at least one is found. From d4122d945ad05de650e5b7fe575417be22c9d07f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 2 Jul 2020 10:36:29 +0200 Subject: [PATCH 13/14] better include the new header :( --- .../Generic_clip_output_builder.h | 241 ++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Generic_clip_output_builder.h diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Generic_clip_output_builder.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Generic_clip_output_builder.h new file mode 100644 index 00000000000..f391696cb3a --- /dev/null +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Corefinement/Generic_clip_output_builder.h @@ -0,0 +1,241 @@ +// Copyright (c) 2020 GeometryFactory (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Sebastien Loriot + +#ifndef CGAL_POLYGON_MESH_PROCESSING_INTERNAL_GENERIC_CLIP_OUTPUT_BUILDER_H +#define CGAL_POLYGON_MESH_PROCESSING_INTERNAL_GENERIC_CLIP_OUTPUT_BUILDER_H + +#include + + +#include + +#include +#include +#include + +#include +#include + +#include + + +namespace CGAL { +namespace Polygon_mesh_processing { +namespace Corefinement { + + +namespace PMP=Polygon_mesh_processing; +namespace params=PMP::parameters; + +template +class Generic_clip_output_builder +{ +//Default typedefs + typedef typename Default::Get< + Kernel_, + typename Kernel_traits< + typename boost::property_traits::value_type + >::Kernel >::type Kernel; + +// graph_traits typedefs + typedef TriangleMesh TM; + typedef boost::graph_traits GT; + typedef typename GT::edge_descriptor edge_descriptor; + typedef typename GT::face_descriptor face_descriptor; + typedef typename GT::halfedge_descriptor halfedge_descriptor; + typedef typename GT::vertex_descriptor vertex_descriptor; +// Internal typedefs + typedef std::size_t Node_id; + typedef std::pair Node_id_pair; + // to maintain a halfedge on each polyline per TriangleMesh + pair + // with first = "is the key (pair) was reversed?" and + // second is the number of edges -1 in the polyline + typedef std::map< Node_id_pair, + std::pair< std::map, + std::pair > > + An_edge_per_polyline_map; + + typedef boost::unordered_map Node_id_map; + typedef boost::unordered_map Edge_map; +//Data members + TriangleMesh &tm1, &tm2; + // property maps of input meshes + const VertexPointMap vpm1; + const VertexPointMap vpm2; + Ecm1 ecm1; + FaceIdMap1 fids1; + bool use_compact_clipper; + + // mapping vertex to node id + Node_id_map vertex_to_node_id1; + + // orientation of input surface meshes + bool is_tm2_inside_out; + // constants + const Node_id NID; + + typename An_edge_per_polyline_map::iterator last_polyline; + + Node_id get_node_id(vertex_descriptor v, + const Node_id_map& node_ids) + { + typename Node_id_map::const_iterator it = node_ids.find(v); + if (it == node_ids.end()) + return NID; + return it->second; + } + +public: + + Generic_clip_output_builder(TriangleMesh& tm1, + TriangleMesh& tm2, + const VertexPointMap vpm1, + const VertexPointMap vpm2, + const Ecm1& ecm1, + FaceIdMap1 fids1, + bool use_compact_clipper) + : tm1(tm1), tm2(tm2) + , vpm1(vpm1), vpm2(vpm2) + , ecm1(ecm1) + , fids1(fids1) + , use_compact_clipper(use_compact_clipper) + , is_tm2_inside_out( !PMP::is_outward_oriented(tm2, parameters::vertex_point_map(vpm2)) ) + , NID((std::numeric_limits::max)()) + {} + +// functions called by the intersection visitor + void start_new_polyline(Node_id, Node_id) {} + void add_node_to_polyline(Node_id) {} + void set_edge_per_polyline(TriangleMesh&, Node_id_pair, halfedge_descriptor){} + + void set_vertex_id(vertex_descriptor v, Node_id node_id, const TriangleMesh& tm) + { + CGAL_assertion(&tm == &tm1); + vertex_to_node_id1.insert( std::make_pair(v, node_id) ); + } + + template + void operator()( + const Nodes_vector& nodes, + bool /* input_have_coplanar_faces */, + const boost::dynamic_bitset<>& /* is_node_of_degree_one */, + const Mesh_to_map_node&) + { + // The property map must be either writable or well-initialized + if( CGAL::internal::Is_writable_property_map::value && + !BGL::internal::is_index_map_valid(fids1, num_faces(tm1), faces(tm1)) ) + { + BGL::internal::initialize_face_index_map(fids1, tm1); + } + CGAL_assertion(BGL::internal::is_index_map_valid(fids1, num_faces(tm1), faces(tm1))); + + // (1) Assign a patch id to each face indicating in which connected + // component limited by intersection edges of the surface they are. + std::vector tm1_patch_ids( num_faces(tm1),NID ); + + std::size_t nb_patches_tm1 = + PMP::connected_components(tm1, + bind_property_maps(fids1,make_property_map(&tm1_patch_ids[0])), + params::edge_is_constrained_map(ecm1) + .face_index_map(fids1)); + + std::vector tm1_patch_sizes(nb_patches_tm1, 0); + for(std::size_t i : tm1_patch_ids) + if(i!=NID) + ++tm1_patch_sizes[i]; + + // Use the class Side_of_triangle_mesh to classify each patch + boost::dynamic_bitset<> patch_status_not_set_tm1(nb_patches_tm1); + patch_status_not_set_tm1.set(); + + typedef Side_of_triangle_mesh Inside_poly_test; + + CGAL::Bounded_side in_tm2 = is_tm2_inside_out + ? ON_UNBOUNDED_SIDE : ON_BOUNDED_SIDE; + + Inside_poly_test inside_tm2(tm2, vpm2); + std::size_t nb_classified=0; + std::vector cc_to_keep; + for(face_descriptor f : faces(tm1)) + { + const std::size_t f_id = get(fids1, f); + const std::size_t patch_id = tm1_patch_ids[ f_id ]; + if ( patch_status_not_set_tm1.test( patch_id ) ) + { + patch_status_not_set_tm1.reset( patch_id ); + halfedge_descriptor h = halfedge(f, tm1); + Node_id index_p1 = get_node_id(target(h, tm1), vertex_to_node_id1); + std::array fnids = { index_p1, index_p1, index_p1 }; + if (index_p1 != NID) + { + h=next(h, tm1); + index_p1 = get_node_id(target(h, tm1), vertex_to_node_id1); + fnids[1]=index_p1; + if (index_p1 != NID) + { + h=next(h, tm1); + index_p1 = get_node_id(target(h, tm1), vertex_to_node_id1); + fnids[2]=index_p1; + } + } + if (index_p1 != NID) + { + typename Nodes_vector::Exact_kernel ek; + typedef typename Nodes_vector::Exact_kernel::Point_3 Exact_point_3; + Exact_point_3 e_centroid = centroid(nodes.exact_node(fnids[0]), + nodes.exact_node(fnids[1]), + nodes.exact_node(fnids[2])); + + Bounded_side position = inside_tm2(e_centroid, ek); + if ( position==ON_BOUNDARY ) + { + if (use_compact_clipper) + { + cc_to_keep.push_back(patch_id); + } + } + else + if ( position == in_tm2 ) + { + cc_to_keep.push_back(patch_id); + } + } + else + { + Bounded_side position = inside_tm2( get(vpm1, target(h, tm1))); + CGAL_assertion( position != ON_BOUNDARY); + if ( position == in_tm2 ) + { + cc_to_keep.push_back(patch_id); + } + } + if ( ++nb_classified==nb_patches_tm1) break; + } + } + + PMP::keep_connected_components(tm1, cc_to_keep, bind_property_maps(fids1,make_property_map(&tm1_patch_ids[0]))); + } +}; + + +} } } // CGAL::Polygon_mesh_processing::Corefinement + +#undef CGAL_COREF_FUNCTION_CALL +#endif // CGAL_POLYGON_MESH_PROCESSING_INTERNAL_GENERIC_CLIP_OUTPUT_BUILDER_H From 715aa45a2be281135d3e94513e7a14334d4973d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 4 Aug 2020 08:22:01 +0200 Subject: [PATCH 14/14] rephrase --- .../include/CGAL/Polygon_mesh_processing/clip.h | 2 +- .../include/CGAL/Polygon_mesh_processing/corefinement.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h index ad1201317fe..24a8471edd5 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h @@ -540,7 +540,7 @@ generic_clip_impl( * will not be part of the output. Default value is `true`. * \cgalParamEnd * \cgalParamBegin{do_not_modify} (`np_c` only) if `true`, `clipper` will not be modified. The default value is `false`. - * An interesting property of this option is that when set to `true`, + * If this option is set to `true`, * `tm` is no longer required to be without self-intersection. * Setting this option to `true` will automatically set `throw_on_self_intersection` to `false` * and `clip_volume` to `false`. diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h index 209f6eaf2b4..a248d24b257 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/corefinement.h @@ -627,8 +627,8 @@ corefine_and_compute_difference( TriangleMesh& tm1, * will be thrown if at least one is found (`np1` only). * \cgalParamEnd * \cgalParamBegin{do_not_modify} if `true`, the corresponding mesh will not be updated. The default value is `false`. - * Obviously if this parameter is set to `true` for both meshes nothing will be done. - * An interesting property of this option is that when set to `true` for one mesh, + * If this parameter is set to `true` for both meshes nothing will be done. + * If this option is set to `true` for one mesh, * the other mesh is no longer required to be without self-intersection. * \cgalParamEnd * \cgalNamedParamsEnd