add backward compatible visitor calls

This commit is contained in:
Sébastien Loriot 2025-04-13 19:26:20 +02:00
parent e001c7ce53
commit dc024f7e03
2 changed files with 121 additions and 13 deletions

View File

@ -516,6 +516,34 @@ generic_clip_impl(
functor(CGAL::Emptyset_iterator(), false, true); functor(CGAL::Emptyset_iterator(), false, true);
} }
#ifndef CGAL_PLANE_CLIP_DO_NOT_USE_TRIANGULATION
template <class PolygonMesh, class Clip_visitor>
struct Visitor_wrapper_for_triangulate_face
{
using face_descriptor = typename boost::graph_traits<PolygonMesh>::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<PolygonMesh>::null_face(), pm, pm);
clip_visitor.after_face_copy(boost::graph_traits<PolygonMesh>::null_face(), pm, f_new, pm);
}
};
#endif
} // end of internal namespace } // end of internal namespace
/** /**
@ -667,7 +695,10 @@ clip(TriangleMesh& tm,
* \cgalParamNEnd * \cgalParamNEnd
* *
* \cgalParamNBegin{visitor} * \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<PolygonMesh>::null_halfedge()` and `boost::graph_traits<PolygonMesh>::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`} * \cgalParamType{a class model of `PMPCorefinementVisitor`}
* \cgalParamDefault{`Corefinement::Default_visitor<PolygonMesh>`} * \cgalParamDefault{`Corefinement::Default_visitor<PolygonMesh>`}
* \cgalParamNEnd * \cgalParamNEnd
@ -731,6 +762,7 @@ bool clip(PolygonMesh& pm,
{ {
using parameters::choose_parameter; using parameters::choose_parameter;
using parameters::get_parameter; using parameters::get_parameter;
using parameters::get_parameter_reference ;
using halfedge_descriptor = typename boost::graph_traits<PolygonMesh>::halfedge_descriptor; using halfedge_descriptor = typename boost::graph_traits<PolygonMesh>::halfedge_descriptor;
@ -739,6 +771,13 @@ bool clip(PolygonMesh& pm,
auto vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), auto vpm = choose_parameter(get_parameter(np, internal_np::vertex_point),
get_property_map(vertex_point, pm)); get_property_map(vertex_point, pm));
using Default_visitor = Corefinement::Default_visitor<PolygonMesh>;
Default_visitor default_visitor;
using Visitor_ref = typename internal_np::Lookup_named_param_def<internal_np::visitor_t, NamedParameters, Default_visitor>::reference;
Visitor_ref visitor = choose_parameter(get_parameter_reference(np, internal_np::visitor), default_visitor);
constexpr bool has_visitor = !std::is_same_v<Default_visitor, std::remove_cv_t<std::remove_reference_t<Visitor_ref>>>;
typedef typename internal_np::Lookup_named_param_def < typedef typename internal_np::Lookup_named_param_def <
internal_np::concurrency_tag_t, internal_np::concurrency_tag_t,
NamedParameters, NamedParameters,
@ -769,16 +808,15 @@ bool clip(PolygonMesh& pm,
.do_not_triangulate_faces(!triangulate) .do_not_triangulate_faces(!triangulate)
.throw_on_self_intersection(!allow_self_intersections && .throw_on_self_intersection(!allow_self_intersections &&
throw_on_self_intersection) throw_on_self_intersection)
.visitor(visitor)
.concurrency_tag(Concurrency_tag())); .concurrency_tag(Concurrency_tag()));
if (allow_self_intersections) if (allow_self_intersections)
clip_volume=false; clip_volume=false;
if (clip_volume && !is_closed(pm)) clip_volume=false; if (clip_volume && !is_closed(pm)) clip_volume=false;
if (clip_volume && !use_compact_clipper) use_compact_clipper=true; if (clip_volume && !use_compact_clipper) use_compact_clipper=true;
auto fcc = get(dynamic_face_property_t<std::size_t>(), pm); auto fcc = get(dynamic_face_property_t<std::size_t>(), pm);
std::size_t nbcc = connected_components(pm, fcc, CGAL::parameters::edge_is_constrained_map(ecm)); 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) for (halfedge_descriptor h : borders)
{ {
visitor.before_face_copy(boost::graph_traits<PolygonMesh>::null_face(), pm, pm);
Euler::fill_hole(h, pm); Euler::fill_hole(h, pm);
visitor.after_face_copy(boost::graph_traits<PolygonMesh>::null_face(), pm, face(h, pm), pm);
#ifndef CGAL_PLANE_CLIP_DO_NOT_USE_TRIANGULATION #ifndef CGAL_PLANE_CLIP_DO_NOT_USE_TRIANGULATION
if (triangulate) if (triangulate)
{
if constexpr (!has_visitor)
triangulate_face(face(h,pm), pm, parameters::vertex_point_map(vpm).geom_traits(traits)); triangulate_face(face(h,pm), pm, parameters::vertex_point_map(vpm).geom_traits(traits));
else
{
using Base_visitor = std::remove_cv_t<std::remove_reference_t<Visitor_ref>>;
internal::Visitor_wrapper_for_triangulate_face<PolygonMesh, Base_visitor> visitor_wrapper(pm, visitor);
triangulate_face(face(h,pm), pm, parameters::vertex_point_map(vpm).geom_traits(traits).visitor(visitor_wrapper));
}
}
#endif #endif
} }
} }
@ -1051,7 +1101,10 @@ void split(TriangleMesh& tm,
* \cgalParamNEnd * \cgalParamNEnd
* *
* \cgalParamNBegin{visitor} * \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<PolygonMesh>::null_halfedge()` and `boost::graph_traits<PolygonMesh>::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`} * \cgalParamType{a class model of `PMPCorefinementVisitor`}
* \cgalParamDefault{`Corefinement::Default_visitor<TriangleMesh>`} * \cgalParamDefault{`Corefinement::Default_visitor<TriangleMesh>`}
* \cgalParamNEnd * \cgalParamNEnd

View File

@ -32,12 +32,38 @@ namespace Polygon_mesh_processing {
template <class PolygonMesh> template <class PolygonMesh>
struct Default_cut_visitor struct Default_cut_visitor
{ {
/// called before splitting an edge using halfedge_descriptor = typename boost::graph_traits<PolygonMesh>::halfedge_descriptor;
void before_edge_split(typename boost::graph_traits<PolygonMesh>::halfedge_descriptor, PolygonMesh&) {} using face_descriptor = typename boost::graph_traits<PolygonMesh>::face_descriptor;
/// called after an edge split, `h` = the new halfedge pointing to the new vertex using vertex_descriptor = typename boost::graph_traits<PolygonMesh>::vertex_descriptor;
void edge_split(typename boost::graph_traits<PolygonMesh>::halfedge_descriptor, PolygonMesh&) {}
/// gives access to all the vertex that are on the cutting plane /// @name Functions used for tracking face splits
void vertices_on_cut(const std::vector<typename boost::graph_traits<PolygonMesh>::vertex_descriptor>&, PolygonMesh&){} /// @{
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 // TODO: doc me or hide me in the np
@ -223,6 +249,8 @@ void refine_with_plane(PolygonMesh& pm,
Default_visitor default_visitor; Default_visitor default_visitor;
Visitor_ref visitor = choose_parameter(get_parameter_reference(np, internal_np::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<Default_visitor, std::remove_cv_t<std::remove_reference_t<Visitor_ref>>>;
auto vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), auto vpm = choose_parameter(get_parameter(np, internal_np::vertex_point),
get_property_map(vertex_point, pm)); get_property_map(vertex_point, pm));
@ -284,6 +312,8 @@ void refine_with_plane(PolygonMesh& pm,
break; break;
case ON_ORIENTED_BOUNDARY: case ON_ORIENTED_BOUNDARY:
at_least_one_on=true; at_least_one_on=true;
visitor.intersection_point_detected(on_obnd.size(), 2, halfedge(v, pm), boost::graph_traits<PolygonMesh>::null_halfedge(),
pm, pm, true, false);
on_obnd.push_back(v); on_obnd.push_back(v);
} }
} }
@ -326,13 +356,17 @@ void refine_with_plane(PolygonMesh& pm,
else else
if (get(vertex_os, tgt)!=CGAL::ON_ORIENTED_BOUNDARY && if (get(vertex_os, tgt)!=CGAL::ON_ORIENTED_BOUNDARY &&
get(vertex_os, src)!=get(vertex_os, tgt)) get(vertex_os, src)!=get(vertex_os, tgt))
{
visitor.intersection_point_detected(on_obnd.size()+inters.size(), 2, halfedge(e, pm), boost::graph_traits<PolygonMesh>::null_halfedge(),
pm, pm, false, false);
inters.push_back(e); inters.push_back(e);
} }
} }
}
if (all_in || all_out) if (all_in || all_out)
{ {
visitor.vertices_on_cut(on_obnd, pm); //visitor.vertices_on_cut(on_obnd, pm);
return; return;
} }
@ -358,6 +392,7 @@ void refine_with_plane(PolygonMesh& pm,
} }
//TODO: parallel for //TODO: parallel for
std::size_t vid=on_obnd.size();
for (edge_descriptor e : inters) for (edge_descriptor e : inters)
{ {
halfedge_descriptor h = halfedge(e, pm); halfedge_descriptor h = halfedge(e, pm);
@ -368,8 +403,10 @@ void refine_with_plane(PolygonMesh& pm,
visitor.before_edge_split(h, pm); visitor.before_edge_split(h, pm);
h = CGAL::Euler::split_edge(h, pm); h = CGAL::Euler::split_edge(h, pm);
put(vpm, target(h, pm), ip); put(vpm, target(h, pm), ip);
visitor.new_vertex_added(vid, target(h,pm), pm);
put(vertex_os, target(h, pm), ON_ORIENTED_BOUNDARY); put(vertex_os, target(h, pm), ON_ORIENTED_BOUNDARY);
visitor.edge_split(h, pm); visitor.edge_split(h, pm);
visitor.after_edge_split();
if (was_marked) if (was_marked)
put(ecm, edge(h, pm), true); put(ecm, edge(h, pm), true);
@ -378,9 +415,10 @@ void refine_with_plane(PolygonMesh& pm,
h=prev(opposite(h,pm),pm); h=prev(opposite(h,pm),pm);
if (!is_border(h, pm)) if (!is_border(h, pm))
splitted_faces[face(h, pm)].push_back(h); 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 // collect faces to be cut that have one vertex on the cut plane
for (vertex_descriptor v : on_obnd) for (vertex_descriptor v : on_obnd)
@ -403,12 +441,17 @@ void refine_with_plane(PolygonMesh& pm,
CGAL_assertion( nb_hedges%2 ==0 ); CGAL_assertion( nb_hedges%2 ==0 );
visitor.before_subface_creations(f_and_hs.first, pm);
if (nb_hedges==2) if (nb_hedges==2)
{ {
halfedge_descriptor h1=f_and_hs.second[0], h2=f_and_hs.second[1]; 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 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); 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); put(edge_is_marked, edge(res, pm), true);
visitor.add_retriangulation_edge(res, pm);
if (triangulate) if (triangulate)
{ {
@ -418,6 +461,7 @@ void refine_with_plane(PolygonMesh& pm,
halfedge_descriptor newh = halfedge_descriptor newh =
CGAL::Euler::split_face(res, next(next(res, pm), pm), pm); CGAL::Euler::split_face(res, next(next(res, pm), pm), pm);
put(edge_is_marked, edge(newh, pm), false); put(edge_is_marked, edge(newh, pm), false);
visitor.add_retriangulation_edge(newh, pm);
} }
else else
{ {
@ -425,8 +469,10 @@ void refine_with_plane(PolygonMesh& pm,
if (!is_triangle(res, pm)) if (!is_triangle(res, pm))
{ {
// TODO: take the criteria in triangulate_faces ? // TODO: take the criteria in triangulate_faces ?
visitor.before_subface_created(pm);
halfedge_descriptor newh = halfedge_descriptor newh =
CGAL::Euler::split_face(res, next(next(res, pm), pm), pm); 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); 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]; 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 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); 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); put(edge_is_marked, edge(res, pm), true);
visitor.add_retriangulation_edge(res, pm);
} }
} }
visitor.after_subface_creations(pm);
} }
} }