mirror of https://github.com/CGAL/cgal
711 lines
28 KiB
C++
711 lines
28 KiB
C++
// Copyright (c) 2019 GeometryFactory Sarl (France).
|
|
// All rights reserved.
|
|
//
|
|
// This file is part of CGAL (www.cgal.org).
|
|
// You can redistribute it and/or modify it under the terms of the GNU
|
|
// General Public License as published by the Free Software Foundation,
|
|
// either version 3 of the License, or (at your option) any later version.
|
|
//
|
|
// Licensees holding a valid commercial license may use this file in
|
|
// accordance with the commercial license agreement provided with the software.
|
|
//
|
|
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
|
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
//
|
|
// $URL$
|
|
// $Id$
|
|
// SPDX-License-Identifier: GPL-3.0+
|
|
//
|
|
// Author(s) : Laurent Rineau
|
|
|
|
#ifndef CGAL_CONFORMING_DELAUNAY_TRIANGULATION_3_H
|
|
#define CGAL_CONFORMING_DELAUNAY_TRIANGULATION_3_H
|
|
|
|
#include <CGAL/Triangulation_3/internal/CDT_3_config.h>
|
|
|
|
#include <CGAL/license/Triangulation_3.h>
|
|
|
|
#include <CGAL/Triangulation_2/internal/Polyline_constraint_hierarchy_2.h>
|
|
#include <CGAL/Triangulation_segment_traverser_3.h>
|
|
|
|
#include <CGAL/Mesh_3/io_signature.h>
|
|
#include <CGAL/IO/File_binary_mesh_3.h>
|
|
|
|
#include <boost/container/flat_set.hpp>
|
|
#include <boost/container/small_vector.hpp> /// @TODO Requires Boost 1.66
|
|
|
|
#include <fstream>
|
|
|
|
namespace CGAL {
|
|
|
|
enum class CDT_3_vertex_type { FREE, CORNER, STEINER_ON_EDGE, STEINER_IN_FACE };
|
|
|
|
template <typename Gt, typename Vb = Triangulation_vertex_base_3<Gt> >
|
|
class Conforming_Delaunay_triangulation_vertex_base_3 : public Vb {
|
|
public:
|
|
int nb_of_incident_constraints = 0;
|
|
void* c_id = nullptr;
|
|
|
|
// To get correct vertex type in TDS
|
|
template < class TDS3 >
|
|
struct Rebind_TDS {
|
|
typedef typename Vb::template Rebind_TDS<TDS3>::Other Vb3;
|
|
typedef Conforming_Delaunay_triangulation_vertex_base_3 <Gt, Vb3> Other;
|
|
};
|
|
|
|
using Vb::Vb;
|
|
|
|
template<typename Triangulation>
|
|
auto constraint_id(const Triangulation&) const {
|
|
using C_id = typename Triangulation::Constraint_id;
|
|
using Vertex_list_ptr = decltype(std::declval<C_id>().vl_ptr());
|
|
return C_id(static_cast<Vertex_list_ptr>(c_id));
|
|
}
|
|
|
|
CDT_3_vertex_type vertex_type() const { return m_vertex_type; }
|
|
void set_vertex_type(CDT_3_vertex_type type) { m_vertex_type = type; }
|
|
bool is_Steiner_vertex_on_edge() const { return m_vertex_type == CDT_3_vertex_type::STEINER_ON_EDGE; }
|
|
bool is_Steiner_vertex_in_face() const { return m_vertex_type == CDT_3_vertex_type::STEINER_IN_FACE; }
|
|
|
|
static std::string io_signature() {
|
|
return Get_io_signature<Vb>()();
|
|
}
|
|
private:
|
|
CDT_3_vertex_type m_vertex_type = CDT_3_vertex_type::FREE;
|
|
};
|
|
|
|
|
|
template <typename T_3>
|
|
class Conforming_Delaunay_triangulation_3 : public T_3 {
|
|
public:
|
|
using Vertex_handle = typename T_3::Vertex_handle;
|
|
using Edge = typename T_3::Edge;
|
|
using Facet = typename T_3::Facet;
|
|
using Cell_handle = typename T_3::Cell_handle;
|
|
using Point = typename T_3::Point;
|
|
using Line = typename T_3::Geom_traits::Line_3;
|
|
using Locate_type = typename T_3::Locate_type;
|
|
|
|
protected:
|
|
struct Compare_vertex_handle {
|
|
const T_3* tr;
|
|
Compare_vertex_handle(const T_3* tr) : tr(tr) {}
|
|
bool operator()(const Vertex_handle va, const Vertex_handle vb) const {
|
|
return tr->compare_xyz(tr->point(va), tr->point(vb)) == SMALLER;
|
|
}
|
|
};
|
|
|
|
using Constraint_hierarchy =
|
|
Polyline_constraint_hierarchy_2<Vertex_handle, Compare_vertex_handle, const Point&>;
|
|
public:
|
|
using Constraint_id = typename Constraint_hierarchy::Constraint_id;
|
|
|
|
protected:
|
|
using Subconstraint = typename Constraint_hierarchy::Subconstraint;
|
|
|
|
auto display_vert(Vertex_handle v) const{
|
|
std::stringstream os;
|
|
os.precision(17);
|
|
os << IO::oformat(v) << "=(" << this->tr.point(v) << ")";
|
|
return os.str();
|
|
};
|
|
auto display_subcstr(Subconstraint subconstraint) const {
|
|
std::stringstream os;
|
|
os << "[ " << display_vert(subconstraint.first)
|
|
<< " - " << display_vert(subconstraint.second) << " ]";
|
|
return os.str();
|
|
};
|
|
|
|
class Insert_in_conflict_visitor
|
|
{
|
|
Conforming_Delaunay_triangulation_3<T_3>& self;
|
|
public:
|
|
Insert_in_conflict_visitor(Conforming_Delaunay_triangulation_3& self) : self(self) {}
|
|
|
|
template <class InputIterator>
|
|
void process_cells_in_conflict(InputIterator cell_it, InputIterator end) {
|
|
for( ; cell_it != end; ++cell_it )
|
|
for( int i = 0; i < self.tr.dimension(); ++i )
|
|
for( int j = i+1; j < self.tr.dimension()+1; ++j ) {
|
|
auto v1 = (*cell_it)->vertex(i);
|
|
auto v2 = (*cell_it)->vertex(j);
|
|
if(self.tr.is_infinite(v1) || self.tr.is_infinite(v2)) continue;
|
|
auto [contexts_begin, contexts_end] =
|
|
self.constraint_hierarchy.contexts_range(v1, v2);
|
|
if(contexts_begin != contexts_end) {
|
|
self.add_to_subconstraints_to_conform(v1, v2,
|
|
contexts_begin->id());
|
|
}
|
|
}
|
|
}
|
|
void reinsert_vertices(Vertex_handle) const {}
|
|
Vertex_handle replace_vertex(Cell_handle c, int index, const Point& ) const
|
|
{
|
|
return c->vertex(index);
|
|
}
|
|
void hide_point(Cell_handle, const Point& ) const {}
|
|
|
|
void insert_Steiner_point_on_constraint([[maybe_unused]] Constraint_id constraint,
|
|
[[maybe_unused]] Vertex_handle va,
|
|
[[maybe_unused]] Vertex_handle vb,
|
|
[[maybe_unused]] Vertex_handle v_Steiner) const
|
|
{
|
|
}
|
|
|
|
Vertex_handle insert_in_triangulation(const Point& p, Locate_type lt, Cell_handle c, int li, int lj) {
|
|
return self.insert_impl_do_not_split(p, lt, c, li, lj, *this);
|
|
}
|
|
};
|
|
|
|
auto make_subconstraint(Vertex_handle va, Vertex_handle vb) {
|
|
return make_sorted_pair(va, vb);
|
|
}
|
|
|
|
void add_to_subconstraints_to_conform(Vertex_handle va, Vertex_handle vb,
|
|
Constraint_id id) {
|
|
const auto pair = make_subconstraint(va, vb);
|
|
#if CGAL_DEBUG_CDT_3 & 32
|
|
std::cerr << "tr.subconstraints_to_conform.push("
|
|
<< display_subcstr(pair) << ")\n";
|
|
#endif // CGAL_DEBUG_CDT_3
|
|
subconstraints_to_conform.push({pair, id});
|
|
}
|
|
|
|
template <typename Visitor>
|
|
Constraint_id insert_constrained_edge_impl(Vertex_handle va, Vertex_handle vb,
|
|
Visitor&) {
|
|
if(va != vb) {
|
|
const Constraint_id c_id = constraint_hierarchy.insert_constraint(va, vb);
|
|
pair_of_vertices_to_cid.emplace(make_sorted_pair(va, vb), c_id);
|
|
// traverse all the vertices along [va, vb] and add pairs of consecutive
|
|
// vertices as sub-constraints.
|
|
std::for_each(tr.segment_traverser_simplices_begin(va, vb), tr.segment_traverser_simplices_end(),
|
|
[&, prev = Vertex_handle{}](auto simplex) mutable {
|
|
// std::cerr << "- " << oformat(simplex, With_point_tag{}) << '\n';
|
|
if(simplex.dimension() == 0) {
|
|
const auto v = static_cast<Vertex_handle>(simplex);
|
|
v->c_id = c_id.vl_ptr();
|
|
++v->nb_of_incident_constraints;
|
|
if(prev != Vertex_handle{}) {
|
|
if(v != vb) {
|
|
v->set_vertex_type(CDT_3_vertex_type::STEINER_ON_EDGE);
|
|
constraint_hierarchy.add_Steiner(prev, vb, v);
|
|
}
|
|
add_to_subconstraints_to_conform(prev, v, c_id);
|
|
}
|
|
prev = v;
|
|
}
|
|
});
|
|
return c_id;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
template <typename Visitor>
|
|
Vertex_handle insert_impl_do_not_split(const Point &p, Locate_type lt, Cell_handle c,
|
|
int li, int lj, Visitor& visitor)
|
|
{
|
|
switch (tr.dimension()) {
|
|
case 3: {
|
|
typename T_3::Conflict_tester_3 tester(p, this);
|
|
return tr.insert_in_conflict(p, lt, c, li, lj, tester,
|
|
visitor);
|
|
} // dim 3
|
|
case 2: {
|
|
typename T_3::Conflict_tester_2 tester(p, this);
|
|
return tr.insert_in_conflict(p, lt, c, li, lj, tester,
|
|
visitor);
|
|
} // dim 2
|
|
default:
|
|
// dimension <= 1
|
|
// Do not use the generic insert.
|
|
return tr.insert(p, c);
|
|
}
|
|
}
|
|
|
|
template <typename Visitor>
|
|
Vertex_handle insert_impl(const Point &p, Locate_type lt, Cell_handle c,
|
|
int li, int lj, Visitor& visitor)
|
|
{
|
|
Vertex_handle v1, v2;
|
|
bool split_constrained_edge = false;
|
|
if(lt == T_3::EDGE) {
|
|
v1 = c->vertex(li);
|
|
v2 = c->vertex(lj);
|
|
if(constraint_hierarchy.is_subconstrained_edge(v1, v2)) {
|
|
split_constrained_edge = true;
|
|
}
|
|
}
|
|
Vertex_handle new_vertex = insert_impl_do_not_split(p, lt, c, li, lj, visitor);
|
|
if(split_constrained_edge) {
|
|
constraint_hierarchy.split_constraint(v1, v2, new_vertex);
|
|
}
|
|
return new_vertex;
|
|
}
|
|
|
|
void update_bbox(const Point& p) {
|
|
bbox = bbox + p.bbox();
|
|
if(max_bbox_edge_length) {
|
|
update_max_bbox_edge_length();
|
|
}
|
|
}
|
|
|
|
void update_max_bbox_edge_length() {
|
|
double d_x = bbox.xmax() - bbox.xmin();
|
|
double d_y = bbox.ymax() - bbox.ymin();
|
|
double d_z = bbox.zmax() - bbox.zmin();
|
|
|
|
max_bbox_edge_length = (std::max)(d_x, (std::max)(d_y, d_z));
|
|
}
|
|
|
|
public:
|
|
void set_segment_vertex_epsilon(double epsilon) {
|
|
segment_vertex_epsilon = epsilon;
|
|
}
|
|
|
|
Vertex_handle insert(const Point &p, Locate_type lt, Cell_handle c,
|
|
int li, int lj)
|
|
{
|
|
update_bbox(p);
|
|
auto v = insert_impl(p, lt, c, li, lj, insert_in_conflict_visitor);
|
|
restore_Delaunay(insert_in_conflict_visitor);
|
|
return v;
|
|
}
|
|
|
|
Vertex_handle insert(const Point &p, Cell_handle start = {}) {
|
|
Locate_type lt;
|
|
int li, lj;
|
|
|
|
Cell_handle c = tr.locate(p, lt, li, lj, start);
|
|
return insert(p, lt, c, li, lj);
|
|
}
|
|
|
|
Constraint_id insert_constrained_edge(Vertex_handle va, Vertex_handle vb)
|
|
{
|
|
const auto id = insert_constrained_edge_impl(va, vb, insert_in_conflict_visitor);
|
|
restore_Delaunay(insert_in_conflict_visitor);
|
|
return id;
|
|
}
|
|
|
|
bool is_conforming() const {
|
|
return std::all_of(constraint_hierarchy.sc_begin(),
|
|
constraint_hierarchy.sc_end(),
|
|
[this](const auto &sc) {
|
|
const auto va = sc.first.first;
|
|
const auto vb = sc.first.second;
|
|
const auto is_edge = this->tr.tds().is_edge(va, vb);
|
|
#if CGAL_DEBUG_CDT_3 & 128 && __has_include(<format>)
|
|
std::cerr << std::format("is_conforming>> Edge is 3D: {} ({} , {})\n",
|
|
is_edge,
|
|
CGAL::IO::oformat(this->point(va)),
|
|
CGAL::IO::oformat(this->point(vb)));
|
|
#endif // CGAL_DEBUG_CDT_3
|
|
return is_edge;
|
|
});
|
|
}
|
|
|
|
bool write_missing_segments_file(std::ostream &out) {
|
|
bool any_missing_segment = false;
|
|
std::for_each(
|
|
constraint_hierarchy.sc_begin(), constraint_hierarchy.sc_end(),
|
|
[this, &out, &any_missing_segment](const auto &sc) {
|
|
if (!tr.tds().is_edge(sc.first.first, sc.first.second)) {
|
|
const auto v0 = sc.first.first;
|
|
const auto v1 = sc.first.second;
|
|
out << "2 " << this->tr.point(v0) << " " << this->tr.point(v1)
|
|
<< '\n';
|
|
any_missing_segment = true;
|
|
}
|
|
});
|
|
return any_missing_segment;
|
|
}
|
|
|
|
void write_all_segments_file(std::ostream &out) {
|
|
std::for_each(
|
|
constraint_hierarchy.sc_begin(), constraint_hierarchy.sc_end(),
|
|
[this, &out](const auto &sc) {
|
|
const auto v0 = sc.first.first;
|
|
const auto v1 = sc.first.second;
|
|
out << "2 " << this->tr.point(v0) << " " << this->tr.point(v1) << '\n';
|
|
});
|
|
}
|
|
|
|
/// @{
|
|
/// remove functions cannot be called
|
|
void remove(Vertex_handle) = delete;
|
|
void remove_cluster() = delete;
|
|
/// @}
|
|
|
|
static std::string io_signature() {
|
|
return Get_io_signature<T_3>()();
|
|
}
|
|
protected:
|
|
void debug_dump(std::string filename) {
|
|
{
|
|
std::ofstream dump(filename + ".binary.cgal", std::ios::binary);
|
|
CGAL::IO::save_binary_file(dump, *this);
|
|
}
|
|
{
|
|
std::ofstream dump(filename + "_point.xyz");
|
|
dump.precision(17);
|
|
for(auto vh: this->finite_vertex_handles()){
|
|
dump << this->point(vh) << '\n';
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename Visitor>
|
|
void restore_Delaunay(Visitor& visitor) {
|
|
while(!subconstraints_to_conform.empty()) {
|
|
auto pair = subconstraints_to_conform.top();
|
|
subconstraints_to_conform.pop();
|
|
if(!constraint_hierarchy.is_subconstrained_edge(pair.first.first,
|
|
pair.first.second)) {
|
|
continue;
|
|
}
|
|
#if CGAL_DEBUG_CDT_3 & 32
|
|
std::cerr << "tr.subconstraints_to_conform.pop()="
|
|
<< display_subcstr(pair.first) << "\n";
|
|
#endif // CGAL_DEBUG_CDT_3
|
|
conform_subconstraint(pair.first, pair.second, visitor);
|
|
}
|
|
}
|
|
|
|
template <typename Visitor>
|
|
Vertex_handle insert_Steiner_point_on_subconstraint(
|
|
Point steiner_pt, Cell_handle hint,
|
|
Subconstraint subconstraint, Constraint_id constraint, Visitor& visitor)
|
|
{
|
|
const Vertex_handle va = subconstraint.first;
|
|
const Vertex_handle vb = subconstraint.second;
|
|
Locate_type lt;
|
|
int li, lj;
|
|
const Cell_handle c = tr.locate(steiner_pt, lt, li, lj, hint);
|
|
const Vertex_handle v = visitor.insert_in_triangulation(steiner_pt, lt, c, li, lj);
|
|
v->set_vertex_type(CDT_3_vertex_type::STEINER_ON_EDGE);
|
|
if(lt != T_3::VERTEX) {
|
|
v->nb_of_incident_constraints = 1;
|
|
v->c_id = constraint.vl_ptr();
|
|
}
|
|
constraint_hierarchy.add_Steiner(va, vb, v);
|
|
visitor.insert_Steiner_point_on_constraint(constraint, va, vb, v);
|
|
add_to_subconstraints_to_conform(va, v, constraint);
|
|
add_to_subconstraints_to_conform(v, vb, constraint);
|
|
return v;
|
|
}
|
|
|
|
/// Return `true` if a Steiner point was inserted
|
|
template <typename Visitor>
|
|
bool conform_subconstraint(Subconstraint subconstraint,
|
|
Constraint_id constraint,
|
|
Visitor& visitor)
|
|
{
|
|
const Vertex_handle va = subconstraint.first;
|
|
const Vertex_handle vb = subconstraint.second;
|
|
CGAL_assertion(va != vb);
|
|
if (!tr.tds().is_edge(va, vb)) {
|
|
const auto& [steiner_pt, hint, ref_vertex] = construct_Steiner_point(constraint, subconstraint);
|
|
[[maybe_unused]] const auto v =
|
|
insert_Steiner_point_on_subconstraint(steiner_pt, hint, subconstraint, constraint, visitor);
|
|
#if CGAL_CDT_3_DEBUG_CONFORMING
|
|
std::cerr << " new vertex " << display_vert(v) << '\n';
|
|
#endif // CGAL_CDT_3_DEBUG_CONFORMING
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Constraint_id constraint_from_extremities(Vertex_handle va, Vertex_handle vb) const {
|
|
if (va->nb_of_incident_constraints == 0 || vb->nb_of_incident_constraints == 0)
|
|
{
|
|
return {};
|
|
}
|
|
auto it = pair_of_vertices_to_cid.find(make_sorted_pair(va, vb));
|
|
if(it != pair_of_vertices_to_cid.end()) {
|
|
return it->second;
|
|
}
|
|
return {};
|
|
// @TODO: cleanup the rest of the function, and `constraint_around`
|
|
Constraint_id c_id = constraint_around(va, vb, false);
|
|
if(c_id != Constraint_id{}) return c_id;
|
|
c_id = constraint_around(vb, va, false);
|
|
if(c_id != Constraint_id{}) return c_id;
|
|
c_id = constraint_around(va, vb, true);
|
|
return c_id;
|
|
}
|
|
|
|
auto constraint_extremities(Constraint_id c_id) const {
|
|
CGAL_assertion(std::find(this->constraint_hierarchy.c_begin(),
|
|
this->constraint_hierarchy.c_end(), c_id) != this->constraint_hierarchy.c_end());
|
|
CGAL_assertion(this->constraint_hierarchy.vertices_in_constraint_begin(c_id) !=
|
|
this->constraint_hierarchy.vertices_in_constraint_end(c_id));
|
|
#if CGAL_DEBUG_CDT_3 & 8
|
|
std::cerr << "constraint " << (void*) c_id.vl_ptr() << " has "
|
|
<< c_id.vl_ptr()->skip_size() << " vertices\n";
|
|
#endif // CGAL_DEBUG_CDT_3
|
|
auto it = this->constraint_hierarchy.vertices_in_constraint_begin(c_id);
|
|
const auto end = this->constraint_hierarchy.vertices_in_constraint_end(c_id);
|
|
const auto c_va = *it;
|
|
while(std::next(it) != end)
|
|
++it;
|
|
const auto c_vb = *it;
|
|
return std::make_pair(c_va, c_vb);
|
|
}
|
|
|
|
Constraint_id constraint_around(Vertex_handle va, Vertex_handle vb, bool expensive = true) const {
|
|
auto constraint_id_goes_to_vb = [this, va, vb](Constraint_id c_id) {
|
|
const auto [c_va, c_vb] = constraint_extremities(c_id);
|
|
if (va == c_va && vb == c_vb)
|
|
return true;
|
|
if (vb == c_va && va == c_vb)
|
|
return true;
|
|
return false;
|
|
}; // end lambda constraint_id_goes_to_vb
|
|
if (va->nb_of_incident_constraints == 1)
|
|
{
|
|
const Constraint_id c_id = va->constraint_id(*this);
|
|
CGAL_assertion(c_id != Constraint_id{});
|
|
if(constraint_id_goes_to_vb(c_id)) return c_id;
|
|
} else if (expensive == true && va->nb_of_incident_constraints > 1) {
|
|
boost::container::small_vector<Vertex_handle, 64> adj_vertices;
|
|
this->finite_adjacent_vertices(va, std::back_inserter(adj_vertices));
|
|
for(auto other_v: adj_vertices) {
|
|
for(auto context: this->constraint_hierarchy.contexts_range(va, other_v)) {
|
|
const Constraint_id c_id = context.id();
|
|
if(constraint_id_goes_to_vb(c_id)) return c_id;
|
|
}
|
|
}
|
|
}
|
|
return Constraint_id{};
|
|
}
|
|
|
|
struct Construct_Steiner_point_return_type {
|
|
typename T_3::Geom_traits::Point_3 point;
|
|
Cell_handle hint;
|
|
Vertex_handle reference_vertex;
|
|
};
|
|
|
|
Construct_Steiner_point_return_type
|
|
construct_Steiner_point(Constraint_id constraint_id, Subconstraint subconstraint)
|
|
{
|
|
auto& gt = tr.geom_traits();
|
|
auto angle_functor = gt.angle_3_object();
|
|
auto compare_angle_functor = gt.compare_angle_3_object();
|
|
auto vector_functor = gt.construct_vector_3_object();
|
|
auto midpoint_functor = gt.construct_midpoint_3_object();
|
|
auto scaled_vector_functor = gt.construct_scaled_vector_3_object();
|
|
auto sq_length_functor = gt.compute_squared_length_3_object();
|
|
auto sc_product_functor = gt.compute_scalar_product_3_object();
|
|
auto translate_functor = gt.construct_translated_point_3_object();
|
|
|
|
const Vertex_handle va = subconstraint.first;
|
|
const Vertex_handle vb = subconstraint.second;
|
|
const auto& pa = tr.point(va);
|
|
const auto& pb = tr.point(vb);
|
|
const auto [orig_va, orig_vb] = constraint_extremities(constraint_id);
|
|
const auto& orig_pa = tr.point(orig_va);
|
|
const auto& orig_pb = tr.point(orig_vb);
|
|
|
|
if(this->dimension() < 2) {
|
|
std::cerr << "dim < 2: midpoint\n";
|
|
return {midpoint_functor(pa, pb), va->cell(), va};
|
|
}
|
|
|
|
#if CGAL_DEBUG_CDT_3 & 0x10
|
|
std::cerr << "construct_Steiner_point( " << display_vert(va) << " , "
|
|
<< display_vert(vb) << " )\n";
|
|
auto debug_simplex = [&](auto simplex) {
|
|
std::cerr << " - " << IO::oformat(simplex, With_point_tag{}) << '\n';
|
|
};
|
|
#endif // CGAL_DEBUG_CDT_3
|
|
|
|
namespace bc = boost::container;
|
|
bc::flat_set<Vertex_handle, std::less<Vertex_handle>,
|
|
bc::small_vector<Vertex_handle, 256>>
|
|
encroaching_vertices;
|
|
auto register_vertex = [this,&encroaching_vertices](Vertex_handle v) {
|
|
if(tr.is_infinite(v)) return;
|
|
// std::cerr << "register_vertex " << display_vert(v) << '\n';
|
|
encroaching_vertices.insert(v);
|
|
};
|
|
auto fill_encroaching_vertices = [&](const auto simplex) {
|
|
#if CGAL_DEBUG_CDT_3 & 0x10
|
|
debug_simplex(simplex);
|
|
#endif // CGAL_DEBUG_CDT_3
|
|
auto visit_cell = [&](Cell_handle cell) {
|
|
for(int i = 0, end = this->tr.dimension() + 1; i < end; ++i) {
|
|
const auto v = cell->vertex(i);
|
|
register_vertex(v);
|
|
}
|
|
};
|
|
switch(simplex.dimension()) {
|
|
case 3: {
|
|
const auto cell = static_cast<Cell_handle>(simplex);
|
|
visit_cell(cell);
|
|
} break;
|
|
case 2: {
|
|
const auto [cell, facet_index] = static_cast<Facet>(simplex);
|
|
visit_cell(cell);
|
|
const auto [other_cell, other_index] = tr.mirror_facet({cell, facet_index});
|
|
register_vertex(other_cell->vertex(other_index));
|
|
break;
|
|
}
|
|
case 1: {
|
|
auto circ = tr.incident_cells(static_cast<Edge>(simplex));
|
|
CGAL_assertion(circ != nullptr);
|
|
const auto end = circ;
|
|
do {
|
|
visit_cell(circ);
|
|
} while(++circ != end);
|
|
} break;
|
|
case 0: {
|
|
const auto v = static_cast<Vertex_handle>(simplex);
|
|
if(v != va && v != vb) {
|
|
std::cerr << "!! The constraint passes through a vertex! ";
|
|
std::cerr << " -> " << display_vert(v) << '\n';
|
|
debug_dump("bug-through-vertex");
|
|
CGAL_error();
|
|
}
|
|
} break;
|
|
default: CGAL_unreachable();
|
|
} // end switch
|
|
};
|
|
std::for_each(tr.segment_traverser_simplices_begin(va, vb), tr.segment_traverser_simplices_end(),
|
|
fill_encroaching_vertices);
|
|
auto vector_of_encroaching_vertices = encroaching_vertices.extract_sequence();
|
|
#if CGAL_DEBUG_CDT_3 & 0x10
|
|
std::cerr << " -> vector_of_encroaching_vertices (before filter):\n";
|
|
std::for_each(vector_of_encroaching_vertices.begin(),
|
|
vector_of_encroaching_vertices.end(),
|
|
[this](Vertex_handle v){
|
|
std::cerr << " " << this->display_vert(v) << '\n';
|
|
});
|
|
#endif // CGAL_DEBUG_CDT_3
|
|
auto end = std::remove_if(vector_of_encroaching_vertices.begin(),
|
|
vector_of_encroaching_vertices.end(),
|
|
[va, vb, pa, pb, &angle_functor, this](Vertex_handle v) {
|
|
if(va == v || vb == v) return true;
|
|
return angle_functor(pa,
|
|
this->tr.point(v),
|
|
pb) == ACUTE;
|
|
});
|
|
#if CGAL_DEBUG_CDT_3 & 0x10
|
|
std::cerr << " -> vector_of_encroaching_vertices (after filter):\n";
|
|
std::for_each(vector_of_encroaching_vertices.begin(), end,
|
|
[this](Vertex_handle v){
|
|
std::cerr << " " << this->display_vert(v) << '\n';
|
|
});
|
|
#endif // CGAL_DEBUG_CDT_3
|
|
CGAL_assertion(vector_of_encroaching_vertices.begin() != end);
|
|
|
|
const auto reference_vertex_it = std::max_element(
|
|
vector_of_encroaching_vertices.begin(), end,
|
|
[pa, pb, &compare_angle_functor, this](Vertex_handle v1,
|
|
Vertex_handle v2) {
|
|
return compare_angle_functor(pa, this->tr.point(v1), pb,
|
|
pa, this->tr.point(v2), pb) == SMALLER;
|
|
});
|
|
CGAL_assertion(reference_vertex_it != end);
|
|
#if CGAL_CDT_3_DEBUG_CONFORMING
|
|
std::cerr << " -> reference point: " << display_vert(*reference_vertex_it)
|
|
<< '\n';
|
|
#endif // CGAL_CDT_3_DEBUG_CONFORMING
|
|
const auto reference_vertex = *reference_vertex_it;
|
|
const auto& reference_point = tr.point(reference_vertex);
|
|
|
|
const auto vector_ab = vector_functor(pa, pb);
|
|
|
|
if(reference_vertex->is_Steiner_vertex_on_edge()) {
|
|
CGAL_assertion(reference_vertex->nb_of_incident_constraints == 1);
|
|
const auto ref_constraint_id = reference_vertex->constraint_id(*this);
|
|
const auto [ref_va, ref_vb] = constraint_extremities(ref_constraint_id);
|
|
#if CGAL_CDT_3_DEBUG_CONFORMING
|
|
std::cerr << " reference point is on constraint: " << display_vert(ref_va)
|
|
<< " " << display_vert(ref_vb) << '\n'
|
|
<< " original constraint: " << display_vert(orig_va)
|
|
<< " " << display_vert(orig_vb) << '\n';
|
|
#endif // CGAL_CDT_3_DEBUG_CONFORMING
|
|
const auto vector_orig_ab = vector_functor(orig_pa, orig_pb);
|
|
const auto length_ab = CGAL::approximate_sqrt(sq_length_functor(vector_ab));
|
|
auto return_orig_result_point =
|
|
[&](auto lambda, Point orig_pa, Point orig_pb)
|
|
-> Construct_Steiner_point_return_type
|
|
{
|
|
const auto vector_orig_ab = vector_functor(orig_pa, orig_pb);
|
|
const auto inter_point = translate_functor(orig_pa, scaled_vector_functor(vector_orig_ab, lambda));
|
|
const auto dist_a_result = CGAL::approximate_sqrt(sq_length_functor(vector_functor(pa, inter_point)));
|
|
const auto ratio = dist_a_result / length_ab;
|
|
const auto result_point = (ratio < 0.2 || ratio > 0.8)
|
|
? midpoint_functor(pa, pb)
|
|
: inter_point;
|
|
|
|
#if CGAL_CDT_3_DEBUG_CONFORMING
|
|
std::cerr << " ref ratio = " << ratio << '\n';
|
|
std::cerr << " -> Steiner point: " << result_point << '\n';
|
|
#endif // CGAL_CDT_3_DEBUG_CONFORMING
|
|
return {result_point, reference_vertex->cell(), reference_vertex};
|
|
};
|
|
|
|
const auto length_orig_ab = CGAL::approximate_sqrt(sq_length_functor(vector_orig_ab));
|
|
if(ref_va == orig_va || ref_vb == orig_va) {
|
|
const auto vector_orig_a_ref = vector_functor(orig_pa, reference_point);
|
|
const auto length_orig_a_ref = CGAL::approximate_sqrt(sq_length_functor(vector_orig_a_ref));
|
|
const auto lambda = length_orig_a_ref / length_orig_ab;
|
|
return return_orig_result_point(lambda, orig_pa, orig_pb);
|
|
} else if(ref_va == orig_vb || ref_vb == orig_vb) {
|
|
const auto vector_orig_b_ref = vector_functor(orig_pb, reference_point);
|
|
const auto length_orig_b_ref = CGAL::approximate_sqrt(sq_length_functor(vector_orig_b_ref));
|
|
const auto lambda = length_orig_b_ref / length_orig_ab;
|
|
return return_orig_result_point(lambda, orig_pb, orig_pa);
|
|
}
|
|
} else {
|
|
if(segment_vertex_epsilon > 0) {
|
|
if(!max_bbox_edge_length) {
|
|
update_max_bbox_edge_length();
|
|
}
|
|
auto sq_dist = squared_distance(reference_point, Line{orig_pa, orig_pb});
|
|
if(sq_dist < CGAL::square(segment_vertex_epsilon * *max_bbox_edge_length)) {
|
|
std::stringstream ss;
|
|
ss.precision(std::cerr.precision());
|
|
ss << "A constrained segment is too close to a vertex.\n";
|
|
ss << " -> vertex " << display_vert(reference_vertex) << '\n';
|
|
ss << " -> constrained segment " << display_vert(orig_va) << " - " << display_vert(orig_vb) << '\n';
|
|
ss << " -> distance = " << CGAL::approximate_sqrt(sq_dist) << '\n';
|
|
ss << " -> max_bbox_edge_length = " << *max_bbox_edge_length << '\n';
|
|
throw std::runtime_error(ss.str());
|
|
}
|
|
}
|
|
}
|
|
// compute the projection of the reference point
|
|
const auto vector_a_ref = vector_functor(pa, reference_point);
|
|
const auto lambda = sc_product_functor(vector_a_ref, vector_ab) / sq_length_functor(vector_ab);
|
|
const auto result_point = (lambda < 0.2 || lambda > 0.8)
|
|
? midpoint_functor(pa, pb)
|
|
: translate_functor(pa, scaled_vector_functor(vector_ab, lambda));
|
|
|
|
#if CGAL_CDT_3_DEBUG_CONFORMING
|
|
std::cerr << " lambda = " << lambda << '\n';
|
|
std::cerr << " -> Steiner point: " << result_point << '\n';
|
|
#endif // CGAL_CDT_3_DEBUG_CONFORMING
|
|
return {result_point, reference_vertex->cell(), reference_vertex};
|
|
}
|
|
|
|
protected:
|
|
T_3& tr = *this;
|
|
Compare_vertex_handle comp = {this};
|
|
Constraint_hierarchy constraint_hierarchy = {comp};
|
|
Bbox_3 bbox{};
|
|
double segment_vertex_epsilon = 1e-8;
|
|
std::optional<double> max_bbox_edge_length;
|
|
std::map<std::pair<Vertex_handle, Vertex_handle>, Constraint_id> pair_of_vertices_to_cid;
|
|
Insert_in_conflict_visitor insert_in_conflict_visitor = {*this};
|
|
|
|
std::stack<std::pair<Subconstraint, Constraint_id> >
|
|
subconstraints_to_conform;
|
|
};
|
|
|
|
} // end CGAL
|
|
|
|
#endif // CGAL_CONFORMING_DELAUNAY_TRIANGULATION_3_H
|