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 277772864c2..b32fef71dd8 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h @@ -516,6 +516,34 @@ generic_clip_impl( functor(CGAL::Emptyset_iterator(), false, true); } + + + +#ifndef CGAL_PLANE_CLIP_DO_NOT_USE_TRIANGULATION +template +struct Visitor_wrapper_for_triangulate_face +{ + using face_descriptor = typename boost::graph_traits::face_descriptor; + + Clip_visitor& clip_visitor; + const PolygonMesh& pm; + + Visitor_wrapper_for_triangulate_face(const PolygonMesh& pm, Clip_visitor& clip_visitor) + : pm(pm) + , clip_visitor(clip_visitor) + {} + + void before_subface_creations(face_descriptor /* f_split */) {} + void after_subface_creations() {} + + void after_subface_created(face_descriptor f_new) + { + clip_visitor.before_face_copy(boost::graph_traits::null_face(), pm, pm); + clip_visitor.after_face_copy(boost::graph_traits::null_face(), pm, f_new, pm); + } +}; +#endif + } // end of internal namespace /** @@ -667,7 +695,10 @@ clip(TriangleMesh& tm, * \cgalParamNEnd * * \cgalParamNBegin{visitor} - * \cgalParamDescription{a visitor used to track the creation of new faces} + * \cgalParamDescription{a visitor used to track the creation of new faces, edges, and faces. + * Note that as there are no mesh associated with `plane`, + * `boost::graph_traits::null_halfedge()` and `boost::graph_traits::null_face()` will be used when calling + * functions of the visitor expecting a halfedge or a face from `plane`. Similarly, `pm` will be used as the mesh of `plane`.} * \cgalParamType{a class model of `PMPCorefinementVisitor`} * \cgalParamDefault{`Corefinement::Default_visitor`} * \cgalParamNEnd @@ -731,6 +762,7 @@ bool clip(PolygonMesh& pm, { using parameters::choose_parameter; using parameters::get_parameter; + using parameters::get_parameter_reference ; using halfedge_descriptor = typename boost::graph_traits::halfedge_descriptor; @@ -739,6 +771,13 @@ bool clip(PolygonMesh& pm, auto vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), get_property_map(vertex_point, pm)); + + using Default_visitor = Corefinement::Default_visitor; + Default_visitor default_visitor; + using Visitor_ref = typename internal_np::Lookup_named_param_def::reference; + Visitor_ref visitor = choose_parameter(get_parameter_reference(np, internal_np::visitor), default_visitor); + constexpr bool has_visitor = !std::is_same_v>>; + typedef typename internal_np::Lookup_named_param_def < internal_np::concurrency_tag_t, NamedParameters, @@ -769,16 +808,15 @@ bool clip(PolygonMesh& pm, .do_not_triangulate_faces(!triangulate) .throw_on_self_intersection(!allow_self_intersections && throw_on_self_intersection) + .visitor(visitor) .concurrency_tag(Concurrency_tag())); - if (allow_self_intersections) clip_volume=false; if (clip_volume && !is_closed(pm)) clip_volume=false; if (clip_volume && !use_compact_clipper) use_compact_clipper=true; - auto fcc = get(dynamic_face_property_t(), pm); std::size_t nbcc = connected_components(pm, fcc, CGAL::parameters::edge_is_constrained_map(ecm)); @@ -815,10 +853,22 @@ bool clip(PolygonMesh& pm, for (halfedge_descriptor h : borders) { + visitor.before_face_copy(boost::graph_traits::null_face(), pm, pm); Euler::fill_hole(h, pm); + visitor.after_face_copy(boost::graph_traits::null_face(), pm, face(h, pm), pm); + #ifndef CGAL_PLANE_CLIP_DO_NOT_USE_TRIANGULATION if (triangulate) - triangulate_face(face(h,pm), pm, parameters::vertex_point_map(vpm).geom_traits(traits)); + { + if constexpr (!has_visitor) + triangulate_face(face(h,pm), pm, parameters::vertex_point_map(vpm).geom_traits(traits)); + else + { + using Base_visitor = std::remove_cv_t>; + internal::Visitor_wrapper_for_triangulate_face visitor_wrapper(pm, visitor); + triangulate_face(face(h,pm), pm, parameters::vertex_point_map(vpm).geom_traits(traits).visitor(visitor_wrapper)); + } + } #endif } } @@ -1051,7 +1101,10 @@ void split(TriangleMesh& tm, * \cgalParamNEnd * * \cgalParamNBegin{visitor} - * \cgalParamDescription{a visitor used to track the creation of new faces} + * \cgalParamDescription{a visitor used to track the creation of new faces, edges, and vertices. + * Note that as there are no mesh associated with `plane`, + * `boost::graph_traits::null_halfedge()` and `boost::graph_traits::null_face()` will be used when calling + * functions of the visitor expecting a halfedge or a face from `plane`. Similarly, `pm` will be used as the mesh of `plane`.}} * \cgalParamType{a class model of `PMPCorefinementVisitor`} * \cgalParamDefault{`Corefinement::Default_visitor`} * \cgalParamNEnd diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/refine_with_plane.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/refine_with_plane.h index 588fcf4b375..e37ac5b68f3 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/refine_with_plane.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/refine_with_plane.h @@ -32,12 +32,38 @@ namespace Polygon_mesh_processing { template struct Default_cut_visitor { - /// called before splitting an edge - void before_edge_split(typename boost::graph_traits::halfedge_descriptor, PolygonMesh&) {} - /// called after an edge split, `h` = the new halfedge pointing to the new vertex - void edge_split(typename boost::graph_traits::halfedge_descriptor, PolygonMesh&) {} - /// gives access to all the vertex that are on the cutting plane - void vertices_on_cut(const std::vector::vertex_descriptor>&, PolygonMesh&){} + using halfedge_descriptor = typename boost::graph_traits::halfedge_descriptor; + using face_descriptor = typename boost::graph_traits::face_descriptor; + using vertex_descriptor = typename boost::graph_traits::vertex_descriptor; + + /// @name Functions used for tracking face splits + /// @{ + void before_subface_creations(face_descriptor /* f_split */, const PolygonMesh& /* pm */){} + void after_subface_creations(const PolygonMesh& /* pm */){} + void before_subface_created(const PolygonMesh& /* pm */){} + void after_subface_created(face_descriptor /* f_new */, const PolygonMesh& /* pm */){} + /// @} + + /// @name Functions used for tracking edge splits and edge creation + /// @{ + void before_edge_split(halfedge_descriptor, PolygonMesh&) {} + void edge_split(halfedge_descriptor, PolygonMesh&) {} + void after_edge_split(){} + void add_retriangulation_edge(halfedge_descriptor h, const PolygonMesh& pm){} + /// @} + + /// @name Functions used when a new vertex is created + ///@{ + void intersection_point_detected(std::size_t /* i_id */, + int /* sdim */, + halfedge_descriptor /* h_e */, + halfedge_descriptor /* h_f */, + const PolygonMesh& /* tm_e */, + const PolygonMesh& /* tm_f */, + bool /* is_target_coplanar */, + bool /* is_source_coplanar */){} + void new_vertex_added(std::size_t /* i_id */, vertex_descriptor /* v */, const PolygonMesh& /* pm */) {} + ///@} }; // TODO: doc me or hide me in the np @@ -223,6 +249,8 @@ void refine_with_plane(PolygonMesh& pm, Default_visitor default_visitor; Visitor_ref visitor = choose_parameter(get_parameter_reference(np, internal_np::visitor), default_visitor); + constexpr bool has_visitor = !std::is_same_v>>; + auto vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), get_property_map(vertex_point, pm)); @@ -284,6 +312,8 @@ void refine_with_plane(PolygonMesh& pm, break; case ON_ORIENTED_BOUNDARY: at_least_one_on=true; + visitor.intersection_point_detected(on_obnd.size(), 2, halfedge(v, pm), boost::graph_traits::null_halfedge(), + pm, pm, true, false); on_obnd.push_back(v); } } @@ -326,13 +356,17 @@ void refine_with_plane(PolygonMesh& pm, else if (get(vertex_os, tgt)!=CGAL::ON_ORIENTED_BOUNDARY && get(vertex_os, src)!=get(vertex_os, tgt)) + { + visitor.intersection_point_detected(on_obnd.size()+inters.size(), 2, halfedge(e, pm), boost::graph_traits::null_halfedge(), + pm, pm, false, false); inters.push_back(e); + } } } if (all_in || all_out) { - visitor.vertices_on_cut(on_obnd, pm); + //visitor.vertices_on_cut(on_obnd, pm); return; } @@ -358,6 +392,7 @@ void refine_with_plane(PolygonMesh& pm, } //TODO: parallel for + std::size_t vid=on_obnd.size(); for (edge_descriptor e : inters) { halfedge_descriptor h = halfedge(e, pm); @@ -368,8 +403,10 @@ void refine_with_plane(PolygonMesh& pm, visitor.before_edge_split(h, pm); h = CGAL::Euler::split_edge(h, pm); put(vpm, target(h, pm), ip); + visitor.new_vertex_added(vid, target(h,pm), pm); put(vertex_os, target(h, pm), ON_ORIENTED_BOUNDARY); visitor.edge_split(h, pm); + visitor.after_edge_split(); if (was_marked) put(ecm, edge(h, pm), true); @@ -378,9 +415,10 @@ void refine_with_plane(PolygonMesh& pm, h=prev(opposite(h,pm),pm); if (!is_border(h, pm)) splitted_faces[face(h, pm)].push_back(h); + ++vid; } - visitor.vertices_on_cut(on_obnd, pm); + // visitor.vertices_on_cut(on_obnd, pm); // collect faces to be cut that have one vertex on the cut plane for (vertex_descriptor v : on_obnd) @@ -403,12 +441,17 @@ void refine_with_plane(PolygonMesh& pm, CGAL_assertion( nb_hedges%2 ==0 ); + visitor.before_subface_creations(f_and_hs.first, pm); + if (nb_hedges==2) { halfedge_descriptor h1=f_and_hs.second[0], h2=f_and_hs.second[1]; CGAL_assertion(next(h1,pm)!=h2 && next(h2,pm)!=h1); // the edge does not already exist + visitor.before_subface_created(pm); halfedge_descriptor res = CGAL::Euler::split_face(h1, h2, pm); + visitor.after_subface_created(face(res, pm), pm); put(edge_is_marked, edge(res, pm), true); + visitor.add_retriangulation_edge(res, pm); if (triangulate) { @@ -418,6 +461,7 @@ void refine_with_plane(PolygonMesh& pm, halfedge_descriptor newh = CGAL::Euler::split_face(res, next(next(res, pm), pm), pm); put(edge_is_marked, edge(newh, pm), false); + visitor.add_retriangulation_edge(newh, pm); } else { @@ -425,8 +469,10 @@ void refine_with_plane(PolygonMesh& pm, if (!is_triangle(res, pm)) { // TODO: take the criteria in triangulate_faces ? + visitor.before_subface_created(pm); halfedge_descriptor newh = CGAL::Euler::split_face(res, next(next(res, pm), pm), pm); + visitor.after_subface_created(face(newh, pm), pm); put(edge_is_marked, edge(newh, pm), false); } } @@ -447,10 +493,19 @@ void refine_with_plane(PolygonMesh& pm, { halfedge_descriptor h1=f_and_hs.second[i], h2=f_and_hs.second[i+1]; CGAL_assertion(next(h1,pm)!=h2 && next(h2,pm)!=h1); // the edge does not already exist + visitor.before_subface_created(pm); halfedge_descriptor res = CGAL::Euler::split_face(h1, h2, pm); + if constexpr (has_visitor) + { + if (face(h1,pm)!=face(h2,pm)) + visitor.after_subface_created(face(res, pm), pm); + } put(edge_is_marked, edge(res, pm), true); + visitor.add_retriangulation_edge(res, pm); } } + + visitor.after_subface_creations(pm); } }