Merge remote-tracking branch 'cgal/master' into T2-Document_projection_traits_3-maxGimeno

This commit is contained in:
Maxime Gimeno
2021-08-09 09:14:23 +02:00
293 changed files with 29734 additions and 2178 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,588 @@
// Copyright (c) 2019 GeometryFactory SARL (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, Martin Skrodzki, Dmitry Anisimov
#ifndef CGAL_PMP_INTERNAL_AABB_TRAVERSAL_TRAITS_WITH_HAUSDORFF_DISTANCE
#define CGAL_PMP_INTERNAL_AABB_TRAVERSAL_TRAITS_WITH_HAUSDORFF_DISTANCE
#include <CGAL/license/Polygon_mesh_processing/distance.h>
// STL includes.
#include <vector>
#include <iostream>
// CGAL includes.
#include <CGAL/AABB_tree.h>
#include <CGAL/AABB_traits.h>
#include <CGAL/AABB_face_graph_triangle_primitive.h>
namespace CGAL {
// Bounds.
template< class Kernel,
class Face_handle_1,
class Face_handle_2>
struct Bounds {
using FT = typename Kernel::FT;
FT m_infinity_value = -FT(1);
Bounds(const FT infinity_value) :
m_infinity_value(infinity_value) { }
FT lower = m_infinity_value;
FT upper = m_infinity_value;
// TODO: update
Face_handle_2 tm2_lface = Face_handle_2();
Face_handle_2 tm2_uface = Face_handle_2();
std::pair<Face_handle_1, Face_handle_2> lpair = default_face_pair();
std::pair<Face_handle_1, Face_handle_2> upair = default_face_pair();
const std::pair<Face_handle_1, Face_handle_2> default_face_pair() const {
return std::make_pair(Face_handle_1(), Face_handle_2());
}
};
// Candidate triangle.
template< class Kernel,
class Face_handle_1,
class Face_handle_2>
struct Candidate_triangle {
using FT = typename Kernel::FT;
using Triangle_3 = typename Kernel::Triangle_3;
using Candidate_bounds = Bounds<Kernel, Face_handle_1, Face_handle_2>;
Candidate_triangle(
const Triangle_3& triangle, const Candidate_bounds& bounds, const Face_handle_1& fh) :
triangle(triangle), bounds(bounds), tm1_face(fh)
{ }
Triangle_3 triangle;
Candidate_bounds bounds;
Face_handle_1 tm1_face;
// TODO: no need to use bounds.lower?
bool operator>(const Candidate_triangle& other) const {
CGAL_assertion(bounds.upper >= FT(0));
CGAL_assertion(other.bounds.upper >= FT(0));
return bounds.upper < other.bounds.upper;
}
bool operator<(const Candidate_triangle& other) const {
CGAL_assertion(bounds.upper >= FT(0));
CGAL_assertion(other.bounds.upper >= FT(0));
return bounds.upper > other.bounds.upper;
}
};
// Hausdorff primitive traits on TM2.
template< class AABBTraits,
class Query,
class Kernel,
class TriangleMesh1,
class TriangleMesh2,
class VPM2>
class Hausdorff_primitive_traits_tm2
{
using FT = typename Kernel::FT;
using Point_3 = typename Kernel::Point_3;
using Vector_3 = typename Kernel::Vector_3;
using Triangle_3 = typename Kernel::Triangle_3;
using Project_point_3 = typename Kernel::Construct_projected_point_3;
using Face_handle_1 = typename boost::graph_traits<TriangleMesh1>::face_descriptor;
using Face_handle_2 = typename boost::graph_traits<TriangleMesh2>::face_descriptor;
using Local_bounds = Bounds<Kernel, Face_handle_1, Face_handle_2>;
using TM2_face_to_triangle_map = Triangle_from_face_descriptor_map<TriangleMesh2, VPM2>;
public:
using Priority = FT;
Hausdorff_primitive_traits_tm2(
const AABBTraits& traits,
const TriangleMesh2& tm2, const VPM2& vpm2,
const Local_bounds& local_bounds,
const FT h_v0_lower_init,
const FT h_v1_lower_init,
const FT h_v2_lower_init) :
m_traits(traits), m_tm2(tm2), m_vpm2(vpm2),
m_face_to_triangle_map(&m_tm2, m_vpm2),
h_local_bounds(local_bounds) {
// Initialize the global and local bounds with the given values.
h_v0_lower = h_v0_lower_init;
h_v1_lower = h_v1_lower_init;
h_v2_lower = h_v2_lower_init;
}
// Explore the whole tree, i.e. always enter children if the method
// do_intersect() below determines that it is worthwhile.
bool go_further() const { return true; }
// Compute the explicit Hausdorff distance to the given primitive.
template<class Primitive>
void intersection(const Query& query, const Primitive& primitive) {
/* Have reached a single triangle, process it.
/ Determine the distance according to
/ min_{b \in primitive} ( max_{vertex in query} ( d(vertex, b) ) )
/
/ Here, we only have one triangle in B, i.e. tm2. Thus, it suffices to
/ compute the distance of the vertices of the query triangles to the
/ primitive triangle and use the maximum of the obtained distances.
*/
// The query object is a triangle from TM1, get its vertices.
const Point_3 v0 = query.vertex(0);
const Point_3 v1 = query.vertex(1);
const Point_3 v2 = query.vertex(2);
CGAL_assertion(primitive.id() != Face_handle_2());
const Triangle_3 triangle = get(m_face_to_triangle_map, primitive.id());
// Compute distances of the vertices to the primitive triangle in TM2.
const FT v0_dist = CGAL::approximate_sqrt(CGAL::squared_distance(m_project_point(triangle, v0), v0));
if (v0_dist < h_v0_lower) h_v0_lower = v0_dist; // it is () part of (11) in the paper
const FT v1_dist = CGAL::approximate_sqrt(CGAL::squared_distance(m_project_point(triangle, v1), v1));
if (v1_dist < h_v1_lower) h_v1_lower = v1_dist; // it is () part of (11) in the paper
const FT v2_dist = CGAL::approximate_sqrt(CGAL::squared_distance(m_project_point(triangle, v2), v2));
if (v2_dist < h_v2_lower) h_v2_lower = v2_dist; // it is () part of (11) in the paper
// Get the distance as maximizers over all vertices.
const FT distance_lower = (CGAL::max)((CGAL::max)(h_v0_lower, h_v1_lower), h_v2_lower); // it is (11) in the paper
const FT distance_upper = (CGAL::max)((CGAL::max)(v0_dist, v1_dist), v2_dist); // it is () part of (10) in the paper
CGAL_assertion(distance_lower >= FT(0));
CGAL_assertion(distance_upper >= FT(0));
CGAL_assertion(distance_upper >= distance_lower);
// Since we are at the level of a single triangle in TM2, distance_upper is
// actually the correct Hausdorff distance from the query triangle in
// TM1 to the primitive triangle in TM2.
CGAL_assertion(h_local_bounds.lower >= FT(0));
if (distance_lower < h_local_bounds.lower) {
h_local_bounds.lower = distance_lower;
h_local_bounds.tm2_lface = primitive.id();
}
CGAL_assertion(h_local_bounds.upper >= FT(0));
if (distance_upper < h_local_bounds.upper) { // it is (10) in the paper
h_local_bounds.upper = distance_upper;
h_local_bounds.tm2_uface = primitive.id();
}
CGAL_assertion(h_local_bounds.upper >= h_local_bounds.lower);
}
// Determine whether child nodes will still contribute to a smaller
// Hausdorff distance and thus have to be entered.
template<class Node>
std::pair<bool, Priority>
do_intersect_with_priority(const Query& query, const Node& node) const {
// Get the bounding box of the nodes.
const auto bbox = node.bbox();
// Get the vertices of the query triangle.
const Point_3 v0 = query.vertex(0);
const Point_3 v1 = query.vertex(1);
const Point_3 v2 = query.vertex(2);
// Find the axis aligned bbox of the triangle.
const Point_3 tri_min = Point_3(
(CGAL::min)((CGAL::min)(v0.x(), v1.x()), v2.x()),
(CGAL::min)((CGAL::min)(v0.y(), v1.y()), v2.y()),
(CGAL::min)((CGAL::min)(v0.z(), v1.z()), v2.z()));
const Point_3 tri_max = Point_3(
(CGAL::max)((CGAL::max)(v0.x(), v1.x()), v2.x()),
(CGAL::max)((CGAL::max)(v0.y(), v1.y()), v2.y()),
(CGAL::max)((CGAL::max)(v0.z(), v1.z()), v2.z()));
// Compute distance of the bounding boxes.
// Distance along the x-axis.
FT dist_x = FT(0);
if (tri_max.x() < bbox.min(0)) {
dist_x = bbox.min(0) - tri_max.x();
} else if (bbox.max(0) < tri_min.x()) {
dist_x = tri_min.x() - bbox.max(0);
}
// Distance along the y-axis.
FT dist_y = FT(0);
if (tri_max.y() < bbox.min(1)) {
dist_y = bbox.min(1) - tri_max.y();
} else if (bbox.max(1) < tri_min.y()) {
dist_y = tri_min.y() - bbox.max(1);
}
// Distance along the z-axis.
FT dist_z = FT(0);
if (tri_max.z() < bbox.min(2)) {
dist_z = bbox.min(2) - tri_max.z();
} else if (bbox.max(2) < tri_min.z()) {
dist_z = tri_min.z() - bbox.max(2);
}
// Lower bound on the distance between the two bounding boxes is given
// as the length of the diagonal of the bounding box between them.
const FT dist = CGAL::approximate_sqrt(Vector_3(dist_x, dist_y, dist_z).squared_length());
// See Algorithm 2.
// Check whether investigating the bbox can still lower the Hausdorff
// distance and improve the current global bound. If so, enter the box.
CGAL_assertion(h_local_bounds.lower >= FT(0));
if (dist <= h_local_bounds.lower) {
return std::make_pair(true , -dist);
} else {
return std::make_pair(false, FT(0));
}
}
template<class Node>
bool do_intersect(const Query& query, const Node& node) const {
return this->do_intersect_with_priority(query, node).first;
}
// Return the local Hausdorff bounds computed for the passed query triangle.
Local_bounds get_local_bounds() const {
return h_local_bounds;
}
template<class PrimitiveConstIterator>
void traverse_group(const Query& query, PrimitiveConstIterator group_begin, PrimitiveConstIterator group_end) {
for (PrimitiveConstIterator it = group_begin; it != group_end; ++it) {
this->intersection(query, *it);
}
}
private:
// Input data.
const AABBTraits& m_traits;
const TriangleMesh2& m_tm2;
const VPM2& m_vpm2;
const TM2_face_to_triangle_map m_face_to_triangle_map;
// Local Hausdorff bounds for the query triangle.
Local_bounds h_local_bounds;
FT h_v0_lower, h_v1_lower, h_v2_lower;
Project_point_3 m_project_point;
};
// Hausdorff primitive traits on TM1.
template< class AABBTraits,
class Query,
class Kernel,
class TriangleMesh1,
class TriangleMesh2,
class VPM1,
class VPM2>
class Hausdorff_primitive_traits_tm1
{
using FT = typename Kernel::FT;
using Point_3 = typename Kernel::Point_3;
using Vector_3 = typename Kernel::Vector_3;
using Triangle_3 = typename Kernel::Triangle_3;
using TM2_primitive = AABB_face_graph_triangle_primitive<TriangleMesh2, VPM2>;
using TM2_traits = AABB_traits<Kernel, TM2_primitive>;
using TM2_tree = AABB_tree<TM2_traits>;
using TM2_hd_traits = Hausdorff_primitive_traits_tm2<TM2_traits, Triangle_3, Kernel, TriangleMesh1, TriangleMesh2, VPM2>;
using TM1_face_to_triangle_map = Triangle_from_face_descriptor_map<TriangleMesh1, VPM1>;
using Face_handle_1 = typename boost::graph_traits<TriangleMesh1>::face_descriptor;
using Face_handle_2 = typename boost::graph_traits<TriangleMesh2>::face_descriptor;
using Global_bounds = Bounds<Kernel, Face_handle_1, Face_handle_2>;
using Candidate = Candidate_triangle<Kernel, Face_handle_1, Face_handle_2>;
using Heap_type = std::priority_queue<Candidate>;
public:
using Priority = FT;
Hausdorff_primitive_traits_tm1(
const AABBTraits& traits, const TM2_tree& tree,
const TriangleMesh1& tm1, const TriangleMesh2& tm2,
const VPM1& vpm1, const VPM2& vpm2,
const FT error_bound,
const FT infinity_value,
const FT initial_bound,
const FT distance_bound) :
m_traits(traits),
m_tm1(tm1), m_tm2(tm2),
m_vpm1(vpm1), m_vpm2(vpm2),
m_tm2_tree(tree),
m_face_to_triangle_map(&m_tm1, m_vpm1),
m_error_bound(error_bound),
m_infinity_value(infinity_value),
m_initial_bound(initial_bound),
m_distance_bound(distance_bound),
h_global_bounds(m_infinity_value),
m_early_quit(false) {
CGAL_precondition(m_error_bound >= FT(0));
CGAL_precondition(m_infinity_value >= FT(0));
CGAL_precondition(m_initial_bound >= m_error_bound);
// Initialize the global bounds with 0, they will only grow.
// If we leave zero here, then we are very slow even for big input error bounds!
// Instead, we can use m_error_bound as our initial guess to filter out all pairs,
// which are already within this bound. It makes the code faster for close meshes.
// We also use initial_lower_bound here to accelerate the symmetric distance computation.
h_global_bounds.lower = m_initial_bound; // = FT(0);
h_global_bounds.upper = m_initial_bound; // = FT(0);
}
// Explore the whole tree, i.e. always enter children if the methods
// do_intersect() below determine that it is worthwhile.
bool go_further() const {
return !m_early_quit;
}
// Compute the explicit Hausdorff distance to the given primitive.
template<class Primitive>
void intersection(const Query&, const Primitive& primitive) {
if (m_early_quit) return;
// Set initial tight bounds.
CGAL_assertion(primitive.id() != Face_handle_1());
std::pair<Face_handle_1, Face_handle_2> fpair;
const FT max_dist = get_maximum_distance(primitive.id(), fpair);
CGAL_assertion(max_dist >= FT(0));
CGAL_assertion(fpair.first == primitive.id());
Bounds<Kernel, Face_handle_1, Face_handle_2> initial_bounds(m_infinity_value);
initial_bounds.lower = max_dist + m_error_bound;
initial_bounds.upper = max_dist + m_error_bound;
initial_bounds.tm2_lface = fpair.second;
initial_bounds.tm2_uface = fpair.second;
// Call Culling on B with the single triangle found.
TM2_hd_traits traversal_traits_tm2(
m_tm2_tree.traits(), m_tm2, m_vpm2,
initial_bounds, // tighter bounds, in the paper, they start from infinity, see below
// Bounds<Kernel, Face_handle_1, Face_handle_2>(m_infinity_value), // starting from infinity
m_infinity_value,
m_infinity_value,
m_infinity_value);
const Triangle_3 triangle = get(m_face_to_triangle_map, fpair.first);
m_tm2_tree.traversal_with_priority(triangle, traversal_traits_tm2);
// Update global Hausdorff bounds according to the obtained local bounds.
const auto local_bounds = traversal_traits_tm2.get_local_bounds();
CGAL_assertion(local_bounds.lower >= FT(0));
CGAL_assertion(local_bounds.upper >= FT(0));
CGAL_assertion(local_bounds.upper >= local_bounds.lower);
CGAL_assertion(local_bounds.lpair == initial_bounds.default_face_pair());
CGAL_assertion(local_bounds.upair == initial_bounds.default_face_pair());
CGAL_assertion(h_global_bounds.lower >= FT(0));
if (local_bounds.lower > h_global_bounds.lower) { // it is (6) in the paper, see also Algorithm 1
h_global_bounds.lower = local_bounds.lower;
h_global_bounds.lpair.first = fpair.first;
h_global_bounds.lpair.second = local_bounds.tm2_lface;
}
CGAL_assertion(h_global_bounds.upper >= FT(0));
if (local_bounds.upper > h_global_bounds.upper) { // it is (6) in the paper, see also Algorithm 1
h_global_bounds.upper = local_bounds.upper;
h_global_bounds.upair.first = fpair.first;
h_global_bounds.upair.second = local_bounds.tm2_uface;
}
CGAL_assertion(h_global_bounds.upper >= h_global_bounds.lower);
// Store the triangle given as primitive here as candidate triangle
// together with the local bounds it obtained to send it to subdivision later.
m_candidiate_triangles.push(Candidate(triangle, local_bounds, fpair.first));
}
// Determine whether child nodes will still contribute to a larger
// Hausdorff distance and thus have to be entered.
template<class Node>
std::pair<bool, Priority>
do_intersect_with_priority(const Query&, const Node& node) {
// Check if we can stop already here. Since our bounds only grow, in case, we are
// above the user-defined max distance bound, we return. This way, the user can
// early detect that he is behind his thresholds.
if (m_distance_bound >= FT(0) && !m_early_quit) {
CGAL_assertion(h_global_bounds.lower >= FT(0));
CGAL_assertion(h_global_bounds.upper >= FT(0));
CGAL_assertion(h_global_bounds.upper >= h_global_bounds.lower);
const FT hdist = (h_global_bounds.lower + h_global_bounds.upper) / FT(2);
m_early_quit = (hdist >= m_distance_bound);
// std::cout << "- hdist: " << hdist << std::endl;
// std::cout << "- early quit: " << m_early_quit << std::endl;
}
if (m_early_quit) return std::make_pair(false, FT(0));
// Have reached a node, determine whether or not to enter it.
// Get the bounding box of the nodes.
const auto bbox = node.bbox();
// Compute its center.
const Point_3 center = Point_3(
(bbox.min(0) + bbox.max(0)) / FT(2),
(bbox.min(1) + bbox.max(1)) / FT(2),
(bbox.min(2) + bbox.max(2)) / FT(2));
// Find the point from TM2 closest to the center.
const Point_3 closest = m_tm2_tree.closest_point(center);
// Compute the difference vector between the bbox center and the closest point in tm2.
Vector_3 difference = Vector_3(closest, center);
// Shift the vector to be the difference between the farthest corner
// of the bounding box away from the closest point on TM2.
FT diff_x = (bbox.max(0) - bbox.min(0)) / FT(2);
if (difference.x() < 0) diff_x = diff_x * -FT(1);
FT diff_y = (bbox.max(1) - bbox.min(1)) / FT(2);
if (difference.y() < 0) diff_y = diff_y * -FT(1);
FT diff_z = (bbox.max(2) - bbox.min(2)) / FT(2);
if (difference.z() < 0) diff_z = diff_z * -FT(1);
difference = difference + Vector_3(diff_x, diff_y, diff_z); // it is (9) in the paper
// Compute distance from the farthest corner of the bbox to the closest point in TM2.
const FT dist = CGAL::approximate_sqrt(difference.squared_length());
// See Algorithm 1 here.
// If the distance is larger than the global lower bound, enter the node, i.e. return true.
CGAL_assertion(h_global_bounds.lower >= FT(0));
if (dist > h_global_bounds.lower) {
return std::make_pair(true , +dist);
} else {
return std::make_pair(false, FT(0));
}
}
template<class Node>
bool do_intersect(const Query& query, const Node& node) {
return this->do_intersect_with_priority(query, node).first;
}
template<class PrimitiveConstIterator>
void traverse_group(const Query& query, PrimitiveConstIterator group_begin, PrimitiveConstIterator group_end) {
CGAL_assertion_msg(false, "ERROR: we should not call the group traversal on TM1!");
for (PrimitiveConstIterator it = group_begin; it != group_end; ++it) {
this->intersection(query, *it);
}
}
bool early_quit() const {
return m_early_quit;
}
// Return those triangles from TM1, which are candidates for including a
// point realizing the Hausdorff distance.
Heap_type& get_candidate_triangles() {
return m_candidiate_triangles;
}
// Return the global Hausdorff bounds computed for the passed query triangle.
Global_bounds get_global_bounds() {
CGAL_assertion(h_global_bounds.lower >= FT(0));
CGAL_assertion(h_global_bounds.upper >= FT(0));
CGAL_assertion(h_global_bounds.upper >= h_global_bounds.lower);
update_global_bounds();
return h_global_bounds;
}
// Here, we return the maximum distance from one of the face corners
// to the second mesh. We also return a pair of realizing this distance faces.
FT get_maximum_distance(
const Face_handle_1 tm1_lface,
std::pair<Face_handle_1, Face_handle_2>& fpair) const {
const auto triangle = get(m_face_to_triangle_map, tm1_lface);
const Point_3 v0 = triangle.vertex(0);
const Point_3 v1 = triangle.vertex(1);
const Point_3 v2 = triangle.vertex(2);
const auto pair0 = m_tm2_tree.closest_point_and_primitive(v0);
const auto pair1 = m_tm2_tree.closest_point_and_primitive(v1);
const auto pair2 = m_tm2_tree.closest_point_and_primitive(v2);
const auto sq_dist0 = std::make_pair(
CGAL::squared_distance(v0, pair0.first), pair0.second);
const auto sq_dist1 = std::make_pair(
CGAL::squared_distance(v1, pair1.first), pair1.second);
const auto sq_dist2 = std::make_pair(
CGAL::squared_distance(v2, pair2.first), pair2.second);
const auto mdist1 = (sq_dist0.first > sq_dist1.first) ? sq_dist0 : sq_dist1;
const auto mdist2 = (mdist1.first > sq_dist2.first) ? mdist1 : sq_dist2;
Face_handle_2 tm2_uface = mdist2.second;
fpair = std::make_pair(tm1_lface, tm2_uface);
return CGAL::approximate_sqrt(mdist2.first);
}
private:
// Input data.
const AABBTraits& m_traits;
const TriangleMesh1& m_tm1;
const TriangleMesh2& m_tm2;
const VPM1& m_vpm1;
const VPM2& m_vpm2;
const TM2_tree& m_tm2_tree;
const TM1_face_to_triangle_map m_face_to_triangle_map;
// Internal bounds and values.
const FT m_error_bound;
const FT m_infinity_value;
const FT m_initial_bound;
const FT m_distance_bound;
Global_bounds h_global_bounds;
bool m_early_quit;
// All candidate triangles.
Heap_type m_candidiate_triangles;
// In case, we did not enter any loop, we set the realizing triangles here.
void update_global_bounds() {
if (m_candidiate_triangles.size() > 0) {
const auto top = m_candidiate_triangles.top();
if (h_global_bounds.lpair.first == Face_handle_1())
h_global_bounds.lpair.first = top.tm1_face;
if (h_global_bounds.lpair.second == Face_handle_2())
h_global_bounds.lpair.second = top.bounds.tm2_lface;
if (h_global_bounds.upair.first == Face_handle_1())
h_global_bounds.upair.first = top.tm1_face;
if (h_global_bounds.upair.second == Face_handle_2())
h_global_bounds.upair.second = top.bounds.tm2_uface;
} else {
std::pair<Face_handle_1, Face_handle_2> fpair;
get_maximum_distance(*(faces(m_tm1).begin()), fpair);
CGAL_assertion(fpair.first == *(faces(m_tm1).begin()));
if (h_global_bounds.lpair.first == Face_handle_1())
h_global_bounds.lpair.first = fpair.first;
if (h_global_bounds.lpair.second == Face_handle_2())
h_global_bounds.lpair.second = fpair.second;
if (h_global_bounds.upair.first == Face_handle_1())
h_global_bounds.upair.first = fpair.first;
if (h_global_bounds.upair.second == Face_handle_2())
h_global_bounds.upair.second = fpair.second;
}
}
};
}
#endif // CGAL_PMP_INTERNAL_AABB_TRAVERSAL_TRAITS_WITH_HAUSDORFF_DISTANCE
@@ -28,10 +28,13 @@
#include <CGAL/Lazy.h> // needed for CGAL::exact(FT)/CGAL::exact(Lazy_exact_nt<T>)
#include <boost/container/small_vector.hpp>
#include <boost/unordered_set.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/dynamic_bitset.hpp>
#include <utility>
#include <algorithm>
#ifdef DOXYGEN_RUNNING
#define CGAL_PMP_NP_TEMPLATE_PARAMETERS NamedParameters
@@ -50,6 +53,14 @@ public:
namespace Polygon_mesh_processing {
namespace internal {
inline void rearrange_face_ids(boost::container::small_vector<std::size_t, 4>& ids)
{
auto min_elem = std::min_element(ids.begin(), ids.end());
std::rotate(ids.begin(), min_elem, ids.end());
}
}//namespace internal
/**
* \ingroup measure_grp
* computes the length of an edge of a given polygon mesh.
@@ -820,6 +831,192 @@ centroid(const TriangleMesh& tmesh)
return centroid(tmesh, CGAL::Polygon_mesh_processing::parameters::all_default());
}
/**
* \ingroup measure_grp
* identifies faces only present in `m1` and `m2` as well as the faces present
* in both polygon meshes. Two faces are matching if they have the same
* orientation and the same points.
*
* @tparam PolygonMesh1 a model of `HalfedgeListGraph` and `FaceListGraph`
* @tparam PolygonMesh2 a model of `HalfedgeListGraph` and `FaceListGraph`
* @tparam FaceOutputIterator1 model of `OutputIterator`
holding `boost::graph_traits<PolygonMesh1>::%face_descriptor`.
* @tparam FaceOutputIterator2 model of `OutputIterator`
holding `boost::graph_traits<PolygonMesh2>::%face_descriptor`.
* @tparam FacePairOutputIterator model of `OutputIterator`
holding `std::pair<boost::graph_traits<PolygonMesh1>::%face_descriptor,
boost::graph_traits<PolygonMesh2>::%face_descriptor`.
*
* @tparam NamedParameters1 a sequence of \ref bgl_namedparameters "Named Parameters"
* @tparam NamedParameters2 a sequence of \ref bgl_namedparameters "Named Parameters"
*
* @param m1 the first `PolygonMesh`
* @param m2 the second `PolygonMesh`
* @param common output iterator collecting the faces that are common to both meshes.
* @param m1_only output iterator collecting the faces that are only in `m1`
* @param m2_only output iterator collecting the faces that are only in `m2`
* @param np1 an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below
* @param np2 an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below
*
* \cgalNamedParamsBegin
* \cgalParamNBegin{vertex_point_map}
* \cgalParamDescription{a property map associating points to the vertices of `m1`}
* \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits<PolygonMesh1>::%vertex_descriptor`
* as key type and `%Point_3` as value type. `%Point_3` must be `LessThanComparable`.}
* \cgalParamDefault{`boost::get(CGAL::vertex_point, m1)`}
* \cgalParamExtra{The same holds for `m2` and `PolygonMesh2` and the point type must be the same for both meshes.}
* \cgalParamNEnd
*
* \cgalParamNBegin{vertex_index_map}
* \cgalParamDescription{a property map associating to each vertex of `m1` a unique index between `0` and `num_vertices(m1) - 1`, and similarly for `m2`.}
* \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits<Graph>::%vertex_descriptor`
* as key type and `std::size_t` as value type}
* \cgalParamDefault{an automatically indexed internal map}
* \cgalParamExtra{If this parameter is not passed, internal machinery will create and initialize
* a face index property map, either using the internal property map if it exists
* or using an external map. The latter might result in - slightly - worsened performance
* in case of non-constant complexity for index access. The same holds for `m2` and `PolygonMesh2`.}
* \cgalParamNEnd
* \cgalNamedParamsEnd
*
*/
template< typename PolygonMesh1,
typename PolygonMesh2,
typename FacePairOutputIterator,
typename FaceOutputIterator1,
typename FaceOutputIterator2,
typename NamedParameters1,
typename NamedParameters2 >
void match_faces(const PolygonMesh1& m1, const PolygonMesh2& m2,
FacePairOutputIterator common, FaceOutputIterator1 m1_only, FaceOutputIterator2 m2_only,
const NamedParameters1& np1, const NamedParameters2& np2)
{
typedef typename GetVertexPointMap<PolygonMesh1, NamedParameters1>::const_type VPMap1;
typedef typename GetVertexPointMap<PolygonMesh2, NamedParameters2>::const_type VPMap2;
typedef typename GetInitializedVertexIndexMap<PolygonMesh1, NamedParameters1>::const_type VIMap1;
typedef typename GetInitializedVertexIndexMap<PolygonMesh2, NamedParameters2>::const_type VIMap2;
typedef typename boost::property_traits<VPMap2>::value_type Point_3;
typedef typename boost::graph_traits<PolygonMesh1>::face_descriptor face_descriptor_1;
using parameters::choose_parameter;
using parameters::get_parameter;
const VPMap1 vpm1 = choose_parameter(get_parameter(np1, internal_np::vertex_point),
get_const_property_map(vertex_point, m1));
const VPMap2 vpm2 = choose_parameter(get_parameter(np2, internal_np::vertex_point),
get_const_property_map(vertex_point, m2));
CGAL_static_assertion_msg((boost::is_same<typename boost::property_traits<VPMap1>::value_type,
typename boost::property_traits<VPMap2>::value_type>::value),
"Both vertex point maps must have the same point type.");
const VIMap1 vim1 = get_initialized_vertex_index_map(m1, np1);
const VIMap2 vim2 = get_initialized_vertex_index_map(m2, np2);
std::map<Point_3, std::size_t> point_id_map;
std::vector<std::size_t> m1_vertex_id(num_vertices(m1), -1);
std::vector<std::size_t> m2_vertex_id(num_vertices(m2), -1);
boost::dynamic_bitset<> shared_vertices(m1_vertex_id.size() + m2_vertex_id.size());
//iterate both meshes to set ids of all points, and set vertex/point_id maps.
std::size_t id = 0;
for(auto v : vertices(m1))
{
const typename boost::property_traits<VPMap1>::reference p = get(vpm1, v);
auto res = point_id_map.emplace(p, id);
if(res.second)
++id;
m1_vertex_id[get(vim1, v)] = res.first->second;
}
for(auto v : vertices(m2))
{
const typename boost::property_traits<VPMap2>::reference p = get(vpm2, v);
auto res = point_id_map.emplace(p, id);
if(res.second)
++id;
else
shared_vertices.set(res.first->second);
m2_vertex_id[get(vim2, v)] = res.first->second;
}
//fill a set with the "faces point-ids" of m1 and then iterate faces of m2 to compare.
std::map<boost::container::small_vector<std::size_t, 4>, face_descriptor_1> m1_faces_map;
for(auto f : faces(m1))
{
bool all_shared = true;
boost::container::small_vector<std::size_t, 4> ids;
for(auto v : CGAL::vertices_around_face(halfedge(f, m1), m1))
{
std::size_t vid = m1_vertex_id[get(vim1, v)];
ids.push_back(vid);
if(!shared_vertices.test(vid))
{
all_shared = false;
break;
}
}
if(all_shared)
{
internal::rearrange_face_ids(ids);
m1_faces_map.emplace(ids, f);
}
else
*m1_only++ = f;
}
for(auto f : faces(m2))
{
boost::container::small_vector<std::size_t, 4> ids;
bool all_shared = true;
for(auto v : CGAL::vertices_around_face(halfedge(f, m2), m2))
{
std::size_t vid = m2_vertex_id[get(vim2, v)];
ids.push_back(vid);
if(!shared_vertices.test(vid))
{
all_shared = false;
break;
}
}
if(all_shared)
{
internal::rearrange_face_ids(ids);
auto it = m1_faces_map.find(ids);
if(it != m1_faces_map.end())
{
*common++ = std::make_pair(it->second, f);
m1_faces_map.erase(it);
}
else
{
*m2_only++ = f;
}
}
else
*m2_only++ = f;
}
//all shared faces have been removed from the map, so all that remains must go in m1_only
for(const auto& it : m1_faces_map)
{
*m1_only++ = it.second;
}
}
template<typename PolygonMesh1, typename PolygonMesh2, typename FacePairOutputIterator, typename FaceOutputIterator1, typename FaceOutputIterator2, typename NamedParameters>
void match_faces(const PolygonMesh1& m1, const PolygonMesh2& m2,
FacePairOutputIterator common, FaceOutputIterator1 m1_only, FaceOutputIterator2 m2_only,
const NamedParameters& np)
{
match_faces(m1, m2, common, m1_only, m2_only, np, parameters::all_default());
}
template<typename PolygonMesh1, typename PolygonMesh2, typename FacePairOutputIterator, typename FaceOutputIterator1, typename FaceOutputIterator2>
void match_faces(const PolygonMesh1& m1, const PolygonMesh2& m2,
FacePairOutputIterator common, FaceOutputIterator1 m1_only, FaceOutputIterator2 m2_only)
{
match_faces(m1, m2, common, m1_only, m2_only, parameters::all_default(), parameters::all_default());
}
} // namespace Polygon_mesh_processing
} // namespace CGAL
@@ -47,11 +47,11 @@
// }
//
// The code below uses the version of
// https://github.com/wangbolun300/fast-envelope avaiable on 7th of October 2020
// https://github.com/wangbolun300/fast-envelope available on 7th of October 2020.
//
// The code below only use the high level algorithms of checking that a query
// is covered by a set of prisms, where each prism is an offset for an input triangle.
// That is, we do not use indirect predicates
// The code below only uses the high-level algorithms checking that a query
// is covered by a set of prisms, where each prism is the offset of an input triangle.
// That is, we do not use indirect predicates.
#ifndef CGAL_POLYGON_MESH_PROCESSING_POLYHEDRAL_ENVELOPE_H
#define CGAL_POLYGON_MESH_PROCESSING_POLYHEDRAL_ENVELOPE_H