dust3d/thirdparty/cgal/CGAL-5.1/include/CGAL/internal/hypothesis.h

1153 lines
67 KiB
C++

// Copyright (c) 2018 Liangliang Nan. All rights reserved.
//
// This file is part of CGAL (www.cgal.org)
//
// $URL: https://github.com/CGAL/cgal/blob/v5.1/Polygonal_surface_reconstruction/include/CGAL/internal/hypothesis.h $
// $Id: hypothesis.h e9d41d7 2020-04-21T10:03:00+02:00 Maxime Gimeno
// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s) : Liangliang Nan
#ifndef CGAL_POLYGONAL_SURFACE_RECONSTRUCTION_HYPOTHESIS_H
#define CGAL_POLYGONAL_SURFACE_RECONSTRUCTION_HYPOTHESIS_H
#include <CGAL/license/Polygonal_surface_reconstruction.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Projection_traits_xy_3.h>
#include <CGAL/convex_hull_2.h>
#include <CGAL/bounding_box.h>
#include <CGAL/intersections.h>
#include <CGAL/assertions.h>
#include <CGAL/internal/parameters.h>
#include <CGAL/internal/point_set_with_planes.h>
#include <set>
#include <unordered_map>
namespace CGAL {
namespace internal {
/**
* Generates candidate faces by pairwise intersecting of the supporting planes of
* the planar segments.
*/
template <typename Kernel>
class Hypothesis
{
private:
typedef typename Kernel::FT FT;
typedef typename Kernel::Point_3 Point;
typedef typename Kernel::Point_2 Point2;
typedef typename Kernel::Vector_3 Vector;
typedef typename Kernel::Line_3 Line;
typedef typename Kernel::Segment_3 Segment;
typedef typename Kernel::Plane_3 Plane;
typedef internal::Planar_segment<Kernel> Planar_segment;
typedef internal::Point_set_with_planes<Kernel> Point_set_with_planes;
typedef CGAL::Surface_mesh<Point> Polygon_mesh;
typedef typename Polygon_mesh::Face_index Face_descriptor;
typedef typename Polygon_mesh::Edge_index Edge_descriptor;
typedef typename Polygon_mesh::Vertex_index Vertex_descriptor;
typedef typename Polygon_mesh::Halfedge_index Halfedge_descriptor;
public:
Hypothesis();
~Hypothesis();
void generate(Point_set_with_planes& point_set, Polygon_mesh& candidate_faces);
/// 'Intersection' represents a set of faces intersecting at a common edge.
/// \note The faces are represented by their halfedges.
struct Intersection : public std::vector<Halfedge_descriptor> {
const Point* s;
const Point* t;
};
typedef typename std::vector<Intersection> Adjacency;
/// Extracts the adjacency of the pairwise intersection.
/// The extracted adjacency will be used to formulate the hard constraints
/// in the face selection stage.
Adjacency extract_adjacency(const Polygon_mesh& candidate_faces);
private:
// Merges near co-planar segments
void refine_planes();
// Constructs a mesh representing the bounding box of the point set
void construct_bbox_mesh(Polygon_mesh& bbox_mesh);
// Construct a mesh from the segments bounded by the bounding box mesh
void construct_proxy_mesh(const Polygon_mesh& bbox_mesh, Polygon_mesh& candidate_faces);
// Pairwise intersection
void pairwise_intersection(Polygon_mesh& candidate_faces);
// Counts the number of points that are with the dist_threshold to its supporting plane
std::size_t number_of_points_on_plane(const Planar_segment* s, const Plane* plane, FT dist_threshold);
// Merges two planar segments;
void merge(Planar_segment* s1, Planar_segment* s2);
// Pre-computes all potential intersections of plane triplets
void compute_triplet_intersections();
// Queries the intersecting point for a plane triplet
const Point* query_intersection(const Plane* plane1, const Plane* plane2, const Plane* plane3);
bool halfedge_exists(Vertex_descriptor v1, Vertex_descriptor v2, const Polygon_mesh& mesh);
// Tests if 'face' insects 'plane'
bool do_intersect(const Polygon_mesh& mesh, Face_descriptor face, const Plane* plane);
// Cuts face using the cutting_plane and returns the new faces
std::vector<Face_descriptor> cut(Face_descriptor face, const Plane* cutting_plane, Polygon_mesh& mesh);
// Collects all faces in 'mesh' that intersect 'face'. Store in std::set() so easier to erase
std::set<Face_descriptor> collect_intersecting_faces(Face_descriptor face, const Polygon_mesh& mesh);
// Represents an intersecting point at an edge
struct EdgePos {
EdgePos(Edge_descriptor e, const Point* p) : edge(e), pos(p) {}
Edge_descriptor edge;
const Point* pos;
};
// Computes the intersecting points of face and cutting_plane. The intersecting points are returned
// by 'existing_vts' (if the plane intersects the face at its vertices) and 'new_vts' (if the plane
// intersects the face at its edges).
void compute_intersections(const Polygon_mesh& mesh,
Face_descriptor face, const Plane* cutting_plane,
std::vector<Vertex_descriptor>& existing_vts,
std::vector<EdgePos>& new_vts
);
// This function will
// - split an edge denoted by 'ep'
// - assign the new edges the supporting faces
// - return the halfedge pointing to the new vertex
// Internally it uses Euler split_edge().
Halfedge_descriptor split_edge(Polygon_mesh& mesh, const EdgePos& ep, const Plane* cutting_plane);
// Clears cached intermediate results
void clear();
private:
// The input point cloud with planes
Point_set_with_planes * point_set_;
// The intersection of the planes can be unreliable when the planes are near parallel.
// Here are the tricks we use in our implementation:
// - We first test if an intersection exists for every pair of planes. We then collect
// plane triplets such that every pair in the plane triplet intersect. This is achieved
// by testing each plane against the known intersecting pairs.
// - The 3D vertices of the final faces are obtained by computing the intersections of
// the plane triplets. To cope with limited floating point precision, each vertex is
// identified by the pointers of (in an increasing order) of the three planes from
// which it is computed. By doing so, two vertices with almost identical positions can
// be distinguished. This turned out to be quite robust in handling very close and near
// parallel planes.
// The supporting planes of all planar segments and the bounding box faces
std::vector<const Plane*> supporting_planes_;
// Precomputed intersecting points of all plane triplets
std::vector<const Point*> intersecting_points_;
typedef typename std::unordered_map<const Plane*, const Point*> Plane_to_point_map;
typedef typename std::unordered_map<const Plane*, Plane_to_point_map> Two_planes_to_point_map;
typedef typename std::unordered_map<const Plane*, Two_planes_to_point_map> Planes_to_point_map;
Planes_to_point_map triplet_intersections_;
};
//////////////////////////////////////////////////////////////////////////
// implementation
template <typename Kernel>
Hypothesis<Kernel>::Hypothesis()
{
}
template <typename Kernel>
Hypothesis<Kernel>::~Hypothesis()
{
clear();
}
template <typename Kernel>
void Hypothesis<Kernel>::clear() {
for (std::size_t i = 0; i < supporting_planes_.size(); ++i)
delete supporting_planes_[i];
supporting_planes_.clear();
for (std::size_t i = 0; i < intersecting_points_.size(); ++i)
delete intersecting_points_[i];
intersecting_points_.clear();
triplet_intersections_.clear();
}
template <typename Kernel>
void Hypothesis<Kernel>::generate(Point_set_with_planes& point_set, Polygon_mesh& candidate_faces) {
point_set_ = &point_set;
refine_planes();
Polygon_mesh bbox_mesh;
construct_bbox_mesh(bbox_mesh);
construct_proxy_mesh(bbox_mesh, candidate_faces);
pairwise_intersection(candidate_faces);
}
/// \cond SKIP_IN_MANUAL
template <typename Planar_segment>
class SegmentSizeIncreasing
{
public:
SegmentSizeIncreasing() {}
bool operator()(const Planar_segment* s0, const Planar_segment* s1) const {
return s0->size() < s1->size();
}
};
template <typename Planar_segment>
class SegmentSizeDecreasing
{
public:
SegmentSizeDecreasing() {}
bool operator()(const Planar_segment* s0, const Planar_segment* s1) const {
return s0->size() > s1->size();
}
};
template <typename FT, typename Vector>
void normalize(Vector& v) {
FT s = std::sqrt(v.squared_length());
if (s > 1e-30)
s = FT(1) / s;
v *= s;
}
template <typename BBox>
typename BBox::FT bbox_radius(const BBox& box) {
typedef typename BBox::FT FT;
FT dx = box.xmax() - box.xmin();
FT dy = box.ymax() - box.ymin();
FT dz = box.zmax() - box.zmin();
return FT(0.5) * std::sqrt(dx * dx + dy * dy + dz * dz);
}
// Computes the intersection of a plane triplet
// Returns true if the intersection exists (p returns the point)
template <typename Plane, typename Point>
bool intersect_plane_triplet(const Plane* plane1, const Plane* plane2, const Plane* plane3, Point& p) {
if (plane1 == plane2 || plane1 == plane3 || plane2 == plane3)
return false;
CGAL::Object obj = CGAL::intersection(*plane1, *plane2, *plane3);
// pt is the intersection point of the 3 planes
if (const Point* pt = CGAL::object_cast<Point>(&obj)) {
p = *pt;
return true;
}
else {
// If reached here, the reason might be:
// (1) two or more are parallel;
// (2) they intersect at the same line
// We can simply ignore these cases
return false;
}
}
template <typename VT>
void sort_increasing(VT& v1, VT& v2, VT& v3) {
VT vmin = 0;
if (v1 < v2 && v1 < v3)
vmin = v1;
else if (v2 < v1 && v2 < v3)
vmin = v2;
else
vmin = v3;
VT vmid = 0;
if ((v1 > v2 && v1 < v3) || (v1 < v2 && v1 > v3))
vmid = v1;
else if ((v2 > v1 && v2 < v3) || (v2 < v1 && v2 > v3))
vmid = v2;
else
vmid = v3;
VT vmax = 0;
if (v1 > v2 && v1 > v3)
vmax = v1;
else if (v2 > v1 && v2 > v3)
vmax = v2;
else
vmax = v3;
v1 = vmin;
v2 = vmid;
v3 = vmax;
}
/// \endcond
template <typename Kernel>
std::size_t Hypothesis<Kernel>::number_of_points_on_plane(const Planar_segment* s, const Plane* plane, FT dist_threshold) {
CGAL_assertion(const_cast<Planar_segment*>(s)->point_set() == point_set_);
std::size_t count = 0;
const typename Point_set_with_planes::Point_map& points = point_set_->point_map();
for (std::size_t i = 0; i < s->size(); ++i) {
std::size_t idx = s->at(i);
const Point& p = points[idx];
FT sdist = CGAL::squared_distance(*plane, p);
FT dist = std::sqrt(sdist);
if (dist < dist_threshold)
++count;
}
return count;
}
template <typename Kernel>
void Hypothesis<Kernel>::merge(Planar_segment* s1, Planar_segment* s2) {
CGAL_assertion(const_cast<Planar_segment*>(s1)->point_set() == point_set_);
CGAL_assertion(const_cast<Planar_segment*>(s2)->point_set() == point_set_);
std::vector< Planar_segment* >& segments = point_set_->planar_segments();
std::vector<std::size_t> points_indices;
points_indices.insert(points_indices.end(), s1->begin(), s1->end());
points_indices.insert(points_indices.end(), s2->begin(), s2->end());
Planar_segment* s = new Planar_segment(point_set_);
s->insert(s->end(), points_indices.begin(), points_indices.end());
s->fit_supporting_plane();
segments.push_back(s);
typename std::vector< Planar_segment* >::iterator pos = std::find(segments.begin(), segments.end(), s1);
if (pos != segments.end()) {
Planar_segment* tmp = *pos;
const Plane* plane = tmp->supporting_plane();
segments.erase(pos);
delete tmp;
delete plane;
}
else
std::cerr << "Fatal error: should not reach here" << std::endl;
pos = std::find(segments.begin(), segments.end(), s2);
if (pos != segments.end()) {
Planar_segment* tmp = *pos;
const Plane* plane = tmp->supporting_plane();
segments.erase(pos);
delete tmp;
delete plane;
}
else
std::cerr << "Fatal error: should not reach here" << std::endl;
}
template <typename Kernel>
void Hypothesis<Kernel>::refine_planes() {
std::vector< Planar_segment* >& segments = point_set_->planar_segments();
const typename Point_set_with_planes::Point_map& points = point_set_->point_map();
FT avg_max_dist = 0;
for (std::size_t i = 0; i < segments.size(); ++i) {
Planar_segment* s = segments[i];
const Plane* plane = s->fit_supporting_plane(); // user may provide invalid plane fitting (we always fit)
FT max_dist = -(std::numeric_limits<FT>::max)();
for (std::size_t j = 0; j < s->size(); ++j) {
std::size_t idx = s->at(j);
const Point& p = points[idx];
FT sdist = CGAL::squared_distance(*plane, p);
max_dist = (std::max)(max_dist, std::sqrt(sdist));
}
avg_max_dist += max_dist;
}
avg_max_dist /= segments.size();
avg_max_dist /= FT(2.0);
FT theta = static_cast<FT>(CGAL_PI * 10.0 / FT(180.0)); // in radian
bool merged = false;
do {
merged = false;
// Segments with less points have less confidences and thus should be merged first.
// So we sort the segments according to their sizes.
std::sort(segments.begin(), segments.end(), internal::SegmentSizeIncreasing<Planar_segment>());
for (std::size_t i = 0; i < segments.size(); ++i) {
Planar_segment* s1 = segments[i];
const Plane* plane1 = s1->supporting_plane();
Vector n1 = plane1->orthogonal_vector();
internal::normalize<FT, Vector>(n1);
FT num_threshold = s1->size() / FT(5.0);
for (std::size_t j = i + 1; j < segments.size(); ++j) {
Planar_segment* s2 = segments[j];
const Plane* plane2 = s2->supporting_plane();
Vector n2 = plane2->orthogonal_vector();
internal::normalize<FT, Vector>(n2);
if (std::abs(n1 * n2) > std::cos(theta)) {
std::size_t set1on2 = number_of_points_on_plane(s1, plane2, avg_max_dist);
std::size_t set2on1 = number_of_points_on_plane(s2, plane1, avg_max_dist);
if (set1on2 > num_threshold || set2on1 > num_threshold) {
merge(s1, s2);
merged = true;
break;
}
}
}
if (merged)
break;
}
} while (merged);
std::sort(segments.begin(), segments.end(), internal::SegmentSizeDecreasing<Planar_segment>());
// Stores all the supporting planes
for (std::size_t i = 0; i < segments.size(); ++i) {
Planar_segment* s = segments[i];
const Plane* plane = s->supporting_plane();
supporting_planes_.push_back(plane);
}
}
template <typename Kernel>
void Hypothesis<Kernel>::construct_bbox_mesh(Polygon_mesh& mesh) {
const typename Point_set_with_planes::Point_map& points = point_set_->point_map();
typedef typename Kernel::Iso_cuboid_3 BBox;
const BBox& box = CGAL::bounding_box(points.begin(), points.end());
FT dx = box.xmax() - box.xmin();
FT dy = box.ymax() - box.ymin();
FT dz = box.zmax() - box.zmin();
FT radius = FT(0.5) * std::sqrt(dx * dx + dy * dy + dz * dz);
FT offset = radius * FT(0.05);
// make the box larger to ensure all points are enclosed.
FT xmin = box.xmin() - offset, xmax = box.xmax() + offset;
FT ymin = box.ymin() - offset, ymax = box.ymax() + offset;
FT zmin = box.zmin() - offset, zmax = box.zmax() + offset;
mesh.clear();
Vertex_descriptor v0 = mesh.add_vertex(Point(xmin, ymin, zmin)); // 0
Vertex_descriptor v1 = mesh.add_vertex(Point(xmax, ymin, zmin)); // 1
Vertex_descriptor v2 = mesh.add_vertex(Point(xmax, ymin, zmax)); // 2
Vertex_descriptor v3 = mesh.add_vertex(Point(xmin, ymin, zmax)); // 3
Vertex_descriptor v4 = mesh.add_vertex(Point(xmax, ymax, zmax)); // 4
Vertex_descriptor v5 = mesh.add_vertex(Point(xmax, ymax, zmin)); // 5
Vertex_descriptor v6 = mesh.add_vertex(Point(xmin, ymax, zmin)); // 6
Vertex_descriptor v7 = mesh.add_vertex(Point(xmin, ymax, zmax)); // 7
mesh.add_face(v0, v1, v2, v3);
mesh.add_face(v1, v5, v4, v2);
mesh.add_face(v1, v0, v6, v5);
mesh.add_face(v4, v5, v6, v7);
mesh.add_face(v0, v3, v7, v6);
mesh.add_face(v2, v4, v7, v3);
// The supporting plane of each face
typename Polygon_mesh::template Property_map<Face_descriptor, const Plane*> face_supporting_planes =
mesh.template add_property_map<Face_descriptor, const Plane*>("f:supp_plane").first;
// The supporting planes of each edge
typename Polygon_mesh::template Property_map<Edge_descriptor, std::set<const Plane*> > edge_supporting_planes
= mesh.template add_property_map<Edge_descriptor, std::set<const Plane*> >("e:supp_plane").first;
// The supporting planes of each vertex
typename Polygon_mesh::template Property_map<Vertex_descriptor, std::set<const Plane*> > vertex_supporting_planes
= mesh.template add_property_map<Vertex_descriptor, std::set<const Plane*> >("v:supp_plane").first;
// Assigns the original plane for each face
const typename Polygon_mesh::template Property_map<Vertex_descriptor, Point>& coords = mesh.points();
for(auto fd : mesh.faces()) {
Halfedge_descriptor h = mesh.halfedge(fd);
Vertex_descriptor va = mesh.target(h); const Point& pa = coords[va]; h = mesh.next(h);
Vertex_descriptor vb = mesh.target(h); const Point& pb = coords[vb]; h = mesh.next(h);
Vertex_descriptor vc = mesh.target(h); const Point& pc = coords[vc];
const Plane* plane = new Plane(pa, pb, pc);
supporting_planes_.push_back(plane);
face_supporting_planes[fd] = plane;
}
// Assigns the original planes for each edge
for( auto ed : mesh.edges()) {
Halfedge_descriptor h1 = mesh.halfedge(ed);
Halfedge_descriptor h2 = mesh.opposite(h1);
Face_descriptor f1 = mesh.face(h1);
Face_descriptor f2 = mesh.face(h2);
CGAL_assertion(f1 != Polygon_mesh::null_face()); // the bbox mesh is closed
CGAL_assertion(f2 != Polygon_mesh::null_face()); // the bbox mesh is closed
const Plane* plane1 = face_supporting_planes[f1];
const Plane* plane2 = face_supporting_planes[f2];
CGAL_assertion(plane1 && plane2 && plane1 != plane2);
edge_supporting_planes[ed].insert(plane1);
edge_supporting_planes[ed].insert(plane2);
CGAL_assertion(edge_supporting_planes[ed].size() == 2);
}
// Assigns the original planes for each vertex
for(auto vd : mesh.vertices()) {
CGAL_assertion(vertex_supporting_planes[vd].size() == 0);
CGAL::Halfedge_around_target_circulator<Polygon_mesh> hbegin(vd, mesh), done(hbegin);
do {
Halfedge_descriptor h = *hbegin;
Face_descriptor f = mesh.face(h);
const Plane* plane = face_supporting_planes[f];
vertex_supporting_planes[vd].insert(plane);
++hbegin;
} while (hbegin != done);
CGAL_assertion(vertex_supporting_planes[vd].size() == 3);
}
std::sort(supporting_planes_.begin(), supporting_planes_.end());
CGAL_assertion(mesh.is_valid());
}
template <typename Kernel>
void Hypothesis<Kernel>::construct_proxy_mesh(const Polygon_mesh& bbox_mesh, Polygon_mesh& candidate_faces) {
// Properties of the bbox_mesh
typename Polygon_mesh::template Property_map<Edge_descriptor, std::set<const Plane*> > bbox_edge_supporting_planes
= bbox_mesh.template property_map<Edge_descriptor, std::set<const Plane*> >("e:supp_plane").first;
typename Polygon_mesh::template Property_map<Vertex_descriptor, std::set<const Plane*> > bbox_vertex_supporting_planes
= bbox_mesh.template property_map<Vertex_descriptor, std::set<const Plane*> >("v:supp_plane").first;
// The properties of the proxy mesh
candidate_faces.clear();
// The supporting plane of each face
typename Polygon_mesh::template Property_map<Face_descriptor, const Plane*> face_supporting_planes
= candidate_faces.template add_property_map<Face_descriptor, const Plane*>("f:supp_plane").first;
// The supporting planar segment of each face
typename Polygon_mesh::template Property_map<Face_descriptor, Planar_segment*> face_supporting_segments
= candidate_faces.template add_property_map<Face_descriptor, Planar_segment*>("f:supp_segment").first;
// The supporting planes of each edge
typename Polygon_mesh::template Property_map<Edge_descriptor, std::set<const Plane*> > edge_supporting_planes
= candidate_faces.template add_property_map<Edge_descriptor, std::set<const Plane*> >("e:supp_plane").first;
// The supporting planes of each vertex
typename Polygon_mesh::template Property_map<Vertex_descriptor, std::set<const Plane*> > vertex_supporting_planes
= candidate_faces.template add_property_map<Vertex_descriptor, std::set<const Plane*> >("v:supp_plane").first;
const std::vector<Planar_segment*>& segments = point_set_->planar_segments();
const typename Polygon_mesh::template Property_map<Vertex_descriptor, Point>& coords = bbox_mesh.points();
for (std::size_t i = 0; i < segments.size(); ++i) {
Planar_segment* g = segments[i];
const Plane* cutting_plane = g->supporting_plane();
std::vector<Point> intersecting_points;
std::vector< std::set<const Plane*> > intersecting_points_source_planes;
for(auto ed : bbox_mesh.edges()) {
Vertex_descriptor sd = bbox_mesh.vertex(ed, 0);
Vertex_descriptor td = bbox_mesh.vertex(ed, 1);
const Point& s = coords[sd];
const Point& t = coords[td];
CGAL::Oriented_side ss = cutting_plane->oriented_side(s);
CGAL::Oriented_side st = cutting_plane->oriented_side(t);
if ((ss == CGAL::ON_POSITIVE_SIDE && st == CGAL::ON_NEGATIVE_SIDE) || (ss == ON_NEGATIVE_SIDE && st == CGAL::ON_POSITIVE_SIDE)) {
CGAL::Object obj = CGAL::intersection(*cutting_plane, Line(s, t));
if (const Point* p = CGAL::object_cast<Point>(&obj)) {
intersecting_points.push_back(*p);
std::set<const Plane*> planes = bbox_edge_supporting_planes[ed];
planes.insert(cutting_plane);
CGAL_assertion(planes.size() == 3);
intersecting_points_source_planes.push_back(planes);
}
else
std::cerr << "Fatal error: should have intersection" << std::endl;
}
else {
if (ss == CGAL::ON_ORIENTED_BOUNDARY && st != CGAL::ON_ORIENTED_BOUNDARY) {
intersecting_points.push_back(s);
const std::set<const Plane*>& planes = bbox_vertex_supporting_planes[sd];
CGAL_assertion(planes.size() == 3);
intersecting_points_source_planes.push_back(planes);
}
else if (st == CGAL::ON_ORIENTED_BOUNDARY && ss != CGAL::ON_ORIENTED_BOUNDARY) {
intersecting_points.push_back(t);
const std::set<const Plane*>& planes = bbox_vertex_supporting_planes[td];
CGAL_assertion(planes.size() == 3);
intersecting_points_source_planes.push_back(planes);
}
else {
// The intersection is the current edge, nothing to do
}
}
}
// Decides the orientation of the points
if (intersecting_points.size() >= 3) {
std::list<Point> pts;
for (std::size_t i = 0; i < intersecting_points.size(); ++i) {
const Point& p = intersecting_points[i];
const Point2& q = cutting_plane->to_2d(p);
pts.push_back(Point(q.x(), q.y(), FT(i))); // the z component stores the point index
}
typedef CGAL::Projection_traits_xy_3<Kernel> Projection;
std::list<Point> hull;
CGAL::convex_hull_2(pts.begin(), pts.end(), std::back_inserter(hull), Projection());
std::vector<Point> ch;
std::vector< std::set<const Plane*> > ch_source_planes;
for (typename std::list<Point>::iterator it = hull.begin(); it != hull.end(); ++it) {
std::size_t idx = std::size_t(it->z());
ch.push_back(intersecting_points[idx]);
ch_source_planes.push_back(intersecting_points_source_planes[idx]);
}
if (ch.size() >= 3) {
std::vector<Vertex_descriptor> descriptors;
for (std::size_t j = 0; j < ch.size(); ++j) {
Vertex_descriptor vd = candidate_faces.add_vertex(ch[j]);
descriptors.push_back(vd);
vertex_supporting_planes[vd] = ch_source_planes[j];
CGAL_assertion(vertex_supporting_planes[vd].size() == 3);
}
Face_descriptor fd = candidate_faces.add_face(descriptors);
face_supporting_segments[fd] = g;
face_supporting_planes[fd] = cutting_plane;
// Assigns each edge the supporting planes
CGAL::Halfedge_around_face_circulator<Polygon_mesh> hbegin(candidate_faces.halfedge(fd), candidate_faces), done(hbegin);
do {
Halfedge_descriptor hd = *hbegin;
Edge_descriptor ed = candidate_faces.edge(hd);
Vertex_descriptor s_vd = candidate_faces.source(hd);
Vertex_descriptor t_vd = candidate_faces.target(hd);
const std::set<const Plane*>& s_planes = vertex_supporting_planes[s_vd];
const std::set<const Plane*>& t_planes = vertex_supporting_planes[t_vd];
std::set<const Plane*> common_planes;
std::set_intersection(s_planes.begin(), s_planes.end(), t_planes.begin(), t_planes.end(), std::inserter(common_planes, common_planes.begin()));
if (common_planes.size() == 2) {
CGAL_assertion(edge_supporting_planes[ed].size() == 0);
edge_supporting_planes[ed] = common_planes;
CGAL_assertion(edge_supporting_planes[ed].size() == 2);
}
else // If reached here, there must be topological errors.
std::cerr << "topological error" << std::endl;
++hbegin;
} while (hbegin != done);
}
}
}
CGAL_assertion(candidate_faces.is_valid());
}
template <typename Kernel>
void Hypothesis<Kernel>::compute_triplet_intersections() {
triplet_intersections_.clear();
if (supporting_planes_.size() < 4) // no closed surface will be constructed from less than 4 planes
return;
for (std::size_t i = 0; i < supporting_planes_.size(); ++i) {
const Plane* plane1 = supporting_planes_[i];
for (std::size_t j = i + 1; j < supporting_planes_.size(); ++j) {
const Plane* plane2 = supporting_planes_[j];
for (std::size_t k = j + 1; k < supporting_planes_.size(); ++k) {
const Plane* plane3 = supporting_planes_[k];
CGAL_assertion(plane1 < plane2 && plane2 < plane3);
Point p;
if (internal::intersect_plane_triplet<Plane, Point>(plane1, plane2, plane3, p)) {
// Stores the intersection for future query
Point* new_point = new Point(p);
triplet_intersections_[plane1][plane2][plane3] = new_point;
intersecting_points_.push_back(new_point);
}
}
}
}
}
template <typename Kernel>
const typename Hypothesis<Kernel>::Point*
Hypothesis<Kernel>::query_intersection(const Plane* min_plane, const Plane* mid_plane, const Plane* max_plane) {
CGAL_assertion(min_plane < mid_plane);
CGAL_assertion(mid_plane < max_plane);
if (triplet_intersections_.find(min_plane) == triplet_intersections_.end())
return nullptr;
Two_planes_to_point_map& map2 = triplet_intersections_[min_plane];
if (map2.find(mid_plane) == map2.end())
return nullptr;
Plane_to_point_map& map1 = map2[mid_plane];
if (map1.find(max_plane) == map1.end())
return nullptr;
return map1[max_plane];
}
template <typename Kernel>
typename Hypothesis<Kernel>::Halfedge_descriptor
Hypothesis<Kernel>::split_edge(Polygon_mesh& mesh, const EdgePos& ep, const Plane* cutting_plane) {
// The supporting planes of each edge
typename Polygon_mesh::template Property_map<Edge_descriptor, std::set<const Plane*> > edge_supporting_planes =
mesh.template property_map<Edge_descriptor, std::set<const Plane*> >("e:supp_plane").first;
// The supporting planes of each vertex
typename Polygon_mesh::template Property_map<Vertex_descriptor, std::set<const Plane*> > vertex_supporting_planes
= mesh.template property_map<Vertex_descriptor, std::set<const Plane*> >("v:supp_plane").first;
// We cannot use const reference, because it will become invalid after splitting
std::set<const Plane*> sfs = edge_supporting_planes[ep.edge];
CGAL_assertion(sfs.size() == 2);
Halfedge_descriptor h = Euler::split_edge(mesh.halfedge(ep.edge), mesh);
if (h == Polygon_mesh::null_halfedge()) // failed splitting edge
return h;
Vertex_descriptor v = mesh.target(h);
if (v == Polygon_mesh::null_vertex()) // failed splitting edge
return Polygon_mesh::null_halfedge();
typename Polygon_mesh::template Property_map<Vertex_descriptor, Point>& coords = mesh.points();
coords[v] = *ep.pos;
Edge_descriptor e1 = mesh.edge(h);
edge_supporting_planes[e1] = sfs;
Edge_descriptor e2 = mesh.edge(mesh.next(h));
edge_supporting_planes[e2] = sfs;
vertex_supporting_planes[v] = sfs;
vertex_supporting_planes[v].insert(cutting_plane);
CGAL_assertion(vertex_supporting_planes[v].size() == 3);
return h;
}
// Cuts f using the cutter and returns the new faces
template <typename Kernel>
std::vector<typename Hypothesis<Kernel>::Face_descriptor>
Hypothesis<Kernel>::cut(Face_descriptor face, const Plane* cutting_plane, Polygon_mesh& mesh) {
std::vector<Face_descriptor> new_faces;
// The supporting plane of each face
typename Polygon_mesh::template Property_map<Face_descriptor, const Plane*> face_supporting_planes =
mesh.template property_map<Face_descriptor, const Plane*>("f:supp_plane").first;
const Plane* supporting_plane = face_supporting_planes[face];
if (supporting_plane == cutting_plane)
return new_faces;
// The supporting planar segment of each face
typename Polygon_mesh::template Property_map<Face_descriptor, Planar_segment*> face_supporting_segments =
mesh.template property_map<Face_descriptor, Planar_segment*>("f:supp_segment").first;
// The supporting planes of each edge
typename Polygon_mesh::template Property_map<Edge_descriptor, std::set<const Plane*> > edge_supporting_planes =
mesh.template property_map<Edge_descriptor, std::set<const Plane*> >("e:supp_plane").first;
Planar_segment* supporting_segment = face_supporting_segments[face];
std::vector<Vertex_descriptor> existing_vts;
std::vector<EdgePos> new_vts;
compute_intersections(mesh, face, cutting_plane, existing_vts, new_vts);
// We need to check here because new faces are emerging
if (existing_vts.size() + new_vts.size() != 2)
return new_faces;
else if (existing_vts.size() == 2) {
// Tests if the two intersecting points are both very close to an existing vertex.
// Since we allow snapping, we test if the two intersecting points are the same.
if (existing_vts[0] == existing_vts[1])
return new_faces;
// Tests if an edge already exists, i.e., the plane cuts at this edge
if (halfedge_exists(existing_vts[0], existing_vts[1], mesh))
return new_faces;
}
Halfedge_descriptor h0 = Polygon_mesh::null_halfedge();
Halfedge_descriptor h1 = Polygon_mesh::null_halfedge();
if (existing_vts.size() == 2) { // cutting_plane cuts the face at two existing vertices (not an edge)
h0 = mesh.halfedge(existing_vts[0]);
h1 = mesh.halfedge(existing_vts[1]);
}
else if (existing_vts.size() == 1) {
h0 = mesh.halfedge(existing_vts[0]);
h1 = split_edge(mesh, new_vts[0], cutting_plane);
}
else if (new_vts.size() == 2) {
h0 = split_edge(mesh, new_vts[0], cutting_plane);
h1 = split_edge(mesh, new_vts[1], cutting_plane);
}
CGAL_assertion(h0 != Polygon_mesh::null_halfedge());
CGAL_assertion(h1 != Polygon_mesh::null_halfedge());
// To split the face, `h0` and `h1` must be incident to the same face
if (mesh.face(h0) != face) {
Halfedge_descriptor end = h0;
do {
h0 = mesh.opposite(mesh.next(h0)); // h0 = h0->next()->opposite();
if (mesh.face(h0) == face)
break;
} while (h0 != end);
}
CGAL_assertion(mesh.face(h0) == face);
if (mesh.face(h1) != face) {
Halfedge_descriptor end = h1;
do {
h1 = mesh.opposite(mesh.next(h1)); // h1 = h1->next()->opposite();
if (mesh.face(h1) == face)
break;
} while (h1 != end);
}
CGAL_assertion(mesh.face(h1) == face);
Halfedge_descriptor h = Euler::split_face(h0, h1, mesh);
if (h == Polygon_mesh::null_halfedge() || mesh.face(h) == Polygon_mesh::null_face()) {
std::cerr << "Fatal error. could not split face" << std::endl;
return new_faces;
}
Edge_descriptor e = mesh.edge(h);
edge_supporting_planes[e].insert(supporting_plane);
edge_supporting_planes[e].insert(cutting_plane);
CGAL_assertion(edge_supporting_planes[e].size() == 2);
// Now the two faces
Face_descriptor f1 = mesh.face(h);
face_supporting_segments[f1] = supporting_segment;
face_supporting_planes[f1] = supporting_plane;
new_faces.push_back(f1);
Face_descriptor f2 = mesh.face(mesh.opposite(h));
face_supporting_segments[f2] = supporting_segment;
face_supporting_planes[f2] = supporting_plane;
new_faces.push_back(f2);
return new_faces;
}
template <typename Kernel>
void Hypothesis<Kernel>::compute_intersections(const Polygon_mesh& mesh,
Face_descriptor face, const Plane* cutting_plane,
std::vector<Vertex_descriptor>& existing_vts,
std::vector<EdgePos>& new_vts)
{
existing_vts.clear();
new_vts.clear();
// The supporting plane of each face
typename Polygon_mesh::template Property_map<Face_descriptor, const Plane*> face_supporting_planes =
mesh.template property_map<Face_descriptor, const Plane*>("f:supp_plane").first;
const Plane* supporting_plane = face_supporting_planes[face];
if (supporting_plane == cutting_plane)
return;
typename Polygon_mesh::template Property_map<Edge_descriptor, std::set<const Plane*> > edge_supporting_planes
= mesh.template property_map<Edge_descriptor, std::set<const Plane*> >("e:supp_plane").first;
const typename Polygon_mesh::template Property_map<Vertex_descriptor, Point>& coords = mesh.points();
Halfedge_descriptor cur = mesh.halfedge(face);
Halfedge_descriptor end = cur;
do {
Edge_descriptor ed = mesh.edge(cur);
const std::set<const Plane*>& supporting_planes = edge_supporting_planes[ed];
if (supporting_planes.find(cutting_plane) != supporting_planes.end()) // the edge lies on the cutting plane
return;
Vertex_descriptor s_vd = mesh.source(cur);
Vertex_descriptor t_vd = mesh.target(cur);
const Point& s = coords[s_vd];
const Point& t = coords[t_vd];
CGAL::Oriented_side s_side = cutting_plane->oriented_side(s);
CGAL::Oriented_side t_side = cutting_plane->oriented_side(t);
if (t_side == CGAL::ON_ORIENTED_BOUNDARY) {
if (s_side == CGAL::ON_ORIENTED_BOUNDARY) // the edge lies on the cutting plane
return;
else
existing_vts.push_back(t_vd);
}
else if (
(s_side == CGAL::ON_POSITIVE_SIDE && t_side == CGAL::ON_NEGATIVE_SIDE) ||
(s_side == CGAL::ON_NEGATIVE_SIDE && t_side == CGAL::ON_POSITIVE_SIDE)) // intersects at the interior of the edge
{
FT s_sdist = CGAL::squared_distance(*cutting_plane, s);
FT t_sdist = CGAL::squared_distance(*cutting_plane, t);
if (s_sdist <= CGAL::snap_squared_distance_threshold<FT>()) // plane cuts at vertex 's'
existing_vts.push_back(s_vd);
else if (t_sdist <= CGAL::snap_squared_distance_threshold<FT>()) // plane cuts at vertex 't'
existing_vts.push_back(t_vd);
else {
const Plane* plane1 = *(supporting_planes.begin());
const Plane* plane2 = *(supporting_planes.rbegin());
const Plane* plane3 = const_cast<const Plane*>(cutting_plane);
if (plane3 != plane1 && plane3 != plane2) {
internal::sort_increasing(plane1, plane2, plane3);
const Point* p = query_intersection(plane1, plane2, plane3);
if (p) {
if (CGAL::squared_distance(*p, s) <= CGAL::snap_squared_distance_threshold<FT>()) // snap to 's'
existing_vts.push_back(s_vd);
else if (CGAL::squared_distance(*p, t) <= CGAL::snap_squared_distance_threshold<FT>()) // snap to 't'
existing_vts.push_back(t_vd);
else
new_vts.push_back(EdgePos(ed, p));
}
else
std::cerr << "Fatal error: should have intersection" << std::endl;
}
else
std::cerr << "Fatal error: should not have duplicated planes." << std::endl;
}
}
else {
// Nothing needs to do here, we will test the next edge
}
cur = mesh.next(cur);
} while (cur != end);
}
template <typename Kernel>
bool Hypothesis<Kernel>::halfedge_exists(Vertex_descriptor v1, Vertex_descriptor v2, const Polygon_mesh& mesh) {
Halfedge_descriptor h = mesh.halfedge(v1);
Halfedge_descriptor end = h;
do {
Halfedge_descriptor opp = mesh.opposite(h);
if (mesh.target(opp) == v2)
return true;
h = mesh.prev(opp);
} while (h != end);
return false;
}
// Tests if face 'f' insects plane 'plane'
template <typename Kernel>
bool Hypothesis<Kernel>::do_intersect(const Polygon_mesh& mesh, Face_descriptor f, const Plane* plane) {
std::vector<Vertex_descriptor> existing_vts;
std::vector<EdgePos> new_vts;
compute_intersections(mesh, f, plane, existing_vts, new_vts);
if (existing_vts.size() == 2) {
if (!halfedge_exists(existing_vts[0], existing_vts[1], mesh))
return true;
}
else if (existing_vts.size() + new_vts.size() == 2)
return true;
return false;
}
// Collects all faces in 'mesh' that intersect 'face', stored in std::set() so easier to erase
template <typename Kernel>
std::set<typename Hypothesis<Kernel>::Face_descriptor> Hypothesis<Kernel>::collect_intersecting_faces(Face_descriptor face, const Polygon_mesh& mesh)
{
// The supporting plane of each face
typename Polygon_mesh::template Property_map<Face_descriptor, const Plane*> face_supporting_planes =
mesh.template property_map<Face_descriptor, const Plane*>("f:supp_plane").first;
// The supporting planar segment of each face
typename Polygon_mesh::template Property_map<Face_descriptor, Planar_segment*> face_supporting_segments =
mesh.template property_map<Face_descriptor, Planar_segment*>("f:supp_segment").first;
std::set<Face_descriptor> intersecting_faces;
for(auto f : mesh.faces()) {
if (f == face ||
face_supporting_segments[f] == face_supporting_segments[face] ||
face_supporting_planes[f] == face_supporting_planes[face])
{
continue;
}
const Plane* plane = face_supporting_planes[face];
CGAL_assertion(plane != nullptr);
if (do_intersect(mesh, f, plane))
intersecting_faces.insert(f);
}
return intersecting_faces;
}
template <typename Kernel>
void Hypothesis<Kernel>::pairwise_intersection(Polygon_mesh& candidate_faces)
{
// Pre-computes all potential intersection of plane triplets
compute_triplet_intersections();
// Since we are going to split faces, we can not use the reference.
// const Polygon_mesh::Face_range& all_faces = mesh.faces();
// So we make a local copy
std::vector<Face_descriptor> all_faces(candidate_faces.faces().begin(), candidate_faces.faces().end());
// The supporting plane of each face
typename Polygon_mesh::template Property_map<Face_descriptor, const Plane*> face_supporting_planes
= candidate_faces.template property_map<Face_descriptor, const Plane*>("f:supp_plane").first;
for (std::size_t i = 0; i < all_faces.size(); ++i) {
Face_descriptor face = all_faces[i];
const Plane* face_plane = face_supporting_planes[face];
std::set<Face_descriptor> intersecting_faces = collect_intersecting_faces(face, candidate_faces);
if (intersecting_faces.empty())
continue;
std::vector<Face_descriptor> cutting_faces(intersecting_faces.begin(), intersecting_faces.end());
// 1. 'face' will be cut by all the intersecting faces
// \note After each cut, the original face doesn't exist any more and it is replaced by multiple pieces.
// then each piece will be cut by another face.
std::vector<Face_descriptor> faces_to_be_cut;
faces_to_be_cut.push_back(face);
while (!intersecting_faces.empty()) {
Face_descriptor cutting_face = *(intersecting_faces.begin());
const Plane* cutting_plane = face_supporting_planes[cutting_face];
std::set<Face_descriptor> new_faces; // stores the new faces
std::set<Face_descriptor> remained_faces; // faces that will be cut later
for (std::size_t j = 0; j < faces_to_be_cut.size(); ++j) {
Face_descriptor current_face = faces_to_be_cut[j];
std::vector<Face_descriptor> tmp = cut(current_face, cutting_plane, candidate_faces);
new_faces.insert(tmp.begin(), tmp.end());
if (tmp.empty()) { // no actual cut occurred. The face will be cut later
remained_faces.insert(current_face);
}
}
// The new faces might be cut by other faces
faces_to_be_cut = std::vector<Face_descriptor>(new_faces.begin(), new_faces.end());
// Don't forget the remained faces
faces_to_be_cut.insert(faces_to_be_cut.end(), remained_faces.begin(), remained_faces.end());
// The job of cutting_face is done, remove it
intersecting_faces.erase(cutting_face);
}
// 2. All the cutting_faces will be cut by f.
for (std::size_t j = 0; j < cutting_faces.size(); ++j) {
cut(cutting_faces[j], face_plane, candidate_faces);
}
}
CGAL_assertion(candidate_faces.is_valid());
}
template <class Kernel>
typename Hypothesis<Kernel>::Adjacency Hypothesis<Kernel>::extract_adjacency(const Polygon_mesh& candidate_faces)
{
typename Polygon_mesh::template Property_map<Vertex_descriptor, std::set<const Plane*> > vertex_supporting_planes
= candidate_faces.template property_map<Vertex_descriptor, std::set<const Plane*> >("v:supp_plane").first;
// An edge is denoted by its two end points
typedef typename std::unordered_map<const Point*, std::set<Halfedge_descriptor> > Edge_map;
typedef typename std::unordered_map<const Point*, Edge_map > Face_pool;
Face_pool face_pool;
for(auto h : candidate_faces.halfedges()) {
Face_descriptor f = candidate_faces.face(h);
if (f == Polygon_mesh::null_face())
continue;
Vertex_descriptor sd = candidate_faces.source(h);
Vertex_descriptor td = candidate_faces.target(h);
const std::set<const Plane*>& set_s = vertex_supporting_planes[sd];
const std::set<const Plane*>& set_t = vertex_supporting_planes[td];
CGAL_assertion(set_s.size() == 3);
CGAL_assertion(set_t.size() == 3);
std::vector<const Plane*> s_planes(set_s.begin(), set_s.end());
CGAL_assertion(s_planes[0] < s_planes[1]);
CGAL_assertion(s_planes[1] < s_planes[2]);
const Point* s = triplet_intersections_[s_planes[0]][s_planes[1]][s_planes[2]];
std::vector<const Plane*> t_planes(set_t.begin(), set_t.end());
CGAL_assertion(t_planes[0] < t_planes[1]);
CGAL_assertion(t_planes[1] < t_planes[2]);
const Point* t = triplet_intersections_[t_planes[0]][t_planes[1]][t_planes[2]];
if (s > t)
std::swap(s, t);
face_pool[s][t].insert(candidate_faces.halfedge(f));
}
Adjacency fans;
typename Face_pool::const_iterator it = face_pool.begin();
for (; it != face_pool.end(); ++it) {
const Point* s = it->first;
const Edge_map& tmp = it->second;
typename Edge_map::const_iterator cur = tmp.begin();
for (; cur != tmp.end(); ++cur) {
const Point* t = cur->first;
const std::set<Halfedge_descriptor>& faces = cur->second;
Intersection fan;
fan.s = s;
fan.t = t;
fan.insert(fan.end(), faces.begin(), faces.end());
fans.push_back(fan);
}
}
return fans;
}
} //namespace internal
} //namespace CGAL
#endif // CGAL_POLYGONAL_SURFACE_RECONSTRUCTION_HYPOTHESIS_H