Merge pull request #6021 from MaelRL/PMP-Snap_with_KD_tree-GF

Improvements for PMP's snapping code
This commit is contained in:
Laurent Rineau 2021-09-29 16:59:26 +02:00
commit bb58df3ae5
3 changed files with 905 additions and 284 deletions

View File

@ -37,7 +37,8 @@ void vertices_as_halfedges(const VertexRange& vertex_range,
*out++ = halfedge(v, pmesh);
}
// Assigns at each vertex the 'tolerance' value as tolerance, but bounded by a percentage of the length of its shortest incident edge
// Assigns at each vertex the 'tolerance' value as tolerance,
// but bounded by a percentage of the length of its shortest incident edge
template <typename HalfedgeRange,
typename ToleranceMap,
typename PolygonMesh,
@ -116,9 +117,14 @@ bool is_collinear_with_tolerance(const typename GeomTraits::Point_3& p, // va ==
const FT sp = gt.compute_scalar_product_3_object()(va, vb);
// To avoid junctions like:
// ----va vb----- from being locked since it could be needed in ------------- later
// | | ----va vb---
// | |
// ----va vb-----
// | |
//
// from being locked since it could be needed in a configuration such as
// -------------
// ----va vb---
// | |
// later
if(sp < FT(0))
return false;
@ -129,6 +135,119 @@ bool is_collinear_with_tolerance(const typename GeomTraits::Point_3& p, // va ==
return (CGAL::square(sp) >= sq_va_l * sq_vb_l * sq_cos);
}
template <typename PolygonMesh>
struct Snapping_default_visitor
{
bool m_stop = false;
bool stop() { return m_stop; }
// ------------------------------- Preprocessing ------------------------------
// Called when starting preprocessing phase of the mesh.
void start_mesh_preprocessing() { }
// Called at the end of the preprocessing phase of the mesh.
void end_mesh_preprocessing() { }
// ------------------------------- Vertex - Vertex -------------------------------
// Called when starting snapping a range of vertices of `A` and a range of vertices of `B`.
void start_vertex_vertex_phase() { }
// Called when finished snapping a range of vertices of `A` and a range of vertices of `B`.
void end_vertex_vertex_phase() { }
// Called when trying to find `v`-`v_other` pair (with `v` in `A` and `v_other` in `B`)
//
// Available only if vertex-vertex pair detection is based on the kd-tree.
// Must be threadsafe if parallelism is used.
template <typename Vertex, typename Mesh>
void on_vertex_vertex_inquiry(const Vertex /*v*/, const Mesh&) { }
// Called when a match between two ranges of vertices is found.
// All vertices in `va` have the same position (and same for `vb`).
//
// Must be threadsafe if parallelism is used.
template <typename VertexContainer, typename Mesh>
void on_vertex_vertex_match(const VertexContainer& /*va*/, const Mesh&,
const VertexContainer& /*vb*/, const Mesh&) { }
// Called before proceeding with actual vertex-vertex snapping.
void start_vertex_vertex_snapping() { }
// Called after proceeding with actual vertex-vertex snapping.
void end_vertex_vertex_snapping() { }
// Called before two ranges of vertices are snapped together.
// All vertices in `va` have the same position (and same for `vb`).
template <typename VertexContainer, typename Mesh>
void before_vertex_vertex_snap(const VertexContainer& /*va*/, const Mesh&,
const VertexContainer& /*vb*/, const Mesh&) { }
// Called after two ranges of vertices have been snapped.
template <typename VertexContainer, typename Mesh>
void after_vertex_vertex_snap(const VertexContainer& /*va*/, const Mesh&,
const VertexContainer& /*vb*/, const Mesh&) { }
// ------------------------------- Vertex - Edge -------------------------------
// Called when starting snapping a range of vertices of A onto edges of B
void start_first_vertex_edge_phase() { }
// Called when finished snapping a range of vertices of A onto edges of B
void end_first_vertex_edge_phase() { }
// Called when starting snapping a range of vertices of B onto edges of A.
// Not called if A and B are the same mesh.
void start_second_vertex_edge_phase() { }
// Called when starting snapping a range of vertices of B onto edges of A.
// Not called if A and B are the same mesh.
void end_second_vertex_edge_phase() { }
// Called when trying to find `v`-`edge` pair
//
// Available only if vertex-vertex pair detection is based on the kd-tree.
// Must be threadsafe if parallelism is used.
template <typename Vertex, typename Mesh>
void on_vertex_edge_inquiry(const Vertex /*v*/, const Mesh& /*tm*/) { }
// Called when a match between a vertex and an edge is found.
//
// Must be threadsafe if parallelism is used.
template <typename Vertex, typename Edge, typename Mesh, typename Point>
void on_vertex_edge_match(const Vertex, const Mesh&, const Edge, const Mesh&, const Point&) { }
// Called before proceeding with actual snapping.
void start_vertex_edge_snapping() { }
// Called after proceeding with actual snapping.
void end_vertex_edge_snapping() { }
// Called before a new vertex is created.
template <typename Edge, typename Mesh, typename Point>
void before_vertex_edge_snap(const Edge, const Mesh&, const Point&) { }
// Called after a new vertex has been created, splitting an edge.
template <typename Vertex, typename Mesh>
void after_vertex_edge_snap(const Vertex /*new_vertex*/, const Mesh&) { }
// ------------------------------- Two passes (segmentation or not) ------------------------------
// Called at the start of the snapping pass that is restricted to compatible patch (first pass).
void start_patch_snapping() { }
// Called at the end of the snapping pass that is restricted to compatible patch (first pass).
void end_patch_snapping() { }
// Called at the start of the second snapping pass, all elements are potentially compatible (second pass).
void start_global_snapping() { }
// Called at the end of the second snapping pass, all elements are potentially compatible (second pass).
void end_global_snapping() { }
};
} // namespace internal
} // namespace Polygon_mesh_processing
} // namespace CGAL

