mirror of https://github.com/CGAL/cgal
790 lines
28 KiB
C++
790 lines
28 KiB
C++
// Copyright (c) 2023 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) : Sven Oesau, Florent Lafarge, Dmitry Anisimov, Simon Giraudot
|
|
|
|
#ifndef CGAL_KSP_3_FINALIZER_H
|
|
#define CGAL_KSP_3_FINALIZER_H
|
|
|
|
#include <CGAL/license/Kinetic_space_partition.h>
|
|
#include <CGAL/Polygon_mesh_processing/connected_components.h>
|
|
|
|
// Internal includes.
|
|
#include <CGAL/KSP/utils.h>
|
|
#include <CGAL/KSP/debug.h>
|
|
#include <CGAL/KSP/parameters.h>
|
|
|
|
#include <CGAL/KSP_3/Data_structure.h>
|
|
|
|
namespace CGAL {
|
|
namespace KSP_3 {
|
|
namespace internal {
|
|
|
|
#ifdef DOXYGEN_RUNNING
|
|
#else
|
|
|
|
template<typename GeomTraits, typename IntersectionKernel>
|
|
class Finalizer {
|
|
|
|
public:
|
|
using Kernel = GeomTraits;
|
|
|
|
private:
|
|
using Intersection_kernel = IntersectionKernel;
|
|
using FT = typename Kernel::FT;
|
|
using Point_2 = typename Kernel::Point_2;
|
|
using Point_3 = typename Kernel::Point_3;
|
|
using Vector_2 = typename Kernel::Vector_2;
|
|
using Vector_3 = typename Kernel::Vector_3;
|
|
using Segment_3 = typename Kernel::Segment_3;
|
|
using Line_3 = typename Kernel::Line_3;
|
|
using Plane_3 = typename Kernel::Plane_3;
|
|
using Direction_2 = typename Kernel::Direction_2;
|
|
using Tetrahedron_3 = typename Kernel::Tetrahedron_3;
|
|
|
|
using From_exact = CGAL::Cartesian_converter<IntersectionKernel, Kernel>;
|
|
using To_exact = CGAL::Cartesian_converter<Kernel, IntersectionKernel>;
|
|
|
|
using Data_structure = CGAL::KSP_3::internal::Data_structure<Kernel, IntersectionKernel>;
|
|
|
|
using IVertex = typename Data_structure::IVertex;
|
|
using IEdge = typename Data_structure::IEdge;
|
|
|
|
using PVertex = typename Data_structure::PVertex;
|
|
using PEdge = typename Data_structure::PEdge;
|
|
using PFace = typename Data_structure::PFace;
|
|
|
|
using Support_plane = typename Data_structure::Support_plane;
|
|
using Intersection_graph = typename Data_structure::Intersection_graph;
|
|
using Volume_cell = typename Data_structure::Volume_cell;
|
|
|
|
using Mesh = typename Data_structure::Mesh;
|
|
using Vertex_index = typename Data_structure::Vertex_index;
|
|
using Face_index = typename Data_structure::Face_index;
|
|
using Edge_index = typename Data_structure::Edge_index;
|
|
using Halfedge_index = typename Data_structure::Halfedge_index;
|
|
|
|
using F_component_map = typename Mesh::template Property_map<typename Support_plane::Face_index, typename boost::graph_traits<typename Support_plane::Mesh>::faces_size_type>;
|
|
using E_constraint_map = typename Mesh::template Property_map<typename Support_plane::Edge_index, bool>;
|
|
|
|
struct Vertex_info {
|
|
bool tagged;
|
|
PVertex pvertex;
|
|
IVertex ivertex;
|
|
Vertex_info() :
|
|
tagged(false),
|
|
pvertex(Data_structure::null_pvertex()),
|
|
ivertex(Data_structure::null_ivertex())
|
|
{ }
|
|
};
|
|
|
|
struct Face_info {
|
|
std::size_t index;
|
|
std::size_t input;
|
|
Face_info() :
|
|
index(static_cast<std::size_t>(-1)),
|
|
input(static_cast<std::size_t>(-1))
|
|
{ }
|
|
};
|
|
|
|
using Parameters = KSP::internal::Parameters_3<FT>;
|
|
|
|
public:
|
|
Finalizer(Data_structure& data, const Parameters& parameters) :
|
|
m_data(data), m_parameters(parameters)
|
|
{ }
|
|
|
|
void create_polyhedra() {
|
|
if (m_parameters.debug) {
|
|
for (std::size_t sp = 0; sp < m_data.number_of_support_planes(); sp++) {
|
|
dump_2d_surface_mesh(m_data, sp, m_data.prefix() + "after-partition-sp" + std::to_string(sp));
|
|
}
|
|
}
|
|
|
|
std::cout.precision(20);
|
|
CGAL_assertion(m_data.check_bbox());
|
|
CGAL_assertion(m_data.check_interior());
|
|
CGAL_assertion(m_data.check_vertices());
|
|
CGAL_assertion(m_data.check_edges());
|
|
|
|
merge_facets_connected_components();
|
|
|
|
create_volumes();
|
|
|
|
if (m_parameters.debug) {
|
|
for (const auto& v : m_data.volumes())
|
|
dump_volume(m_data, v.pfaces, "volumes/" + m_data.prefix() + std::to_string(v.index), true, v.index);
|
|
}
|
|
CGAL_assertion(m_data.check_faces());
|
|
}
|
|
|
|
private:
|
|
Data_structure& m_data;
|
|
const Parameters& m_parameters;
|
|
|
|
/*******************************
|
|
** EXTRACTING VOLUMES **
|
|
********************************/
|
|
|
|
void calculate_centroid(Volume_cell& volume) {
|
|
// First find a point in the interior of the volume cell.
|
|
FT x = 0, y = 0, z = 0;
|
|
FT num = 0;
|
|
for (const PVertex& v : volume.pvertices) {
|
|
Point_3 p = m_data.point_3(v);
|
|
x += p.x();
|
|
y += p.y();
|
|
z += p.z();
|
|
num += 1;
|
|
}
|
|
Point_3 inside(x / num, y / num, z / num);
|
|
|
|
// Now create a vector of tetrahedrons.
|
|
std::vector<Tetrahedron_3> tets;
|
|
tets.reserve(volume.pvertices.size());
|
|
|
|
for (const PFace& f : volume.pfaces) {
|
|
// Orientation of the tetrahedron depends on the orientation of the support plane of the polygon.
|
|
Support_plane sp = m_data.support_plane(f.first);
|
|
Vector_3 n = sp.plane().orthogonal_vector();
|
|
const Mesh& m = sp.mesh();
|
|
Halfedge_index first = m.halfedge(f.second);
|
|
Point_3 p = m_data.point_3(PVertex(f.first, m.target(first)));
|
|
|
|
bool positive_side = (inside - p) * n > 0;
|
|
|
|
Halfedge_index h = m.next(first);
|
|
Point_3 a = m_data.point_3(PVertex(f.first, m.target(h)));
|
|
h = m.next(h);
|
|
do {
|
|
Point_3 b = m_data.point_3(PVertex(f.first, m.target(h)));
|
|
|
|
if (positive_side)
|
|
tets.push_back(Tetrahedron_3(p, a, b, inside));
|
|
else
|
|
tets.push_back(Tetrahedron_3(p, b, a, inside));
|
|
|
|
a = b;
|
|
h = m.next(h);
|
|
} while (h != first);
|
|
}
|
|
|
|
volume.centroid = CGAL::centroid(tets.begin(), tets.end(), CGAL::Dimension_tag<3>());
|
|
}
|
|
|
|
void create_volumes() {
|
|
// Initialize an empty volume map.
|
|
auto& volumes = m_data.volumes();//std::vector<Volume_cell>
|
|
auto& map_volumes = m_data.pface_neighbors();//std::map<PFace, std::pair<int, int>
|
|
|
|
volumes.clear();
|
|
map_volumes.clear();
|
|
for (std::size_t i = 0; i < m_data.number_of_support_planes(); ++i) {
|
|
const auto pfaces = m_data.pfaces(i);
|
|
for (const auto pface : pfaces)
|
|
map_volumes[pface] = std::make_pair(-1, -1);
|
|
}
|
|
|
|
for (std::size_t sp = 0; sp < m_data.number_of_support_planes(); sp++) {
|
|
for (auto pface : m_data.pfaces(sp)) {
|
|
segment_adjacent_volumes(pface, volumes, map_volumes);
|
|
}
|
|
}
|
|
|
|
std::map<PFace, std::size_t>& face2index = m_data.face_to_index();
|
|
std::vector<std::pair<int, int> >& face2volumes = m_data.face_to_volumes();
|
|
std::size_t num_faces = 0;
|
|
|
|
// Adjust neighbor information in volumes
|
|
for (std::size_t i = 0; i < volumes.size(); i++) {
|
|
auto& v = volumes[i];
|
|
v.index = i;
|
|
v.faces.resize(v.pfaces.size());
|
|
for (std::size_t f = 0; f < volumes[i].pfaces.size(); f++) {
|
|
auto& pf = volumes[i].pfaces[f];
|
|
auto it = face2index.find(pf);
|
|
if (it == face2index.end()) {
|
|
face2index[pf] = num_faces;
|
|
v.faces[f] = num_faces++;
|
|
}
|
|
else
|
|
v.faces[f] = it->second;
|
|
}
|
|
|
|
if (face2volumes.size() < num_faces)
|
|
face2volumes.resize(num_faces, std::pair<int, int>(-1, -1));
|
|
|
|
for (std::size_t j = 0; j < volumes[i].neighbors.size(); j++) {
|
|
auto& pair = map_volumes.at(volumes[i].pfaces[j]);
|
|
if (pair.second == -1)
|
|
pair.second = -static_cast<int>(volumes[i].pfaces[j].first + 1);
|
|
volumes[i].neighbors[j] = (pair.first == static_cast<int>(i)) ? pair.second : pair.first;
|
|
face2volumes[v.faces[j]] = pair;
|
|
}
|
|
}
|
|
|
|
m_data.face_is_part_of_input_polygon().resize(num_faces);
|
|
for (const auto& p : face2index)
|
|
m_data.face_is_part_of_input_polygon()[p.second] = m_data.support_plane(p.first.first).is_initial(p.first.second);
|
|
|
|
m_data.face_to_vertices().resize(num_faces);
|
|
m_data.face_to_support_plane().resize(num_faces);
|
|
|
|
for (auto& volume : volumes) {
|
|
create_cell_pvertices(volume);
|
|
calculate_centroid(volume);
|
|
volume.pface_oriented_outwards.resize(volume.pfaces.size());
|
|
for (std::size_t i = 0; i < volume.pfaces.size(); i++) {
|
|
PVertex vtx = *m_data.pvertices_of_pface(volume.pfaces[i]).begin();
|
|
volume.pface_oriented_outwards[i] = ((m_data.point_3(vtx) - volume.centroid) * m_data.support_plane(volume.pfaces[i]).plane().orthogonal_vector() < 0);
|
|
}
|
|
}
|
|
|
|
remove_collinear_vertices();
|
|
}
|
|
|
|
void segment_adjacent_volumes(const PFace& pface, std::vector<Volume_cell>& volumes, std::map<PFace, std::pair<int, int> >& map_volumes) {
|
|
|
|
// Check whether this face is already part of one or two volumes
|
|
auto& pair = map_volumes.at(pface);
|
|
if (pair.first != -1 && pair.second != -1) return;
|
|
|
|
const std::size_t uninitialized = static_cast<std::size_t>(-1);
|
|
std::size_t volume_indices[] = { uninitialized, uninitialized };
|
|
//std::size_t other[] = { static_cast<std::size_t>(-1), static_cast<std::size_t>(-1) };
|
|
|
|
// Start new volume cell
|
|
// First of pair is positive side, second is negative
|
|
if (pair.first == -1) {
|
|
volume_indices[0] = volumes.size();
|
|
pair.first = static_cast<int>(volumes.size());
|
|
volumes.push_back(Volume_cell());
|
|
volumes.back().add_pface(pface, pair.second);
|
|
}
|
|
else {
|
|
//other[0] = pair.first;
|
|
if (pface.first < 6)
|
|
// Thus for a bbox pair.second is always -1. Thus if pair.first is already set, there is nothing to do.
|
|
return;
|
|
}
|
|
|
|
if (pair.second == -1 && pface.first >= 6) {
|
|
volume_indices[1] = volumes.size();
|
|
pair.second = static_cast<int>(volumes.size());
|
|
volumes.push_back(Volume_cell());
|
|
volumes.back().add_pface(pface, pair.first);
|
|
}
|
|
//else other[1] = pair.second;
|
|
|
|
// 0 is queue on positive side, 1 is queue on negative side
|
|
std::queue<std::pair<PFace, Oriented_side> > queue[2];
|
|
|
|
// Get neighbors with smallest dihedral angle
|
|
const auto pedges = m_data.pedges_of_pface(pface);
|
|
std::vector<PFace> neighbor_faces, adjacent_faces;
|
|
for (const auto pedge : pedges) {
|
|
CGAL_assertion(m_data.has_iedge(pedge));
|
|
|
|
m_data.incident_faces(m_data.iedge(pedge), neighbor_faces);
|
|
|
|
if (neighbor_faces.size() == 2) {
|
|
// If there is only one neighbor, the edge is on the edge of the bbox.
|
|
// Thus the only neighbor needs to be a bbox face.
|
|
PFace neighbor = (neighbor_faces[0] == pface) ? neighbor_faces[1] : neighbor_faces[0];
|
|
CGAL_assertion(neighbor.first < 6 && pface.first < 6);
|
|
Oriented_side side = oriented_side(pface, neighbor);
|
|
Oriented_side inverse_side = oriented_side(neighbor, pface);
|
|
CGAL_assertion(side != COPLANAR && inverse_side != COPLANAR);
|
|
|
|
if (side == ON_POSITIVE_SIDE && volume_indices[0] != uninitialized) {
|
|
if (associate(neighbor, volume_indices[0], inverse_side, volumes, map_volumes))
|
|
queue[0].push(std::make_pair(neighbor, inverse_side));
|
|
}
|
|
else if (side == ON_NEGATIVE_SIDE && volume_indices[1] != uninitialized)
|
|
if (associate(neighbor, volume_indices[1], inverse_side, volumes, map_volumes))
|
|
queue[1].push(std::make_pair(neighbor, inverse_side));
|
|
|
|
continue;
|
|
}
|
|
|
|
PFace positive_side, negative_side;
|
|
|
|
find_adjacent_faces(pface, pedge, neighbor_faces, positive_side, negative_side);
|
|
CGAL_assertion(positive_side != negative_side);
|
|
|
|
if (volume_indices[0] != uninitialized) {
|
|
Oriented_side inverse_side = (positive_side.first == pface.first) ? ON_POSITIVE_SIDE : oriented_side(positive_side, pface);
|
|
if (associate(positive_side, volume_indices[0], inverse_side, volumes, map_volumes))
|
|
queue[0].push(std::make_pair(positive_side, inverse_side));
|
|
}
|
|
|
|
if (volume_indices[1] != uninitialized) {
|
|
Oriented_side inverse_side = (negative_side.first == pface.first) ? ON_NEGATIVE_SIDE : oriented_side(negative_side, pface);
|
|
if (associate(negative_side, volume_indices[1], inverse_side, volumes, map_volumes))
|
|
queue[1].push(std::make_pair(negative_side, inverse_side));
|
|
}
|
|
}
|
|
|
|
// Propagate both queues if volumes on either side of the pface are not segmented.
|
|
for (std::size_t i = 0; i < 2; i++) {
|
|
if (volume_indices[i] != uninitialized) {
|
|
while (!queue[i].empty()) {
|
|
propagate_volume(queue[i], volume_indices[i], volumes, map_volumes);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void propagate_volume(std::queue<std::pair<PFace, Oriented_side> >& queue, std::size_t volume_index, std::vector<Volume_cell>& volumes, std::map<PFace, std::pair<int, int> >& map_volumes) {
|
|
PFace pface;
|
|
Oriented_side seed_side;
|
|
std::tie(pface, seed_side) = queue.front();
|
|
queue.pop();
|
|
|
|
// Get neighbors with smallest dihedral angle
|
|
const auto pedges = m_data.pedges_of_pface(pface);
|
|
std::vector<PFace> neighbor_faces, adjacent_faces;
|
|
for (const auto pedge : pedges) {
|
|
CGAL_assertion(m_data.has_iedge(pedge));
|
|
m_data.incident_faces(m_data.iedge(pedge), neighbor_faces);
|
|
|
|
if (neighbor_faces.size() == 2) {
|
|
// If there is only one neighbor, the edge is on the corner of the bbox.
|
|
// Thus the only neighbor needs to be a bbox face.
|
|
PFace neighbor = (neighbor_faces[0] == pface) ? neighbor_faces[1] : neighbor_faces[0];
|
|
CGAL_assertion(neighbor.first < 6 && pface.first < 6);
|
|
CGAL_assertion(oriented_side(pface, neighbor) == seed_side);
|
|
|
|
Oriented_side inverse_side = oriented_side(neighbor, pface);
|
|
|
|
CGAL_assertion(inverse_side == ON_POSITIVE_SIDE);
|
|
|
|
if (associate(neighbor, volume_index, inverse_side, volumes, map_volumes))
|
|
queue.push(std::make_pair(neighbor, inverse_side));
|
|
continue;
|
|
}
|
|
|
|
PFace positive_side, negative_side;
|
|
|
|
find_adjacent_faces(pface, pedge, neighbor_faces, positive_side, negative_side);
|
|
CGAL_assertion(positive_side != negative_side);
|
|
|
|
if (seed_side == ON_POSITIVE_SIDE) {
|
|
Oriented_side inverse_side = (pface.first == positive_side.first) ? seed_side : oriented_side(positive_side, pface);
|
|
if (associate(positive_side, volume_index, inverse_side, volumes, map_volumes))
|
|
queue.push(std::make_pair(positive_side, inverse_side));
|
|
}
|
|
else {
|
|
Oriented_side inverse_side = (pface.first == negative_side.first) ? seed_side : oriented_side(negative_side, pface);
|
|
if (associate(negative_side, volume_index, inverse_side, volumes, map_volumes))
|
|
queue.push(std::make_pair(negative_side, inverse_side));
|
|
}
|
|
}
|
|
}
|
|
|
|
bool associate(const PFace& pface, std::size_t volume_index, Oriented_side side, std::vector<Volume_cell>& volumes, std::map<PFace, std::pair<int, int> >& map_volumes) {
|
|
auto& pair = map_volumes.at(pface);
|
|
|
|
CGAL_assertion(side != COPLANAR);
|
|
|
|
if (side == ON_POSITIVE_SIDE) {
|
|
if (pair.first == static_cast<int>(volume_index))
|
|
return false;
|
|
|
|
pair.first = static_cast<int>(volume_index);
|
|
volumes[volume_index].add_pface(pface, pair.second);
|
|
return true;
|
|
}
|
|
else if (side == ON_NEGATIVE_SIDE) {
|
|
if (pair.second == static_cast<int>(volume_index))
|
|
return false;
|
|
|
|
pair.second = static_cast<int>(volume_index);
|
|
volumes[volume_index].add_pface(pface, pair.first);
|
|
return true;
|
|
}
|
|
|
|
CGAL_assertion(false);
|
|
return false;
|
|
}
|
|
|
|
void find_adjacent_faces(const PFace& pface, const PEdge& pedge, const std::vector<PFace>& neighbor_faces, PFace& positive_side, PFace& negative_side) const {
|
|
CGAL_assertion(neighbor_faces.size() > 2);
|
|
|
|
// for each face, find vertex that is not collinear with the edge
|
|
// take 2d directions orthogonal to edge
|
|
// sort and take neighbors to current face
|
|
|
|
To_exact to_exact;
|
|
|
|
const Segment_3 segment = m_data.segment_3(pedge);
|
|
IEdge ie = m_data.iedge(pedge);
|
|
typename Intersection_kernel::Point_3 source = m_data.point_3(m_data.igraph().source(ie));
|
|
typename Intersection_kernel::Vector_3 norm(source, m_data.point_3(m_data.igraph().target(ie)));
|
|
|
|
norm = KSP::internal::normalize(norm);
|
|
|
|
const typename Intersection_kernel::Plane_3 plane(source, norm);
|
|
typename Intersection_kernel::Point_2 source2d = plane.to_2d(source);
|
|
|
|
std::vector< std::pair<typename Intersection_kernel::Direction_2, PFace> > dir_edges;
|
|
|
|
// Get orientation towards edge of current face
|
|
typename Intersection_kernel::Point_2 v2d = plane.to_2d(to_exact(m_data.centroid_of_pface(pface)));
|
|
dir_edges.push_back(std::make_pair(typename Intersection_kernel::Direction_2(source2d - v2d), pface));
|
|
|
|
// Get orientation towards edge of other faces
|
|
for (const PFace& face : neighbor_faces) {
|
|
if (face == pface)
|
|
continue;
|
|
|
|
// Taking just the direction of the line instead of the point? (Still need to take care of the sign)
|
|
auto& sp = m_data.support_plane(face.first);
|
|
auto& mesh = sp.mesh();
|
|
auto h = mesh.halfedge(face.second);
|
|
auto first = h;
|
|
|
|
typename Intersection_kernel::Point_3 point;
|
|
typename Intersection_kernel::FT dist = 0;
|
|
do {
|
|
typename Intersection_kernel::Point_3 p = m_data.point_3(m_data.ivertex(PVertex(face.first, mesh.target(h))));
|
|
typename Intersection_kernel::Vector_3 dist_in_plane = (p - source);
|
|
dist_in_plane -= norm * (dist_in_plane * norm);
|
|
typename Intersection_kernel::FT d = dist_in_plane.squared_length();
|
|
|
|
if (d > dist) {
|
|
dist = d;
|
|
point = p;
|
|
}
|
|
h = mesh.next(h);
|
|
} while (first != h);
|
|
|
|
dir_edges.push_back(std::make_pair(typename Intersection_kernel::Direction_2(source2d - plane.to_2d(point)), face));
|
|
}
|
|
|
|
CGAL_assertion(dir_edges.size() == neighbor_faces.size());
|
|
|
|
// Sort directions
|
|
std::sort(dir_edges.begin(), dir_edges.end(), [&](
|
|
const std::pair<typename Intersection_kernel::Direction_2, PFace>& p,
|
|
const std::pair<typename Intersection_kernel::Direction_2, PFace>& q) -> bool {
|
|
return p.first < q.first;
|
|
}
|
|
);
|
|
|
|
std::size_t n = dir_edges.size();
|
|
for (std::size_t i = 0; i < n; ++i) {
|
|
if (dir_edges[i].second == pface) {
|
|
|
|
const std::size_t im = (i + n - 1) % n;
|
|
const std::size_t ip = (i + 1) % n;
|
|
|
|
// Check which side the faces are on
|
|
Orientation im_side = oriented_side(pface, dir_edges[im].second);
|
|
Orientation ip_side = oriented_side(pface, dir_edges[ip].second);
|
|
|
|
//The angles decide where to push them, not necessarily the side.
|
|
//At least in case of a bbox corner intersected with a third plane breaks this.
|
|
|
|
// Both on the negative side is not possible
|
|
CGAL_assertion(im_side != ON_NEGATIVE_SIDE || ip_side != ON_NEGATIVE_SIDE);
|
|
CGAL_assertion(im_side != COPLANAR || ip_side != COPLANAR);
|
|
|
|
// If two are positive it has to be a bbox corner situation.
|
|
if (im_side == ON_POSITIVE_SIDE && ip_side == ON_POSITIVE_SIDE) {
|
|
CGAL_assertion(pface.first < 6);
|
|
if (dir_edges[im].second.first < 6) {
|
|
positive_side = dir_edges[ip].second;
|
|
negative_side = dir_edges[im].second;
|
|
}
|
|
else if (dir_edges[ip].second.first < 6) {
|
|
positive_side = dir_edges[im].second;
|
|
negative_side = dir_edges[ip].second;
|
|
}
|
|
else CGAL_assertion(false);
|
|
}
|
|
else if (ip_side == ON_POSITIVE_SIDE || im_side == ON_NEGATIVE_SIDE) {
|
|
positive_side = dir_edges[ip].second;
|
|
negative_side = dir_edges[im].second;
|
|
}
|
|
else {
|
|
positive_side = dir_edges[im].second;
|
|
negative_side = dir_edges[ip].second;
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
CGAL_assertion_msg(false, "ERROR: NEXT PFACE IS NOT FOUND!");
|
|
}
|
|
|
|
Oriented_side oriented_side(const PFace& a, const PFace& b) const {
|
|
FT max_dist = 0;
|
|
if (a.first == b.first)
|
|
return CGAL::ON_ORIENTED_BOUNDARY;
|
|
Oriented_side side = CGAL::ON_ORIENTED_BOUNDARY;
|
|
const typename Intersection_kernel::Plane_3& p = m_data.support_plane(a.first).exact_plane();
|
|
for (auto v : m_data.pvertices_of_pface(b)) {
|
|
side = p.oriented_side(m_data.point_3(m_data.ivertex(v)));
|
|
if (side != CGAL::ON_ORIENTED_BOUNDARY)
|
|
return side;
|
|
}
|
|
|
|
return CGAL::ON_ORIENTED_BOUNDARY;
|
|
}
|
|
|
|
void merge_facets_connected_components() {
|
|
// Purpose: merge facets between the same volumes. Every pair of volumes can have at most one contact polygon (which also has to be convex)
|
|
// Precondition: all volumes are convex, the contact area between each pair of volumes is empty or convex
|
|
std::vector<E_constraint_map> edge_constraint_maps(m_data.number_of_support_planes());
|
|
|
|
for (std::size_t sp = 0; sp < m_data.number_of_support_planes(); sp++) {
|
|
//dump_2d_surface_mesh(m_data, sp, "face_merge/" + m_data.prefix() + std::to_string(sp) + "-before");
|
|
typename Support_plane::Mesh& mesh = m_data.support_plane(sp).mesh();
|
|
|
|
edge_constraint_maps[sp] = mesh.template add_property_map<typename Support_plane::Edge_index, bool>("e:keep", true).first;
|
|
F_component_map fcm = mesh.template add_property_map<typename Support_plane::Face_index, typename boost::graph_traits<typename Support_plane::Mesh>::faces_size_type>("f:component", 0).first;
|
|
|
|
for (auto e : mesh.edges()) {
|
|
IEdge iedge = m_data.iedge(PEdge(sp, e));
|
|
|
|
if (is_occupied(iedge, sp))
|
|
edge_constraint_maps[sp][e] = true;
|
|
else
|
|
edge_constraint_maps[sp][e] = false;
|
|
}
|
|
|
|
CGAL::Polygon_mesh_processing::connected_components(mesh, fcm, CGAL::parameters::edge_is_constrained_map(edge_constraint_maps[sp]));
|
|
|
|
merge_connected_components(sp, mesh, fcm, edge_constraint_maps[sp]);
|
|
|
|
mesh.collect_garbage();
|
|
}
|
|
}
|
|
|
|
void merge_connected_components(std::size_t sp_idx, typename Support_plane::Mesh& mesh, F_component_map& fcm, E_constraint_map ecm) {
|
|
using Halfedge = typename Support_plane::Halfedge_index;
|
|
using Face = typename Support_plane::Face_index;
|
|
|
|
std::vector<bool> initial_component;
|
|
std::size_t num_components = 0;
|
|
|
|
for (const auto& f : mesh.faces())
|
|
num_components = (std::max<std::size_t>)(num_components, fcm[f]);
|
|
|
|
initial_component.resize(num_components + 1, false);
|
|
Support_plane& sp = m_data.support_plane(sp_idx);
|
|
for (const auto& f : mesh.faces()) {
|
|
if (sp.is_initial(f))
|
|
initial_component[fcm[f]] = true;
|
|
}
|
|
|
|
//std::vector<Halfedge> remove_edges;
|
|
std::vector<bool> remove_vertices(mesh.vertices().size(), true);
|
|
std::vector<bool> remove_faces(mesh.faces().size(), true);
|
|
|
|
std::vector<bool> visited_halfedges(mesh.halfedges().size(), false);
|
|
for (auto h : mesh.halfedges()) {
|
|
Point_3 s = sp.to_3d(mesh.point(mesh.source(h)));
|
|
Point_3 t = sp.to_3d(mesh.point(mesh.target(h)));
|
|
std::vector<Point_3> pts;
|
|
pts.push_back(s);
|
|
pts.push_back(t);
|
|
if (visited_halfedges[h])
|
|
continue;
|
|
|
|
visited_halfedges[h] = true;
|
|
// Skip the outside edges.
|
|
if (mesh.face(h) == mesh.null_face())
|
|
continue;
|
|
|
|
Face f0 = mesh.face(h);
|
|
|
|
typename boost::graph_traits<typename Support_plane::Mesh>::faces_size_type c0 = fcm[f0], c_other;
|
|
Face f_other = mesh.face(mesh.opposite(h));
|
|
|
|
// Check whether the edge is between different components.
|
|
if (f_other != mesh.null_face()) {
|
|
if (c0 == fcm[f_other]) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
set_halfedge(f0, h, mesh);
|
|
remove_faces[f0] = false;
|
|
if (initial_component[c0])
|
|
sp.set_initial(f0);
|
|
|
|
// Find halfedge loop around component.
|
|
std::vector<Halfedge> loop;
|
|
loop.push_back(h);
|
|
remove_vertices[mesh.target(h)] = false;
|
|
Halfedge first = h;
|
|
do {
|
|
Halfedge n = h;
|
|
//Point_3 tn = m_data.support_plane(sp).to_3d(mesh.point(mesh.target(h)));
|
|
|
|
do {
|
|
if (n == h)
|
|
n = mesh.next(n);
|
|
else
|
|
n = mesh.next(mesh.opposite(n));
|
|
|
|
//Point_3 tn2 = m_data.support_plane(sp).to_3d(mesh.point(mesh.target(h)));
|
|
visited_halfedges[n] = true;
|
|
|
|
//Face_index fn = mesh.face(n);
|
|
|
|
f_other = mesh.face(mesh.opposite(n));
|
|
if (f_other == mesh.null_face())
|
|
break;
|
|
c_other = fcm[f_other];
|
|
if (c0 == c_other && ecm[Edge_index(n >> 1)])
|
|
std::cout << "edge and face constraint map inconsistent1" << std::endl;
|
|
|
|
if (c0 != c_other && !ecm[Edge_index(n >> 1)])
|
|
std::cout << "edge and face constraint map inconsistent2" << std::endl;
|
|
} while (c0 == c_other && n != h);
|
|
|
|
if (n == h) {
|
|
// Should not happen.
|
|
std::cout << "Searching for next edge of connected component failed" << std::endl;
|
|
}
|
|
loop.push_back(n);
|
|
set_face(n, f0, mesh);
|
|
set_next(h, n, mesh);
|
|
set_halfedge(mesh.target(h), h, mesh);
|
|
remove_vertices[mesh.target(n)] = false;
|
|
h = n;
|
|
} while (h != first);
|
|
}
|
|
|
|
// Remove all vertices in remove_vertices and all edges marked in constrained list
|
|
for (auto f : mesh.faces()) {
|
|
if (remove_faces[f])
|
|
remove_face(f, mesh);
|
|
}
|
|
|
|
for (auto e : mesh.edges()) {
|
|
// If edge is not marked as constrained it can be removed.
|
|
if (!ecm[e]) {
|
|
remove_edge(e, mesh);
|
|
}
|
|
}
|
|
|
|
for (auto v : mesh.vertices()) {
|
|
if (remove_vertices[v])
|
|
remove_vertex(v, mesh);
|
|
}
|
|
|
|
if (!mesh.is_valid(true)) {
|
|
std::cout << "mesh is not valid after merging faces of sp " << sp_idx << std::endl;
|
|
}
|
|
}
|
|
|
|
bool is_occupied(IEdge iedge, std::size_t sp) {
|
|
const std::set<std::size_t> planes = m_data.intersected_planes(iedge);
|
|
for (std::size_t j : planes) {
|
|
if (sp == j)
|
|
continue;
|
|
|
|
m_data.support_plane(j).mesh().is_valid(true);
|
|
|
|
for (auto e2 : m_data.support_plane(j).mesh().edges()) {
|
|
if (iedge == m_data.iedge(PEdge(j, e2))) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void create_cell_pvertices(Volume_cell& cell) {
|
|
From_exact from_exact;
|
|
std::vector<int>& ivertex2vertex = m_data.ivertex_to_index();
|
|
std::vector<Point_3>& vertices = m_data.vertices();
|
|
std::vector<typename Intersection_kernel::Point_3>& exact_vertices = m_data.exact_vertices();
|
|
std::vector<std::vector<std::size_t> >& face2vertices = m_data.face_to_vertices();
|
|
std::vector<std::size_t>& face2sp = m_data.face_to_support_plane();
|
|
cell.pvertices.clear();
|
|
for (std::size_t f = 0; f < cell.pfaces.size(); f++) {
|
|
const auto& pface = cell.pfaces[f];
|
|
bool face_filled = !face2vertices[cell.faces[f]].empty();
|
|
|
|
if (!face_filled) {
|
|
face2vertices[cell.faces[f]].reserve(m_data.pvertices_of_pface(pface).size());
|
|
face2sp[cell.faces[f]] = pface.first;
|
|
}
|
|
|
|
for (const auto pvertex : m_data.pvertices_of_pface(pface)) {
|
|
CGAL_assertion(m_data.has_ivertex(pvertex));
|
|
cell.pvertices.insert(pvertex);
|
|
|
|
IVertex ivertex = m_data.ivertex(pvertex);
|
|
if (ivertex2vertex[ivertex] == -1) {
|
|
ivertex2vertex[ivertex] = static_cast<int>(vertices.size());
|
|
if (!face_filled)
|
|
face2vertices[cell.faces[f]].push_back(vertices.size());
|
|
else
|
|
std::cout << "Should not happen" << std::endl;
|
|
vertices.push_back(from_exact(m_data.point_3(ivertex)));
|
|
exact_vertices.push_back(m_data.point_3(ivertex));
|
|
}
|
|
else if (!face_filled)
|
|
face2vertices[cell.faces[f]].push_back(ivertex2vertex[ivertex]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void remove_collinear_vertices() {
|
|
auto& vertices = m_data.face_to_vertices();
|
|
std::vector<bool> coll(m_data.exact_vertices().size(), true);
|
|
std::unordered_map<std::size_t, std::vector<std::size_t> > vtx2face;
|
|
|
|
for (std::size_t f = 0; f < vertices.size(); f++) {
|
|
for (std::size_t i = 0; i < vertices[f].size(); i++) {
|
|
if (!coll[vertices[f][i]])
|
|
continue;
|
|
const typename Intersection_kernel::Point_3& a = m_data.exact_vertices()[vertices[f][(i - 1 + vertices[f].size()) % vertices[f].size()]];
|
|
const typename Intersection_kernel::Point_3& b = m_data.exact_vertices()[vertices[f][i]];
|
|
const typename Intersection_kernel::Point_3& c = m_data.exact_vertices()[vertices[f][(i + 1) % vertices[f].size()]];
|
|
if (!CGAL::collinear(a, b, c))
|
|
coll[vertices[f][i]] = false;
|
|
else
|
|
vtx2face[vertices[f][i]].push_back(f);
|
|
}
|
|
}
|
|
|
|
for (std::size_t i = 0; i < coll.size(); i++) {
|
|
if (!coll[i])
|
|
continue;
|
|
const auto& f = vtx2face[i];
|
|
for (std::size_t j = 0; j < f.size(); j++) {
|
|
for (std::size_t v = 0; v < vertices[f[j]].size(); v++) {
|
|
if (vertices[f[j]][v] == i) {
|
|
vertices[f[j]].erase(vertices[f[j]].begin() + v);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
#endif //DOXYGEN_RUNNING
|
|
|
|
} // namespace internal
|
|
} // namespace KSP_3
|
|
} // namespace CGAL
|
|
|
|
#endif // CGAL_KSP_3_FINALIZER_H
|