View File

@ -92,6 +92,7 @@ void simplify_range(HalfedgeRange& halfedge_range,
typedef typename GT::FT FT;
typedef typename GetVertexPointMap<TriangleMesh, NamedParameters>::type VPM;
typedef typename boost::property_traits<VPM>::value_type Point;
typedef typename boost::property_traits<VPM>::reference Point_ref;
using parameters::get_parameter;
@ -125,10 +126,10 @@ void simplify_range(HalfedgeRange& halfedge_range,
// @fixme what if the source vertex is not to be snapped? Tolerance cannot be obtained...
// and where should the post-collapse vertex be since we can't move the source vertex...
// --> simply don't collapse?
const FT min_tol = (std::min)(get(tolerance_map, vs), get(tolerance_map, vt));
const FT max_tol = (std::max)(get(tolerance_map, vs), get(tolerance_map, vt));
const FT tol_s = get(tolerance_map, vs), tol_t = get(tolerance_map, vt);
const FT max_tol = (std::max)(tol_s, tol_t);
if(gt.compare_squared_distance_3_object()(ps,pt,CGAL::square(max_tol))==SMALLER)
if(gt.compare_squared_distance_3_object()(ps,pt,CGAL::square(max_tol)) == SMALLER)
{
const halfedge_descriptor prev_h = prev(h, tm);
const halfedge_descriptor next_h = next(h, tm);
@ -136,11 +137,23 @@ void simplify_range(HalfedgeRange& halfedge_range,
// check that the border has at least 4 edges not to create degenerate volumes
if(border_size(h, tm) >= 4)
{
const FT h_sq_length = gt.compute_squared_distance_3_object()(ps, pt);
vertex_descriptor v = Euler::collapse_edge(edge(h, tm), tm);
const FT lambda = tol_s / (tol_s + tol_t);
const Point new_p = ps + lambda * (pt - ps);
put(vpm, v, gt.construct_midpoint_3_object()(ps, pt));
put(tolerance_map, v, min_tol + 0.5 * CGAL::approximate_sqrt(h_sq_length));
// If we're collapsing onto a vertex that is not allowed to move, keep it fixed
const FT min_tol = (std::min)(tol_s, tol_t);
FT new_tolerance = min_tol;
if(!is_zero(min_tol))
{
if(tol_t > tol_s) // the new point is closer to ps than to pt
new_tolerance += CGAL::approximate_sqrt(CGAL::squared_distance(new_p, ps));
else
new_tolerance += CGAL::approximate_sqrt(CGAL::squared_distance(new_p, pt));
}
vertex_descriptor v = Euler::collapse_edge(edge(h, tm), tm);
put(vpm, v, new_p);
put(tolerance_map, v, new_tolerance);
if(get(range_halfedges, prev_h))
edges_to_test.insert(prev_h);
@ -453,7 +466,8 @@ template <typename ConcurrencyTag = CGAL::Sequential_tag,
typename VertexWithTolerance, typename TriangleMesh,
typename EdgeToSplitMap, typename AABBTree,
typename VertexPatchMap_S, typename FacePatchMap_T,
typename VPMS, typename VPMT, typename GT>
typename VPMS, typename VPMT, typename GT,
typename Visitor>
void find_splittable_edge(const VertexWithTolerance& vertex_with_tolerance,
EdgeToSplitMap& edges_to_split,
const AABBTree* aabb_tree_ptr,
@ -463,7 +477,8 @@ void find_splittable_edge(const VertexWithTolerance& vertex_with_tolerance,
const TriangleMesh& tm_T,
FacePatchMap_T face_patch_map_T,
VPMT vpm_T,
const GT& gt)
const GT& gt,
Visitor& visitor)
{
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
@ -477,6 +492,9 @@ void find_splittable_edge(const VertexWithTolerance& vertex_with_tolerance,
typedef internal::Projection_traits<TriangleMesh, VPMS, VPMT, FacePatchMap_T, AABB_traits> Projection_traits;
if(visitor.stop())
throw CGAL::internal::Throw_at_output_exception();
// by construction the whole range has the same position
const halfedge_descriptor h = vertex_with_tolerance.first;
const vertex_descriptor v = target(h, tm_S);
@ -488,6 +506,8 @@ void find_splittable_edge(const VertexWithTolerance& vertex_with_tolerance,
std::cout << "--------------------------- Query: " << v << " (" << query << ")" << std::endl;
#endif
visitor.on_vertex_edge_inquiry(v, tm_S);
Projection_traits traversal_traits(h, patch_id, sq_tolerance, tm_S, vpm_S,
tm_T, face_patch_map_T, vpm_T, aabb_tree_ptr->traits());
aabb_tree_ptr->traversal(query, traversal_traits);
@ -522,7 +542,6 @@ void find_splittable_edge(const VertexWithTolerance& vertex_with_tolerance,
if(!is_close_enough)
return;
CGAL_assertion(get(vpm_T, source(closest_e, tm_T)) != query &&
get(vpm_T, target(closest_e, tm_T)) != query);
@ -534,43 +553,17 @@ void find_splittable_edge(const VertexWithTolerance& vertex_with_tolerance,
// Using a map because we need to know if the same halfedge is split multiple times
Edges_to_split_map_inserter<ConcurrencyTag>()(edges_to_split, closest_h, vertex_with_tolerance.first, closest_p);
visitor.on_vertex_edge_match(v, tm_S, closest_h, tm_T, closest_p);
}
#ifdef CGAL_LINKED_WITH_TBB
template <typename PointWithToleranceContainer,
typename TriangleMesh, typename EdgeToSplitMap, typename AABBTree,
typename VertexPatchMap_S, typename FacePatchMap_T,
typename VPMS, typename VPMT, typename GT>
struct Find_splittable_edge_for_parallel_for
typename VPMS, typename VPMT, typename GT, typename Visitor>
struct Parallel_find_splittable_edge
{
Find_splittable_edge_for_parallel_for(const PointWithToleranceContainer& points_with_tolerance,
EdgeToSplitMap& edges_to_split,
const AABBTree* aabb_tree_ptr,
const TriangleMesh& tm_S,
const VertexPatchMap_S vertex_patch_map_S,
const VPMS vpm_S,
const TriangleMesh& tm_T,
const FacePatchMap_T face_patch_map_T,
const VPMT vpm_T,
const GT& gt)
:
m_points_with_tolerance(points_with_tolerance),
m_edges_to_split(edges_to_split), m_aabb_tree_ptr(aabb_tree_ptr),
m_tm_S(tm_S), m_vertex_patch_map_S(vertex_patch_map_S), m_vpm_S(vpm_S),
m_tm_T(tm_T), m_face_patch_map_T(face_patch_map_T), m_vpm_T(vpm_T),
m_gt(gt)
{ }
void operator()(const tbb::blocked_range<size_t>& r) const
{
for(std::size_t i=r.begin(); i!=r.end(); ++i)
{
find_splittable_edge<CGAL::Parallel_tag>(m_points_with_tolerance[i], m_edges_to_split, m_aabb_tree_ptr,
m_tm_S, m_vertex_patch_map_S, m_vpm_S,
m_tm_T, m_face_patch_map_T, m_vpm_T, m_gt);
}
}
private:
const PointWithToleranceContainer& m_points_with_tolerance;
EdgeToSplitMap& m_edges_to_split;
@ -582,18 +575,51 @@ private:
const FacePatchMap_T m_face_patch_map_T;
const VPMT m_vpm_T;
const GT& m_gt;
Visitor& m_visitor;
public:
Parallel_find_splittable_edge(const PointWithToleranceContainer& points_with_tolerance,
EdgeToSplitMap& edges_to_split,
const AABBTree* aabb_tree_ptr,
const TriangleMesh& tm_S,
const VertexPatchMap_S vertex_patch_map_S,
const VPMS vpm_S,
const TriangleMesh& tm_T,
const FacePatchMap_T face_patch_map_T,
const VPMT vpm_T,
const GT& gt,
Visitor& visitor)
:
m_points_with_tolerance(points_with_tolerance),
m_edges_to_split(edges_to_split), m_aabb_tree_ptr(aabb_tree_ptr),
m_tm_S(tm_S), m_vertex_patch_map_S(vertex_patch_map_S), m_vpm_S(vpm_S),
m_tm_T(tm_T), m_face_patch_map_T(face_patch_map_T), m_vpm_T(vpm_T),
m_gt(gt), m_visitor(visitor)
{ }
void operator()(const tbb::blocked_range<size_t>& r) const
{
for(std::size_t i=r.begin(); i!=r.end(); ++i)
{
find_splittable_edge<CGAL::Parallel_tag>(m_points_with_tolerance[i], m_edges_to_split, m_aabb_tree_ptr,
m_tm_S, m_vertex_patch_map_S, m_vpm_S,
m_tm_T, m_face_patch_map_T, m_vpm_T, m_gt, m_visitor);
}
}
};
#endif
template <typename EdgesToSplitContainer,
typename TriangleMesh, typename GeomTraits,
typename VPMS, typename VPMT>
typename TriangleMesh,
typename VPMS, typename VPMT, typename GeomTraits,
typename Visitor>
std::size_t split_edges(EdgesToSplitContainer& edges_to_split,
TriangleMesh& tm_S,
VPMS vpm_S,
TriangleMesh& tm_T,
VPMT vpm_T,
const GeomTraits& gt,
Visitor& visitor,
const bool is_source_mesh_fixed) // when snapping is B --> A and the mesh B is fixed
{
#ifdef CGAL_PMP_SNAP_DEBUG
@ -611,10 +637,14 @@ std::size_t split_edges(EdgesToSplitContainer& edges_to_split,
typedef std::vector<Vertex_with_new_position> Vertices_with_new_position;
typedef std::pair<const halfedge_descriptor, Vertices_with_new_position> Edge_and_splitters;
std::size_t snapped_n = 0;
#ifdef CGAL_PMP_SNAPPING_PRINT_RUNTIME
CGAL::Real_timer timer;
timer.start();
#endif
visitor.start_vertex_edge_snapping();
std::size_t snapped_n = 0;
for(Edge_and_splitters& es : edges_to_split)
{
@ -642,6 +672,9 @@ std::size_t split_edges(EdgesToSplitContainer& edges_to_split,
Point previous_split_position = get(vpm_S, *(vertices(tm_S).begin())); // dummy value to avoid "used uninitialized" warning
for(const Vertex_with_new_position& vnp : splitters)
{
if(visitor.stop())
return snapped_n;
const halfedge_descriptor splitter_h = vnp.first;
const vertex_descriptor splitter_v = target(splitter_h, tm_S);
const Point new_position = is_source_mesh_fixed ? get(vpm_S, splitter_v) : vnp.second;
@ -668,9 +701,13 @@ std::size_t split_edges(EdgesToSplitContainer& edges_to_split,
vertex_descriptor new_v = boost::graph_traits<TriangleMesh>::null_vertex();
if(do_split)
{
visitor.before_vertex_edge_snap(h_to_split, tm_T, vnp.second);
CGAL::Euler::split_edge(h_to_split, tm_T);
new_v = source(h_to_split, tm_T);
put(vpm_T, new_v, new_position); // position of the new point on the target mesh
visitor.after_vertex_edge_snap(new_v, tm_T);
}
if(!is_source_mesh_fixed)
@ -740,6 +777,13 @@ std::size_t split_edges(EdgesToSplitContainer& edges_to_split,
}
}
visitor.end_vertex_edge_snapping();
#ifdef CGAL_PMP_SNAPPING_PRINT_RUNTIME
timer.stop();
std::cout << "time for actual snapping (vertex-edge) " << timer.time() << " s." << std::endl;
#endif
return snapped_n;
}
@ -757,6 +801,7 @@ template <typename ConcurrencyTag = CGAL::Sequential_tag,
typename HalfedgeRange, typename TriangleMesh,
typename LockedVertexMap, typename LockedHalfedgeMap, typename ToleranceMap,
typename VertexPatchMap_S, typename FacePatchMap_T,
typename Visitor,
typename SourceNamedParameters, typename TargetNamedParameters>
std::size_t snap_non_conformal_one_way(const HalfedgeRange& halfedge_range_S,
TriangleMesh& tm_S,
@ -768,6 +813,7 @@ std::size_t snap_non_conformal_one_way(const HalfedgeRange& halfedge_range_S,
FacePatchMap_T face_patch_map_T,
LockedHalfedgeMap locked_halfedges_T,
const bool is_source_mesh_fixed,
Visitor& visitor,
const SourceNamedParameters& snp,
const TargetNamedParameters& tnp)
{
@ -839,7 +885,7 @@ std::size_t snap_non_conformal_one_way(const HalfedgeRange& halfedge_range_S,
for(auto& p : vertices_to_snap)
p.second = point_tolerance_map[get(vpm_S, target(p.first, tm_S))];
// Since we're inserting primitives one by one, we can't pass this shared data in the constructor of the tree
// Since primitives are inserted one by one, the shared data cannot be in the constructor of the tree
AABB_Traits aabb_traits;
aabb_traits.set_shared_data(tm_T, vpm_T);
AABB_tree aabb_tree(aabb_traits);
@ -863,10 +909,15 @@ std::size_t snap_non_conformal_one_way(const HalfedgeRange& halfedge_range_S,
std::cout << "Collect edges to split with " << vertices_to_snap.size() << " vertices" << std::endl;
#endif
#ifdef CGAL_PMP_SNAPPING_PRINT_RUNTIME
CGAL::Real_timer timer;
timer.start();
#endif
#ifndef CGAL_LINKED_WITH_TBB
CGAL_static_assertion_msg (!(std::is_convertible<ConcurrencyTag, Parallel_tag>::value),
"Parallel_tag is enabled but TBB is unavailable.");
#else
#else // CGAL_LINKED_WITH_TBB
if(std::is_convertible<ConcurrencyTag, Parallel_tag>::value)
{
#ifdef CGAL_PMP_SNAP_DEBUG
@ -875,22 +926,45 @@ std::size_t snap_non_conformal_one_way(const HalfedgeRange& halfedge_range_S,
typedef tbb::concurrent_hash_map<halfedge_descriptor,
Vertices_with_new_position> Concurrent_edge_to_split_container;
typedef internal::Find_splittable_edge_for_parallel_for<
typedef internal::Parallel_find_splittable_edge<
Vertices_with_tolerance, TriangleMesh,
Concurrent_edge_to_split_container, AABB_tree,
VertexPatchMap_S, FacePatchMap_T, VPMS, VPMT, GT> Functor;
CGAL::Real_timer timer;
timer.start();
VertexPatchMap_S, FacePatchMap_T, VPMS, VPMT, GT, Visitor> Functor;
Concurrent_edge_to_split_container edges_to_split;
Functor f(vertices_to_snap, edges_to_split, &aabb_tree,
tm_S, vertex_patch_map_S, vpm_S, tm_T, face_patch_map_T, vpm_T, gt);
tbb::parallel_for(tbb::blocked_range<std::size_t>(0, vertices_to_snap.size()), f);
tm_S, vertex_patch_map_S, vpm_S, tm_T, face_patch_map_T, vpm_T,
gt, visitor);
std::cout << "Time to gather edges: " << timer.time() << std::endl;
try
{
tbb::parallel_for(tbb::blocked_range<std::size_t>(0, vertices_to_snap.size()), f);
}
catch(const CGAL::internal::Throw_at_output_exception&)
{
return 0;
}
#if TBB_USE_CAPTURED_EXCEPTION
catch(const tbb::captured_exception& e)
{
const std::string tn1(e.name());
const std::string tn2(typeid(const CGAL::internal::Throw_at_output_exception&).name());
if(tn1 != tn2)
{
std::cerr << "Unexpected throw: " << tn1 << std::endl;
throw;
}
return split_edges(edges_to_split, tm_S, vpm_S, tm_T, vpm_T, gt, is_source_mesh_fixed);
return 0;
}
#endif
#ifdef CGAL_PMP_SNAPPING_PRINT_RUNTIME
timer.stop();
std::cout << "time for find split edges (parallel): " << timer.time() << std::endl;
#endif
return split_edges(edges_to_split, tm_S, vpm_S, tm_T, vpm_T, gt, visitor, is_source_mesh_fixed);
}
else
#endif // CGAL_LINKED_WITH_TBB
@ -900,14 +974,28 @@ std::size_t snap_non_conformal_one_way(const HalfedgeRange& halfedge_range_S,
#endif
std::map<halfedge_descriptor, Vertices_with_new_position> edges_to_split;
for(const Vertex_with_tolerance& vt : vertices_to_snap)
try
{
internal::find_splittable_edge(vt, edges_to_split, &aabb_tree,
tm_S, vertex_patch_map_S, vpm_S,
tm_T, face_patch_map_T, vpm_T, gt);
for(const Vertex_with_tolerance& vt : vertices_to_snap)
{
internal::find_splittable_edge(vt, edges_to_split, &aabb_tree,
tm_S, vertex_patch_map_S, vpm_S,
tm_T, face_patch_map_T, vpm_T,
gt, visitor);
}
}
catch(const CGAL::internal::Throw_at_output_exception&)
{
return 0;
}
return split_edges(edges_to_split, tm_S, vpm_S, tm_T, vpm_T, gt, is_source_mesh_fixed);
#ifdef CGAL_PMP_SNAPPING_PRINT_RUNTIME
timer.stop();
std::cout << "time for find split edges (sequential): " << timer.time() << std::endl;
#endif
return split_edges(edges_to_split, tm_S, vpm_S, tm_T, vpm_T, gt, visitor, is_source_mesh_fixed);
}
}
@ -999,15 +1087,28 @@ std::size_t snap_non_conformal(HalfedgeRange& halfedge_range_A,
internal_np::face_patch_t, NamedParameters_B,
Constant_property_map<face_descriptor, std::size_t> /*default*/ >::type Face_patch_map_B;
typedef typename internal_np::Lookup_named_param_def <
internal_np::visitor_t,
NamedParameters_A,
internal::Snapping_default_visitor<TriangleMesh> // default
>::reference Visitor;
using CGAL::parameters::choose_parameter;
using CGAL::parameters::is_default_parameter;
using CGAL::parameters::get_parameter;
using CGAL::parameters::get_parameter_reference;
const bool is_same_mesh = (&tm_A == &tm_B);
const bool simplify_first_mesh = choose_parameter(get_parameter(np_A, internal_np::do_simplify_border), false);
const bool simplify_second_mesh = choose_parameter(get_parameter(np_B, internal_np::do_simplify_border), false);
const bool is_second_mesh_fixed = choose_parameter(get_parameter(np_B, internal_np::do_lock_mesh), false);
internal::Snapping_default_visitor<TriangleMesh> default_visitor;
Visitor& visitor = choose_parameter(get_parameter_reference(np_A, internal_np::visitor), default_visitor);
if(visitor.stop())
return 0;
// vertex-vertex and vertex-edge snapping is only considered within compatible patches
Face_patch_map_A face_patch_map_A = choose_parameter(get_parameter(np_A, internal_np::face_patch),
Constant_property_map<face_descriptor, std::size_t>(-1));
@ -1053,6 +1154,9 @@ std::size_t snap_non_conformal(HalfedgeRange& halfedge_range_A,
/// #1 and #1bis (Simplification of borders)
//////////////////////////////////////////////////////////////////////////////////////////////////
if(visitor.stop())
return 0;
#ifdef CGAL_PMP_SNAP_DEBUG
std::cout << "Simplify ranges (" << simplify_first_mesh << " " << simplify_second_mesh << ")..." << std::endl;
#endif
@ -1079,6 +1183,8 @@ std::size_t snap_non_conformal(HalfedgeRange& halfedge_range_A,
/// #2 (Two-way vertex-vertex snapping)
//////////////////////////////////////////////////////////////////////////////////////////////////
std::size_t snapped_n = 0;
// We keep in memory pairs of source/target edges that are stitchable after vertex-vertex snapping
// --> these halfedges should not be considered as targets in non-conformal snapping
// Similarly, matching vertices whose incident edges have matching directions are also locked
@ -1087,8 +1193,6 @@ std::size_t snap_non_conformal(HalfedgeRange& halfedge_range_A,
Locked_halfedges locked_halfedges_A = get(Halfedge_bool_tag(), tm_A);
Locked_halfedges locked_halfedges_B = get(Halfedge_bool_tag(), tm_B);
std::size_t snapped_n = 0;
std::vector<std::pair<vertex_descriptor, vertex_descriptor> > locked_vertices;
std::vector<halfedge_descriptor> locked_halfedges_A_vector, locked_halfedges_B_vector;
@ -1130,17 +1234,24 @@ std::size_t snap_non_conformal(HalfedgeRange& halfedge_range_A,
#endif
//////////////////////////////////////////////////////////////////////////////////////////////////
/// #3 (Two one-way vertex-edge snapping)
/// #3 (Two one-way vertex-edge snappings)
//////////////////////////////////////////////////////////////////////////////////////////////////
if(visitor.stop())
return snapped_n;
#ifdef CGAL_PMP_SNAP_DEBUG
std::cout << " ///////////// Two one-way vertex-edge snapping (A --> B) " << std::endl;
#endif
visitor.start_first_vertex_edge_phase();
snapped_n += internal::snap_non_conformal_one_way<ConcurrencyTag>(
halfedge_range_A, tm_A, tolerance_map_A, vertex_patch_map_A, locked_vertices_A,
halfedge_range_B, tm_B, face_patch_map_B, locked_halfedges_B,
false /*source is never fixed*/, np_A, np_B);
false /*source is never fixed*/, visitor, np_A, np_B);
visitor.end_first_vertex_edge_phase();
#ifdef CGAL_PMP_SNAP_DEBUG_OUTPUT
std::ofstream("results/vertex_edge_A.off") << std::setprecision(17) << tm_A;
@ -1149,14 +1260,21 @@ std::size_t snap_non_conformal(HalfedgeRange& halfedge_range_A,
if(!is_self_snapping)
{
if(visitor.stop())
return snapped_n;
#ifdef CGAL_PMP_SNAP_DEBUG
std::cout << " ///////////// Two one-way vertex-edge snapping (B --> A) " << std::endl;
std::cout << " ///////////// Two one-way vertex-edge snapping (B --> A) " << std::endl;
#endif
visitor.start_second_vertex_edge_phase();
snapped_n += internal::snap_non_conformal_one_way<ConcurrencyTag>(
halfedge_range_B, tm_B, tolerance_map_B, vertex_patch_map_B, locked_vertices_B,
halfedge_range_A, tm_A, face_patch_map_A, locked_halfedges_A,
is_second_mesh_fixed, np_B, np_A);
is_second_mesh_fixed, visitor, np_B, np_A);
visitor.end_second_vertex_edge_phase();
}
return snapped_n;