Merge pull request #8186 from lrineau/Triangulation_3-CDT_3-lrineau

New package: CGAL 3D conforming constrained Delaunay triangulations
This commit is contained in:
Sébastien Loriot 2025-06-26 21:57:17 +02:00
commit 78cfeacf01
235 changed files with 97872 additions and 2217 deletions

24
.gitignore vendored
View File

@ -1148,3 +1148,27 @@ Polygonal_surface_reconstruction/examples/build*
Polygonal_surface_reconstruction/test/build*
Solver_interface/examples/build*
/Mesh_3/examples/Mesh_3/indicator_0.inr.gz
/*.off
/*.xyz
/r0*
all_segments.polylines.txt
Data/data/meshes/*.*.edge
Data/data/meshes/*.*.ele
Data/data/meshes/*.*.face
Data/data/meshes/*.*.mesh
Data/data/meshes/*.*.node
Data/data/meshes/*.*.smesh
Data/data/meshes/*.*.vtk
Data/data/meshes/*.log
Data/data/meshes/*.off-cdt-output.off
dump_*.off
dump_*.txt
dump-*.binary.cgal
dump-*.polylines.txt
dump-*.xyz
dump.off.mesh
log.txt
patches_after_merge.ply
CMakeUserPresets.json
/.cache
compile_commands.json

View File

@ -302,12 +302,11 @@ public:
typename AT::Bounding_box operator()(ConstPrimitiveIterator first,
ConstPrimitiveIterator beyond) const
{
typename AT::Bounding_box bbox = m_traits.compute_bbox(*first,m_traits.bbm);
for(++first; first != beyond; ++first)
{
bbox = bbox + m_traits.compute_bbox(*first,m_traits.bbm);
}
return bbox;
return std::accumulate(first, beyond,
typename AT::Bounding_box{} /* empty bbox */,
[this](const typename AT::Bounding_box& bbox, const Primitive& pr) {
return bbox + m_traits.compute_bbox(pr, m_traits.bbm);
});
}
};

View File

@ -199,9 +199,7 @@ namespace CGAL {
}
/// returns the axis-aligned bounding box of the whole tree.
/// \pre `!empty()`
const Bounding_box bbox() const {
CGAL_precondition(!empty());
if(size() > 1)
return root_node()->bbox();
else

View File

@ -269,7 +269,7 @@ Kml::Nodes Kml::generate_ids_approx(Placemarks& placemarks, const double eps) {
for (const auto& node : lring->nodes) {
// check if there is a node sufficiently close to the current one
auto node_index = std::numeric_limits<std::size_t>::max();
auto node_index = (std::numeric_limits<std::size_t>::max)();
for (std::size_t i = 0; i < nodes.size(); ++i) {
const auto dist = node.distance_to(nodes[i]);
if (dist < eps) {
@ -278,7 +278,7 @@ Kml::Nodes Kml::generate_ids_approx(Placemarks& placemarks, const double eps) {
}
}
if (node_index == std::numeric_limits<std::size_t>::max()) {
if (node_index == (std::numeric_limits<std::size_t>::max)()) {
// insert new node
nodes.push_back(node);
const auto node_id = nodes.size() - 1;
@ -301,7 +301,7 @@ Kml::Nodes Kml::generate_ids_approx(Placemarks& placemarks, const double eps) {
}
// find the pair of closest nodes
double min_dist = std::numeric_limits<double>::max();
double min_dist = (std::numeric_limits<double>::max)();
std::size_t ni1 = 0;
std::size_t ni2 = 0;
std::size_t num_nodes = nodes.size();

View File

@ -31,7 +31,7 @@ digraph example {
}
\enddot
\cgalHeading{Notations}
\cgalHeading{Notations}
<dl>
<dt>`G`</dt> <dd>A type that is a model of a graph concept.</dd>
@ -51,8 +51,8 @@ and adds the requirement for traversal of all vertices in a graph.
<center>
<table>
<tr>
<th width="800">Associated Type</th>
<th width="800">Description</th>
<th class="associated-type">Associated Type</th>
<th class="description">Description</th>
</tr>
<tr>
<td>`boost::graph_traits<G>::%vertex_iterator`</td>
@ -66,9 +66,9 @@ and adds the requirement for traversal of all vertices in a graph.
<table>
<tr>
<th width="392">Valid Expression</th>
<th width="392">Return Type</th>
<th width="800">Description</th>
<th class="valid-expression">Valid Expression</th>
<th class="return-type">Return Type</th>
<th class="description">Description</th>
</tr>
<tr>
<td>`vertices(g)`</td>
@ -92,8 +92,8 @@ and adds the requirement for traversal of all edges in a graph.
<center>
<table>
<tr>
<th width="800">Associated Type</th>
<th width="800">Description</th>
<th class="associated-type">Associated Type</th>
<th class="description">Description</th>
</tr>
<tr>
<td>`boost::graph_traits<G>::%edge_iterator`</td>
@ -107,9 +107,9 @@ and adds the requirement for traversal of all edges in a graph.
<table>
<tr>
<th width="392">Valid Expression</th>
<th width="392">Return Type</th>
<th width="800">Description</th>
<th class="valid-expression">Valid Expression</th>
<th class="return-type">Return Type</th>
<th class="description">Description</th>
</tr>
<tr>
<td>`edges(g)`</td>
@ -143,8 +143,8 @@ and adds the notion of halfedges, where each edge corresponds to two opposite ha
<center>
<table>
<tr>
<th width="800">Associated Type</th>
<th width="800">Description</th>
<th class="associated-type">Associated Type</th>
<th class="description">Description</th>
</tr>
<tr>
<td>`boost::graph_traits<G>::%halfedge_descriptor`</td>
@ -154,9 +154,9 @@ and adds the notion of halfedges, where each edge corresponds to two opposite ha
<table>
<tr>
<th width="392">Valid Expression</th>
<th width="392">Return Type</th>
<th width="800">Description</th>
<th class="valid-expression">Valid Expression</th>
<th class="return-type">Return Type</th>
<th class="description">Description</th>
</tr>
<tr>
<td>`edge(h, g)`</td>
@ -222,9 +222,9 @@ update the incidence information between vertices and halfedges.
<center>
<table>
<tr>
<th width="392">Valid Expression</th>
<th width="392">Return Type</th>
<th width="800">Description</th>
<th class="valid-expression">Valid Expression</th>
<th class="return-type">Return Type</th>
<th class="description">Description</th>
</tr>
<tr>
<td>`add_vertex(g)`</td>
@ -272,8 +272,8 @@ and adds the requirements for traversal of all halfedges in the graph.
<center>
<table>
<tr>
<th width="800">Associated Type</th>
<th width="800">Description</th>
<th class="associated-type">Associated Type</th>
<th class="description">Description</th>
</tr>
<tr>
<td>`boost::graph_traits<G>::%halfedge_iterator`</td>
@ -287,9 +287,9 @@ and adds the requirements for traversal of all halfedges in the graph.
<table>
<tr>
<th width="392">Valid Expression</th>
<th width="392">Return Type</th>
<th width="800">Description</th>
<th class="valid-expression">Valid Expression</th>
<th class="return-type">Return Type</th>
<th class="description">Description</th>
</tr>
<tr>
<td>`halfedges(g)`</td>
@ -315,8 +315,8 @@ face.
<center>
<table>
<tr>
<th width="800">Associated Type</th>
<th width="800">Description</th>
<th class="associated-type">Associated Type</th>
<th class="description">Description</th>
</tr>
<tr>
<td>`boost::graph_traits<G>::%face_descriptor`</td>
@ -326,9 +326,9 @@ face.
<table>
<tr>
<th width="392">Valid Expression</th>
<th width="392">Return Type</th>
<th width="800">Description</th>
<th class="valid-expression">Valid Expression</th>
<th class="return-type">Return Type</th>
<th class="description">Description</th>
</tr>
<tr>
<td>`face(h, g)`</td>
@ -361,9 +361,9 @@ the requirement for operations to add faces and to modify face-halfedge relation
<center>
<table>
<tr>
<th width="392">Valid Expression</th>
<th width="392">Return Type</th>
<th width="800">Description</th>
<th class="valid-expression">Valid Expression</th>
<th class="return-type">Return Type</th>
<th class="description">Description</th>
</tr>
<tr>
<td>`add_face(g)`</td>
@ -401,8 +401,8 @@ the requirement for traversal of all faces in a graph.
<center>
<table>
<tr>
<th width="800">Associated Type</th>
<th width="800">Description</th>
<th class="associated-type">Associated Type</th>
<th class="description">Description</th>
</tr>
<tr>
<td>`boost::graph_traits<G>::%face_iterator`</td>
@ -416,9 +416,9 @@ the requirement for traversal of all faces in a graph.
<table>
<tr>
<th width="392">Valid Expression</th>
<th width="392">Return Type</th>
<th width="800">Description</th>
<th class="valid-expression">Valid Expression</th>
<th class="return-type">Return Type</th>
<th class="description">Description</th>
</tr>
<tr>
<td>`faces(g)`</td>

View File

@ -227,6 +227,32 @@ struct GetGeomTraits
NamedParametersVPM>::type type;
};
namespace internal {
// Similar helper for polygon soups
template <typename PointRange, typename PolygonRange>
struct Polygon_types
{
typedef typename boost::range_value<PointRange>::type Point_3;
typedef typename boost::range_value<PolygonRange>::type Polygon_3;
typedef typename boost::range_iterator<Polygon_3>::type V_ID_iterator;
typedef typename std::iterator_traits<V_ID_iterator>::value_type V_ID;
typedef typename std::vector<Polygon_3>::size_type P_ID;
};
}
template <typename PointRange, typename PolygonRange, typename NamedParameters>
struct GetPolygonGeomTraits
{
typedef typename internal_np::Lookup_named_param_def <
internal_np::geom_traits_t,
NamedParameters,
typename CGAL::Kernel_traits<
typename internal::Polygon_types<
PointRange, PolygonRange>::Point_3 >::type
> ::type type;
};
// Define the following structs:
//
// GetInitializedVertexIndexMap

View File

@ -16,7 +16,6 @@
#include <map>
#include <vector>
#include <utility>
#include <boost/graph/adjacency_list.hpp>
#include <CGAL/assertions.h>
#include <CGAL/tags.h>
@ -143,32 +142,27 @@ void duplicate_terminal_vertices(Graph& graph,
{
typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex_descriptor;
typedef typename boost::graph_traits<Graph>::edge_descriptor edge_descriptor;
typedef typename boost::graph_traits<Graph>::vertex_iterator vertex_iterator;
typedef typename boost::graph_traits<Graph>::out_edge_iterator out_edge_iterator;
vertex_iterator b,e;
std::tie(b,e) = vertices(graph);
std::vector<vertex_descriptor> V(b,e);
auto [b, e] = vertices(graph);
std::vector<vertex_descriptor> V(b, e); // copy vertices, because the graph may change
std::vector<edge_descriptor> out_edges_of_v; // used to store the out edges of a vertex
// created here to avoid allocating it in the loop
for(vertex_descriptor v : V)
{
typename boost::graph_traits<OrigGraph>::vertex_descriptor orig_v = graph[v];
typename boost::graph_traits<Graph>::degree_size_type deg = degree(v, graph);
auto orig_v = graph[v];
auto deg = degree(v, graph);
if (deg != 2 || is_terminal(orig_v, orig))
{
out_edge_iterator b, e;
std::tie(b, e) = out_edges(v, graph);
std::vector<edge_descriptor> out_edges_of_v(b, e);
auto [b, e] = out_edges(v, graph);
out_edges_of_v.assign(b, e); // same as creating a new vector from the range [b,e)
for (unsigned int i = 1; i < out_edges_of_v.size(); ++i)
{
edge_descriptor e = out_edges_of_v[i];
typename boost::graph_traits<OrigGraph>::edge_descriptor orig_e =
graph[e];
auto orig_e = graph[e];
vertex_descriptor w = target(e, graph);
remove_edge(e, graph);
vertex_descriptor vc = add_vertex(graph);
graph[vc] = orig_v;
const std::pair<edge_descriptor, bool> pair = add_edge(vc, w, graph);
graph[pair.first] = orig_e;
vertex_descriptor vc = add_vertex(orig_v, graph);
add_edge(vc, w, orig_e, graph);
}
}
}
@ -271,8 +265,7 @@ split_graph_into_polylines(const Graph& graph,
V2vmap v2vmap;
for(Graph_vertex_descriptor v : make_range(vertices(graph))){
vertex_descriptor vc = add_vertex(g_copy);
g_copy[vc] = v;
vertex_descriptor vc = add_vertex(v, g_copy);
v2vmap[v] = vc;
}
@ -281,9 +274,7 @@ split_graph_into_polylines(const Graph& graph,
Graph_vertex_descriptor vt = target(e,graph);
CGAL_warning_msg(vs != vt, "ignore self loops");
if(vs != vt){
const std::pair<edge_descriptor, bool> pair =
add_edge(v2vmap[vs],v2vmap[vt],g_copy);
g_copy[pair.first] = e;
add_edge(v2vmap[vs], v2vmap[vt], e, g_copy);
}
}
}

View File

@ -63,6 +63,7 @@ else()
message(STATUS "NOTICE: Tests that use OpenMesh will not be compiled.")
endif()
set(CMAKE_POLICY_DEFAULT_CMP0167 NEW)
find_package(VTK 9.0 QUIET COMPONENTS CommonCore IOCore IOLegacy IOXML FiltersCore FiltersSources)
if (VTK_FOUND AND VTK_LIBRARIES)
message(STATUS "VTK ${VTK_VERSION} found ${VTK_LIBRARIES}")

View File

@ -12,7 +12,7 @@ are of a type that is a model of the concept
\cgalRefines{CopyConstructible,Assignable,DefaultConstructible}
\cgalHasModelsBegin
\cgalHasModels{GAL::Polynomial_for_spheres_2_}
\cgalHasModels{CGAL::Polynomial_for_spheres_2_}
\cgalHasModelsEnd
\sa `AlgebraicKernelForSpheres`

View File

@ -22,10 +22,11 @@
#include <CGAL/assertions.h>
#include <CGAL/use.h>
#include <CGAL/tags.h>
#include <CGAL/type_traits.h>
#include <cstddef>
#include <functional>
#include <iterator>
#include <utility>
// These are name redefinitions for backwards compatibility
// with the pre iterator-traits style adaptors.
@ -640,6 +641,34 @@ operator+( Dist n, const Iterator_from_circulator<C,Ref,Ptr>& circ) {
return tmp += n;
}
template <class Circ>
class Range_from_circulator {
private:
Circ anchor;
public:
using pointer = CGAL::cpp20::remove_cvref_t<decltype(anchor.operator->())>;
using const_pointer = CGAL::cpp20::remove_cvref_t<decltype(std::as_const(anchor).operator->())>;
using reference = decltype(*anchor);
using const_reference = decltype(*std::as_const(anchor));
using iterator = Iterator_from_circulator<Circ, reference, pointer>;
using const_iterator = Iterator_from_circulator<Circ, const_reference, const_pointer>;
iterator begin() {
return iterator(&anchor, 0);
}
const_iterator begin() const {
return const_iterator(&anchor, 0);
}
iterator end() {
return anchor == nullptr ? iterator(&anchor, 0) : iterator(&anchor, 1);
}
const_iterator end() const {
return anchor == nullptr ? const_iterator(&anchor, 0) : const_iterator(&anchor, 1);
}
Range_from_circulator() = default;
Range_from_circulator(const Circ& c) : anchor(get_min_circulator(c)) {}
};
template < class C >
class Container_from_circulator {
private:

View File

@ -0,0 +1,30 @@
/*!
\ingroup PkgConstrainedTriangulation3Concepts
\cgalConcept
The concept `ConformingConstrainedDelaunayTriangulationCellBase_3` refines the concept
`TriangulationCellBase_3` and and describes the requirements for a base cell class of
the `CGAL::Conforming_constrained_Delaunay_triangulation_3` class.
\cgalRefines{TriangulationCellBase_3, BaseWithTimeStamp}
\cgalHasModelsBegin
\cgalHasModels{CGAL::Conforming_constrained_Delaunay_triangulation_cell_base_3}
\cgalHasModelsEnd
\sa `ConformingConstrainedDelaunayTriangulationVertexBase_3`
*/
class ConformingConstrainedDelaunayTriangulationCellBase_3 {
public:
/// @name Access Functions
///
/// The following functions return a reference to an object of type
/// `CGAL::Conforming_constrained_Delaunay_triangulation_cell_data_3`, that contains
/// the per-cell data required by the implementation of the
/// `CGAL::Conforming_constrained_Delaunay_triangulation_3` class.
/// @{
CGAL::Conforming_constrained_Delaunay_triangulation_cell_data_3& ccdt_3_data();
const CGAL::Conforming_constrained_Delaunay_triangulation_cell_data_3& ccdt_3_data() const;
/// @}
};

View File

@ -0,0 +1,87 @@
/*!
\ingroup PkgConstrainedTriangulation3Concepts
\cgalConcept
The concept `ConformingConstrainedDelaunayTriangulationTraits_3` specifies the requirements
for the geometric traits class of the triangulation used as the first template
parameter `%Triangulation` in the function template
`CGAL::make_conforming_constrained_Delaunay_triangulation_3()`.
\cgalRefines{DelaunayTriangulationTraits_3, ProjectionTraitsGeometricTraits_3}
\cgalHasModelsBegin
\cgalHasModels{CGAL::Exact_predicates_inexact_constructions_kernel (recommended)}
\cgalHasModelsBare{All models of the concept `Kernel`}
\cgalHasModelsEnd
*/
class ConformingConstrainedDelaunayTriangulationTraits_3 {
public:
/// \name Operations
/// The following functions give access to the predicate and construction objects:
/// @{
/*!
* returns a function object model of `Kernel::Angle_3`
*/
unspecified_type angle_3_object();
/*!
* returns a function object model of `Kernel::CompareAngle_3`
*/
unspecified_type compare_angle_3_object();
/*!
* returns a function object model of `Kernel::ComputeScalarProduct_3`
*/
unspecified_type compute_scalar_product_3_object();
/*!
* returns a function object model of `Kernel::ComputeSquaredLength_3`
*/
unspecified_type compute_squared_length_3_object();
/*!
* returns a function object model of `Kernel::ConstructCrossProductVector_3`
*/
unspecified_type construct_cross_product_vector_3_object();
/*!
* returns a function object model of `Kernel::ConstructMidpoint_3`
*/
unspecified_type construct_midpoint_3_object();
/*!
* returns a function object model of `Kernel::ConstructVector_3`
*/
unspecified_type construct_vector_3_object();
/*!
* returns a function object model of `Kernel::ConstructScaledVector_3`
*/
unspecified_type construct_scaled_vector_3_object();
/*!
* returns a function object model of `Kernel::ConstructSumOfVectors_3`
*/
unspecified_type construct_sum_of_vectors_3_object();
/*!
* returns a function object model of `Kernel::ConstructTranslatedPoint_3`
*/
unspecified_type construct_translated_point_3_object();
/*!
* returns a function object model of `Kernel::ConstructVertex_3`
*/
unspecified_type construct_vertex_3_object();
/*!
* returns a predicate object model of `Kernel::IsDegenerate_3`
*/
unspecified_type is_degenerate_3_object();
/// @}
};

View File

@ -0,0 +1,31 @@
/*!
\ingroup PkgConstrainedTriangulation3Concepts
\cgalConcept
The concept `ConformingConstrainedDelaunayTriangulationVertexBase_3` refines the concept
`TriangulationVertexBase_3` and describes the requirements for a base vertex class of
the `CGAL::Conforming_constrained_Delaunay_triangulation_3` class.
\cgalRefines{TriangulationVertexBase_3, BaseWithTimeStamp}
\cgalHasModelsBegin
\cgalHasModels{CGAL::Conforming_constrained_Delaunay_triangulation_vertex_base_3}
\cgalHasModelsEnd
\sa `ConformingConstrainedDelaunayTriangulationCellBase_3`
*/
class ConformingConstrainedDelaunayTriangulationVertexBase_3 {
public:
/// @name Access Functions
///
/// The following functions return a reference to an object of type
/// `CGAL::Conforming_constrained_Delaunay_triangulation_vertex_data_3`, that contains
/// the per-vertex data required by the implementation of the
/// `CGAL::Conforming_constrained_Delaunay_triangulation_3` class.
/// @{
CGAL::Conforming_constrained_Delaunay_triangulation_vertex_data_3& ccdt_3_data();
const CGAL::Conforming_constrained_Delaunay_triangulation_vertex_data_3& ccdt_3_data() const;
/// @}
}

View File

@ -0,0 +1,345 @@
namespace CGAL {
/*!
\mainpage User Manual
\anchor Chapter_CT_3
\anchor userchapterct3
\cgalAutoToc
\author Laurent Rineau and Jane Tournois
\cgalFigureAnchor{CT_3_pyramid_fig}
<img src="cdt_title_pyramid.png" style="max-width:60%;min-width=20%"/>
<BR>
\section CT_3_CCDT_3 Constrained Triangulations in 3D
3D triangulations partition space and are useful in many applications. In some cases, it is
important to ensure that specific faces, such as those representing the sharp features of an object, appear in the output.
When a triangulation exactly respects these constraints, it is called a _constrained_ triangulation.
However, it is sometimes only possible to preserve the geometry of the constraints, but not their exact
combinatorics. In such cases, additional points, called _Steiner_ _points_, must be inserted. This process
results in a _conforming_ triangulation.
This package implements an algorithm for constructing conforming triangulations of 3D polygonal
constraints. Specifically, it requires that these piecewise linear constraints are provided as a
_piecewise linear complex_&nbsp;(PLC). The resulting triangulations are of type `Triangulation_3`,
as described in the chapter \ref PkgTriangulation3.
The article by Cohen-Steiner et al. \cgalCite{cgal:cohen2002conforming} discusses the problem of
constructing conforming Delaunay triangulations and proposes an algorithm to address it.
Si et al.'s work \cgalCite{si2005meshing}, \cgalCite{cgal:si2008cdt3}, \cgalCite{si2015tetgen},
presents an algorithm for computing conforming constrained
Delaunay triangulations in 3D.
\section CT_3_definitions Definitions
This section introduces the key concepts necessary to understand and use this package effectively.
\subsection CT_3_PLC Piecewise Linear Complex
A _piecewise linear complex_&nbsp;(PLC) is the three-dimensional generalization of a
planar straight-line graph. It consists of a finite set of vertices, edges, and polygonal faces
that satisfy the following properties:
- The vertices and edges of the PLC form a simplicial complex: two edges may intersect only at a
shared vertex.
- The boundary of each polygonal face in the PLC is an ordered list of vertices from the PLC, forming
one closed loop.
- Each polygonal face must be a simple polygon, i.e., its edges don't intersect,
except consecutive edges, which intersect at their common vertex.
- Each polygonal face must be planar, meaning all its vertices lie on the same plane.
- Each polygonal face may have one or more holes, each of them also represented by an ordered list of vertices
from the PLC, forming a closed loop.
- If two polygons in the PLC intersect, their intersection is a union of edges and vertices from the
PLC. In particular, the interiors of two polygons cannot overlap.
Polygons in a PLC may be non-convex and may have holes.
\cgalFigureAnchor{CT_3_plc_fig}
<center>
<img src="plc.png" style="max-width:60%;"/>
</center>
\cgalFigureCaptionBegin{CT_3_plc_fig}
A piecewise linear complex, composed of planar faces connected by edges and vertices.
\cgalFigureCaptionEnd
\subsection CT_3_CDT Conforming Constrained Delaunay Triangulation
The algorithms developed in this package are designed to compute a constrained Delaunay
triangulation that contains a given set of polygonal constraints in 3D as a subcomplex.
A triangulation is a _Delaunay triangulation_ if the circumscribing sphere of any simplex
in the triangulation contains no vertex in its interior (see chapter \ref PkgTriangulation3
for more details on Delaunay triangulations).
A _constrained Delaunay triangulation_ of a PLC is a constrained triangulation that is as close as
possible to being Delaunay, given that some faces are marked as _constrained_. More precisely, a
triangulation is _constrained Delaunay_ if, for any simplex \f$s\f$ of the triangulation, the
interior of its circumscribing sphere contains no vertex of the triangulation that is _visible_ from
any point in the interior of the simplex \f$s\f$. Two points are _visible_ if the open line segment
joining them does not intersect any polygonal face of the PLC, except for polygons that are coplanar with
the segment.
In 3D, constrained triangulations do not always exist. This can be demonstrated using the example of
Sch&ouml;nhardt polyhedra \cgalCite{s-udzvd-28} (see \cgalFigureRef{CT_3_schonhardt_fig}),
\cgalCite{b-ip-48a}. Shewchuk \cgalCite{cgal:shewchuk1998condition} demonstrated that for any PLC,
there exists a refined
version of the original PLC that admits a constrained Delaunay triangulation. This refinement is
achieved by adding Steiner vertices to the input edges and polygons. The constrained triangulation
built on this refined PLC is known as a _conforming constrained Delaunay triangulation_ (CCDT for
short). \cgalFigureRef{CT_3_plc2cdt_fig} illustrates an example of a conforming constrained
Delaunay triangulation constructed from a PLC.
\cgalFigureAnchor{CT_3_schonhardt_fig}
<center>
<img src="schonhardt.png" style="max-width:25%;"/>
</center>
\cgalFigureCaptionBegin{CT_3_schonhardt_fig}
A Sch&ouml;nhardt polyhedron.
\cgalFigureCaptionEnd
\cgalFigureAnchor{CT_3_plc2cdt_fig}
<center>
<img src="plc_to_cdt.png" style="max-width:70%;"/>
</center>
\cgalFigureCaptionBegin{CT_3_plc2cdt_fig}
Left: PLC (360 vertices);
Right: CCDT (2452 vertices).
\cgalFigureCaptionEnd
The algorithm implemented in this package is based on the work of Hang Si et al., who developed particular
algorithms for constructing conforming constrained Delaunay triangulations from PLCs.
The corresponding implementation takes with floating point numbers as coordinates
\cgalCite{si2005meshing}, \cgalCite{cgal:si2008cdt3}, \cgalCite{si2015tetgen}.
\section CT_3_design Software Design
\subsection CT_3_representation_of_PLCs Representation of Piecewise Linear Complexes
There is no universal or canonical way to represent all possible PLCs in \cgal.
Any polyhedral surface is a PLC, so any model of `FaceListGraph`, such as `CGAL::Surface_mesh`, can be
used to represent such a PLC.
In this representation, the geometric structure of the PLC is directly mapped to the elements
of the `CGAL::Surface_mesh`:
- vertices of the PLC geometrically correspond to vertices of the surface mesh,
- edges of the PLC correspond to edges of the surface mesh,
- and polygonal faces of the PLC correspond to faces of the surface mesh,
covering the surface of a geometric object.
However, PLCs represented in this way are limited to being
manifold (that is, each edge belongs to exactly two faces), and their faces cannot have holes.
A PLC can also be represented as a polygon soup: a collection of vertices and a set of polygons, where
each polygon is defined by an ordered list of vertices, without explicit connectivity information between
polygons. For a polygon soup to represent a valid PLC, its polygons must satisfy the properties described
in the previous section. This approach allows for the representation of non-manifold geometries; however,
polygons in a polygon soup cannot have holes.
This package also provides a way to group polygons into distinct surface patches using a property map,
named `plc_face_id`.
Each polygon can be assigned a _patch_ identifier, allowing multiple polygons to form a continuous surface patch,
which may include holes. Some necessary geometric conditions must be satisfied for these patches to be
used in the conforming constrained Delaunay triangulation construction:
- Each patch must be planar, meaning all polygonal faces in the patch lie on the same plane;
- The polygonal faces of the patch must not intersect except at their shared edges.
When this property map is provided, the input PLC is interpreted in terms of its polygonal faces,
edges and vertices as follows:
- Each polygonal face of the PLC is defined as the union of input polygons sharing the same patch identifier;
- The edges of the PLC are those from the surface mesh or polygon soup that satisfy one of the following conditions:
-- they are adjacent to only one polygonal face;
-- they are adjacent to two polygonal faces with different patch identifiers;
-- they are adjacent to more than two polygonal faces with differing patch identifiers, indicating non-manifold features of the PLC.
- The vertices of the PLC are the ones lying on the boundaries of surface patches in the original surface mesh or polygon soup.
\subsection CT_3_api API
This package provides a primary class, `CGAL::Conforming_constrained_Delaunay_triangulation_3`.
This class is templated by a
geometric traits class and an underlying triangulation class, allowing for flexibility and
customization.
In addition to the main class, the package includes several auxiliary classes that define the types
of vertices, cells, and associated metadata used within the triangulation. These supporting classes
enable users to extend or adapt the triangulation data structure to their specific needs.
Two overloads of the constructor function \link
PkgConstrainedTriangulation3FunctionsPolygonSoupOrMesh
`CGAL::make_conforming_constrained_Delaunay_triangulation_3()`\endlink are provided to facilitate the creation
of a `CGAL::Conforming_constrained_Delaunay_triangulation_3` object from either a surface mesh or a polygon soup.
\subsection CT_3_geomtraits Traits and Kernel Choice
The requirements for geometric objects and operations are specified by the traits class concept
`ConformingConstrainedDelaunayTriangulationTraits_3`. Any CGAL kernel is a model of this concept.
However, because this package builds upon the 3D Triangulation package, it inherits the requirement
that the traits class must provide exact predicates.
A key aspect of this algorithm is the creation of new points, known as Steiner points, which are
inserted on the segments and polygons of the input PLC. If a traits class with inexact constructions
is used, it cannot be guaranteed that these points will lie exactly on the intended segments or polygons.
As a result, the output will only approximate the input, with the accuracy limited by the rounding
of the computed Steiner points.
Furthermore, when using inexact constructions, the algorithm may fail if the input PLC contains
non-adjacent simplices that are too close to each other. In such cases, the triangulation process
will emit an error if the distance between simplices falls below an internally computed threshold.
An error message describing the involved simplices will be displayed on the standard output.
If the issue is caused by poorly shaped triangles, functions such as
`CGAL::Polygon_mesh_processing::remove_almost_degenerate_faces()` may help resolve the problem.
\section CT_3_examples Examples
\subsection CT_3_example_ccdt Build a Conforming Constrained Delaunay Triangulation
The following example illustrates how to use the helper function
`CGAL::make_conforming_constrained_Delaunay_triangulation_3()` to construct a conforming constrained
Delaunay triangulation from a given PLC.
The triangulation is saved in the MEDIT file format, using the
\link PkgCDT3IOFunctions `CGAL::IO::write_MEDIT()` \endlink function.
\cgalExample{Constrained_triangulation_3/conforming_constrained_Delaunay_triangulation_3.cpp }
\subsection CT_3_example_ccdt_soup Build a Conforming Constrained Delaunay Triangulation from a Polygon Soup
You can also construct a conforming constrained Delaunay triangulation from a polygon soup.
The following example demonstrates how to create such a triangulation from a collection of polygons
without explicit connectivity information.
\cgalExample{Constrained_triangulation_3/ccdt_3_from_soup.cpp }
\subsection CT_3_example_ccdt_fimap Build a Conforming Constrained Delaunay Triangulation with Known Polygon Identifiers
If the user already knows the set of polygonal face identifiers to associate with each PLC face, this information can be
provided and preserved throughout the construction of the conforming constrained Delaunay
triangulation.
The following example demonstrates how to detect planar surface patches and remesh them as coarsely
as possible using
\link CGAL::Polygon_mesh_processing::remesh_planar_patches(const TriangleMeshIn&,PolygonMeshOut&,const NamedParametersIn&,const NamedParametersOut&) `CGAL::Polygon_mesh_processing::remesh_planar_patches()` \endlink
from the \ref PkgPolygonMeshProcessing package.
The resulting patches and segmentation are then used to build a conforming constrained Delaunay triangulation.
When the named parameter `plc_face_id` is specified, each constrained facet in the 3D triangulation
is assigned to the corresponding input PLC face, as identified by the provided property map.
If this parameter is not specified, each input polygonal face is assigned a unique face index.
Figure \cgalFigureRef{CT_3_ccdt_examples_fig} shows the benefit of using the `plc_face_id` property map.
On the last line of the figure, the input PLC is enriched with a segmentation of the planar faces,
provided via the `plc_face_id` property map. In the resulting conforming constrained Delaunay triangulation,
only the boundary edges of the PLC faces are constrained, while the other edges never get inserted as
edges of the 3D triangulation.
Without the `plc_face_id` property map, all edges of the PLC faces are constrained,
each PLC face is considered as a constraint,
possibly resulting in a 3D triangulation with surfaces that are more refined than necessary.
\cgalExample{Constrained_triangulation_3/conforming_constrained_Delaunay_triangulation_3_fimap.cpp}
\cgalFigureRef{CT_3_ccdt_examples_fig} shows the input and output of this triangulation construction example.
\subsection CT_3_example_ccdt_region_growing_fimap Build a Conforming Constrained Delaunay Triangulation with Detected Polygon Identifiers
If the user does not know the set of polygonal face identifiers to associate with each PLC face, this information can be
automatically detected using the
\link CGAL::Polygon_mesh_processing::region_growing_of_planes_on_faces(const PolygonMesh& mesh,RegionMap region_map,const NamedParameters& np) `CGAL::Polygon_mesh_processing::region_growing_of_planes_on_faces()`\endlink
function from the \ref PkgPolygonMeshProcessing package.
The following example demonstrates how to detect planar surface patches and build a conforming
constrained Delaunay triangulation using the detected segmentation. The named parameter `plc_face_id`
is used to associate each facet of the triangulation with the corresponding input PLC face.
\cgalExample{Constrained_triangulation_3/ccdt_3_fimap_region_growing.cpp}
\subsection CT_3_examples_preprocessing Preprocessing the Input for Conforming Constrained Delaunay Triangulations
Given a PLC, the algorithms in this package can construct a conforming constrained Delaunay triangulation, provided
the input surface can be represented as a valid surface mesh or a collection of surface meshes,
and does not contain self-intersections.
Several preprocessing functions are available in the \ref PkgPolygonMeshProcessing package to help ensure these
preconditions are met.
The following example demonstrates how to construct a conforming constrained Delaunay triangulation from
an input mesh that is not triangulated and may contain self-intersections,
using autorefinement.
\cgalExample{Constrained_triangulation_3/ccdt_3_after_autorefinement.cpp }
The function
\link CGAL::Polygon_mesh_processing::does_self_intersect(const FaceRange&, const TriangleMesh&, const NamedParameters&) `CGAL::Polygon_mesh_processing::does_self_intersect()` \endlink
is used to detect self-intersections,
but it requires the input mesh to be triangulated. Therefore, the input mesh must first be triangulated
using
\link CGAL::Polygon_mesh_processing::triangulate_faces(FaceRange,PolygonMesh&,const NamedParameters&) `CGAL::Polygon_mesh_processing::triangulate_faces()`\endlink
before performing the self-intersection check.
If self-intersections are found, the triangulated mesh is converted into a triangle soup, which is then
processed with
\link CGAL::Polygon_mesh_processing::autorefine_triangle_soup(PointRange&,TriangleRange&,const NamedParameters&) `CGAL::Polygon_mesh_processing::autorefine_triangle_soup()`\endlink
to resolve the self-intersections.
\subsection CT_3_example_remesh Remeshing a Conforming Constrained Delaunay Triangulation
After constructing the triangulation, you can improve its quality or adapt it to a specific sizing
field by applying the
\link CGAL::tetrahedral_isotropic_remeshing(CGAL::Triangulation_3<Traits, TDS, SLDS>&, const SizingFunction&, const NamedParameters&) `CGAL::tetrahedral_isotropic_remeshing()`\endlink
function from the \ref PkgTetrahedralRemeshing package.
The following example demonstrates how to remesh a conforming constrained Delaunay triangulation.
\cgalExample{Constrained_triangulation_3/remesh_constrained_Delaunay_triangulation_3.cpp }
\subsection CT_3_examples_figure Figures
The following table of figures (\cgalFigureRef{CT_3_ccdt_examples_fig}) illustrates some results of the examples provided in this package.
The left column shows the input PLC, while the right column displays the resulting conforming
constrained Delaunay triangulation.
From top to bottom, the lines show different input PLC, from the same input triangulated surface and,
for each of them, the resulting conforming constrained Delaunay triangulation.
The input data are:
<ul>
<li> the input PLC with no preprocessing;
<li> the input PLC with a segmentation of the surface patches done with `CGAL::Polygon_mesh_processing::region_growing_of_planes_on_faces()` as done in example \ref CT_3_example_ccdt_region_growing_fimap;</li>
<li> the input PLC, remeshed and not triangulated, with a segmentation of the surface patches, done with `CGAL::Polygon_mesh_processing::remesh_planar_patches()` as in example \ref CT_3_example_ccdt_fimap;</li>
<li> the input PLC, isotropically remeshed, with a segmentation of the surface patches, done with `CGAL::Polygon_mesh_processing::region_growing_of_planes_on_faces()` as in example \ref CT_3_example_ccdt_region_growing_fimap.</li>
</ul>
On the fourth line, the input PLC is remeshed using `CGAL::Polygon_mesh_processing::isotropic_remeshing()`.
The resulting conforming constrained Delaunay triangulation contains fewer vertices than the input
remeshed and segmented input PLC. This reduction occurs because only the boundary edges of the PLC faces
are marked as constraints in the triangulation;
interior edges that do not lie on the boundaries of surface patches (as defined by `plc_face_id`) are ignored.
As a result, these non-boundary edges are omitted from the triangulation, leading to a coarser triangulation.
\cgalFigureAnchor{CT_3_ccdt_examples_fig}
<center>
<img src="ccdt_examples.png" style="max-width:50%;"/>
</center>
\cgalFigureCaptionBegin{CT_3_ccdt_examples_fig}
A collection of conforming constrained Delaunay triangulations built from different inputs.
The left column shows the input PLC, while the right column displays the resulting 3D triangulation.<br>
\cgalFigureCaptionEnd
\section CT_3_history Implementation History
The initial version of this package was implemented by Laurent Rineau and released in
\cgal&nbsp;6.1&nbsp;(2025). Jane Tournois contributed to the documentation and helped improve the API.
The package design and algorithms are grounded in the theoretical work of
Hang&nbsp;Si et al. on meshing algorithms&nbsp;\cgalCite{si2005meshing},&nbsp;\cgalCite{si2015tetgen}.
*/
} /* namespace CGAL */

View File

@ -0,0 +1,9 @@
@INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS}
PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - 3D Constrained Triangulations"
HTML_EXTRA_FILES = ${CGAL_PACKAGE_DOC_DIR}/fig/plc.png \
${CGAL_PACKAGE_DOC_DIR}/fig/schonhardt.png \
${CGAL_PACKAGE_DOC_DIR}/fig/plc_to_cdt.png \
${CGAL_PACKAGE_DOC_DIR}/fig/ccdt_fpmap.png \
${CGAL_PACKAGE_DOC_DIR}/fig/cdt_title_pyramid.png \
${CGAL_PACKAGE_DOC_DIR}/fig/ccdt_examples.png
QUIET = YES

View File

@ -0,0 +1,77 @@
/*!
\defgroup PkgConstrainedTriangulation3Ref Reference Manual
\defgroup PkgConstrainedTriangulation3Concepts Concepts
\ingroup PkgConstrainedTriangulation3Ref
\defgroup PkgConstrainedTriangulation3Classes Classes and Class Templates
\ingroup PkgConstrainedTriangulation3Ref
\defgroup PkgConstrainedTriangulation3FunctionsPolygonSoupOrMesh Free Functions for Creating Conforming Constrained Delaunay Triangulations
\ingroup PkgConstrainedTriangulation3Ref
\defgroup PkgCDT3IOFunctions Output Functions
\ingroup PkgConstrainedTriangulation3Ref
\defgroup PkgDrawCDT_3 Draw a 3D Constrained Triangulation
\ingroup PkgConstrainedTriangulation3Ref
\defgroup PkgConstrainedTriangulation3Functions Other Functions
\ingroup PkgConstrainedTriangulation3Ref
\addtogroup PkgConstrainedTriangulation3Ref
\cgalPkgDescriptionBegin{3D Constrained Triangulations,PkgConstrainedTriangulation3}
\cgalPkgPicture{small-pyramid.png}
\cgalPkgSummaryBegin
\cgalPkgAuthors{Laurent Rineau and Jane Tournois}
\cgalPkgDesc{This package implements the construction of a 3D Constrained Delaunay triangulation.
This triangulation is a generalization of a 3D Delaunay Triangulation which conforms to the set of faces
of a 3D _piecewise linear complex_ (PLC), ensuring that these faces are part of the triangulation.
As not all PLCs are tetrahedralizable,
the algorithm may insert Steiner points to construct the constrained triangulation.
}
\cgalPkgManuals{Chapter_CT_3,PkgConstrainedTriangulation3Ref}
\cgalPkgSummaryEnd
\cgalPkgShortInfoBegin
\cgalPkgSince{6.1}
\cgalPkgDependsOn{\ref PkgTriangulation3 "3D Triangulations"}
\cgalPkgBib{cgal:rt-cdt3}
\cgalPkgLicense{\ref licensesGPL "GPL"}
\cgalPkgDemo{CGAL Lab,CGALlab.zip}
\cgalPkgShortInfoEnd
\cgalPkgDescriptionEnd
\cgalClassifedRefPages
\cgalCRPSection{Concepts}
- `ConformingConstrainedDelaunayTriangulationTraits_3`
- `ConformingConstrainedDelaunayTriangulationVertexBase_3`
- `ConformingConstrainedDelaunayTriangulationCellBase_3`
\cgalCRPSection{Functions}
- \link PkgConstrainedTriangulation3FunctionsPolygonSoupOrMesh `CGAL::make_conforming_constrained_Delaunay_triangulation_3()` \endlink: the main function to create
a conforming constrained Delaunay triangulation in 3D, an instance of the class template
`CGAL::Conforming_constrained_Delaunay_triangulation_3`.
- `CGAL::Tetrahedral_remeshing::get_remeshing_triangulation()`
\cgalCRPSubsection{Classes}
- `CGAL::Conforming_constrained_Delaunay_triangulation_3<Traits, Triangulation>`
- `CGAL::Conforming_constrained_Delaunay_triangulation_vertex_base_3<Traits, Vertex_base>`
- `CGAL::Conforming_constrained_Delaunay_triangulation_cell_base_3<Traits, Cell_base>`
\cgalCRPSection{Draw a 3D Constrained Triangulation}
- \link PkgDrawCDT_3 `CGAL::draw()` \endlink
\cgalCRPSection{Output Functions}
- \link PkgCDT3IOFunctions `CGAL::IO::write_MEDIT()` \endlink
*/

View File

@ -0,0 +1,13 @@
Basic_viewer
BGL
Kernel_23
Manual
Polygon_mesh_processing
Property_map
SMDS_3
STL_Extension
Stream_support
Surface_mesh
TDS_3
Tetrahedral_remeshing
Triangulation_3

View File

@ -0,0 +1,48 @@
/*!
\example Constrained_triangulation_3/conforming_constrained_Delaunay_triangulation_3.cpp
@brief
Simple example demonstrating the usage of the Constrained_triangulation_3 package.<br>
It constructs a 3D constrained Delaunay triangulation from a polygon mesh.
*/
/*!
\example Constrained_triangulation_3/ccdt_3_from_soup.cpp
@brief
Simple example demonstrating the usage of the Constrained_triangulation_3 package.<br>
It constructs a constrained Delaunay triangulation from a polygon soup.
*/
/*!
\example Constrained_triangulation_3/remesh_constrained_Delaunay_triangulation_3.cpp
@brief
How to use `CGAL::tetrahedral_isotropic_remeshing` with a constrained Delaunay triangulation.
*/
/*!
\example Constrained_triangulation_3/conforming_constrained_Delaunay_triangulation_3_fimap.cpp
@brief
From a non-manifold OFF file, construct the constrained Delaunay triangulation.
*/
/*!
\example Constrained_triangulation_3/ccdt_3_after_autorefinement.cpp
@brief
From a self-intersecting and non-triangulated surface in an OFF file,
construct the constrained Delaunay triangulation after preprocessing by autorefinement.
*/
/*!
\example Constrained_triangulation_3/ccdt_3_check_preconditions.cpp
\example Constrained_triangulation_3/ccdt_3_fimap_region_growing.cpp
\example Constrained_triangulation_3/ccdt_3_from_soup_fimap.cpp
\example Constrained_triangulation_3/ccdt_3_preprocessing.cpp
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,40 @@
cmake_minimum_required(VERSION 3.12...3.31)
project(Constrained_triangulation_3_Examples)
find_package(CGAL REQUIRED COMPONENTS Qt6)
find_package(Eigen3 QUIET)
include(CGAL_Eigen3_support)
add_compile_definitions(QT_NO_KEYWORDS)
create_single_source_cgal_program(conforming_constrained_Delaunay_triangulation_3.cpp)
create_single_source_cgal_program(conforming_constrained_Delaunay_triangulation_3_fimap.cpp)
create_single_source_cgal_program(ccdt_3_from_soup.cpp)
create_single_source_cgal_program(ccdt_3_from_soup_fimap.cpp)
create_single_source_cgal_program(ccdt_3_after_autorefinement.cpp)
create_single_source_cgal_program(ccdt_3_preprocessing.cpp)
create_single_source_cgal_program(ccdt_3_check_preconditions.cpp)
create_single_source_cgal_program(remesh_constrained_Delaunay_triangulation_3.cpp)
if(TARGET CGAL::Eigen3_support)
create_single_source_cgal_program(ccdt_3_fimap_region_growing.cpp)
target_link_libraries(ccdt_3_fimap_region_growing PUBLIC CGAL::Eigen3_support)
endif()
if(CGAL_Qt6_FOUND)
target_link_libraries(conforming_constrained_Delaunay_triangulation_3 PUBLIC CGAL::CGAL_Basic_viewer)
target_link_libraries(ccdt_3_from_soup PUBLIC CGAL::CGAL_Basic_viewer)
target_link_libraries(ccdt_3_from_soup_fimap PUBLIC CGAL::CGAL_Basic_viewer)
target_link_libraries(ccdt_3_after_autorefinement PUBLIC CGAL::CGAL_Basic_viewer)
target_link_libraries(ccdt_3_preprocessing PUBLIC CGAL::CGAL_Basic_viewer)
target_link_libraries(ccdt_3_check_preconditions PUBLIC CGAL::CGAL_Basic_viewer)
else()
message(STATUS "NOTICE: The example 'conforming_constrained_Delaunay_triangulation_3' cannot draw the result without Qt6.")
endif()
find_package(TBB QUIET)
include(CGAL_TBB_support)
if(TARGET CGAL::TBB_support)
target_link_libraries(ccdt_3_from_soup_fimap PUBLIC CGAL::TBB_support)
endif()

View File

@ -0,0 +1,71 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/IO/polygon_mesh_io.h>
#include <CGAL/IO/write_MEDIT.h>
#include <CGAL/Polygon_mesh_processing/autorefinement.h>
#include <CGAL/Polygon_mesh_processing/polygon_soup_self_intersections.h>
#include <CGAL/Polygon_mesh_processing/self_intersections.h>
#include <CGAL/Surface_mesh/Surface_mesh.h>
#include <CGAL/draw_constrained_triangulation_3.h>
#include <CGAL/make_conforming_constrained_Delaunay_triangulation_3.h>
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
using Point = K::Point_3;
using Surface_mesh = CGAL::Surface_mesh<Point>;
namespace PMP = CGAL::Polygon_mesh_processing;
int main(int argc, char* argv[])
{
const auto filename = (argc > 1) ? argv[1]
: CGAL::data_file_path("meshes/spheres_intersecting.off");
CGAL::Surface_mesh<K::Point_3> mesh;
if(!CGAL::IO::read_polygon_mesh(filename, mesh)) {
std::cerr << "Error: cannot read file " << filename << std::endl;
return EXIT_FAILURE;
}
std::cout << "Number of facets in " << filename << ": "
<< mesh.number_of_faces() << "\n";
CGAL::Conforming_constrained_Delaunay_triangulation_3<K> ccdt;
if(PMP::does_self_intersect(mesh))
{
std::cout << "Mesh self-intersects, performing autorefine...\n";
// use a polygon soup as container as the output will most likely be non-manifold
std::vector<Point> points;
std::vector<std::vector<std::size_t>> polygons;
PMP::polygon_mesh_to_polygon_soup(mesh, points, polygons);
PMP::autorefine_triangle_soup(points, polygons);
std::cout << "Number of input triangles after autorefine: "
<< polygons.size() << "\n";
if(PMP::does_polygon_soup_self_intersect(points, polygons))
{
std::cerr << "Error: mesh still self-intersects after autorefine\n";
std::cerr << "Run autorefinement again with an exact kernel ";
std::cerr << "such as CGAL::Exact_predicates_exact_constructions_kernel \n";
return EXIT_FAILURE;
}
ccdt = CGAL::make_conforming_constrained_Delaunay_triangulation_3(points, polygons);
}
else
{
ccdt = CGAL::make_conforming_constrained_Delaunay_triangulation_3(mesh);
}
std::cout << "Number of constrained facets in the CDT: "
<< ccdt.number_of_constrained_facets() << '\n';
std::ofstream ofs(argc > 2 ? argv[2] : "out.mesh");
ofs.precision(17);
CGAL::IO::write_MEDIT(ofs, ccdt);
CGAL::draw(ccdt);
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,108 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh/Surface_mesh.h>
#include <CGAL/Polygon_mesh_processing/triangulate_faces.h>
#include <CGAL/Polygon_mesh_processing/self_intersections.h>
#include <CGAL/boost/graph/copy_face_graph.h>
#include <CGAL/Polygon_mesh_processing/polygon_mesh_to_polygon_soup.h>
#include <CGAL/make_conforming_constrained_Delaunay_triangulation_3.h>
#include <CGAL/draw_constrained_triangulation_3.h>
#include <CGAL/IO/polygon_mesh_io.h>
#include <CGAL/IO/polygon_soup_io.h>
#include <CGAL/IO/write_MEDIT.h>
#include <vector>
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
using Point = K::Point_3;
using Surface_mesh = CGAL::Surface_mesh<Point>;
namespace PMP = CGAL::Polygon_mesh_processing;
// Function to verify preconditions for a mesh input
bool verify_preconditions_mesh(const Surface_mesh& mesh)
{
if(CGAL::is_triangle_mesh(mesh))
return !CGAL::Polygon_mesh_processing::does_self_intersect(mesh);
Surface_mesh triangle_mesh;
CGAL::copy_face_graph(mesh, triangle_mesh);
bool tri_ok = CGAL::Polygon_mesh_processing::triangulate_faces(triangle_mesh);
if(!tri_ok)
return false;
return !CGAL::Polygon_mesh_processing::does_self_intersect(triangle_mesh);
}
// Function to verify preconditions for a polygon soup input
template <typename PointRange, typename PolygonRange>
bool verify_preconditions_soup(const PointRange& points, const PolygonRange& polygons)
{
return !CGAL::Polygon_mesh_processing::does_polygon_soup_self_intersect(points, polygons);
}
int main(int argc, char* argv[])
{
const auto filename = (argc > 1) ? argv[1]
: CGAL::data_file_path("meshes/cubes.off");
// Read polygon soup
using Points = std::vector<Point>;
using PLC_face = std::vector<std::size_t>;
using PLC_faces = std::vector<PLC_face>;
Points points;
PLC_faces faces;
if(!CGAL::IO::read_polygon_soup(filename, points, faces))
{
std::cerr << "Error: cannot read file " << filename << std::endl;
return EXIT_FAILURE;
}
// When possible, convert polygon soup to polygon mesh
CGAL::Surface_mesh<K::Point_3> mesh;
if(CGAL::Polygon_mesh_processing::is_polygon_soup_a_polygon_mesh(faces))
{
CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh(points, faces, mesh);
std::cout << "Number of facets in " << filename << ": " << mesh.number_of_faces() << "\n";
}
// Verify preconditions for the mesh input
const bool is_polygon_mesh = !mesh.is_empty();
if(is_polygon_mesh && !verify_preconditions_mesh(mesh))
{
std::cerr << "Error: input mesh is not a valid input for CCDT_3\n";
return EXIT_FAILURE;
}
else if(!verify_preconditions_soup(points, faces))
{
std::cerr << "Error: input polygon soup is not a valid input for CCDT_3\n";
return EXIT_FAILURE;
}
// Build conforming constrained Delaunay triangulation
auto ccdt = is_polygon_mesh
? CGAL::make_conforming_constrained_Delaunay_triangulation_3(mesh)
: CGAL::make_conforming_constrained_Delaunay_triangulation_3(points, faces);
// Print mesh details
if(ccdt.number_of_constrained_facets() == 0)
{
std::cerr << "Error: no constrained facets in the CDT.\n";
std::cerr << "Invalid input.\n";
return EXIT_SUCCESS;
}
std::cout << "Number of constrained facets in the CDT: "
<< ccdt.number_of_constrained_facets() << '\n';
// Output
std::ofstream ofs(argc > 2 ? argv[2] : "out.mesh");
ofs.precision(17);
CGAL::IO::write_MEDIT(ofs, ccdt);
CGAL::draw(ccdt);
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,76 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/IO/write_MEDIT.h>
#include <CGAL/Polygon_mesh_processing/IO/polygon_mesh_io.h>
#include <CGAL/Polygon_mesh_processing/bbox.h>
#include <CGAL/Polygon_mesh_processing/region_growing.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/make_conforming_constrained_Delaunay_triangulation_3.h>
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
using Mesh = CGAL::Surface_mesh<K::Point_3>;
using face_descriptor = boost::graph_traits<Mesh>::face_descriptor;
namespace PMP = CGAL::Polygon_mesh_processing;
int main(int argc, char* argv[])
{
std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/cross_quad.off");
Mesh mesh;
if(!PMP::IO::read_polygon_mesh(filename, mesh)) {
std::cerr << "Invalid input." << std::endl;
return 1;
}
std::cout << "Read " << mesh.number_of_vertices() << " vertices and "
<< mesh.number_of_faces() << " facets\n";
auto [face_patch_map, _] =mesh.add_property_map<face_descriptor, std::size_t>("f:patch_id");
const auto bbox = CGAL::Polygon_mesh_processing::bbox(mesh);
const double bbox_max_span = (std::max)({bbox.x_span(), bbox.y_span(), bbox.z_span()});
std::cout << "Merging facets into coplanar patches..." << std::endl;
auto number_of_patches = CGAL::Polygon_mesh_processing::region_growing_of_planes_on_faces(
mesh,
face_patch_map,
CGAL::parameters::maximum_distance(bbox_max_span * 1.e-6)
.maximum_angle(5.));
for(auto f: faces(mesh))
{
// if region growing did not assign a patch id, assign one
if(get(face_patch_map, f) == static_cast<std::size_t>(-1)) {
put(face_patch_map, f, number_of_patches++);
}
}
std::cout << "Number of patches: " << number_of_patches << std::endl;
filename = argc > 2 ? argv[2] : "mesh.ply";
CGAL::IO::write_polygon_mesh(filename, mesh,
CGAL::parameters::stream_precision(17)
.use_binary_mode(false)
.face_patch_map(face_patch_map));
std::cout << "-- Wrote segmented mesh to \"" << filename << "\"\n";
std::cout << "Creating a conforming constrained Delaunay triangulation...\n";
auto ccdt = CGAL::make_conforming_constrained_Delaunay_triangulation_3(mesh,
CGAL::parameters::plc_face_id(face_patch_map));
std::cout << "Number of vertices in the CDT: "
<< ccdt.triangulation().number_of_vertices() << '\n'
<< "Number of constrained facets in the CDT: "
<< ccdt.number_of_constrained_facets() << '\n';
// Write the CDT to a file, with the PLC face ids
filename = argc > 3 ? argv[3] : "out.mesh";
std::ofstream out(filename);
out.precision(17);
CGAL::IO::write_MEDIT(out, ccdt, CGAL::parameters::with_plc_face_id(true));
std::cout << "-- Wrote CDT to \"" << filename << "\"\n";
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,39 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/IO/polygon_soup_io.h>
#include <CGAL/IO/write_MEDIT.h>
#include <CGAL/draw_constrained_triangulation_3.h>
#include <CGAL/make_conforming_constrained_Delaunay_triangulation_3.h>
#include <vector>
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
int main(int argc, char* argv[])
{
auto filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/cubes.off");
std::vector<K::Point_3> points;
std::vector<std::vector<std::size_t>> polygons;
if(!CGAL::IO::read_polygon_soup(filename, points, polygons)) {
std::cerr << "Error: cannot read file " << filename << std::endl;
return EXIT_FAILURE;
}
std::cout << "Read " << points.size() << " vertices and "
<< polygons.size() << " polygons" << std::endl;
auto ccdt = CGAL::make_conforming_constrained_Delaunay_triangulation_3(points, polygons);
std::cout << "Number of vertices in the CDT: "
<< ccdt.triangulation().number_of_vertices() << '\n'
<< "Number of constrained facets in the CDT: "
<< ccdt.number_of_constrained_facets() << '\n';
std::ofstream ofs(argc > 2 ? argv[2] : "out.mesh");
ofs.precision(17);
CGAL::IO::write_MEDIT(ofs, ccdt);
CGAL::draw(ccdt);
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,68 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/IO/polygon_mesh_io.h>
#include <CGAL/IO/write_MEDIT.h>
#include <CGAL/Polygon_mesh_processing/connected_components.h>
#include <CGAL/Polygon_mesh_processing/polygon_mesh_to_polygon_soup.h>
#include <CGAL/Surface_mesh/Surface_mesh.h>
#include <CGAL/draw_constrained_triangulation_3.h>
#include <CGAL/make_conforming_constrained_Delaunay_triangulation_3.h>
#include <algorithm>
#include <vector>
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
using Point = K::Point_3;
using Surface_mesh = CGAL::Surface_mesh<Point>;
using face_descriptor = Surface_mesh::Face_index;
namespace PMP = CGAL::Polygon_mesh_processing;
int main(int argc, char* argv[])
{
auto filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/cubes_one_patch_id_per_cc.off");
CGAL::Surface_mesh<K::Point_3> mesh;
if(!CGAL::IO::read_polygon_mesh(filename, mesh)) {
std::cerr << "Error: cannot read file " << filename << std::endl;
return EXIT_FAILURE;
}
auto fpmap = mesh.add_property_map<face_descriptor, std::size_t>("f:cc_id").first;
std::cout << "Read " << mesh.number_of_vertices() << " vertices and "
<< mesh.number_of_faces() << " polygons" << std::endl;
PMP::connected_components(mesh, fpmap);
std::cout << "Number of connected components: "
<< fpmap[*std::max_element(mesh.faces_begin(), mesh.faces_end(),
[&](face_descriptor f1, face_descriptor f2) {
return fpmap[f1] < fpmap[f2];
})] + 1
<< std::endl;
std::vector<K::Point_3> points;
std::vector<std::vector<std::size_t>> polygons;
PMP::polygon_mesh_to_polygon_soup(mesh, points, polygons);
auto polygon_to_patch_id = [&](std::size_t i) {
return fpmap[*std::next(mesh.faces_begin(), i)];
};
auto soup_fpmap = boost::make_function_property_map<std::size_t>(polygon_to_patch_id);
auto ccdt = CGAL::make_conforming_constrained_Delaunay_triangulation_3(
points, polygons, CGAL::parameters::plc_face_id(soup_fpmap));
std::cout << "Number of vertices in the CDT: "
<< ccdt.triangulation().number_of_vertices() << '\n'
<< "Number of constrained facets in the CDT: "
<< ccdt.number_of_constrained_facets() << '\n';
std::ofstream ofs(argc > 2 ? argv[2] : "out.mesh");
ofs.precision(17);
CGAL::IO::write_MEDIT(ofs, ccdt);
CGAL::draw(ccdt);
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,95 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/IO/write_MEDIT.h>
#include <CGAL/Surface_mesh/Surface_mesh.h>
#include <CGAL/Polygon_mesh_processing/self_intersections.h>
#include <CGAL/Polygon_mesh_processing/autorefinement.h>
#include <CGAL/Polygon_mesh_processing/triangulate_faces.h>
#include <CGAL/boost/graph/copy_face_graph.h>
#include <CGAL/make_conforming_constrained_Delaunay_triangulation_3.h>
#include <CGAL/draw_constrained_triangulation_3.h>
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
using Point = K::Point_3;
using Surface_mesh = CGAL::Surface_mesh<Point>;
namespace PMP = CGAL::Polygon_mesh_processing;
int main(int argc, char* argv[])
{
const auto filename = (argc > 1) ? argv[1]
: CGAL::data_file_path("meshes/mpi_and_sphere.off");
CGAL::Surface_mesh<K::Point_3> mesh;
if(!CGAL::IO::read_polygon_mesh(filename, mesh)) {
std::cerr << "Error: cannot read file " << filename << std::endl;
return EXIT_FAILURE;
}
std::cout << "Number of facets in " << filename << ": "
<< mesh.number_of_faces() << "\n";
// Check if the mesh is a triangle mesh
bool triangle_mesh = CGAL::is_triangle_mesh(mesh);
if(!triangle_mesh)
{
std::cout << "Mesh is not a triangle mesh, triangulate faces"
<< " to check self-intersections...\n";
CGAL::Surface_mesh<K::Point_3> trimesh;
CGAL::copy_face_graph(mesh, trimesh);
PMP::triangulate_faces(trimesh);
if(PMP::does_self_intersect(trimesh))
{
std::cout << "Mesh self-intersects, let's keep the triangulated version"
<< " for future autorefinement\n";
CGAL::copy_face_graph(trimesh, mesh);
mesh = std::move(trimesh);
triangle_mesh = true;
}
}
CGAL::Conforming_constrained_Delaunay_triangulation_3<K> ccdt;
if(triangle_mesh && PMP::does_self_intersect(mesh))
{
std::cout << "Mesh is a self-intersecting triangle mesh, perform autorefinement...\n";
// use a polygon soup as container as the output will most likely be non-manifold
std::vector<K::Point_3> points;
std::vector<std::vector<std::size_t>> polygons;
PMP::polygon_mesh_to_polygon_soup(mesh, points, polygons);
PMP::autorefine_triangle_soup(points, polygons);
std::cout << "Number of facets after preprocessing: "
<< polygons.size() << "\n";
if(PMP::does_polygon_soup_self_intersect(points, polygons))
{
std::cerr << "Error: mesh still self-intersects after autorefine\n";
return EXIT_FAILURE;
}
ccdt = CGAL::make_conforming_constrained_Delaunay_triangulation_3(points, polygons);
}
else
{
std::cout << "Number of facets after preprocessing: "
<< mesh.number_of_faces() << "\n";
ccdt = CGAL::make_conforming_constrained_Delaunay_triangulation_3(mesh);
}
std::cout << "Number of constrained facets in the CDT: "
<< ccdt.number_of_constrained_facets() << '\n';
std::ofstream ofs(argc > 2 ? argv[2] : "out.mesh");
ofs.precision(17);
CGAL::IO::write_MEDIT(ofs, ccdt);
CGAL::draw(ccdt);
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,49 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/IO/polygon_mesh_io.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/make_conforming_constrained_Delaunay_triangulation_3.h>
#include <CGAL/IO/write_MEDIT.h>
#include <cassert>
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
int main(int argc, char* argv[])
{
auto filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/mpi.off");
CGAL::Surface_mesh<K::Point_3> mesh;
if(!CGAL::IO::read_polygon_mesh(filename, mesh)) {
std::cerr << "Error: cannot read file " << filename << std::endl;
return EXIT_FAILURE;
}
std::cout << "Read " << mesh.number_of_vertices() << " vertices and "
<< mesh.number_of_faces() << " faces" << std::endl;
auto ccdt = CGAL::make_conforming_constrained_Delaunay_triangulation_3(mesh);
//! [use of ccdt.triangulation()]
std::cout << "Number of vertices in the CDT: "
<< ccdt.triangulation().number_of_vertices() << '\n';
//! [use of ccdt.triangulation()]
std::cout << "Number of constrained facets in the CDT: "
<< ccdt.number_of_constrained_facets() << '\n';
std::ofstream ofs(argc > 2 ? argv[2] : "out.mesh");
ofs.precision(17);
CGAL::IO::write_MEDIT(ofs, ccdt);
//! [move ccdt to tr]
auto tr = std::move(ccdt).triangulation();
// Now `tr` is a valid `CGAL::Triangulation_3` object that can be used for further processing.
// and the triangulation of `ccdt` is empty.
std::cout << "Number of vertices in the triangulation `tr`: "
<< tr.number_of_vertices() << '\n';
std::cout << "Number of vertices in `ccdt`: "
<< ccdt.triangulation().number_of_vertices() << '\n';
assert(ccdt.triangulation().number_of_vertices() == 0);
//! [move ccdt to tr]
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,60 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Polygon_mesh_processing/remesh_planar_patches.h>
#include <CGAL/make_conforming_constrained_Delaunay_triangulation_3.h>
#include <CGAL/IO/write_MEDIT.h>
#include <CGAL/Polygon_mesh_processing/IO/polygon_mesh_io.h>
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
using Mesh = CGAL::Surface_mesh<K::Point_3>;
using face_descriptor = boost::graph_traits<Mesh>::face_descriptor;
int main(int argc, char* argv[])
{
std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/cross.off");
Mesh input;
if(!CGAL::Polygon_mesh_processing::IO::read_polygon_mesh(filename, input)) {
std::cerr << "Invalid input." << std::endl;
return 1;
}
std::cout << "Read " << input.number_of_vertices() << " vertices and "
<< input.number_of_faces() << " facets\n";
Mesh mesh;
auto plc_facet_map = get(CGAL::face_patch_id_t<int>(), mesh);
// Remesh planar patches and segment the mesh into planar patches
CGAL::Polygon_mesh_processing::remesh_planar_patches(input, mesh,
CGAL::parameters::default_values(),
CGAL::parameters::face_patch_map(plc_facet_map)
.do_not_triangulate_faces(true));
filename = argc > 2 ? argv[2] : "mesh.ply";
CGAL::IO::write_polygon_mesh(filename, mesh,
CGAL::parameters::stream_precision(17));
std::cout << "Wrote segmented mesh to " << filename << "\n";
// Create a conforming constrained Delaunay triangulation from the mesh
auto ccdt = CGAL::make_conforming_constrained_Delaunay_triangulation_3(mesh,
CGAL::parameters::plc_face_id(plc_facet_map));
std::cout << "Number of vertices in the CDT: "
<< ccdt.triangulation().number_of_vertices() << '\n'
<< "Number of constrained facets in the CDT: "
<< ccdt.number_of_constrained_facets() << '\n';
filename = argc > 3 ? argv[3] : "out.mesh";
std::ofstream out(filename);
out.precision(17);
CGAL::IO::write_MEDIT(out, ccdt, CGAL::parameters::with_plc_face_id(true));
std::cout << "Wrote CDT to " << filename << "\n";
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,79 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Conforming_constrained_Delaunay_triangulation_cell_base_3.h>
#include <CGAL/Conforming_constrained_Delaunay_triangulation_vertex_base_3.h>
#include <CGAL/Tetrahedral_remeshing/Remeshing_cell_base_3.h>
#include <CGAL/Tetrahedral_remeshing/Remeshing_vertex_base_3.h>
#include <CGAL/make_conforming_constrained_Delaunay_triangulation_3.h>
#include <CGAL/tetrahedral_remeshing.h>
#include <CGAL/IO/File_medit.h>
#include <CGAL/IO/polygon_mesh_io.h>
#include <CGAL/IO/write_MEDIT.h>
#include <CGAL/property_map.h>
#include <unordered_set>
#include <fstream>
#include <string>
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
using Vbb = CGAL::Tetrahedral_remeshing::Remeshing_vertex_base_3<K>;
using Vb = CGAL::Conforming_constrained_Delaunay_triangulation_vertex_base_3<K, Vbb>;
using Cbb = CGAL::Tetrahedral_remeshing::Remeshing_cell_base_3<K>;
using Cb = CGAL::Conforming_constrained_Delaunay_triangulation_cell_base_3<K, Cbb>;
using Tds = CGAL::Triangulation_data_structure_3<Vb, Cb>;
using Tr = CGAL::Triangulation_3<K, Tds>;
using CCDT = CGAL::Conforming_constrained_Delaunay_triangulation_3<K, Tr>;
// Triangulation for Remeshing
using CCDT_Tr = CCDT::Triangulation;
using Triangulation_3 = CGAL::Triangulation_3<K, CCDT_Tr::Triangulation_data_structure>;
using Vertex_handle = Triangulation_3::Vertex_handle;
using Vertex_pair = std::pair<Vertex_handle, Vertex_handle>;
using Constraints_set = std::unordered_set<Vertex_pair, boost::hash<Vertex_pair>>;
using Constraints_pmap = CGAL::Boolean_property_map<Constraints_set>;
int main(int argc, char* argv[])
{
std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/mpi.off");
CGAL::Surface_mesh<K::Point_3> mesh;
if(!CGAL::IO::read_polygon_mesh(filename, mesh))
{
std::cerr << "Error: cannot read file " << filename << std::endl;
return EXIT_FAILURE;
}
CCDT ccdt = CGAL::make_conforming_constrained_Delaunay_triangulation_3<CCDT>(mesh);
std::ofstream out("ccdt.mesh");
CGAL::IO::write_MEDIT(out, ccdt);
out.close();
Constraints_set constraints;
Constraints_pmap constraints_pmap(constraints);
namespace np = CGAL::parameters;
namespace Tet_remesh = CGAL::Tetrahedral_remeshing;
Tr tr = Tet_remesh::get_remeshing_triangulation(std::move(ccdt),
np::edge_is_constrained_map(constraints_pmap));
std::cout << "Number of vertices in tr: " << tr.number_of_vertices() << std::endl;
CGAL::tetrahedral_isotropic_remeshing(tr,
1., // target edge length
np::number_of_iterations(3)
.edge_is_constrained_map(constraints_pmap));
std::cout << "Number of vertices in tr: "
<< tr.number_of_vertices() << std::endl;
std::ofstream ofs("tr.mesh");
CGAL::IO::write_MEDIT(ofs, tr);
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,993 @@
// Copyright (c) 2019-2024 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) : Laurent Rineau
#ifndef CGAL_CONFORMING_DELAUNAY_TRIANGULATION_3_H
#define CGAL_CONFORMING_DELAUNAY_TRIANGULATION_3_H
#include <CGAL/license/Constrained_triangulation_3.h>
#include <CGAL/Constrained_triangulation_3/internal/config.h>
#include <CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h>
#include <CGAL/Triangulation_2/internal/Polyline_constraint_hierarchy_2.h>
#include <CGAL/Triangulation_segment_traverser_3.h>
#include <CGAL/unordered_flat_set.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/map.hpp>
#include <boost/container/small_vector.hpp>
#include <boost/iterator/function_output_iterator.hpp>
#include <fstream>
#include <bitset>
#ifndef DOXYGEN_RUNNING
namespace CGAL {
template <typename T_3>
class Conforming_Delaunay_triangulation_3 : public T_3 {
public:
static constexpr bool t_3_is_not_movable =
CGAL::cdt_3_msvc_2019_or_older() || false == CGAL::is_nothrow_movable_v<T_3>;
using Geom_traits = typename T_3::Geom_traits;
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;
inline static With_offset_tag with_offset{};
inline static With_point_tag with_point{};
inline static With_point_and_info_tag with_point_and_info{};
Conforming_Delaunay_triangulation_3(const Geom_traits& gt = Geom_traits())
: T_3(gt)
{
}
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 va < vb;
}
};
using Constraint_hierarchy =
Polyline_constraint_hierarchy_2<Vertex_handle, Compare_vertex_handle, Point>;
public:
using Constrained_polyline_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, with_point);
return os.str();
}
auto display_subcstr(Subconstraint subconstraint) const {
auto [va, vb] = subconstraint;
std::stringstream os;
os << "(" << IO::oformat(va, with_offset) << ", " << IO::oformat(vb, with_offset) << ")"
<< ": [ " << display_vert(va) << " - " << display_vert(vb) << " ]";
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) {
auto d = self->tr().dimension();
for( ; cell_it != end; ++cell_it )
for( int i = 0; i < d; ++i )
for( int j = i+1; j <= d; ++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;
if(self->use_finite_edges_map()) {
if(v1 > v2) std::swap(v1, v2);
auto v1_index = v1->time_stamp();
[[maybe_unused]] auto nb_erased = self->all_finite_edges[v1_index].erase(v2);
if constexpr (cdt_3_can_use_cxx20_format()) if(self->debug_finite_edges_map() && nb_erased > 0) {
std::cerr << cdt_3_format("erasing edge {} {}\n", self->display_vert((std::min)(v1, v2)),
self->display_vert((std::max)(v1, v2)));
}
}
auto [contexts_begin, contexts_end] =
self->constraint_hierarchy.contexts(v1, v2);
if(contexts_begin != contexts_end) {
self->add_to_subconstraints_to_conform(v1, v2, contexts_begin->id());
}
}
}
void after_insertion(Vertex_handle v) const {
if(!self->use_finite_edges_map()) return;
CGAL_assertion(self->dimension() > 1);
self->incident_edges(v, boost::make_function_output_iterator([this](Edge e) { self->new_edge(e); }));
self->incident_cells(v, boost::make_function_output_iterator([this, v](Cell_handle c) {
auto v_index = c->index(v);
if(self->dimension() == 2) {
auto j = self->cw(v_index);
auto k = self->ccw(v_index);
self->new_edge(Edge(c, j, k));
} else {
for(int i = 0; i < 3; ++i) {
auto j = self->vertex_triple_index(v_index, i);
auto k = self->vertex_triple_index(v_index, self->cw(i));
self->new_edge(Edge(c, j, k));
}
}
}));
}
void reinsert_vertices(Vertex_handle v) const { after_insertion(v); }
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]] Constrained_polyline_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,
Constrained_polyline_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>
Constrained_polyline_id insert_constrained_edge_impl(Vertex_handle va, Vertex_handle vb,
Visitor&) {
if(va != vb) {
if(segment_vertex_epsilon != 0.) {
auto [min_dist, min_vertex] = min_distance_and_vertex_between_constraint_and_encroaching_vertex(va, vb);
check_segment_vertex_distance_or_throw(va, vb, min_vertex, CGAL::to_double(min_dist),
Check_distance::NON_SQUARED_DISTANCE);
}
const Constrained_polyline_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->ccdt_3_data().set_on_constraint(c_id);
if(prev != Vertex_handle{}) {
if(v != vb) {
v->ccdt_3_data().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 {};
}
void new_edge(Edge e)
{
if(!update_all_finite_edges_) return;
auto [v1, v2] = make_sorted_pair(tr().vertices(e));
if(tr().is_infinite(v1) || tr().is_infinite(v2))
return;
[[maybe_unused]] auto [_, inserted] = all_finite_edges[v1->time_stamp()].insert(v2);
if constexpr (cdt_3_can_use_cxx20_format()) if (debug_finite_edges_map() && inserted) {
if(v2 < v1) std::swap(v1, v2);
std::cerr << cdt_3_format("new_edge({}, {})\n", display_vert(v1), display_vert(v2));
}
}
void new_cell(Cell_handle c) {
auto d = tr().dimension();
for(int i = 0; i < d; ++i) {
for(int j = i+1; j <= d; ++j) {
new_edge(Edge(c, i, j));
}
}
}
auto new_cells_output_iterator()
{
return boost::function_output_iterator([this](Cell_handle c) {
if(use_finite_edges_map())
this->new_cell(c);
});
}
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);
auto v = tr().insert_in_conflict(p, lt, c, li, lj, tester,
visitor);
new_vertex(v);
return v;
} // dim 3
case 2: {
typename T_3::Conflict_tester_2 tester(p, this);
auto v = tr().insert_in_conflict(p, lt, c, li, lj, tester, visitor);
if(use_finite_edges_map()) {
new_vertex(v);
tr().incident_edges(v, boost::make_function_output_iterator([&](Edge e) { this->new_edge(e); }));
}
return v;
} // dim 2
default:
// dimension <= 1
// Do not use the generic insert.
auto v = tr().insert(p, c);
if(use_finite_edges_map()) {
new_vertex(v);
all_finite_edges.clear();
if (debug_finite_edges_map()) std::cerr << "all_finite_edges.clear()\n";
for(auto e: tr().all_edges()) {
new_edge(e);
}
}
return v;
}
}
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_subconstraint(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;
}
bool debug_Steiner_points() const {
return debug_flags[static_cast<int>(Debug_flags::Steiner_points)];
}
void debug_Steiner_points(bool b) {
debug_flags.set(static_cast<int>(Debug_flags::Steiner_points), b);
}
bool debug_input_faces() const {
return debug_flags[static_cast<int>(Debug_flags::input_faces)];
}
void debug_input_faces(bool b) {
debug_flags.set(static_cast<int>(Debug_flags::input_faces), b);
}
bool debug_missing_region() const {
return debug_flags[static_cast<int>(Debug_flags::missing_region)];
}
void debug_missing_region(bool b) {
debug_flags.set(static_cast<int>(Debug_flags::missing_region), b);
}
bool debug_regions() const {
return debug_flags[static_cast<int>(Debug_flags::regions)];
}
void debug_regions(bool b) {
debug_flags.set(static_cast<int>(Debug_flags::regions), b);
}
bool debug_copy_triangulation_into_hole() const {
return debug_flags[static_cast<int>(Debug_flags::copy_triangulation_into_hole)];
}
void debug_copy_triangulation_into_hole(bool b) {
debug_flags.set(static_cast<int>(Debug_flags::copy_triangulation_into_hole), b);
}
bool debug_validity() const {
return debug_flags[static_cast<int>(Debug_flags::validity)];
}
void debug_validity(bool b) {
debug_flags.set(static_cast<int>(Debug_flags::validity), b);
}
bool use_older_cavity_algorithm() const {
return debug_flags[static_cast<int>(Debug_flags::use_older_cavity_algorithm)];
}
void use_older_cavity_algorithm(bool b) {
debug_flags.set(static_cast<int>(Debug_flags::use_older_cavity_algorithm), b);
}
bool debug_finite_edges_map() const {
return debug_flags[static_cast<int>(Debug_flags::debug_finite_edges_map)];
}
void debug_finite_edges_map(bool b) {
debug_flags.set(static_cast<int>(Debug_flags::debug_finite_edges_map), b);
}
bool use_finite_edges_map() const {
return update_all_finite_edges_ && debug_flags[static_cast<int>(Debug_flags::use_finite_edges_map)];
}
void use_finite_edges_map(bool b) {
debug_flags.set(static_cast<int>(Debug_flags::use_finite_edges_map), b);
}
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);
}
Constrained_polyline_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_edge(Vertex_handle va, Vertex_handle vb) const {
const bool is_edge_v1 =
((debug_finite_edges_map() && use_finite_edges_map()) || !use_finite_edges_map()) && tr().tds().is_edge(va, vb);
if(use_finite_edges_map() && va > vb) std::swap(va, vb);
const auto va_index = va->time_stamp();
const bool is_edge_v2 =
use_finite_edges_map() && all_finite_edges[va_index].find(vb) != all_finite_edges[va_index].end();
if(debug_finite_edges_map() && use_finite_edges_map() && is_edge_v1 != is_edge_v2) {
std::cerr << "!! Inconsistent edge status\n";
std::cerr << " -> constraint " << display_vert(va) << " " << display_vert(vb) << '\n';
std::cerr << " -> edge " << (is_edge_v1 ? "is" : "is not") << " in the triangulation\n";
std::cerr << " -> edge " << (is_edge_v2 ? "is" : "is not") << " in all_finite_edges\n";
debug_dump("bug-inconsistent-edge-status");
CGAL_error();
}
const bool is_edge = use_finite_edges_map() ? is_edge_v2 : is_edge_v1;
return is_edge;
}
using T_3::is_edge;
bool is_conforming() const {
return std::all_of(constraint_hierarchy.subconstraints_begin(),
constraint_hierarchy.subconstraints_end(),
[this](const auto &sc) {
const auto [va, vb] = sc;
const auto is_edge = this->is_edge(va, vb);
#if CGAL_DEBUG_CDT_3 & 128 && CGAL_CAN_USE_CXX20_FORMAT
std::cerr << cdt_3_format("is_conforming>> Edge is 3D: {} ({} , {})\n",
is_edge,
CGAL::IO::oformat(va, with_point_and_info),
CGAL::IO::oformat(vb, with_point_and_info));
#endif // CGAL_DEBUG_CDT_3
return is_edge;
});
}
enum class Check_distance { SQUARED_DISTANCE, NON_SQUARED_DISTANCE };
void check_segment_vertex_distance_or_throw(Vertex_handle va,
Vertex_handle vb,
Vertex_handle min_vertex,
double min_dist,
Check_distance option)
{
if(!max_bbox_edge_length) {
update_max_bbox_edge_length();
}
if((option == Check_distance::NON_SQUARED_DISTANCE && min_dist < segment_vertex_epsilon * *max_bbox_edge_length) ||
(option == Check_distance::SQUARED_DISTANCE &&
min_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(min_vertex) << '\n';
ss << " -> constrained segment " << display_vert(va) << " - " << display_vert(vb) << '\n';
ss << " -> distance = " << min_dist << '\n';
ss << " -> max_bbox_edge_length = " << *max_bbox_edge_length << '\n';
CGAL_error_msg(ss.str().c_str());
}
}
auto ancestors_of_Steiner_vertex_on_edge(Vertex_handle v) const {
std::pair<Vertex_handle, Vertex_handle> result;
CGAL_precondition(v->ccdt_3_data().is_Steiner_vertex_on_edge());
CGAL_assertion(v->ccdt_3_data().number_of_incident_constraints() == 1);
const auto v_time_stamp = v->time_stamp();
const auto constrained_polyline_id = v->ccdt_3_data().constrained_polyline_id(*this);
const auto first = this->constraint_hierarchy.vertices_in_constraint_begin(constrained_polyline_id);
const auto end = this->constraint_hierarchy. vertices_in_constraint_end(constrained_polyline_id);
std::cerr << "ancestors_of_Steiner_vertex_on_edge " << display_vert(v) << '\n';
for(auto it = first; it != end; ++it) {
std::cerr << " - " << display_vert(*it) << '\n';
}
CGAL_assertion(first != end);
const auto last = std::prev(this->constraint_hierarchy.vertices_in_constraint_end(constrained_polyline_id));
CGAL_assertion(first != last);
CGAL_assertion((*first)->time_stamp() < v_time_stamp);
CGAL_assertion((*last)->time_stamp() < v_time_stamp);
for(auto it = first; (*it) != v; ++it) {
if((*it)->time_stamp() < v_time_stamp) {
result.first = *it;
}
CGAL_assertion(it != last);
}
for(auto it = last; (*it) != v; --it) {
if((*it)->time_stamp() < v_time_stamp) {
result.second = *it;
}
CGAL_assertion(it != first);
}
return result;
}
auto min_distance_and_vertex_between_constraint_and_encroaching_vertex(Vertex_handle va, Vertex_handle vb) const {
struct Result {
typename T_3::Geom_traits::FT min_dist = (std::numeric_limits<double>::max)();
Vertex_handle v = {};
} result;
const auto vector_of_encroaching_vertices = encroaching_vertices(va, vb);
for(auto v: vector_of_encroaching_vertices) {
const auto dist = CGAL::approximate_sqrt(squared_distance(tr().point(v), Line{tr().point(va), tr().point(vb)}));
if(dist < result.min_dist) {
result = Result{dist, v};
}
}
return result;
}
bool write_missing_segments_file(std::ostream &out) {
bool any_missing_segment = false;
std::for_each(
constraint_hierarchy.subconstraints_begin(), constraint_hierarchy.subconstraints_end(),
[this, &out, &any_missing_segment](const auto &sc) {
const auto [v0, v1] = sc;
if (!this->is_edge(v0, v1)) {
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.subconstraints_begin(), constraint_hierarchy.subconstraints_end(),
[this, &out](const auto &sc) {
const auto [v0, v1] = sc;
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) const {
{
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) {
update_all_finite_edges();
while(!subconstraints_to_conform.empty()) {
const auto [subconstraint, constrained_polyline_id] = subconstraints_to_conform.top();
subconstraints_to_conform.pop();
const auto [va, vb] = subconstraint;
if(!constraint_hierarchy.is_subconstraint(va, vb)) {
continue;
}
#if CGAL_DEBUG_CDT_3 & 32
std::cerr << "tr().subconstraints_to_conform.pop()="
<< display_subcstr(subconstraint) << "\n";
#endif // CGAL_DEBUG_CDT_3
conform_subconstraint(subconstraint, constrained_polyline_id, visitor);
}
}
template <typename Visitor>
Vertex_handle insert_Steiner_point_on_subconstraint(
Point steiner_pt, Cell_handle hint,
Subconstraint subconstraint, Constrained_polyline_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->ccdt_3_data().set_vertex_type(CDT_3_vertex_type::STEINER_ON_EDGE);
if(lt != T_3::VERTEX) {
v->ccdt_3_data().set_on_constraint(constraint);
}
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);
new_vertex(v);
return v;
}
/// Return `true` if a Steiner point was inserted
template <typename Visitor>
bool conform_subconstraint(Subconstraint subconstraint,
Constrained_polyline_id constraint,
Visitor& visitor)
{
const Vertex_handle va = subconstraint.first;
const Vertex_handle vb = subconstraint.second;
CGAL_assertion(va != vb);
if(!this->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(debug_Steiner_points()) {
const auto [c_start, c_end] = constraint_extremities(constraint);
std::cerr << "(" << IO::oformat(va, with_offset) << ", " << IO::oformat(vb, with_offset) << ")";
std::cerr << ": [ " << display_vert(c_start) << " - " << display_vert(c_end) << " ] ";
std::cerr << " new vertex " << display_vert(v) << '\n';
}
return true;
}
return false;
}
Constrained_polyline_id constraint_from_extremities(Vertex_handle va, Vertex_handle vb) const {
if(va->ccdt_3_data().number_of_incident_constraints() == 0 || vb->ccdt_3_data().number_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`
Constrained_polyline_id c_id = constraint_around(va, vb, false);
if(c_id != Constrained_polyline_id{}) return c_id;
c_id = constraint_around(vb, va, false);
if(c_id != Constrained_polyline_id{}) return c_id;
c_id = constraint_around(va, vb, true);
return c_id;
}
auto constraint_extremities(Constrained_polyline_id c_id) const {
CGAL_assertion(std::find(this->constraint_hierarchy.constraints_begin(),
this->constraint_hierarchy.constraints_end(), c_id) != this->constraint_hierarchy.constraints_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
const auto begin = 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 = *begin;
const auto c_vb = *std::prev(end);
return std::make_pair(c_va, c_vb);
}
Constrained_polyline_id constraint_around(Vertex_handle va, Vertex_handle vb, bool expensive = true) const {
auto constraint_id_goes_to_vb = [this, va, vb](Constrained_polyline_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->ccdt_3_data().number_of_incident_constraints() == 1)
{
const Constrained_polyline_id c_id = va->ccdt_3_data().constrained_polyline_id(*this);
CGAL_assertion(c_id != Constrained_polyline_id{});
if(constraint_id_goes_to_vb(c_id)) return c_id;
} else if (expensive == true && va->ccdt_3_data().number_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(va, other_v)) {
const Constrained_polyline_id c_id = context.id();
if(constraint_id_goes_to_vb(c_id)) return c_id;
}
}
}
return Constrained_polyline_id{};
}
struct Construct_Steiner_point_return_type {
typename T_3::Geom_traits::Point_3 point;
Cell_handle hint;
Vertex_handle reference_vertex;
};
auto encroaching_vertices(Vertex_handle va, Vertex_handle vb) const {
auto& gt = tr().geom_traits();
auto angle_functor = gt.angle_3_object();
const auto& pa = tr().point(va);
const auto& pb = tr().point(vb);
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
std::cerr << " - " << IO::oformat(simplex, With_point_tag{}) << '\n';
#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);
if(tr().dimension() > 2) {
const auto [other_cell, other_index] = tr().mirror_facet({cell, facet_index});
register_vertex(other_cell->vertex(other_index));
}
break;
}
case 1: {
auto edge = static_cast<Edge>(simplex);
if(tr().dimension() < 3) {
auto [cell, i, j] = edge;
visit_cell(cell);
if(tr().dimension() < 2) break;
auto neighbor_cell = cell->neighbor(3 - i - j);
visit_cell(neighbor_cell);
break;
}
auto circ = tr().incident_cells(edge);
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!\n";
std::cerr << " -> constraint " << display_vert(va) << " " << display_vert(vb) << '\n';
std::cerr << " -> vertex " << 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, [&](Vertex_handle v) {
std::cerr << " " << this->display_vert(v) << " angle " << approximate_angle(pa, this->tr().point(v), pb)
<< '\n';
});
#endif // CGAL_DEBUG_CDT_3
vector_of_encroaching_vertices.erase(end, vector_of_encroaching_vertices.end());
return vector_of_encroaching_vertices;
}
Construct_Steiner_point_return_type
construct_Steiner_point(Constrained_polyline_id constrained_polyline_id, Subconstraint subconstraint)
{
auto& gt = tr().geom_traits();
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(constrained_polyline_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";
#endif // CGAL_DEBUG_CDT_3
const auto vector_of_encroaching_vertices = encroaching_vertices(va, vb);
CGAL_assertion(vector_of_encroaching_vertices.size() > 0);
const auto reference_vertex_it = std::max_element(
vector_of_encroaching_vertices.begin(), vector_of_encroaching_vertices.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 != vector_of_encroaching_vertices.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->ccdt_3_data().is_Steiner_vertex_on_edge()) {
CGAL_assertion(reference_vertex->ccdt_3_data().number_of_incident_constraints() == 1);
const auto ref_constrained_polyline_id = reference_vertex->ccdt_3_data().constrained_polyline_id(*this);
const auto [ref_va, ref_vb] = constraint_extremities(ref_constrained_polyline_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});
check_segment_vertex_distance_or_throw(orig_va, orig_vb, reference_vertex, CGAL::to_double(sq_dist),
Check_distance::SQUARED_DISTANCE);
}
}
// 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() { return *this; };
const T_3& tr() const { return *this; };
Compare_vertex_handle comp = {this};
Constraint_hierarchy constraint_hierarchy = {comp};
static_assert(CGAL::cdt_3_msvc_2019_or_older() || CGAL::is_nothrow_movable_v<Constraint_hierarchy>);
Bbox_3 bbox{};
double segment_vertex_epsilon = 1e-8;
std::optional<double> max_bbox_edge_length;
using Pair_of_vertex_handles = std::pair<Vertex_handle, Vertex_handle>;
boost::container::map<Pair_of_vertex_handles, Constrained_polyline_id> pair_of_vertices_to_cid;
Insert_in_conflict_visitor insert_in_conflict_visitor = {this};
using Stack_info = std::pair<Subconstraint, Constrained_polyline_id>;
using Subconstraints_to_conform = std::stack<Stack_info, std::vector<Stack_info>>;
Subconstraints_to_conform subconstraints_to_conform;
std::vector<CGAL::unordered_flat_set<Vertex_handle>> all_finite_edges;
bool update_all_finite_edges_ = false;
void update_all_finite_edges() {
if(!update_all_finite_edges_) {
update_all_finite_edges_ = true;
if(use_finite_edges_map()) {
all_finite_edges.clear();
all_finite_edges.resize(tr().number_of_vertices()+1);
for(auto e: tr().all_edges()) {
new_edge(e);
}
}
}
}
void new_vertex(Vertex_handle v) {
if(use_finite_edges_map() && v->time_stamp() >= all_finite_edges.size()) {
all_finite_edges.emplace_back();
CGAL_assertion(v->time_stamp() == all_finite_edges.size() - 1);
}
}
enum class Debug_flags {
Steiner_points = 0,
conforming,
input_faces,
missing_region,
regions,
copy_triangulation_into_hole,
validity,
use_older_cavity_algorithm,
debug_finite_edges_map,
use_finite_edges_map,
nb_of_flags
};
std::bitset<static_cast<int>(Debug_flags::nb_of_flags)> debug_flags{};
bool is_Delaunay = true;
};
} // end CGAL
#endif // not DOXYGEN_RUNNING
#
#endif // CGAL_CONFORMING_DELAUNAY_TRIANGULATION_3_H

View File

@ -0,0 +1,24 @@
// Copyright (c) 2025 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) : Laurent Rineau
#ifndef CGAL_CONFORMING_CONSTRAINED_DELAUNAY_TRIANGULATION_3_FWD_H
#define CGAL_CONFORMING_CONSTRAINED_DELAUNAY_TRIANGULATION_3_FWD_H
#include <CGAL/license/Constrained_triangulation_3.h>
namespace CGAL {
template <typename Traits, typename Tr>
class Conforming_constrained_Delaunay_triangulation_3;
} // namespace CGAL
#endif // CGAL_CONFORMING_CONSTRAINED_DELAUNAY_TRIANGULATION_3_FWD_H

View File

@ -0,0 +1,109 @@
// Copyright (c) 2019-2024 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) : Laurent Rineau
#ifndef CGAL_CONSTRAINED_DELAUNAY_TRIANGULATION_CELL_BASE_3_H
#define CGAL_CONSTRAINED_DELAUNAY_TRIANGULATION_CELL_BASE_3_H
#include <CGAL/license/Constrained_triangulation_3.h>
#include <CGAL/Triangulation_simplex_base_with_time_stamp.h>
#include <CGAL/Conforming_constrained_Delaunay_triangulation_cell_data_3.h>
#include <CGAL/Triangulation_cell_base_3.h>
#include <CGAL/SMDS_3/io_signature.h>
namespace CGAL {
/**
* @ingroup PkgConstrainedTriangulation3Classes
* @brief Cell base class for the 3D conforming constrained Delaunay triangulation.
*
* This class is derived from its template parameter `CellBase` and provides additional functionality
* required by `Conforming_constrained_Delaunay_triangulation_3`.
*
* @tparam Traits The geometric traits class, which must be a model of `ConformingConstrainedDelaunayTriangulationTraits_3`.
* It should be the same as the geometric traits class of the triangulation.
* @tparam CellBase The base class for the cell, which must be a model of `TriangulationCellBase_3`.
*
* @cgalModels{ConformingConstrainedDelaunayTriangulationCellBase_3}
*
* \sa `CGAL::Conforming_constrained_Delaunay_triangulation_vertex_base_3`
*/
template <typename Traits, typename CellBase = Triangulation_cell_base_3<Traits> >
class Conforming_constrained_Delaunay_triangulation_cell_base_3
: public Triangulation_simplex_base_with_time_stamp<CellBase>
{
using Base = Triangulation_simplex_base_with_time_stamp<CellBase>;
Conforming_constrained_Delaunay_triangulation_cell_data_3 ccdt_3_data_;
public:
// To get correct cell type in TDS
template < class TDS3 >
struct Rebind_TDS {
typedef typename CellBase::template Rebind_TDS<TDS3>::Other Cb3;
typedef Conforming_constrained_Delaunay_triangulation_cell_base_3 <Traits, Cb3> Other;
};
// Constructors inherited from the base class
using Base::Base;
Conforming_constrained_Delaunay_triangulation_cell_data_3& ccdt_3_data() {
return ccdt_3_data_;
}
const Conforming_constrained_Delaunay_triangulation_cell_data_3& ccdt_3_data() const {
return ccdt_3_data_;
}
static std::string io_signature() {
static_assert(
std::is_same_v<
decltype(std::declval<Conforming_constrained_Delaunay_triangulation_cell_data_3>().face_constraint_index(0)), int>);
return Get_io_signature<Base>()() + "+(" + Get_io_signature<int>()() + ")[4]";
}
friend std::ostream&
operator<<(std::ostream& os,
const Conforming_constrained_Delaunay_triangulation_cell_base_3& c)
{
os << static_cast<const Base&>(c);
for( unsigned li = 0; li < 4; ++li ) {
if(IO::is_ascii(os)) {
os << " " << c.ccdt_3_data().face_constraint_index(li);
} else {
CGAL::write(os, c.ccdt_3_data().face_constraint_index(li));
}
}
return os;
}
friend std::istream&
operator>>(std::istream& is,
Conforming_constrained_Delaunay_triangulation_cell_base_3& c)
{
is >> static_cast<Base&>(c);
if(!is) return is;
for( int li = 0; li < 4; ++li ) {
int i;
if(IO::is_ascii(is)) {
is >> i;
} else {
CGAL::read(is, i);
}
if(!is) return is;
c.face_id[li] = i;
}
return is;
}
};
} // namespace CGAL
#endif // CGAL_CONSTRAINED_DELAUNAY_TRIANGULATION_CELL_BASE_3_H

View File

@ -0,0 +1,86 @@
// Copyright (c) 2019-2024 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) : Laurent Rineau
#ifndef CGAL_CONSTRAINED_DELAUNAY_TRIANGULATION_CELL_DATA_3_H
#define CGAL_CONSTRAINED_DELAUNAY_TRIANGULATION_CELL_DATA_3_H
#include <CGAL/license/Constrained_triangulation_3.h>
#include <CGAL/Constrained_triangulation_3/internal/config.h>
#include <array>
#include <bitset>
namespace CGAL {
enum class CDT_3_cell_marker {
CLEAR = 0,
IN_REGION = 1,
VISITED = 1,
ON_REGION_BOUNDARY = 2,
nb_of_markers
};
/*!
* @ingroup PkgConstrainedTriangulation3Classes
* @brief Internal per-cell data for \cgal 3D conforming constrained Delaunay triangulations
*
* This class is an internal detail of the implementation of \cgal 3D conforming constrained Delaunay triangulations.
*
* Any model of the `ConformingConstrainedDelaunayTriangulationCellBase_3` concept must include one object of this type
* as a non-static data member.
*/
class Conforming_constrained_Delaunay_triangulation_cell_data_3 {
/// @cond SKIP_IN_MANUAL
template <typename Tr> friend class Conforming_constrained_Delaunay_triangulation_3_impl;
/// @endcond
std::array<CDT_3_signed_index, 4> face_id = { -1, -1, -1, -1 };
std::array<void*, 4> facet_2d = {nullptr, nullptr, nullptr, nullptr};
std::bitset<static_cast<unsigned>(CDT_3_cell_marker::nb_of_markers)> markers;
bool is_marked() const { return markers.any(); }
bool is_marked(CDT_3_cell_marker m) const { return markers.test(static_cast<unsigned>(m)); }
void set_mark(CDT_3_cell_marker m) { markers.set(static_cast<unsigned>(m)); }
void clear_mark(CDT_3_cell_marker m) { markers.reset(static_cast<unsigned>(m)); }
void clear_marks() { markers.reset(); }
template <typename Facet_handle>
void set_facet_constraint(int i, CDT_3_signed_index face_id,
Facet_handle facet_2d)
{
this->face_id[unsigned(i)] = face_id;
this->facet_2d[unsigned(i)] = static_cast<void*>(facet_2d == Facet_handle{} ? nullptr : std::addressof(*facet_2d));
}
template <typename CDT_2>
auto face_2 (const CDT_2& cdt, int i) const {
using Face = typename CDT_2::Face;
auto ptr = static_cast<Face*>(facet_2d[unsigned(i)]);
return cdt.tds().faces().iterator_to(*ptr);
}
public:
/// @{
// @cond SKIP_IN_MANUAL
bool is_facet_constrained(int i) const { return face_id[unsigned(i)] >= 0; }
CDT_3_signed_index face_constraint_index(int i) const {
return face_id[unsigned(i)];
}
void set_face_constraint_index(int i, CDT_3_signed_index index) {
face_id[unsigned(i)] = index;
}
/// @endcond
};
} // namespace CGAL
#endif // CGAL_CONSTRAINED_DELAUNAY_TRIANGULATION_CELL_DATA_3_H

View File

@ -0,0 +1,69 @@
// Copyright (c) 2019-2024 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) : Laurent Rineau
#ifndef CGAL_CONSTRAINED_DELAUNAY_TRIANGULATION_VERTEX_BASE_3_H
#define CGAL_CONSTRAINED_DELAUNAY_TRIANGULATION_VERTEX_BASE_3_H
#include <CGAL/license/Constrained_triangulation_3.h>
#include <CGAL/Triangulation_simplex_base_with_time_stamp.h>
#include <CGAL/Triangulation_vertex_base_3.h>
#include <CGAL/Conforming_constrained_Delaunay_triangulation_vertex_data_3.h>
#include <CGAL/SMDS_3/io_signature.h>
namespace CGAL {
/**
* @ingroup PkgConstrainedTriangulation3Classes
* @brief Vertex base class for the 3D conforming constrained Delaunay triangulation.
*
* This class is derived from its parameter template `VertexBase` and provides additional functionality
* required by `Conforming_constrained_Delaunay_triangulation_3`.
*
* @tparam Traits The geometric traits class, model of `ConformingConstrainedDelaunayTriangulationTraits_3`.
* It must be the same as the geometric traits class of the triangulation.
* @tparam VertexBase The base class for the vertex. It must be a model of `TriangulationVertexBase_3`.
*
* @cgalModels{ConformingConstrainedDelaunayTriangulationVertexBase_3}
*
* \sa `CGAL::Conforming_constrained_Delaunay_triangulation_cell_base_3`
*/
template < typename Traits, typename VertexBase = Triangulation_vertex_base_3<Traits> >
class Conforming_constrained_Delaunay_triangulation_vertex_base_3
: public Triangulation_simplex_base_with_time_stamp<VertexBase>
{
Conforming_constrained_Delaunay_triangulation_vertex_data_3 ccdt_3_data_;
public:
// To get correct vertex type in TDS
template <class TDS3> struct Rebind_TDS
{
using Vb3 = typename VertexBase::template Rebind_TDS<TDS3>::Other;
using Other = Conforming_constrained_Delaunay_triangulation_vertex_base_3<Traits, Vb3>;
};
// constructors, inherited from the base class
using Base = Triangulation_simplex_base_with_time_stamp<VertexBase>;
using Base::Base;
// model of ConformingConstrainedDelaunayTriangulationVertexBase_3
Conforming_constrained_Delaunay_triangulation_vertex_data_3& ccdt_3_data() { return ccdt_3_data_; }
const Conforming_constrained_Delaunay_triangulation_vertex_data_3& ccdt_3_data() const { return ccdt_3_data_; }
static std::string io_signature() {
return Get_io_signature<VertexBase>()();
}
};
} // namespace CGAL
#endif // CGAL_CONSTRAINED_DELAUNAY_TRIANGULATION_VERTEX_BASE_3_H

View File

@ -0,0 +1,156 @@
// Copyright (c) 2019-2024 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) : Laurent Rineau
#ifndef CGAL_CONSTRAINED_DELAUNAY_TRIANGULATION_VERTEX_DATA_3_H
#define CGAL_CONSTRAINED_DELAUNAY_TRIANGULATION_VERTEX_DATA_3_H
#include <CGAL/license/Constrained_triangulation_3.h>
#include <CGAL/assertions.h>
#include <CGAL/Constrained_triangulation_3/internal/config.h>
#include <bitset>
namespace CGAL {
#ifdef DOXYGEN_RUNNING
/*!
* @ingroup PkgConstrainedTriangulation3Classes
* @brief Internal per-vertex data for \cgal 3D conforming constrained Delaunay triangulations
*
* This class is an internal detail of the implementation of \cgal 3D conforming constrained Delaunay triangulations.
*
* Any model of the `ConformingConstrainedDelaunayTriangulationVertexBase_3` concept must include one object of this type
* as a non-static data member.
*/
struct Conforming_constrained_Delaunay_triangulation_vertex_data_3 {};
#else // DOXYGEN_RUNNING
enum class CDT_3_vertex_type { FREE, CORNER, STEINER_ON_EDGE, STEINER_IN_FACE };
enum class CDT_3_vertex_marker {
CLEAR = 0,
REGION_BORDER,
REGION_INSIDE,
CAVITY,
CAVITY_ABOVE,
CAVITY_BELOW,
nb_of_markers
};
class Conforming_constrained_Delaunay_triangulation_vertex_data_3 {
protected:
// TODO: check and improve the compactness of this class
CDT_3_vertex_type m_vertex_type = CDT_3_vertex_type::FREE;
std::bitset<static_cast<int>(CDT_3_vertex_marker::nb_of_markers)> mark{};
struct C_id {
void* ptr = nullptr;
std::size_t id = 0;
friend bool operator==(const C_id& lhs, const C_id& rhs) {
return lhs.ptr == rhs.ptr && lhs.id == rhs.id;
}
};
union U {
struct On_edge {
int nb_of_incident_constraints = 0;
C_id c_id{};
} on_edge;
struct On_face{
CDT_3_signed_index face_index = 0;
} on_face;
} u {U::On_edge{}};
public:
friend bool operator==(const Conforming_constrained_Delaunay_triangulation_vertex_data_3& lhs,
const Conforming_constrained_Delaunay_triangulation_vertex_data_3& rhs) {
if(lhs.m_vertex_type != rhs.m_vertex_type || lhs.mark != rhs.mark) return false;
switch(lhs.m_vertex_type) {
case CDT_3_vertex_type::STEINER_ON_EDGE:
return lhs.u.on_edge.nb_of_incident_constraints == rhs.u.on_edge.nb_of_incident_constraints &&
lhs.u.on_edge.c_id == rhs.u.on_edge.c_id;
case CDT_3_vertex_type::STEINER_IN_FACE:
return lhs.u.on_face.face_index == rhs.u.on_face.face_index;
default:
return true;
}
}
template <typename T>
void set_on_constraint(T constrained_polyline_id) {
++u.on_edge.nb_of_incident_constraints;
u.on_edge.c_id.ptr = constrained_polyline_id.vl_with_info_pointer();
u.on_edge.c_id.id = static_cast<std::size_t>(constrained_polyline_id.index());
}
int number_of_incident_constraints() const {
CGAL_assertion(u.on_edge.nb_of_incident_constraints >= 0);
return u.on_edge.nb_of_incident_constraints;
}
void set_mark(CDT_3_vertex_marker marker) {
mark.set(static_cast<unsigned int>(marker));
}
void clear_marks() {
mark.reset();
}
void clear_mark(CDT_3_vertex_marker marker) {
mark.reset(static_cast<unsigned int>(marker));
}
bool is_marked(CDT_3_vertex_marker marker) const {
return mark.test(static_cast<unsigned int>(marker));
}
bool is_marked() const {
return mark.any();
}
template<typename Triangulation>
auto constrained_polyline_id(const Triangulation&) const {
CGAL_assertion(m_vertex_type != CDT_3_vertex_type::STEINER_IN_FACE);
using C_id = typename Triangulation::Constrained_polyline_id;
using size_type = typename Triangulation::size_type;
using Vertex_list_w_info_ptr = decltype(std::declval<C_id>().vl_with_info_pointer());
auto ptr = static_cast<Vertex_list_w_info_ptr>(u.on_edge.c_id.ptr);
auto id = static_cast<size_type>(u.on_edge.c_id.id);
return C_id{ptr, id};
}
void set_Steiner_vertex_in_face(CDT_3_signed_index face_index) {
m_vertex_type = CDT_3_vertex_type::STEINER_IN_FACE;
u.on_face = typename U::On_face{face_index};
}
CDT_3_signed_index face_index() const {
CGAL_assertion(m_vertex_type == CDT_3_vertex_type::STEINER_IN_FACE);
return u.on_face.face_index;
}
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; }
};
#endif // DOXYGEN_RUNNING
} // namespace CGAL
#if CGAL_CXX20 && __cpp_concepts >= 201911L
# include <concepts>
static_assert(std::regular<CGAL::Conforming_constrained_Delaunay_triangulation_vertex_data_3>);
#endif
#endif // CGAL_CONSTRAINED_DELAUNAY_TRIANGULATION_VERTEX_DATA_3_H

View File

@ -0,0 +1,62 @@
// Copyright (c) 2023-2024 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) : Laurent Rineau
#ifndef CGAL_CDT_3_DEBUG_IO_H
#define CGAL_CDT_3_DEBUG_IO_H
#include <CGAL/license/Constrained_triangulation_3.h>
#include <CGAL/IO/polygon_soup_io.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Polygon_mesh_processing/orient_polygon_soup.h>
#include <CGAL/Polygon_mesh_processing/repair_polygon_soup.h>
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
#include <ostream>
namespace CGAL {
template <typename Tr, typename Facets>
auto export_facets_to_surface_mesh(const Tr& tr, Facets&& facets_range) {
using Point_3 = typename Tr::Geom_traits::Point_3;
const auto size = std::distance(facets_range.begin(), facets_range.end());
std::vector<Point_3> points;
points.reserve(size * 3);
std::vector<std::array<std::size_t, 3>> facets;
facets.reserve(size);
std::size_t i = 0;
for(const auto& [cell, facet_index] : facets_range) {
const auto v0 = cell->vertex(Tr::vertex_triple_index(facet_index, 0));
const auto v1 = cell->vertex(Tr::vertex_triple_index(facet_index, 1));
const auto v2 = cell->vertex(Tr::vertex_triple_index(facet_index, 2));
points.push_back(tr.point(v0));
points.push_back(tr.point(v1));
points.push_back(tr.point(v2));
facets.push_back({i, i+1, i+2});
i += 3;
}
CGAL::Polygon_mesh_processing::merge_duplicate_points_in_polygon_soup(points, facets);
CGAL::Polygon_mesh_processing::orient_polygon_soup(points, facets);
Surface_mesh<Point_3> mesh;
CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh(points, facets, mesh);
return mesh;
}
template <typename Tr, typename Facets>
void write_facets(std::ostream& out, const Tr& tr, Facets&& facets_range) {
const auto mesh = export_facets_to_surface_mesh(tr, std::forward<Facets>(facets_range));
CGAL::IO::write_OFF(out, mesh);
}
}
#endif // CGAL_CDT_3_DEBUG_IO_H

View File

@ -0,0 +1,62 @@
// Copyright (c) 2019-2024 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) : Laurent Rineau
#ifndef CGAL_CDT_3_CONFIG_H
#define CGAL_CDT_3_CONFIG_H
#include <CGAL/license/Constrained_triangulation_3.h>
#include <CGAL/config.h>
#include <CGAL/Constrained_triangulation_3_types.h>
#if CGAL_CAN_USE_CXX20_FORMAT
# define CGAL_CDT_3_CAN_USE_CXX20_FORMAT 1
# include <format>
#endif
namespace CGAL {
constexpr bool cdt_3_msvc_2019_or_older() {
#if defined(_MSC_VER) && (_MSC_VER < 1930)
return true;
#else
return false;
#endif
}
#if CGAL_CDT_3_CAN_USE_CXX20_FORMAT
constexpr bool cdt_3_can_use_cxx20_format() {
return true;
}
template <typename... Args>
decltype(auto) cdt_3_format(std::string_view fmt, const Args&... args) {
return std::vformat(fmt, std::make_format_args(args...));
}
#else // not CGAL_CDT_3_CAN_USE_CXX20_FORMAT
template <typename... Args>
constexpr decltype(auto) cdt_3_format(Args&&...) {
return "";
}
constexpr bool cdt_3_can_use_cxx20_format() {
return false;
}
#endif // not CGAL_CDT_3_CAN_USE_CXX20_FORMAT
} // namespace CGAL
#endif // CGAL_CDT_3_CONFIG_H

View File

@ -0,0 +1,65 @@
// Copyright (c) 2025 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) : Laurent Rineau
#ifndef CGAL_CDT_3_INTERNAL_OSTREAM_REDIRECT_GUARD_H
#define CGAL_CDT_3_INTERNAL_OSTREAM_REDIRECT_GUARD_H
#include <CGAL/license/Constrained_triangulation_3.h>
#include <ostream>
namespace CGAL {
namespace internal {
class ostream_redirect_guard {
public:
// Builder for fluent API
class builder {
public:
explicit builder(std::ostream& target) : target_(target) {}
ostream_redirect_guard to(std::ostream& redirect_to) {
return ostream_redirect_guard(target_, redirect_to);
}
private:
std::ostream& target_;
};
static builder redirect(std::ostream& target) {
return builder(target);
}
ostream_redirect_guard(const ostream_redirect_guard&) = delete;
ostream_redirect_guard& operator=(const ostream_redirect_guard&) = delete;
~ostream_redirect_guard() { restore(); }
ostream_redirect_guard(ostream_redirect_guard&& other) noexcept
: target_(other.target_), old_buf_(other.old_buf_) {
other.old_buf_ = nullptr;
}
private:
friend class builder;
ostream_redirect_guard(std::ostream& target, std::ostream& redirect_to)
: target_(target), old_buf_(target.rdbuf(redirect_to.rdbuf())) {}
void restore() {
if (old_buf_) target_.rdbuf(old_buf_);
old_buf_ = nullptr;
}
std::ostream& target_;
std::streambuf* old_buf_;
};
} // namespace internal
} // namespace CGAL
#endif // CGAL_CDT_3_INTERNAL_OSTREAM_REDIRECT_GUARD_H

View File

@ -0,0 +1,138 @@
// Copyright (c) 2023-2025 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) : Laurent Rineau
#ifndef CGAL_CDT_3_INTERNAL_READ_POLYGON_MESH_FOR_CDT_3_H
#define CGAL_CDT_3_INTERNAL_READ_POLYGON_MESH_FOR_CDT_3_H
#include <CGAL/license/Constrained_triangulation_3.h>
#include <CGAL/Constrained_triangulation_3/internal/cdt_debug_io.h>
#include <CGAL/Constrained_triangulation_3/internal/ostream_redirect_guard.h>
#include <CGAL/Polygon_mesh_processing/polygon_soup_self_intersections.h>
#include <tl/expected.hpp>
#include <sstream>
namespace CGAL {
template <typename PolygonMesh>
struct CDT_3_read_polygon_mesh_output {
tl::expected<PolygonMesh, std::string> polygon_mesh;
std::size_t nb_of_duplicated_points = 0;
std::size_t nb_of_simplified_polygons = 0;
std::size_t nb_of_new_polygons = 0;
std::size_t nb_of_removed_invalid_polygons = 0;
std::size_t nb_of_removed_duplicated_polygons = 0;
std::size_t nb_of_removed_isolated_points = 0;
bool polygon_soup_self_intersects = false;
bool polygon_mesh_is_manifold = true;
};
template <typename PolygonMesh,
typename Points,
typename Faces,
typename NamedParameters = parameters::Default_named_parameters>
CDT_3_read_polygon_mesh_output<PolygonMesh> convert_polygon_soup_to_polygon_mesh_for_cdt_3(
Points& points, Faces& faces, const NamedParameters& np = parameters::default_values())
{
CDT_3_read_polygon_mesh_output<PolygonMesh> result;
using Traits = typename GetPolygonGeomTraits<Points, Faces, NamedParameters>::type;
using parameters::choose_parameter;
using parameters::get_parameter;
auto traits = choose_parameter<Traits>(get_parameter(np, internal_np::geom_traits));
auto verbose = choose_parameter(get_parameter(np, internal_np::verbose), false);
bool do_repair = choose_parameter(get_parameter(np, internal_np::repair_polygon_soup), true);
const auto& return_error = parameters::get_parameter_reference(np, internal_np::callback);
namespace PMP = CGAL::Polygon_mesh_processing;
namespace PMP_internal = PMP::internal;
if (do_repair)
{
result.nb_of_duplicated_points = PMP::merge_duplicate_points_in_polygon_soup(points, faces, np);
result.nb_of_simplified_polygons = PMP_internal::simplify_polygons_in_polygon_soup(points, faces, traits);
result.nb_of_new_polygons = PMP_internal::split_pinched_polygons_in_polygon_soup(points, faces, traits);
result.nb_of_removed_invalid_polygons = PMP_internal::remove_invalid_polygons_in_polygon_soup(points, faces);
result.nb_of_removed_duplicated_polygons = PMP::merge_duplicate_polygons_in_polygon_soup(points, faces, np);
result.nb_of_removed_isolated_points = PMP::remove_isolated_points_in_polygon_soup(points, faces);
}
result.polygon_soup_self_intersects = PMP::does_polygon_soup_self_intersect(points, faces, np);
if (!PMP::orient_polygon_soup(points, faces))
{
result.polygon_mesh_is_manifold = false;
if (verbose)
std::cerr << "Some duplication happened during polygon soup orientation" << std::endl;
}
if (!PMP::is_polygon_soup_a_polygon_mesh(faces))
{
if (verbose)
std::cerr << "Warning: polygon soup does not describe a polygon mesh" << std::endl;
if constexpr (std::is_invocable_v<decltype(return_error)>)
return return_error();
else
return result;
}
PMP::polygon_soup_to_polygon_mesh(points, faces, *result.polygon_mesh, parameters::default_values(), np);
return result;
}
template <typename PolygonMesh, typename NamedParameters = parameters::Default_named_parameters>
CDT_3_read_polygon_mesh_output<PolygonMesh>
read_polygon_mesh_for_cdt_3(const std::string &fname,
const NamedParameters &np = parameters::default_values())
{
CDT_3_read_polygon_mesh_output<PolygonMesh> result;
using VPM = typename CGAL::GetVertexPointMap<PolygonMesh, NamedParameters>::type;
using Point = typename boost::property_traits<VPM>::value_type;
using parameters::choose_parameter;
using parameters::get_parameter;
auto verbose = choose_parameter(get_parameter(np, internal_np::verbose), false);
std::ostringstream local_verbose_output;
{
auto cerr_redirect = CGAL::internal::ostream_redirect_guard::redirect(std::cerr).to(local_verbose_output);
auto return_error = [&]() {
result.polygon_mesh = tl::unexpected(std::move(local_verbose_output).str());
return result;
};
using Points = std::vector<Point>;
using Face = std::vector<std::size_t>;
using Faces = std::vector<Face>;
Points points;
Faces faces;
if(!CGAL::IO::read_polygon_soup(fname, points, faces, CGAL::parameters::verbose(true))) {
if(verbose)
std::cerr << "Warning: cannot read polygon soup" << std::endl;
return return_error();
}
result = convert_polygon_soup_to_polygon_mesh_for_cdt_3<PolygonMesh>(points, faces, np.callback(return_error));
}
if(verbose) {
std::cerr << std::move(local_verbose_output).str();
}
return result;
}
} // end namespace CGAL
#endif // CGAL_CDT_3_INTERNAL_READ_POLYGON_MESH_FOR_CDT_3_H

View File

@ -0,0 +1,29 @@
// Copyright (c) 2024 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) : Laurent Rineau
#ifndef CGAL_CT_3_TYPES_H
#define CGAL_CT_3_TYPES_H
#include <CGAL/license/Constrained_triangulation_3.h>
#include <CGAL/config.h>
namespace CGAL {
/**
* @ingroup PkgConstrainedTriangulation3Classes
* \brief Signed integral type to store the index of constraints.
*/
using CDT_3_signed_index = int; // must be signed
} // namespace CGAL
#endif // CGAL_CT_3_TYPES_H

View File

@ -0,0 +1,108 @@
// Copyright (c) 2019-2024 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) : Jane Tournois
#include "CGAL/type_traits.h"
#include "CGAL/unordered_flat_map.h"
#include <CGAL/Conforming_constrained_Delaunay_triangulation_3.h>
#include <CGAL/Named_function_parameters.h>
#include <CGAL/boost/graph/named_params_helper.h>
#include <CGAL/IO/File_medit.h>
#include <ostream>
namespace CGAL
{
namespace IO
{
/*!
* @ingroup PkgCDT3IOFunctions
* @brief outputs a conforming constrained Delaunay triangulation to
* the MEDIT (`.mesh`) file format.
* See \cgalCite{frey:inria-00069921} for a comprehensive description of this
* file format.
* @param os the output stream
* @param ccdt the conforming constrained Delaunay triangulation to be written
* \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below
*
* \cgalNamedParamsBegin
* \cgalParamNBegin{with_plc_face_id}
* \cgalParamDescription{a Boolean activating the numbering of PLC face identifiers in the output.
* If `ccdt` was constructed with the `plc_face_id` property map given as a named parameter,
* and this parameter is set to `true`,
* the output will contain the corresponding patch identifier for each facet of the triangulation.
* If this parameter is set to `false`, the output will not contain any patch identifier.
* If `ccdt` was not constructed with the `plc_face_id` property map, and this parameter is
* set to `true`, the output will contain a patch identifier for each facet of the triangulation.}
* \cgalParamType{Boolean}
* \cgalParamDefault{`false`}
* \cgalParamNEnd
* \cgalNamedParamsEnd
* \see \ref IOStreamMedit
*/
template <typename Traits,
typename Tr,
typename NamedParameters = parameters::Default_named_parameters>
void write_MEDIT(std::ostream& os,
const Conforming_constrained_Delaunay_triangulation_3<Traits, Tr>& ccdt,
const NamedParameters& np = parameters::default_values())
{
const auto& tr = ccdt.triangulation();
using Tr_ = typename cpp20::remove_cvref_t<decltype(tr)>;
using Vertex_handle = typename Tr_::Vertex_handle;
using Facet = typename Tr_::Facet;
using Cell_handle = typename Tr_::Cell_handle;
CGAL::unordered_flat_map<Cell_handle, bool> cells_in_domain;
for(Cell_handle c : tr.all_cell_handles())
cells_in_domain[c] = true;
std::stack<Cell_handle> stack;
stack.push(tr.infinite_cell());
while(!stack.empty())
{
auto ch = stack.top();
stack.pop();
cells_in_domain[ch] = false;
for(int i = 0; i < 4; ++i)
{
if(ccdt.is_facet_constrained(ch, i))
continue;
auto n = ch->neighbor(i);
if(cells_in_domain[n])
stack.push(n);
}
}
using parameters::choose_parameter;
using parameters::get_parameter;
const bool has_plc_face_id
= choose_parameter(get_parameter(np, internal_np::with_plc_face_id), false);
auto plc_patch_map = boost::make_function_property_map<Facet>([&](const Facet& f)
{ return has_plc_face_id ? f.first->ccdt_3_data().face_constraint_index(f.second) + 1 : 1; });
return SMDS_3::output_to_medit(os,
tr,
tr.finite_vertex_handles(),
ccdt.constrained_facets(),
tr.finite_cell_handles(),
boost::make_function_property_map<Vertex_handle>([](Vertex_handle) { return 0; }),
plc_patch_map,
boost::make_function_property_map<Cell_handle>([&](Cell_handle ch) {
return cells_in_domain[ch] ? 1 : 0;
}));
}
}// end namespace IO
}// end namespace CGAL

View File

@ -0,0 +1,85 @@
// Copyright (c) 2025 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) : Laurent Rineau
#ifndef CGAL_DRAW_CONSTRAINED_T3_H
#define CGAL_DRAW_CONSTRAINED_T3_H
#include <CGAL/license/Constrained_triangulation_3.h>
#include <CGAL/draw_triangulation_3.h>
#include <CGAL/Conforming_constrained_Delaunay_triangulation_3_fwd.h>
#include <CGAL/type_traits.h>
#include <algorithm>
namespace CGAL {
/*!
\ingroup PkgDrawCDT_3
opens a new `CGAL::Qt::Basic_viewer` window and draws the constrained triangulation.
A call to this function blocks the execution of the program until the drawing window is closed. This function requires `CGAL_Qt6`,
and is only available if the macro `CGAL_USE_BASIC_VIEWER` is defined.
Linking with the cmake target `CGAL::CGAL_Basic_viewer` will link with `CGAL_Qt6` and add the definition `CGAL_USE_BASIC_VIEWER`.
*/
template <typename Traits, typename Tr>
void draw(const Conforming_constrained_Delaunay_triangulation_3<Traits, Tr>& ccdt,
const char *title="3D Constrained Triangulation")
{
using Tr_ = CGAL::cpp20::remove_cvref_t<decltype(ccdt.triangulation())>;
using Vertex_handle = typename Tr_::Vertex_handle;
using Cell_handle = typename Tr_::Cell_handle;
using Edge_descriptor = typename Tr_::Finite_edges_iterator;
using Facet_descriptor = typename Tr_::Finite_facets_iterator;
using Face_index = CGAL::cpp20::remove_cvref_t<
decltype(std::declval<Cell_handle>()->ccdt_3_data().face_constraint_index(0))>;
Face_index nb_colors = 0;
std::for_each(
ccdt.constrained_facets_begin(), ccdt.constrained_facets_end(),
[&](const auto& f) {
auto [c, index] = f;
nb_colors = (std::max)(nb_colors, c->ccdt_3_data().face_constraint_index(index) + 1);
});
std::vector<CGAL::IO::Color> colors(nb_colors);
std::generate(colors.begin(), colors.end(), []() {
return CGAL::get_random_color(CGAL::get_default_random());
});
CGAL::Graphics_scene_options<Tr_, Vertex_handle, Edge_descriptor, Facet_descriptor> options;
options.draw_face = [](const Tr_&, Facet_descriptor f) {
auto [c, index] = *f;
return c->ccdt_3_data().is_facet_constrained(index);
};
options.colored_face = [](const Tr_&, Facet_descriptor) {
return true;
};
options.face_color = [&](const Tr_&, Facet_descriptor f) {
auto [c, index] = *f;
return colors[c->ccdt_3_data().face_constraint_index(index)];
};
draw(ccdt.triangulation(), options, title);
}
/*!
\ingroup PkgDrawCDT_3
A shortcut to \link PkgDrawTriangulation3 `CGAL::draw(ccdt.triangulation(), gs_options, title)` \endlink.
*/
template <typename Traits, typename Tr, typename GSOptions>
void draw(const Conforming_constrained_Delaunay_triangulation_3<Traits, Tr>& ccdt,
const GSOptions& gs_options,
const char *title="3D Constrained Triangulation")
{
draw(ccdt.triangulation(), gs_options, title);
}
} // End namespace CGAL
#endif // CGAL_DRAW_CONSTRAINED_T3_H

View File

@ -0,0 +1,231 @@
// Copyright (c) 2024 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) : Laurent Rineau
#ifndef CGAL_MAKE_CONSTRAINED_DELAUNAY_TRIANGULATION_3_H
#define CGAL_MAKE_CONSTRAINED_DELAUNAY_TRIANGULATION_3_H
#include <CGAL/license/Constrained_triangulation_3.h>
#include <CGAL/Conforming_constrained_Delaunay_triangulation_3.h>
#include <CGAL/Conforming_constrained_Delaunay_triangulation_vertex_base_3.h>
#include <CGAL/Conforming_constrained_Delaunay_triangulation_cell_base_3.h>
#include <CGAL/Triangulation_3.h>
#include <CGAL/Triangulation_data_structure_3.h>
#include <CGAL/Named_function_parameters.h>
#include <CGAL/boost/graph/named_params_helper.h>
namespace CGAL {
/*!
\addtogroup PkgConstrainedTriangulation3FunctionsPolygonSoupOrMesh
@{
Free Functions Template for Creating Conforming Constrained Delaunay Triangulations {#PkgConstrainedTriangulation3Functions}
==========================================================================
The following functions create a 3D conforming constrained Delaunay triangulation
from either a polygon soup or a polygon mesh.
Input Data {#make_conforming_constrained_Delaunay_triangulation_3_input_data}
----------
The input data (polygon mesh or polygon soup) represents the polygonal constraints enforced
during the triangulation process.
By default, each face of the input is considered a polygonal constraint for the triangulation. The
named parameter `plc_face_id` can be used to describe larger polygonal constraints, possibly with holes. If
used, this parameter must be a property map that associates each face of the input with a PLC face
identifier. Faces with the same face identifier are considered part of the same surface patch. Each of these
surface patches (defined as the union of the input faces with a given patch identifier) is expected to be a polygon or
a polygon with holes, with coplanar vertices (or nearly coplanar up to the precision of the number type used).
The generated triangulation will conform to the faces of the input, or to the surface patches
described by the `plc_face_id` property map if provided.
In the case where the input contains a non-planar PLC face, building the triangulation may fail with an exception
of type `CGAL::Non_planar_plc_facet_exception`.
\pre The input data must not be coplanar.
\pre The input data must not have self-intersections.
Template Parameters {#make_conforming_constrained_Delaunay_triangulation_3_template_parameters}
-------------------
For both function templates, the template arguments can be deduced from the function arguments, or defaulted.
- The first template argument `Triangulation` defaults to `CGAL::Default`, and in that case the
triangulation type is deduced from the input type and the named parameters (see below).
- The following one or two template arguments are deduced from the input data: either a polygon mesh type,
or a polygon soup defined by two types (a sequence of points and a sequence of sequences of indices).
- The last template argument is the named parameters class, deduced from the function arguments.
Returned Triangulation Type {#make_conforming_constrained_Delaunay_triangulation_3_returned_type}
---------------------------
For both functions, the template parameter `Triangulation` defines the type of the triangulation that is created
and returned by the function.
- If `Triangulation` is `CGAL::Default`, the geometric traits class type is deduced from the input data and
the named parameters. If the named parameter `geom_traits` is provided, the traits class is deduced from it.
Otherwise, the point type of the input data is used to deduce the traits class. Let's call it `Traits`.
The returned triangulation type is then `CGAL::Conforming_constrained_Delaunay_triangulation_3<Traits>`.
- Otherwise, `Triangulation` must be a specialization of the `CGAL::Conforming_constrained_Delaunay_triangulation_3`
class template, with the following requirements:
- its `Vertex` type must be a model of `ConformingConstrainedDelaunayTriangulationVertexBase_3`, and
- its `Cell` type must be a model of `ConformingConstrainedDelaunayTriangulationCellBase_3`.
In both cases, the traits class `Traits` must fulfill the following requirements:
- It must be a model of the concept `ConformingConstrainedDelaunayTriangulationTraits_3`.
- It must have a `Point_3` type that is constructible from the point type of the input data.
@}
*/
/*!
* \ingroup PkgConstrainedTriangulation3FunctionsPolygonSoupOrMesh
*
* \brief creates a 3D constrained Delaunay triangulation conforming to the faces of a polygon mesh.
*
* \tparam PolygonMesh a model of `FaceListGraph`
* \tparam Triangulation an instance of the `CGAL::Conforming_constrained_Delaunay_triangulation_3` class template,
* or `CGAL::Default`
* \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
*
* \param mesh the polygon mesh representing the constraints
* \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below
*
* \cgalNamedParamsBegin
*
* \cgalParamNBegin{vertex_point_map}
* \cgalParamDescription{a property map associating points to the vertices of `mesh`}
* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits<PolygonMesh>::%vertex_descriptor`
* as key type and `%Traits::Point_3` as value type}
* \cgalParamDefault{`boost::get(CGAL::vertex_point, mesh)`}
* \cgalParamExtra{If this parameter is omitted, an internal property map for `CGAL::vertex_point_t`
* must be available in `PolygonMesh`.}
* \cgalParamNEnd
*
* \cgalParamNBegin{plc_face_id}
* \cgalParamDescription{a property map associating a patch identifier to each facet of `mesh`.
* Each identifier corresponds to a planar surface patch. Each surface
* patch can be composed of several facets of `mesh`, forming a planar polygon.}
* \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits<PolygonMesh>::%face_descriptor`
* as key type and with any value type that is a *regular* type (model of `Regular`)}
* \cgalParamExtra{If this parameter is omitted, each facet of `mesh` is considered a separate PLC face.
* Faces of `mesh` with the same patch identifier are considered part of the same PLC face.}
* \cgalParamNEnd
*
* \cgalParamNBegin{geom_traits}
* \cgalParamDescription{an instance of a geometric traits class}
* \cgalParamType{`Traits` as defined above in the section \ref make_conforming_constrained_Delaunay_triangulation_3_returned_type}
* \cgalParamDefault{the default constructed traits object `Traits{}`}
* \cgalParamNEnd
* \cgalNamedParamsEnd
*
* \return a 3D constrained Delaunay triangulation conforming to the faces of the polygon mesh, of a type
* described in the section \ref make_conforming_constrained_Delaunay_triangulation_3_returned_type above.
*
* \pre `mesh` must not have self-intersections.
* For triangulated surfaces, it can be checked using the function
* \link CGAL::Polygon_mesh_processing::does_self_intersect
* `CGAL::Polygon_mesh_processing::does_self_intersect(mesh, np) == false`
* \endlink
*/
template <typename Triangulation = CGAL::Default,
typename PolygonMesh,
typename CGAL_NP_TEMPLATE_PARAMETERS>
auto make_conforming_constrained_Delaunay_triangulation_3(const PolygonMesh &mesh,
const CGAL_NP_CLASS &np = parameters::default_values())
{
using Mesh_geom_traits = typename GetGeomTraits<PolygonMesh, CGAL_NP_CLASS>::type;
using CDT = typename CGAL::Default::Get<Triangulation,
Conforming_constrained_Delaunay_triangulation_3<Mesh_geom_traits>>::type;
CDT cdt(mesh, np);
auto remeshing_cdt{std::move(cdt)};
static_assert(std::is_same_v<decltype(remeshing_cdt), CDT>);
return remeshing_cdt;
}
/*!
* \ingroup PkgConstrainedTriangulation3FunctionsPolygonSoupOrMesh
*
* \brief creates a 3D constrained Delaunay triangulation conforming to the faces of a polygon soup.
*
* \tparam PointRange a model of the concept `RandomAccessContainer` whose value type is the point type
* of the polygon soup
* \tparam PolygonRange a model of the concept `RandomAccessContainer` whose value type is a model of the concept
* `RandomAccessContainer` whose value type is `std::size_t`
* \tparam Triangulation an instance of the `CGAL::Conforming_constrained_Delaunay_triangulation_3` class template,
* or `CGAL::Default`
* \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
*
* \param points a range of points representing the vertices of the polygon soup
* \param polygons a range of ranges of indices representing the faces of the polygon soup
* \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below
*
* \cgalNamedParamsBegin
*
* \cgalParamNBegin{point_map}
* \cgalParamDescription{a property map associating points to the elements of the range `points`}
* \cgalParamType{a model of `ReadablePropertyMap` whose
* key type is the value type of `PointRange`, and
* value type is convertible to the point type of the geometric traits class}
* \cgalParamDefault{`CGAL::Identity_property_map`}
* \cgalParamNEnd
*
* \cgalParamNBegin{plc_face_id}
* \cgalParamDescription{a property map associating a patch identifier to each facet of `soup`.
* Each identifier corresponds to a planar surface patch. Each surface
* patch can be composed of several faces of `soup`, forming a planar polygon.}
* \cgalParamType{a class model of `ReadablePropertyMap` with `std::size_t`
* as key type and with any value type that is a *regular* type (model of `Regular`)}
* \cgalParamExtra{If this parameter is omitted, each facet of the polygon soup is considered a separate PLC face.}
* \cgalParamExtra{Otherwise facets with the same patch identifier are considered part of the same PLC face.}
* \cgalParamNEnd
*
* \cgalParamNBegin{geom_traits}
* \cgalParamDescription{an instance of a geometric traits class}
* \cgalParamType{`Traits` as defined above in the section \ref make_conforming_constrained_Delaunay_triangulation_3_returned_type}
* \cgalParamDefault{the default constructed traits object `Traits{}`}
* \cgalParamNEnd
*
* \cgalNamedParamsEnd
*
* \return a 3D constrained Delaunay triangulation conforming to the faces of the polygon soup, of a type
* described in the section \ref make_conforming_constrained_Delaunay_triangulation_3_returned_type above.
*
* \pre The polygon soup must be free of self-intersections. If the polygon soup is a triangle soup, this is equivalent to:
* \link CGAL::Polygon_mesh_processing::does_triangle_soup_self_intersect
* `CGAL::Polygon_mesh_processing::does_triangle_soup_self_intersect(points, polygons, np) == false`
* \endlink.
*/
template <typename Triangulation = CGAL::Default,
typename PointRange,
typename PolygonRange,
typename NamedParameters = parameters::Default_named_parameters>
auto make_conforming_constrained_Delaunay_triangulation_3(const PointRange &points,
const PolygonRange &polygons,
const NamedParameters &np = parameters::default_values())
{
using Geom_traits = typename GetPolygonGeomTraits<PointRange, PolygonRange, NamedParameters>::type;
using Default_CDT = Conforming_constrained_Delaunay_triangulation_3<Geom_traits>;
using CDT = typename CGAL::Default::Get<Triangulation, Default_CDT>::type;
CDT cdt(points, polygons, np);
auto remeshing_cdt{std::move(cdt)};
static_assert(std::is_same_v<decltype(remeshing_cdt), CDT>);
return remeshing_cdt;
}
} // end namespace CGAL
#endif // CGAL_MAKE_CONSTRAINED_DELAUNAY_TRIANGULATION_3_H

View File

@ -0,0 +1 @@
GeometryFactory (France)

View File

@ -0,0 +1,41 @@
Algebraic_foundations
Arithmetic_kernel
BGL
Basic_viewer
Box_intersection_d
CGAL_Core
Cartesian_kernel
Circulator
Constrained_triangulation_3
Distance_2
Distance_3
Filtered_kernel
Hash_map
Homogeneous_kernel
Installation
Intersections_2
Intersections_3
Interval_support
Kernel_23
Kernel_d
Mesh_3
Modular_arithmetic
Number_types
Polygon
Polygon_mesh_processing
Principal_component_analysis_LGPL
Profiling_tools
Property_map
Random_numbers
SMDS_3
STL_Extension
Solver_interface
Spatial_sorting
Stream_support
Surface_mesh
TDS_2
TDS_3
Triangulation_2
Triangulation_3
Union_find
Weights

View File

@ -0,0 +1 @@
3D constrained triangulations

View File

@ -0,0 +1 @@
GPL (v3 or later)

View File

@ -0,0 +1 @@
Laurent Rineau <laurent.rineau@geometryfactory.com>

View File

@ -0,0 +1,160 @@
cmake_minimum_required(VERSION 3.12...3.31)
project(Constrained_triangulation_3_Tests)
find_package(CGAL REQUIRED)
create_single_source_cgal_program(test_constrained_Delaunay_triangulation_3.cpp)
create_single_source_cgal_program(test_2D_constrained_Delaunay_triangulation_3.cpp)
create_single_source_cgal_program(test_ccdt_remeshing.cpp)
find_package(Eigen3 QUIET)
include(CGAL_Eigen3_support)
include(CGAL_setup_tl-excepted)
# CDT_3: C++20 for structured bindings
add_library(CDT_3_dependencies INTERFACE)
target_compile_features(CDT_3_dependencies INTERFACE cxx_std_20)
target_link_libraries(CDT_3_dependencies INTERFACE CGAL::CGAL CGAL::Data CGAL::Eigen3_support tl::expected)
create_single_source_cgal_program( "cdt_test_insert_constrained_edge_from_EDG_file.cpp")
target_link_libraries(cdt_test_insert_constrained_edge_from_EDG_file PRIVATE CDT_3_dependencies)
create_single_source_cgal_program( "cdt_test_insert_constrained_edge_from_OFF_file.cpp")
target_link_libraries(cdt_test_insert_constrained_edge_from_OFF_file PRIVATE CDT_3_dependencies)
create_single_source_cgal_program( "cdt_3_from_off.cpp")
target_link_libraries(cdt_3_from_off PRIVATE CDT_3_dependencies)
create_single_source_cgal_program( "cdt_3_from_off_with_Epeck.cpp")
target_link_libraries(cdt_3_from_off_with_Epeck PRIVATE CDT_3_dependencies)
create_single_source_cgal_program( "snap_and_cdt3.cpp")
if(cxx_std_20 IN_LIST CMAKE_CXX_COMPILE_FEATURES)
add_executable(cdt_3_from_off_CGAL_DEBUG_CDT_3 cdt_3_from_off)
target_compile_definitions(cdt_3_from_off_CGAL_DEBUG_CDT_3 PRIVATE CGAL_DEBUG_CDT_3=255)
target_link_libraries(cdt_3_from_off_CGAL_DEBUG_CDT_3 PRIVATE CDT_3_dependencies)
cgal_add_test(cdt_3_from_off_CGAL_DEBUG_CDT_3)
endif()
add_executable(test_CDT_3_insert_constrained_edge_from_EDG_file cdt_test_insert_constrained_edge_from_EDG_file.cpp)
target_link_libraries(test_CDT_3_insert_constrained_edge_from_EDG_file PRIVATE CDT_3_dependencies)
target_compile_definitions(test_CDT_3_insert_constrained_edge_from_EDG_file PUBLIC CGAL_TEST_CDT_3_USE_CDT)
cgal_add_test(test_CDT_3_insert_constrained_edge_from_EDG_file)
add_executable(test_CDT_3_insert_constrained_edge_from_OFF_file cdt_test_insert_constrained_edge_from_OFF_file.cpp)
target_link_libraries(test_CDT_3_insert_constrained_edge_from_OFF_file PRIVATE CDT_3_dependencies)
target_compile_definitions(test_CDT_3_insert_constrained_edge_from_OFF_file PUBLIC CGAL_TEST_CDT_3_USE_CDT)
cgal_add_test(test_CDT_3_insert_constrained_edge_from_OFF_file)
function(CGAL_add_cdt3_from_off_test_aux data_name data_dir)
set(options ONLY_MERGE_FACETS)
set(oneValueArgs DATA_FILENAME TIMEOUT)
set(multiValueArgs LABELS)
cmake_parse_arguments(PARSE_ARGV 2 "MY" "${options}" "${oneValueArgs}"
"${multiValueArgs}")
if(NOT MY_DATA_FILENAME)
set(data_filename ${data_name}.off)
else()
set(data_filename ${MY_DATA_FILENAME})
endif()
add_test(NAME "execution of cdt_3_from_off ${data_name}"
COMMAND cdt_3_from_off
--no-merge-facets
${data_dir}/${data_filename}
${CMAKE_CURRENT_BINARY_DIR}/dump_output_${data_name}.off)
if(MY_ONLY_MERGE_FACETS)
set_property(TEST "execution of cdt_3_from_off ${data_name}" PROPERTY DISABLED TRUE)
endif()
cgal_setup_test_properties("execution of cdt_3_from_off ${data_name}" cdt_3_from_off)
add_test(NAME "execution of cdt_3_from_off --merge-facets ${data_name}"
COMMAND cdt_3_from_off
--merge-facets --segment-vertex-epsilon 0 --vertex-vertex-epsilon 0
${data_dir}/${data_filename}
${CMAKE_CURRENT_BINARY_DIR}/dump_output_${data_name}--merge-facets.off)
cgal_setup_test_properties("execution of cdt_3_from_off --merge-facets ${data_name}" cdt_3_from_off)
if(MY_LABELS)
set_property(TEST
"execution of cdt_3_from_off ${data_name}"
"execution of cdt_3_from_off --merge-facets ${data_name}"
APPEND PROPERTY LABELS ${MY_LABELS})
endif()
if(MY_TIMEOUT)
set_tests_properties(
"execution of cdt_3_from_off ${data_name}"
"execution of cdt_3_from_off --merge-facets ${data_name}"
PROPERTIES TIMEOUT "${MY_TIMEOUT}")
endif()
set_property(TEST "execution of cdt_3_from_off ${data_name}" APPEND PROPERTY LABELS CDT_3_WITHOUT_MERGE_FACETS)
endfunction()
function(CGAL_add_cdt3_from_off_test data_name)
CGAL_add_cdt3_from_off_test_aux(${data_name} ${CGAL_DATA_DIR}/meshes)
endfunction()
CGAL_add_cdt3_from_off_test("cube")
CGAL_add_cdt3_from_off_test("sphere")
CGAL_add_cdt3_from_off_test("fandisk")
CGAL_add_cdt3_from_off_test("mpi")
CGAL_add_cdt3_from_off_test("3torus")
CGAL_add_cdt3_from_off_test("cheese-selection")
CGAL_add_cdt3_from_off_test("cheese-selection-2")
function(CGAL_add_cdt3_from_local_off_test data_name)
CGAL_add_cdt3_from_off_test_aux(${data_name} ${CMAKE_CURRENT_SOURCE_DIR}/data)
endfunction()
CGAL_add_cdt3_from_local_off_test(cheese18)
CGAL_add_cdt3_from_local_off_test(cheese23)
CGAL_add_cdt3_from_local_off_test(cheese28)
CGAL_add_cdt3_from_local_off_test(cheese31)
CGAL_add_cdt3_from_local_off_test(cheese36-bis)
CGAL_add_cdt3_from_local_off_test(cheese36)
CGAL_add_cdt3_from_local_off_test(cheese6-PLCerrorWithFace0)
CGAL_add_cdt3_from_local_off_test(HexiCosPot-11)
CGAL_add_cdt3_from_local_off_test(HexiCosPot-1)
CGAL_add_cdt3_from_local_off_test(HexiCosPot-1-min1)
CGAL_add_cdt3_from_local_off_test(HexiCosPot-10a)
CGAL_add_cdt3_from_local_off_test(HexiCosPot-1-reduced)
CGAL_add_cdt3_from_local_off_test(HexiCosPot-1-sv)
CGAL_add_cdt3_from_local_off_test(HexiCosPot-1-sv-reduced)
CGAL_add_cdt3_from_local_off_test(mpi-part)
OPTION(CGAL_CDT_TEST_USE_THINGI OFF "Internal switch to test Thingi10k data set")
if (CGAL_CDT_TEST_USE_THINGI)
CGAL_add_cdt3_from_local_off_test(thingi-1036467-selection3)
CGAL_add_cdt3_from_local_off_test(243014-min2)
CGAL_add_cdt3_from_local_off_test(243014-min3)
CGAL_add_cdt3_from_local_off_test(1435440-min1)
CGAL_add_cdt3_from_local_off_test(1435440-min2)
CGAL_add_cdt3_from_local_off_test(106884-min1)
CGAL_add_cdt3_from_local_off_test(113344-min3)
CGAL_add_cdt3_from_local_off_test(40985-min3)
CGAL_add_cdt3_from_local_off_test(1514904-min8)
CGAL_add_cdt3_from_local_off_test(1147177-min1)
CGAL_add_cdt3_from_local_off_test(1452672-min1)
CGAL_add_cdt3_from_local_off_test(error_mesh-p_not_equal_0-min2)
include(./Thingi10k-CDT.cmake)
endif()
if(cxx_std_20 IN_LIST CMAKE_CXX_COMPILE_FEATURES)
add_test(NAME "execution of cdt_3_from_off_CGAL_DEBUG_CDT_3 3torus" COMMAND cdt_3_from_off_CGAL_DEBUG_CDT_3 ${CGAL_DATA_DIR}/meshes/3torus.off)
cgal_add_compilation_test(cdt_3_from_off_CGAL_DEBUG_CDT_3)
cgal_setup_test_properties("execution of cdt_3_from_off_CGAL_DEBUG_CDT_3 3torus" cdt_3_from_off_CGAL_DEBUG_CDT_3)
endif()
get_directory_property(all_tests TESTS)
foreach(test ${all_tests})
if(test MATCHES cdt|CDT)
get_property(labels TEST ${test} PROPERTY LABELS)
if(NOT Thingi10K IN_LIST labels)
set_property(TEST ${test} APPEND PROPERTY LABELS CDT_3)
endif()
endif()
endforeach()
find_package(ITT QUIET)
if(TARGET ITT::ITT)
target_link_libraries(cdt_3_from_off PRIVATE ITT::ITT)
target_link_libraries(cdt_3_from_off_with_Epeck PRIVATE ITT::ITT)
target_compile_definitions(cdt_3_from_off PRIVATE CGAL_USE_ITT)
target_compile_definitions(cdt_3_from_off_with_Epeck PRIVATE CGAL_USE_ITT)
endif()

View File

@ -0,0 +1,151 @@
if(CGAL_CDT_3_NO_THINGI10K)
return()
endif()
find_path(THINGI10K_DATA_DIR NAME 132423.stl
HINTS ENV HOME
PATH_SUFFIXES Downloads/Thingi10K/raw_meshes
NO_DEFAULT_PATH
NO_CMAKE_FIND_ROOT_PATH
REQUIRED
)
include(./thingi10k_max_10k_solid.cmake)
set(thingi10k_BLACKLIST_WITHOUT_MERGE_FACETS
#106789.stl
# At point 4.0163683382116631 -2.094120689431076 0
# There is a edge of the input mesh that is infinitely close to a vertex.
)
set(thingi10k_FAILED_WITH_MERGE_FACETS
1053875.stl
112926.stl
1147177.stl
118295.stl
123787.stl
126284.stl
135777.stl
1452672.stl
1514900.stl
1514901.stl
1514903.stl
1514904.stl
162100.stl
162336.stl
1743322.stl
186554.stl
186559.stl
237737.stl
239182.stl
255657.stl
285604.stl
285605.stl
288353.stl
288354.stl
288355.stl
383022.stl
43663.stl
45550.stl
464846.stl
472002.stl
472004.stl
472050.stl
472190.stl
500116.stl
520645.stl
54467.stl
55278.stl
57811.stl
57812.stl
59229.stl
67817.stl
69260.stl
71920.stl
74780.stl
90224.stl
904476.stl
904480.stl
96046.stl
96457.stl
96659.stl
97503.stl
)
set(thingi10k_FAILED_WITH_MERGE_FACETS_CTest_20240222_2201
40119.stl
40985.stl
41360.stl
44903.stl
47732.stl
55262.stl
55278.stl
57812.stl
58439.stl
67817.stl
77342.stl
80084.stl
90224.stl
92118.stl
97503.stl
112926.stl
135777.stl
162336.stl
186544.stl
186559.stl
225958.stl
285604.stl
285605.stl
288353.stl
288354.stl
288355.stl
375273.stl
442387.stl
464846.stl
904476.stl
904480.stl
1053875.stl
1452672.stl
1505023.stl
1514904.stl
)
function(CGAL_add_cdt3_test_from_Thingi10k data_name data_filename)
set(options "ONLY_MERGE_FACETS")
set(oneValueArgs TIMEOUT)
set(multiValueArgs LABELS)
cmake_parse_arguments(PARSE_ARGV 2 "MY" "${options}" "${oneValueArgs}"
"${multiValueArgs}")
if(MY_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "Unknown arguments specified: ${MY_UNPARSED_ARGUMENTS}")
endif()
if(MY_TIMEOUT)
set(MY_TIMEOUT_KEYWORD TIMEOUT)
endif()
if(MY_ONLY_MERGE_FACETS)
set(MY_ONLY_MERGE_FACETS ONLY_MERGE_FACETS)
endif()
CGAL_add_cdt3_from_off_test_aux(${data_name} ${THINGI10K_DATA_DIR} DATA_FILENAME ${data_filename}
LABELS ${MY_LABELS}
${MY_TIMEOUT_KEYWORD} ${MY_TIMEOUT}
${MY_ONLY_MERGE_FACETS}
)
endfunction()
foreach(thingi_file_name ${thingi10k_max_10k_solid})
if(thingi_file_name IN_LIST thingi10k_BLACKLIST_WITHOUT_MERGE_FACETS)
set(MY_ONLY_MERGE_FACETS ONLY_MERGE_FACETS)
unset(MY_ONLY_MERGE_FACETS)
endif()
set(LABELS Thingi10K Thingi10K_max_10k_solid)
if(thingi_file_name IN_LIST thingi10k_FAILED_WITH_MERGE_FACETS)
list(APPEND LABELS "Thingi10K_FAIL")
endif()
if(thingi_file_name IN_LIST thingi10k_FAILED_WITH_MERGE_FACETS_CTest_20240222_2201)
list(APPEND LABELS "CTest_20240222_2201_failed_merge_facets")
endif()
get_filename_component(thingi_ID "${thingi_file_name}" NAME_WE)
CGAL_add_cdt3_test_from_Thingi10k(Thingi10K_${thingi_ID} ${thingi_file_name}
TIMEOUT 600 LABELS ${LABELS} ${MY_ONLY_MERGE_FACETS})
endforeach()

View File

@ -0,0 +1,959 @@
#ifdef _MSC_VER
#pragma warning(disable: 4455)
#endif
#if defined(CGAL_DEBUG_CDT_3) && !__has_include(<format>)
#undef CGAL_DEBUG_CDT_3
#endif
// #define CGAL_CDT_2_DEBUG_INTERSECTIONS 1
#define NO_TRY_CATCH 1
// #define CGAL_DEBUG_CDT_3 1
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Delaunay_triangulation_3.h>
#include <CGAL/Conforming_constrained_Delaunay_triangulation_3.h>
#include <CGAL/Conforming_constrained_Delaunay_triangulation_vertex_base_3.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Constrained_triangulation_3/internal/read_polygon_mesh_for_cdt_3.h>
#include <CGAL/IO/File_binary_mesh_3.h>
#include <CGAL/Polygon_mesh_processing/bbox.h>
#include <CGAL/Polygon_mesh_processing/IO/polygon_mesh_io.h>
#include <CGAL/Polygon_mesh_processing/region_growing.h>
#include <CGAL/Polygon_mesh_processing/remesh_planar_patches.h>
#include <boost/graph/graph_traits.hpp>
#include <vector>
#include <cassert>
#include <fstream>
#include <string>
#include <string_view>
#if CGAL_CXX20 && __cpp_lib_concepts >= 201806L && __cpp_lib_ranges >= 201911L
#include <ranges>
#endif
#include <optional>
#include <chrono>
#if CGAL_CDT_3_USE_EPECK
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
using K = CGAL::Exact_predicates_exact_constructions_kernel;
#else // use Epick
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
#endif // use Epick
struct Vb : public CGAL::Conforming_constrained_Delaunay_triangulation_vertex_base_3<K> {};
struct Cb : public CGAL::Conforming_constrained_Delaunay_triangulation_cell_base_3<K> {};
struct Tds: public CGAL::Triangulation_data_structure_3<Vb, Cb> {};
using Base_triantulation = CGAL::Delaunay_triangulation_3<K, Tds>;
using CDT = CGAL::Conforming_constrained_Delaunay_triangulation_3_impl<Base_triantulation>;
using Point = Base_triantulation::Point;
using Point_3 = K::Point_3;
using Mesh = CGAL::Surface_mesh<Point>;
using vertex_descriptor = boost::graph_traits<Mesh>::vertex_descriptor;
using edge_descriptor = boost::graph_traits<Mesh>::edge_descriptor;
using face_descriptor = boost::graph_traits<Mesh>::face_descriptor;
void help(std::ostream& out) {
out << R"!!!!!(
Usage: cdt_3_from_off [options] input.off output.off
input.off: input mesh
output.off: output mesh
--merge-facets/--no-merge-facets: merge facets into patches (set by default)
--merge-facets-old: merge facets using the old method
--use-new-cavity-algorithm/--use-old-cavity-algorithm: use new or old cavity algorithm (default: new)
--failure-expression <expression>: expression to detect bad meshratio)
--ratio <double>: ratio of faces to remove (default: 0.1), if --failure-expression is used
--vertex-vertex-epsilon <double>: epsilon for vertex-vertex min distance (default: 1e-6)
--segment-vertex-epsilon <double>: epsilon for segment-vertex min distance (default: 0)
--coplanar-polygon-max-angle <double>: max angle for coplanar polygons (default: 1)
--coplanar-polygon-max-distance <double>: max distance for coplanar polygons (default: 1e-6)
--dump-patches-after-merge <filename.ply>: dump patches after merging facets in PLY
--dump-surface-mesh-after-merge <filename.off>: dump surface mesh after merging facets in OFF
--dump-patches-borders-prefix <filenames_prefix>: dump patches borders
--dump-after-conforming <filename.off>: dump mesh after conforming in OFF
--no-repair: do not repair the mesh
--reject-self-intersections: reject self-intersecting polygon soups
--no-is-valid: do not call is_valid checks
--debug-input-faces: debug input faces
--debug-missing-regions: debug missing regions
--debug-regions: debug regions
--debug_copy_triangulation_into_hole: debug copy_triangulation_into_hole
--debug-validity: add is_valid checks after modifications to the TDS
--debug-finite-edges-map: debug the use of a hash map for finite edges
--use-finite-edges-map: use a hash map for finite edges (default: false)
--verbose/-V: verbose (can be used several times)
--quiet: do not print anything
--help/-h: print this help
)!!!!!";
}
[[noreturn]] void error(std::string_view message, std::string_view extra = "") {
std::cerr << "Error: " << message << extra << '\n';
std::exit(EXIT_FAILURE);
}
struct CDT_options
{
int verbose_level = 0;
bool need_help = false;
bool quiet = false;
bool merge_facets = true;
bool merge_facets_old_method = false;
bool reject_self_intersections = false;
bool repair_mesh = true;
bool debug_input_faces = false;
bool debug_missing_regions = false;
bool debug_regions = false;
bool debug_copy_triangulation_into_hole = false;
bool use_new_cavity_algorithm = true;
bool debug_validity = false;
bool debug_finite_edges_map = false;
bool use_finite_edges_map = false;
bool call_is_valid = true;
double ratio = 0.1;
double vertex_vertex_epsilon = 1e-14;
double segment_vertex_epsilon = 1e-14;
double coplanar_polygon_max_angle = 5.1;
double coplanar_polygon_max_distance = 1e-6;
std::string failure_assertion_expression {};
std::string input_filename = CGAL::data_file_path("meshes/mpi.off");
std::string output_filename {"dump.off"};
std::string dump_patches_after_merge_filename {};
std::string dump_surface_mesh_after_merge_filename{};
std::string dump_patches_borders_prefix {};
std::string dump_after_conforming_filename {};
CDT_options(int argc, char* argv[]);
};
CDT_options::CDT_options(int argc, char* argv[]) {
const std::vector<std::string_view> args(argv + 1, argv + argc);
int positional = 0;
using std::literals::string_view_literals::operator""sv;
for (auto it = args.begin(); it != args.end(); ++it) {
auto get_next_arg_or_error_out = [&it, &args]() -> std::string {
if(it + 1 == args.end()) {
error("extra argument required after "sv, *it);
}
return std::string(*++it);
};
std::string_view arg = *it;
if(arg == "--merge-facets"sv) {
merge_facets = true;
} else if(arg == "--no-merge-facets"sv) {
merge_facets = false;
} else if(arg == "--use-new-cavity-algorithm"sv) {
use_new_cavity_algorithm = true;
} else if(arg == "--use-old-cavity-algorithm"sv) {
use_new_cavity_algorithm = false;
} else if(arg == "--reject-self-intersections"sv) {
reject_self_intersections = true;
} else if(arg == "--no-repair"sv) {
repair_mesh = false;
} else if(arg == "--merge-facets-old"sv) {
merge_facets = true;
merge_facets_old_method = true;
} else if(arg == "--failure-expression"sv) {
failure_assertion_expression = get_next_arg_or_error_out();
} else if(arg == "--ratio"sv) {
ratio = std::stod(get_next_arg_or_error_out());
} else if(arg == "--dump-patches-after-merge"sv) {
dump_patches_after_merge_filename = get_next_arg_or_error_out();
} else if(arg == "--dump-patches-borders-prefix"sv) {
dump_patches_borders_prefix = get_next_arg_or_error_out();
} else if(arg == "--dump-surface-mesh-after-merge"sv) {
dump_surface_mesh_after_merge_filename = get_next_arg_or_error_out();
} else if(arg == "--dump-after-conforming"sv) {
dump_after_conforming_filename = get_next_arg_or_error_out();
} else if(arg == "--vertex-vertex-epsilon"sv) {
vertex_vertex_epsilon = std::stod(get_next_arg_or_error_out());
} else if(arg == "--segment-vertex-epsilon"sv) {
segment_vertex_epsilon = std::stod(get_next_arg_or_error_out());
} else if(arg == "--coplanar-polygon-max-angle"sv) {
coplanar_polygon_max_angle = std::stod(get_next_arg_or_error_out());
} else if(arg == "--coplanar-polygon-max-distance"sv) {
coplanar_polygon_max_distance = std::stod(get_next_arg_or_error_out());
} else if(arg == "--quiet"sv) {
quiet = true;
} else if(arg == "--no-is-valid"sv) {
call_is_valid = false;
} else if(arg == "--debug-input-faces"sv) {
debug_input_faces = true;
} else if(arg == "--debug-missing-regions"sv) {
debug_missing_regions = true;
} else if(arg == "--debug-regions"sv) {
debug_regions = true;
} else if(arg == "--debug_copy_triangulation_into_hole"sv) {
debug_copy_triangulation_into_hole = true;
} else if(arg == "--debug-validity"sv) {
debug_validity = true;
} else if(arg == "--debug-finite-edges-map"sv) {
debug_finite_edges_map = true;
} else if(arg == "--use-finite-edges-map"sv) {
use_finite_edges_map = true;
} else if(arg == "--verbose"sv || arg == "-V"sv) {
++verbose_level;
} else if(arg == "--help"sv || arg == "-h"sv) {
need_help = true;
} else if(arg[0] == '-') {
error("unknown option "sv, arg);
} else {
switch(positional) {
case 0:
input_filename = arg;
++positional;
break;
case 1:
output_filename = arg;
++positional;
break;
default:
error("too many arguments"sv);
}
}
}
}
#if NO_TRY_CATCH
# define CDT_3_try if (true)
# define CDT_3_catch(X) if (false)
# define CDT_3_throw_exception_again
#else
// Else proceed normally.
# define CDT_3_try try
# define CDT_3_catch(X) catch(X)
# define CDT_3_throw_exception_again throw
#endif
#if CGAL_USE_ITT
# include <ittnotify.h>
# define CGAL_CDT_3_TASK_BEGIN(task_handle) \
std::cerr << "START " << #task_handle << '\n'; \
__itt_task_begin(cdt_3_domain, __itt_null, __itt_null, task_handle);
# define CGAL_CDT_3_TASK_END(task_handle) \
std::cerr << "-STOP " << #task_handle << '\n'; \
__itt_task_end(cdt_3_domain);
auto cdt_3_domain = __itt_domain_create("org.cgal.CDT_3");
auto read_input_task_handle = __itt_string_handle_create("CDT_3: read input file");
auto merge_facets_task_handle = __itt_string_handle_create("CDT_3: merge facets");
auto insert_vertices_task_handle = __itt_string_handle_create("CDT_3: insert vertices");
auto compute_distances_task_handle = __itt_string_handle_create("CDT_3: compute distances");
auto conforming_task_handle = __itt_string_handle_create("CDT_3: conforming");
auto cdt_task_handle = __itt_string_handle_create("CDT_3: cdt");
auto output_task_handle = __itt_string_handle_create("CDT_3: outputs");
auto validation_task_handle = __itt_string_handle_create("CDT_3: validation");
#else // no ITT
# define CGAL_CDT_3_TASK_BEGIN(task_handle)
# define CGAL_CDT_3_TASK_END(task_handle)
#endif // no ITT
int go(Mesh mesh, CDT_options options) {
CDT cdt;
cdt.debug_Steiner_points(options.verbose_level > 0);
cdt.debug_input_faces(options.debug_input_faces);
cdt.debug_missing_region(options.verbose_level > 1 || options.debug_missing_regions);
cdt.debug_regions(options.debug_regions);
cdt.debug_validity(options.debug_validity);
cdt.debug_finite_edges_map(options.debug_finite_edges_map);
cdt.debug_copy_triangulation_into_hole(options.debug_copy_triangulation_into_hole);
cdt.use_older_cavity_algorithm(!options.use_new_cavity_algorithm);
cdt.use_finite_edges_map(options.use_finite_edges_map);
cdt.set_segment_vertex_epsilon(options.segment_vertex_epsilon);
const auto bbox = CGAL::Polygon_mesh_processing::bbox(mesh);
double d_x = bbox.xmax() - bbox.xmin();
double d_y = bbox.ymax() - bbox.ymin();
double d_z = bbox.zmax() - bbox.zmin();
const double bbox_max_width = (std::max)(d_x, (std::max)(d_y, d_z));
double epsilon = options.vertex_vertex_epsilon;
if(!options.quiet) {
std::cout << "Bbox width : " << bbox_max_width << '\n'
<< "Epsilon : " << epsilon << '\n'
<< "Epsilon * Bbox width : " << epsilon * bbox_max_width << "\n\n";
}
auto pmap = get(CGAL::vertex_point, mesh);
auto [patch_id_map, patch_id_map_ok] = mesh.add_property_map<face_descriptor, int>("f:patch_id", -2);
assert(patch_id_map_ok); CGAL_USE(patch_id_map_ok);
auto [v_selected_map, v_selected_map_ok] = mesh.add_property_map<vertex_descriptor, bool>("v:selected", false);
assert(v_selected_map_ok); CGAL_USE(v_selected_map_ok);
auto [edge_is_border_of_patch_map, edge_is_border_of_patch_map_ok] =
mesh.add_property_map<edge_descriptor, bool>("e:is_border_of_patch", false);
assert(edge_is_border_of_patch_map_ok);
CGAL_USE(edge_is_border_of_patch_map_ok);
int nb_patches = 0;
std::vector<std::vector<std::pair<vertex_descriptor, vertex_descriptor>>> patch_edges;
if(options.merge_facets) {
CGAL_CDT_3_TASK_BEGIN(merge_facets_task_handle);
auto start_time = std::chrono::high_resolution_clock::now();
if(options.merge_facets_old_method) {
for(auto f: faces(mesh))
{
if(get(patch_id_map, f) >= 0) continue;
std::stack<face_descriptor> f_stack;
f_stack.push(f);
while(!f_stack.empty()) {
auto f = f_stack.top();
f_stack.pop();
if(get(patch_id_map, f) >= 0) continue;
put(patch_id_map, f, nb_patches);
for(auto h: CGAL::halfedges_around_face(halfedge(f, mesh), mesh)) {
auto opp = opposite(h, mesh);
if(is_border_edge(opp, mesh)) {
continue;
}
auto n = face(opp, mesh);
auto a = get(pmap, source(h, mesh));
auto b = get(pmap, target(h, mesh));
auto c = get(pmap, target(next(h, mesh), mesh));
auto d = get(pmap, target(next(opp, mesh), mesh));
if(CGAL::orientation(a, b, c, d) != CGAL::COPLANAR) {
continue;
}
if(get(patch_id_map, n) >= 0) continue;
f_stack.push(n);
}
}
++nb_patches;
}
} else {
namespace np = CGAL::parameters;
nb_patches = CGAL::Polygon_mesh_processing::region_growing_of_planes_on_faces(
mesh, patch_id_map,
np::maximum_distance(options.coplanar_polygon_max_distance * bbox_max_width)
.maximum_angle(options.coplanar_polygon_max_angle));
for(auto f: faces(mesh)) {
if(get(patch_id_map, f) < 0) {
std::cerr << "warning: face " << f << " has no patch id! Reassign it to " << nb_patches << '\n';
for(auto h: CGAL::halfedges_around_face(halfedge(f, mesh), mesh)) {
std::cerr << " " << target(h, mesh) << ", point " << mesh.point(target(h, mesh)) << '\n';
}
put(patch_id_map, f, nb_patches++);
}
}
if(!options.dump_surface_mesh_after_merge_filename.empty()) {
auto [corner_id_map, corner_id_map_ok] = mesh.add_property_map<vertex_descriptor, std::size_t>("v:corner_id", -1);
assert(corner_id_map_ok);
CGAL_USE(corner_id_map_ok);
const auto nb_corners = CGAL::Polygon_mesh_processing::detect_corners_of_regions(
mesh, patch_id_map, nb_patches, corner_id_map,
np::maximum_distance(options.coplanar_polygon_max_distance * bbox_max_width)
.maximum_angle(options.coplanar_polygon_max_angle)
.edge_is_constrained_map(edge_is_border_of_patch_map));
Mesh merged_mesh;
CGAL::Polygon_mesh_processing::remesh_almost_planar_patches(
mesh, merged_mesh, nb_patches, nb_corners, patch_id_map, corner_id_map, edge_is_border_of_patch_map,
CGAL::parameters::default_values(),
CGAL::parameters::do_not_triangulate_faces(true));
mesh.remove_property_map(corner_id_map);
std::ofstream out(options.dump_surface_mesh_after_merge_filename);
out.precision(17);
out << merged_mesh;
}
}
if (!options.quiet) {
std::cout << "[timings] detected " << nb_patches << " patches in " << std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n";
}
patch_edges.resize(nb_patches);
for(auto h: halfedges(mesh))
{
if(is_border(h, mesh)) continue;
auto f = face(h, mesh);
auto patch_id = get(patch_id_map, f);
auto opp = opposite(h, mesh);
if(is_border(opp, mesh) || patch_id != get(patch_id_map, face(opp, mesh))) {
auto va = source(h, mesh);
auto vb = target(h, mesh);
patch_edges[patch_id].emplace_back(va, vb);
put(v_selected_map, va, true);
put(v_selected_map, vb, true);
}
}
CGAL_CDT_3_TASK_END(merge_facets_task_handle);
if(!options.dump_patches_after_merge_filename.empty()) {
CGAL_CDT_3_TASK_BEGIN(output_task_handle);
std::ofstream out(options.dump_patches_after_merge_filename);
CGAL::IO::write_PLY(out, mesh, CGAL::parameters::stream_precision(17));
CGAL_CDT_3_TASK_END(output_task_handle);
}
}
if(!options.dump_patches_borders_prefix.empty()) {
CGAL_CDT_3_TASK_BEGIN(output_task_handle);
std::set<std::pair<vertex_descriptor, vertex_descriptor>> all_edges;
for(int i = 0; i < nb_patches; ++i) {
std::stringstream ss;
ss << options.dump_patches_borders_prefix << i << ".polylines.txt";
std::ofstream out(ss.str());
out.precision(17);
const auto& edges = patch_edges[i];
for(auto [va, vb]: edges) {
all_edges.insert(CGAL::make_sorted_pair(va, vb));
}
std::cerr << "Patch p#" << i << " has " << edges.size() << " edges\n";
const auto polylines = segment_soup_to_polylines(edges);
for(const auto& polyline: polylines) {
out << polyline.size() << " ";
for(auto v: polyline) {
out << get(pmap, v) << " ";
}
out << '\n';
}
out.close();
std::cerr << " " << polylines.size() << " polylines\n";
for(const auto& polyline: polylines) {
std::cerr << " - " << polyline.size() << " vertices\n";
assert(polyline.front() == polyline.back());
}
}
std::stringstream ss;
ss << options.dump_patches_borders_prefix << "all_edges.polylines.txt";
std::ofstream out(ss.str());
out.precision(17);
const auto polylines = segment_soup_to_polylines(all_edges);
for(const auto& polyline: polylines) {
out << polyline.size() << " ";
for(auto v: polyline) {
out << get(pmap, v) << " ";
}
out << '\n';
}
CGAL_CDT_3_TASK_END(output_task_handle);
}
int exit_code = EXIT_SUCCESS;
auto finally = [&cdt, &options]() {
CGAL_CDT_3_TASK_BEGIN(output_task_handle);
{
auto dump_tets_to_medit = [](std::string fname,
const std::vector<K::Point_3> &points,
const std::vector<std::array<std::size_t, 4>> &indexed_tetra,
const std::vector<std::size_t> &cell_ids)
{
std::ofstream out(fname);
out.precision(17);
out << "MeshVersionFormatted 1\nDimension 3\nVertices\n";
out << points.size() << "\n";
for (const K::Point_3& p : points)
out << p << " 0\n";
out << "Triangles\n0\nTetrahedra\n";
out << indexed_tetra.size() << "\n";
for (std::size_t k=0;k<indexed_tetra.size(); ++k)
out << indexed_tetra[k][0]+1 << " "
<< indexed_tetra[k][1]+1 << " "
<< indexed_tetra[k][2]+1 << " "
<< indexed_tetra[k][3]+1 << " " << cell_ids[k] << "\n";
out <<"End\n";
};
auto& tr = cdt;
std::unordered_map<CDT::Cell_handle, int /*Subdomain_index*/> cells_map;
for(auto ch : tr.all_cell_handles())
{
cells_map[ch] = 1;
}
std::stack<decltype(tr.infinite_cell())> stack;
stack.push(tr.infinite_cell());
while (!stack.empty())
{
auto ch = stack.top();
stack.pop();
cells_map[ch] = 0;
for (int i = 0; i < 4; ++i)
{
if(ch->ccdt_3_data().is_facet_constrained(i))
continue;
auto n = ch->neighbor(i);
if (cells_map[n] == 1)
{
stack.push(n);
}
}
}
std::vector<K::Point_3> points(cdt.number_of_vertices());
for(auto v: cdt.finite_vertex_handles()) {
points.at(v->time_stamp() -1) = v->point();
}
std::vector<std::array<std::size_t, 4>> indexed_tetra;
indexed_tetra.reserve(cdt.number_of_cells());
for(auto ch: cdt.finite_cell_handles()) {
if(cells_map[ch] > 0) {
indexed_tetra.push_back({ch->vertex(0)->time_stamp() -1,
ch->vertex(1)->time_stamp() -1,
ch->vertex(2)->time_stamp() -1,
ch->vertex(3)->time_stamp() -1});
}
}
std::vector<std::size_t> cell_idsl(indexed_tetra.size(), 1);
dump_tets_to_medit(options.output_filename + ".mesh", points, indexed_tetra, cell_idsl);
}
{
std::ofstream dump(options.output_filename);
dump.precision(17);
#if CGAL_CXX20 && __cpp_lib_concepts >= 201806L && __cpp_lib_ranges >= 201911L
cdt.write_facets(dump, cdt, std::views::filter(cdt.finite_facets(), [&](auto f) {
return cdt.is_facet_constrained(f);
}));
#else
auto is_facet_constrained = [&](auto f) { return cdt.is_facet_constrained(f); };
auto it_end = cdt.finite_facets_end();
cdt.write_facets(dump, cdt,
CGAL::make_range(
boost::make_filter_iterator(is_facet_constrained,cdt.finite_facets_begin(), it_end),
boost::make_filter_iterator(is_facet_constrained,it_end, it_end)));
#endif
}
CGAL_CDT_3_TASK_END(output_task_handle);
};
auto [tr_vertex_pmap, tr_vertex_pmap_ok] = mesh.add_property_map<vertex_descriptor, CDT::Vertex_handle>("tr_vertex");
assert(tr_vertex_pmap_ok); CGAL_USE(tr_vertex_pmap_ok);
CGAL_CDT_3_TASK_BEGIN(insert_vertices_task_handle);
auto start_time = std::chrono::high_resolution_clock::now();
CDT::Cell_handle hint{};
for(auto v: vertices(mesh)) {
if(options.merge_facets && false == get(v_selected_map, v)) continue;
auto vh = cdt.insert(get(pmap, v), hint, false);
hint = vh->cell();
put(tr_vertex_pmap, v, vh);
}
if(!options.quiet) {
std::cout << "[timings] inserted vertices in " << std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n";
std::cout << "Number of vertices: " << cdt.number_of_vertices() << "\n\n";
}
if(cdt.dimension() < 3) {
if(!options.quiet) {
std::cout << "current is 2D... inserting the 8 vertices of an extended bounding box\n";
}
if(d_x == 0) d_x = bbox_max_width;
if(d_y == 0) d_y = bbox_max_width;
if(d_z == 0) d_z = bbox_max_width;
cdt.insert(Point(bbox.xmin() - d_x, bbox.ymin() - d_y, bbox.zmin() - d_z));
cdt.insert(Point(bbox.xmin() - d_x, bbox.ymax() + d_y, bbox.zmin() - d_z));
cdt.insert(Point(bbox.xmin() - d_x, bbox.ymin() - d_y, bbox.zmax() + d_z));
cdt.insert(Point(bbox.xmin() - d_x, bbox.ymax() + d_y, bbox.zmax() + d_z));
cdt.insert(Point(bbox.xmax() + d_x, bbox.ymin() - d_y, bbox.zmin() - d_z));
cdt.insert(Point(bbox.xmax() + d_x, bbox.ymax() + d_y, bbox.zmin() - d_z));
cdt.insert(Point(bbox.xmax() + d_x, bbox.ymin() - d_y, bbox.zmax() + d_z));
cdt.insert(Point(bbox.xmax() + d_x, bbox.ymax() + d_y, bbox.zmax() + d_z));
}
CGAL_CDT_3_TASK_END(insert_vertices_task_handle);
start_time = std::chrono::high_resolution_clock::now();
CGAL_CDT_3_TASK_BEGIN(compute_distances_task_handle);
{
#if CGAL_CXX20 && __cpp_lib_concepts >= 201806L && __cpp_lib_ranges >= 201911L
auto [min_sq_distance, min_edge] = (std::ranges::min)(
cdt.finite_edges() | std::views::transform([&](auto edge) { return std::make_pair(cdt.segment(edge).squared_length(), edge); }));
#else
auto trsf = [&](auto edge) { return std::make_pair(cdt.segment(edge).squared_length(), edge); };
auto min_p = trsf(*cdt.finite_edges_begin());
for (auto ite=cdt.finite_edges_begin(); ite!=cdt.finite_edges_end(); ++ite)
{
auto p = trsf(*ite);
if (p < min_p)
p = min_p;
}
auto [min_sq_distance, min_edge] = min_p;
#endif
auto min_distance = CGAL::approximate_sqrt(min_sq_distance);
auto vertices_of_min_edge = cdt.vertices(min_edge);
if(!options.quiet) {
std::cout << "Min distance between vertices: " << min_distance << '\n'
<< " between vertices: : " << CGAL::IO::oformat(vertices_of_min_edge[0], CGAL::With_point_tag{})
<< " " << CGAL::IO::oformat(vertices_of_min_edge[1], CGAL::With_point_tag{}) << "\n\n";
}
if(min_distance < epsilon * bbox_max_width) {
std::cerr << "ERROR: min distance between vertices is too small\n";
exit_code = EXIT_FAILURE;
return exit_code;
}
}
{
double min_distance = (std::numeric_limits<double>::max)();
CDT::Vertex_handle min_va, min_vb, min_vertex;
if(options.merge_facets) {
for(int i = 0; i < nb_patches; ++i) {
const auto& edges = patch_edges[i];
for(auto [vda, vdb]: edges) {
auto va = get(tr_vertex_pmap, vda);
auto vb = get(tr_vertex_pmap, vdb);
auto [min_dist, min_v] = cdt.min_distance_and_vertex_between_constraint_and_encroaching_vertex(va, vb);
if(min_dist < min_distance) {
min_distance = CGAL::to_double(min_dist);
min_va = va;
min_vb = vb;
min_vertex = min_v;
}
}
}
} else {
for(auto face_descriptor : faces(mesh)) {
auto he = halfedge(face_descriptor, mesh);
const auto end = he;
do {
auto va = get(tr_vertex_pmap, source(he, mesh));
auto vb = get(tr_vertex_pmap, target(he, mesh));
auto [min_dist, min_v] = cdt.min_distance_and_vertex_between_constraint_and_encroaching_vertex(va, vb);
if(min_dist < min_distance) {
min_distance = CGAL::to_double(min_dist);
min_va = va;
min_vb = vb;
min_vertex = min_v;
}
he = next(he, mesh);
} while((he = next(he, mesh)) != end);
}
}
if(!options.quiet) {
std::cout << "Min distance between constraint segment and vertex: " << min_distance << '\n'
<< " between segment : "
<< CGAL::IO::oformat(min_va, CDT::Conforming_Dt::with_point) << " "
<< CGAL::IO::oformat(min_vb, CDT::Conforming_Dt::with_point) << '\n'
<< " and vertex : "
<< CGAL::IO::oformat(min_vertex, CDT::Conforming_Dt::with_point) << "\n\n";
}
cdt.check_segment_vertex_distance_or_throw(min_va, min_vb, min_vertex, min_distance,
CDT::Check_distance::NON_SQUARED_DISTANCE);
}
CGAL_CDT_3_TASK_END(compute_distances_task_handle);
if(!options.quiet) {
std::cout << "[timings] compute distances on " << std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n";
}
int poly_id = 0;
CGAL_CDT_3_TASK_BEGIN(conforming_task_handle);
CDT_3_try {
start_time = std::chrono::high_resolution_clock::now();
if(options.merge_facets) {
for(int i = 0; i < nb_patches; ++i) {
auto& edges = patch_edges[i];
auto polylines = segment_soup_to_polylines(edges);
while(true) {
const auto non_closed_polylines_begin =
std::partition(polylines.begin(), polylines.end(),
[](const auto& polyline) { return polyline.front() == polyline.back(); });
if(non_closed_polylines_begin == polylines.end())
break;
edges.clear();
for(auto it = non_closed_polylines_begin; it != polylines.end(); ++it) {
auto& polyline = *it;
for(auto it = polyline.begin(), end = polyline.end() - 1; it != end; ++it) {
edges.emplace_back(*it, *(it + 1));
}
}
polylines.erase(non_closed_polylines_begin, polylines.end());
auto other_polylines = segment_soup_to_polylines(edges);
polylines.insert(polylines.end(),
std::make_move_iterator(other_polylines.begin()),
std::make_move_iterator(other_polylines.end()));
}
std::optional<int> face_index;
for(auto& polyline: polylines) {
assert(polyline.front() == polyline.back());
polyline.pop_back();
#if CGAL_CXX20 && __cpp_lib_concepts >= 201806L && __cpp_lib_ranges >= 201911L
face_index = cdt.insert_constrained_face(
polyline | std::views::transform([&](vertex_descriptor v) { return get(tr_vertex_pmap, v); }),
false,
face_index ? *face_index : -1);
#else
face_index = cdt.insert_constrained_face( CGAL::Iterator_range(CGAL::make_transform_iterator_from_property_map(polyline.begin(), tr_vertex_pmap),
CGAL::make_transform_iterator_from_property_map(polyline.end(), tr_vertex_pmap)),
false,
face_index ? *face_index : -1);
#endif
}
}
} else {
for(auto face_descriptor : faces(mesh)) {
std::vector<Point_3> polygon;
const auto he = halfedge(face_descriptor, mesh);
for(auto vertex_it : CGAL::vertices_around_face(he, mesh)) {
polygon.push_back(get(pmap, vertex_it));
}
#if CGAL_DEBUG_CDT_3
std::cerr << "NEW POLYGON #" << poly_id << '\n';
#endif // CGAL_DEBUG_CDT_3
try {
[[maybe_unused]] auto id = cdt.insert_constrained_polygon(polygon, false);
assert(id == poly_id);
++poly_id;
} catch(int error) {
exit_code = error;
}
// std::ofstream dump("dump.binary.cgal");
// CGAL::Mesh_3::save_binary_file(dump, cdt);
}
} // not merge_facets
if(!options.quiet) {
std::cout << "[timings] registered facets in " << std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n";
}
start_time = std::chrono::high_resolution_clock::now();
cdt.restore_Delaunay();
if(!options.quiet) {
std::cout << "[timings] restored Delaunay (conforming of facets borders) in " << std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n";
}
CGAL_CDT_3_TASK_END(conforming_task_handle);
if(!options.dump_after_conforming_filename.empty()) {
CGAL_CDT_3_TASK_BEGIN(output_task_handle);
using Vertex_index = Mesh::Vertex_index;
[[maybe_unused]] std::size_t time_stamp_counter = 0u;
for(auto v: cdt.finite_vertex_handles()) {
[[maybe_unused]] const auto time_stamp = v->time_stamp();
assert(++time_stamp_counter == time_stamp);
if(!v->ccdt_3_data().is_Steiner_vertex_on_edge()) continue;
const auto [va, vb] = cdt.ancestors_of_Steiner_vertex_on_edge(v);
const auto index_va = Vertex_index{static_cast<unsigned>(va->time_stamp() - 1)};
const auto index_vb = Vertex_index{static_cast<unsigned>(vb->time_stamp() - 1)};
auto [it, end] = CGAL::halfedges_around_source(index_va, mesh);
// std::cerr << " around mesh vertex " << index_va << ", search for vertex " << index_vb << '\n';
// for(auto it2 = it; it2 != end; ++it2) {
// auto he = *it2;
// auto vd = target(he, mesh);
// std::cerr << " " << vd << '\n';
// }
it = std::find_if(it, end, [&mesh, index_vb](auto he) { return target(he, mesh) == index_vb; });
CGAL_assertion(it != end);
auto he = CGAL::Euler::split_edge(*it, mesh);
auto mesh_v = target(he, mesh);
put(pmap, mesh_v, v->point());
assert(mesh_v == Vertex_index{static_cast<unsigned>(time_stamp - 1)});
}
// for(auto e: edges(mesh)) {
// auto he = halfedge(e, mesh);
// auto vd1 = target(he, mesh);
// auto vd2 = source(he, mesh);
// if(!get(v_selected_map, vd1) || !get(v_selected_map, vd2)) continue;
// auto p1 = get(pmap, vd1);
// auto p2 = get(pmap, vd2);
// auto n = cdt.number_of_vertices();
// auto v1 = cdt.insert(p1);
// auto v2 = cdt.insert(p2);
// CGAL_assertion(n == cdt.number_of_vertices());
// auto steiner_vertices = cdt.sequence_of_Steiner_vertices(v1, v2);
// if(!steiner_vertices) continue;
// for(auto v: *steiner_vertices) {
// he = CGAL::Euler::split_edge(he, mesh);
// put(pmap, target(he, mesh), v->point());
// }
// }
std::ofstream out_mesh(options.dump_after_conforming_filename);
out_mesh.precision(17);
out_mesh << mesh;
out_mesh.close();
CGAL_CDT_3_TASK_END(output_task_handle);
}
if(!options.quiet) {
std::cout << "Number of vertices after conforming: " << cdt.number_of_vertices() << "\n\n";
}
CGAL_CDT_3_TASK_BEGIN(validation_task_handle);
CGAL_assertion(!options.call_is_valid || cdt.Base_triantulation::is_valid(true));
CGAL_assertion(!options.call_is_valid || cdt.is_valid(true));
CGAL_assertion(!options.call_is_valid || cdt.is_conforming());
CGAL_CDT_3_TASK_END(validation_task_handle);
if(exit_code == EXIT_SUCCESS) {
try {
CGAL_CDT_3_TASK_BEGIN(cdt_task_handle);
start_time = std::chrono::high_resolution_clock::now();
cdt.restore_constrained_Delaunay();
if(!options.quiet) {
std::cout << "[timings] restored constrained Delaunay in " << std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n";
std::cout << "Number of vertices after CDT: " << cdt.number_of_vertices() << "\n\n";
}
CGAL_CDT_3_TASK_END(cdt_task_handle);
} catch(int error) {
exit_code = error;
}
}
} CDT_3_catch(CGAL::Failure_exception&) {
finally();
CDT_3_throw_exception_again;
}
finally();
CGAL_CDT_3_TASK_BEGIN(validation_task_handle);
CGAL_assertion(!options.call_is_valid || cdt.is_conforming());
CGAL_assertion(!options.call_is_valid || cdt.is_valid(true));
CGAL_CDT_3_TASK_END(validation_task_handle);
return exit_code;
}
int bissect_errors(Mesh mesh, CDT_options options) {
auto nb_buckets = static_cast<int>(std::floor(1 / options.ratio)) + 1;
std::cerr << "RATIO: " << options.ratio << '\n';
const Mesh orig_mesh{mesh};
Mesh bad_mesh{mesh};
int exit_code = EXIT_SUCCESS;
for(int bucket = 0; bucket < nb_buckets;) {
const auto nb_faces = mesh.number_of_faces();
auto nb_to_skip = static_cast<int>(std::round(nb_faces * options.ratio));
if(nb_to_skip < 1) {
nb_to_skip = 1;
nb_buckets = nb_faces;
}
if(bucket == 0) {
std::cerr << "NB BUCKETS: " << nb_buckets << '\n';
}
auto simplify = [&](Mesh& m) {
std::cerr << "nb_to_skip: " << nb_to_skip << '\n';
std::cerr << "bucket: " << bucket << '\n';
const auto start = (std::min)(bucket * nb_to_skip, static_cast<int>(m.number_of_faces()));
const auto end = (std::min)(start + nb_to_skip, static_cast<int>(m.number_of_faces()));
std::cerr << "SKIP from " << start << " to " << end << '\n';
for(auto i = end - 1; i >= start; --i) {
const auto f = m.faces().begin() + i;
CGAL::Euler::remove_face(halfedge(*f, m), m);
}
assert(m.is_valid(true));
std::cerr << "number of faces: " << m.number_of_faces() << '\n';
if(m.number_of_faces() >= nb_faces) {
std::cerr << "ERROR: could not simplify mesh\n";
std::exit(EXIT_FAILURE);
}
};
simplify(mesh);
std::ofstream current("current_mesh.off");
current.precision(17);
current << mesh;
current.close();
try {
auto code = go(mesh, options);
if(code != EXIT_SUCCESS) {
exit_code = code;
}
} catch(CGAL::Failure_exception& e) {
std::cerr << "CAUGHT EXCEPTION: " << e.what() << '\n';
if(std::string(e.what()).find(options.failure_assertion_expression) != std::string::npos)
{
exit_code = EXIT_FAILURE;
std::cerr << "BAD MESH! " << mesh.number_of_faces() << " faces\n";
std::ofstream bad("bad_mesh.off");
bad.precision(17);
bad << mesh;
bad_mesh = mesh;
bucket = 0;
continue;
} else {
exit_code = EXIT_FAILURE;
std::cerr << "ERROR MESH: " << e.what() << '\n';
std::ofstream error("error_mesh.off");
error.precision(17);
error << mesh;
std::cerr << "go on...\n";
}
}
std::cerr << "GOOD MESH :-( " << mesh.number_of_faces() << " faces\n";
mesh = bad_mesh;
++bucket;
}
return exit_code;
}
int main(int argc, char* argv[]) {
CDT::Conforming_Dt::with_offset.offset = -1;
CDT::Conforming_Dt::with_point.offset = -1;
CDT::Conforming_Dt::with_point_and_info.offset = -1;
std::cerr.precision(17);
std::cout.precision(17);
CDT_options options(argc, argv);
if(options.need_help) {
help(std::cout);
return 0;
}
CGAL_CDT_3_TASK_BEGIN(read_input_task_handle);
auto start_time = std::chrono::high_resolution_clock::now();
auto read_options = CGAL::parameters::repair_polygon_soup(options.repair_mesh).verbose(options.verbose_level);
auto result = CGAL::read_polygon_mesh_for_cdt_3<Mesh>(options.input_filename, read_options);
if (!result.polygon_mesh)
{
std::cerr << "Not a valid input file." << std::endl;
std::cerr << "Details:\n" << result.polygon_mesh.error() << std::endl;
return 1;
}
Mesh mesh = std::move(*result.polygon_mesh);
if(!options.quiet) {
std::cout << "[timings] read mesh in " << std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n";
std::cout << "Number of vertices: " << mesh.number_of_vertices() << '\n';
std::cout << "Number of edges: " << mesh.number_of_edges() << '\n';
std::cout << "Number of faces: " << mesh.number_of_faces() << "\n\n";
std::cout << "Processing was successful.\n";
std::cout << " Number of duplicated points: " << result.nb_of_duplicated_points << '\n';
std::cout << " Number of simplified polygons: " << result.nb_of_simplified_polygons << '\n';
std::cout << " Number of new polygons: " << result.nb_of_new_polygons << '\n';
std::cout << " Number of removed invalid polygons: " << result.nb_of_removed_invalid_polygons << '\n';
std::cout << " Number of removed duplicated polygons: " << result.nb_of_removed_duplicated_polygons << '\n';
std::cout << " Number of removed isolated points: " << result.nb_of_removed_isolated_points << '\n';
std::cout << " Polygon soup self-intersects: " << (result.polygon_soup_self_intersects ? "YES" : "no") << '\n';
std::cout << " Polygon mesh is manifold: " << (result.polygon_mesh_is_manifold ? "yes" : "NO") << '\n';
std::cout << std::endl;
}
CGAL_CDT_3_TASK_END(read_input_task_handle);
if(options.reject_self_intersections && result.polygon_soup_self_intersects) {
std::cerr << "ERROR: input mesh self-intersects\n";
return EXIT_FAILURE;
}
if(!options.failure_assertion_expression.empty()) {
return bissect_errors(std::move(mesh), options);
}
auto exit_code = go(std::move(mesh), options);
if(!options.quiet) {
std::cout << "[timings] total time: " << std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start_time).count() << " ms\n";
if(exit_code != 0) std::cout << "ERROR with exit code " << exit_code << '\n';
}
return exit_code;
}

View File

@ -0,0 +1,2 @@
#define CGAL_CDT_3_USE_EPECK 1
#include "cdt_3_from_off.cpp"

View File

@ -0,0 +1,127 @@
#if __has_include(<format>)
#define CGAL_DEBUG_CDT_3 1
#endif
#define CGAL_TRIANGULATION_CHECK_EXPENSIVE 1
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Delaunay_triangulation_3.h>
#include <CGAL/Random.h>
#include <CGAL/Conforming_Delaunay_triangulation_3.h>
#include <CGAL/Conforming_constrained_Delaunay_triangulation_3.h>
#include <CGAL/Triangulation_simplex_base_with_time_stamp.h>
#include <CGAL/draw_triangulation_3.h>
#include <vector>
#include <cassert>
#include <fstream>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Triangulation_data_structure_3<
CGAL::Triangulation_simplex_base_with_time_stamp<CGAL::Conforming_constrained_Delaunay_triangulation_vertex_base_3<K>>,
CGAL::Conforming_constrained_Delaunay_triangulation_cell_base_3<K> > Tds;
typedef CGAL::Delaunay_triangulation_3<K, Tds> Delaunay;
typedef Delaunay::Point Point;
using Point_3 = K::Point_3;
int main()
{
std::cerr.precision(17);
std::cout.precision(17);
auto test1 = []() {
const std::vector<Point_3> points = { { -2, 0, 0 },
{ 2, 0, 0 },
{ 0, 1, -1 },
{ 0, -1, -1 },
{ 0, 0, 1 },
{ -10, -10, -10 },
{ -10, 10, -10 },
{ 10, 10, -10 },
{ 10, -10, -10 },
{ -10, -10, 10 },
{ -10, 10, 10 },
{ 10, 10, 10 },
{ 10, -10, 10 },
};
std::vector<Delaunay::Vertex_handle> vertices;
vertices.reserve(points.size());
CGAL::Conforming_Delaunay_triangulation_3<Delaunay> cdt;
for(auto p: points) vertices.push_back(cdt.insert(p));
Delaunay::Cell_handle c;
assert( cdt.is_valid() );
assert(cdt.is_cell(vertices[0], vertices[2], vertices[3], vertices[4], c));
assert(cdt.is_cell(vertices[1], vertices[2], vertices[3], vertices[4], c));
Delaunay::Cell_handle ch;
int li, lj;
assert(!cdt.is_edge(vertices[0], vertices[1], ch, li, lj));
cdt.insert_constrained_edge(vertices[0], vertices[1]);
cdt.insert_constrained_edge(vertices[5], vertices[1]);
cdt.insert_constrained_edge(vertices[5], vertices[10]);
std::cerr << "test1: "
<< (cdt.is_conforming() ? "OK" : "ERROR: NOT CONFORMING")
<< std::endl;
return cdt.is_conforming() ? 0 : 1;
};
enum Dim { DIM_2D, DIM_3D };
auto test2 = [](std::string filename, Dim dim = DIM_3D) {
#ifndef CGAL_TEST_CDT_3_USE_CDT
CGAL::Conforming_Delaunay_triangulation_3<Delaunay> cdt;
#else
CGAL::Conforming_constrained_Delaunay_triangulation_3_impl<Delaunay> cdt;
#endif
std::ifstream input(filename);
if(!input) throw "file not found";
int n;
input >> n;
std::cerr << n << " lines in the file\n";
while(n-- > 0) {
double x, y;
input >> x >> y;
auto v1 = cdt.insert({x, y, 0}), v3 = v1;
CGAL_assertion(cdt.is_conforming());
if(dim == DIM_3D) {
v3 = cdt.insert({x, y, 1});
CGAL_assertion(cdt.is_conforming());
}
input >> x >> y;
auto v2 = cdt.insert({x, y, 0}), v4 = v1;
CGAL_assertion(cdt.is_conforming());
if(dim == DIM_3D) {
v4 = cdt.insert({x, y, 1});
CGAL_assertion(cdt.is_conforming());
}
cdt.insert_constrained_edge(v1, v2);
CGAL_assertion(cdt.is_conforming());
if(dim == DIM_3D) {
cdt.insert_constrained_edge(v3, v4);
CGAL_assertion(cdt.is_conforming());
}
}
cdt.insert({11, 28, 0});
CGAL_assertion(cdt.is_conforming());
cdt.insert({11, 33, 0});
CGAL_assertion(cdt.is_conforming());
for (auto v : cdt.finite_vertex_handles()) {
std::cout << "Point ( " << v->point() << " )\n";
std::cout << " on " << v->ccdt_3_data().number_of_incident_constraints()
<< " constraint(s)\n";
}
std::cerr << "test2: " << filename << " "
<< (cdt.is_conforming() ? "OK" : "ERROR: NOT CONFORMING")
<< std::endl;
return cdt.is_conforming() ? 0 : 1;
};
return test1() + test2(CGAL::data_file_path("2d_segments/clusters.edg")) +
test2(CGAL::data_file_path("2d_segments/clusters2.edg"))
// // For the moment, Triangulation_segment_traverser_3 does not work in 1D:
// // Expr: _tr->dimension() >= 2
// // File: .../CGAL/Triangulation_3/internal/Triangulation_segment_traverser_3_impl.h
// + test2(CGAL::data_file_path("2d_segments/clusters.edg"), DIM_2D) +
// test2(CGAL::data_file_path("2d_segments/clusters2.edg"), DIM_2D)
;
}

View File

@ -0,0 +1,73 @@
#if __has_include(<format>)
#define CGAL_DEBUG_CDT_3 1
#endif
#define CGAL_TRIANGULATION_CHECK_EXPENSIVE 1
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Delaunay_triangulation_3.h>
#include <CGAL/Random.h>
#include <CGAL/Conforming_Delaunay_triangulation_3.h>
#include <CGAL/Conforming_constrained_Delaunay_triangulation_3.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/IO/File_binary_mesh_3.h>
#include <vector>
#include <cassert>
#include <fstream>
#include <string>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Triangulation_data_structure_3<
CGAL::Conforming_constrained_Delaunay_triangulation_vertex_base_3<K>,
CGAL::Conforming_constrained_Delaunay_triangulation_cell_base_3<K> > Tds;
typedef CGAL::Delaunay_triangulation_3<K, Tds> Delaunay;
typedef Delaunay::Point Point;
using Point_3 = K::Point_3;
typedef CGAL::Surface_mesh<Point> Mesh;
typedef boost::graph_traits<Mesh>::face_descriptor face_descriptor;
int main(int argc, char* argv[])
{
std::cerr.precision(17);
std::cout.precision(17);
const std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/fandisk.off");
std::ifstream input(filename);
Mesh mesh;
if (!input || !(input >> mesh))
{
std::cerr << "Not a valid input file." << std::endl;
return 1;
}
#ifndef CGAL_TEST_CDT_3_USE_CDT
CGAL::Conforming_Delaunay_triangulation_3<Delaunay> cdt_edge;
#else
CGAL::Conforming_constrained_Delaunay_triangulation_3_impl<Delaunay> cdt_edge;
#endif
auto point_map = get(CGAL::vertex_point, mesh);
auto dt_vertex_handle_map =
get(CGAL::dynamic_vertex_property_t<Delaunay::Vertex_handle>(), mesh);
for(auto vertex_descriptor: vertices(mesh)) {
auto vertex_handle = cdt_edge.insert(get(point_map, vertex_descriptor));
put(dt_vertex_handle_map, vertex_descriptor, vertex_handle);
}
for(auto edge_descriptor: edges(mesh)) {
auto s = source(edge_descriptor, mesh);
auto t = target(edge_descriptor, mesh);
cdt_edge.insert_constrained_edge(get(dt_vertex_handle_map, s),
get(dt_vertex_handle_map, t));
}
{
std::ofstream all_edges("all_segments.polylines.txt");
all_edges.precision(17);
cdt_edge.write_all_segments_file(all_edges);
}
assert(cdt_edge.is_conforming());
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,22 @@
OFF
13 5 0
76.621499999999997 42.272500000000001 20.489999999999998
76.621499999999997 32.026499999999999 20.489999999999998
69.537490000000005 35.112499999999997 9.218
67.6845 38.579500000000003 9.218
67.6845 38.579500000000003 51.222000000000001
70.869500000000002 32.25582 49.106000000000002
70.869500000000002 48.061500000000002 49.106000000000002
70.869500000000002 48.061500000000002 1.522
70.869500000000002 32.25582 3
-50.930500000000002 48.061500000000002 51.220999999999997
-50.930500000000002 29.410250000000001 51.220999999999997
-50.930500000000002 29.410250000000001 51.222000000000001
71.505499999999998 42.272500000000001 20.489999999999998
3 5 6 8
3 7 8 6
3 1 12 0
3 9 10 11
3 4 3 2

View File

@ -0,0 +1,37 @@
OFF
24 9 0
29.177140000000001 -11.9015 9.3190000000000008
27.660139999999998 -4.4725000000000001 0.90100000000000002
27.605139999999999 -4.9975009999999997 1.1890000000000001
29.287140000000001 -10.8515 8.7420000000000009
30.858139999999999 -17.755500000000001 16.872
27.550139999999999 -5.5225010000000001 1.478
26.892140000000001 -4.7785000000000002 9.4410000000000007
32.57414 -18.9375 9.6549990000000001
28.742139999999999 -7.0465010000000001 1.536
33.624940000000002 -19.21181 1.719867
33.155610000000003 -19.64996 7.1717440000000003
33.073520000000002 -19.726600000000001 10.307399999999999
27.64414 -6.1995009999999997 10.102
29.494140000000002 -8.4675010000000004 2.1970000000000001
23.812139999999999 5.6904979999999998 1.9830000000000001
23.883120000000002 7.817234 1.031652
24.546140000000001 8.5294989999999995 0.81284809999999996
24.546600000000002 8.5283010000000008 0.81257199999999996
34.17239 -18.798279999999998 3.65543
-5.1928599999999996 17.827500000000001 10.176
31.518650000000001 -13.5115 2.2735919999999998
28.742139999999999 -7.0465010000000001 1.536
27.64414 -6.1995009999999997 10.102
23.418150000000001 7.948887 18.369129999999998
3 1 2 3
3 3 2 4
3 2 5 0
3 8 7 6
3 21 9 10
3 11 20 12
3 13 14 22
3 23 18 15
3 19 16 17

View File

@ -0,0 +1,627 @@
OFF
317 306 0
-93.417749999999998 104.5005 23.143540000000002
-93.397530000000003 104.955 23.335920000000002
-93.379499999999993 105.4179 23.50742
-93.363709999999998 105.88809999999999 23.657689999999999
-93.350179999999995 106.3648 23.786429999999999
-93.338939999999994 106.84690000000001 23.893350000000002
-93.330020000000005 107.3334 23.97824
-93.323430000000002 107.8233 24.04092
-93.319190000000006 108.3156 24.08126
-93.317310000000006 108.8092 24.099170000000001
-93.317790000000002 109.3031 24.094609999999999
-93.320629999999994 109.7963 24.067599999999999
-93.325819999999993 110.2877 24.018190000000001
-93.333359999999999 110.7764 23.946490000000001
-93.343220000000002 111.2612 23.852640000000001
-93.35539 111.74120000000001 23.736840000000001
-93.369839999999996 112.2154 23.599329999999998
-93.38655 112.6828 23.44041
-93.405469999999994 113.14239999999999 23.260400000000001
-93.426559999999995 113.5932 23.059670000000001
-93.449789999999993 114.0343 22.838660000000001
-93.475110000000001 114.4648 22.597819999999999
-93.502449999999996 114.88379999999999 22.33766
-93.531769999999995 115.29040000000001 22.058710000000001
-93.563000000000002 115.6837 21.761569999999999
-93.596080000000001 116.063 21.446860000000001
-93.630930000000006 116.42740000000001 21.115220000000001
-93.667500000000004 116.7761 20.76737
-93.705690000000004 117.1086 20.404019999999999
-93.745419999999996 117.4239 20.025929999999999
-93.786630000000002 117.7216 19.633900000000001
-93.829210000000003 118.0009 19.228739999999998
-93.873090000000005 118.2612 18.811309999999999
-93.91816 118.5022 18.382470000000001
-93.964330000000004 118.7231 17.94313
-94.011520000000004 118.9237 17.494199999999999
-94.059610000000006 119.1033 17.036619999999999
-94.108519999999999 119.26179999999999 16.571339999999999
-94.158119999999997 119.39870000000001 16.099350000000001
-94.208330000000004 119.5137 15.62163
-94.259039999999999 119.6067 15.13917
-94.310140000000004 119.67740000000001 14.653
-94.361530000000002 119.7256 14.164110000000001
-94.413089999999997 119.7514 13.673550000000001
-94.46472 119.75449999999999 13.182320000000001
-94.516310000000004 119.7351 12.691470000000001
-94.567750000000004 119.6931 12.20201
-94.618939999999995 119.62869999999999 11.714969999999999
-94.66977 119.5419 11.23138
-94.720129999999997 119.43300000000001 10.752230000000001
-94.769919999999999 119.3022 10.27854
-94.819029999999998 119.1497 9.8112899999999996
-94.867360000000005 118.976 9.3514630000000007
-94.9148 118.7812 8.9000199999999996
-94.961269999999999 118.5659 8.4579029999999999
-95.006659999999997 118.3305 8.0260400000000001
-95.050880000000006 118.07550000000001 7.6053319999999998
-95.093829999999997 117.8015 7.1966590000000004
-95.135429999999999 117.5089 6.8008769999999998
-95.17559 117.19840000000001 6.4188140000000002
-95.214219999999997 116.8707 6.0512680000000003
-95.251239999999996 116.5264 5.6990080000000001
-95.286580000000001 116.16630000000001 5.3627710000000004
-95.320170000000005 115.7911 5.0432610000000002
-95.351920000000007 115.40170000000001 4.7411459999999996
-95.381780000000006 114.9987 4.4570569999999998
-95.409679999999994 114.5831 4.1915899999999997
-95.435569999999998 114.1557 3.9452980000000002
-95.459379999999996 113.7175 3.7186979999999998
-95.481080000000006 113.2693 3.5122640000000001
-95.500609999999995 112.812 3.3264260000000001
-95.517939999999996 112.3467 3.161575
-95.533019999999993 111.87439999999999 3.0180539999999998
-95.545829999999995 111.3959 2.8961640000000002
-95.556349999999995 110.9123 2.79616
-95.564530000000005 110.4246 2.718251
-95.57038 109.93380000000001 2.6625999999999999
-95.573880000000003 109.441 2.629324
-95.575019999999995 108.9472 2.6184919999999998
-95.573800000000006 108.4534 2.6301260000000002
-95.570210000000003 107.9606 2.6642030000000001
-95.564279999999997 107.47 2.72065
-95.556010000000001 106.9824 2.7993510000000001
-95.545419999999993 106.499 2.9001399999999999
-93.470569999999995 104.8374 22.641030000000001
-93.673500000000004 101.9252 20.71021
-93.620689999999996 101.5883 21.212730000000001
-94.495990000000006 98.166550000000001 12.8848
-94.444649999999996 98.156009999999995 13.373200000000001
-94.393320000000003 98.167820000000006 13.86158
-94.342100000000002 98.201939999999993 14.34891
-94.2911 98.258309999999994 14.834199999999999
-94.240409999999997 98.33681 15.31644
-94.190150000000003 98.437269999999998 15.79463
-94.140420000000006 98.559489999999997 16.267779999999998
-94.091319999999996 98.703220000000002 16.734909999999999
-94.042959999999994 98.868160000000003 17.195060000000002
-93.995429999999999 99.053970000000007 17.647279999999999
-93.948830000000001 99.260260000000002 18.090620000000001
-93.903260000000003 99.486609999999999 18.524180000000001
-93.858819999999994 99.73254 18.94706
-93.81559 99.997559999999993 19.35838
-93.773660000000007 100.2811 19.757280000000002
-93.73312 100.5826 20.142949999999999
-93.694059999999993 100.9014 20.514589999999998
-93.656559999999999 101.23690000000001 20.87143
-94.480429999999998 98.836250000000007 13.03281
-94.850300000000004 99.622569999999996 9.5137219999999996
-94.866339999999994 98.932140000000004 9.3611330000000006
-95.51737 105.5489 3.166982
-95.499979999999994 105.0842 3.332446
-95.48039 104.6276 3.518853
-95.458629999999999 104.18000000000001 3.7258140000000002
-95.43477 103.7424 3.952896
-95.408839999999998 103.31570000000001 4.1996250000000002
-95.380889999999994 102.9008 4.4654850000000001
-95.350999999999999 102.49850000000001 4.7499219999999998
-95.319209999999998 102.1097 5.0523410000000002
-95.285600000000002 101.73520000000001 5.3721110000000003
-95.250240000000005 101.3758 5.708564
-95.213200000000001 101.0322 6.0609960000000003
-95.174549999999996 100.7052 6.4286729999999999
-95.134389999999996 100.3954 6.8108250000000004
-95.092780000000005 100.1035 7.2066549999999996
-95.04983 99.830100000000002 7.6153360000000001
-95.005610000000004 99.575739999999996 8.0360139999999998
-94.960229999999996 99.340980000000002 8.4678120000000003
-94.91377 99.126289999999997 8.9098249999999997
-94.808099999999996 95.907730000000001 9.9152629999999995
-94.86336 96.059650000000005 9.3894450000000003
-94.917940000000002 96.232979999999998 8.8702179999999995
-94.971729999999994 96.427440000000004 8.3584460000000007
-95.024640000000005 96.642700000000005 7.8549769999999999
-95.076599999999999 96.878410000000002 7.3606480000000003
-95.127510000000001 97.134169999999997 6.8762800000000004
-95.177289999999999 97.409559999999999 6.4026759999999996
-95.225849999999994 97.704120000000003 5.9406239999999997
-95.273120000000006 98.01737 5.4908900000000003
-95.319010000000006 98.348780000000005 5.0542210000000001
-95.363460000000003 98.697800000000001 4.6313430000000002
-95.406379999999999 99.063850000000002 4.2229570000000001
-95.447710000000001 99.446340000000006 3.8297409999999998
-95.487380000000002 99.844610000000003 3.4523489999999999
-95.525310000000005 100.258 3.0914069999999998
-95.561459999999997 100.6858 2.7475139999999998
-95.595749999999995 101.12739999999999 2.4212419999999999
-95.628129999999999 101.58199999999999 2.1131319999999998
-95.658559999999994 102.0488 1.823696
-95.686959999999999 102.5271 1.5534140000000001
-95.713310000000007 103.01609999999999 1.3027359999999999
-95.737549999999999 103.5149 1.0720769999999999
-95.759649999999993 104.0228 0.86182000000000003
-95.779570000000007 104.53879999999999 0.67231490000000005
-95.797269999999997 105.0622 0.50387599999999999
-95.812730000000002 105.592 0.35678300000000002
-95.82593 106.1275 0.23128019999999999
-94.773200000000003 97.409880000000001 10.24724
-94.660960000000003 97.171260000000004 11.31517
-94.695849999999993 95.669110000000003 10.98319
-93.304839999999999 101.01609999999999 24.217780000000001
-93.338939999999994 100.5859 23.893339999999998
-93.374809999999997 100.169 23.552040000000002
-93.412400000000005 99.766229999999993 23.19444
-93.451639999999998 99.378129999999999 22.821110000000001
-93.492469999999997 99.005350000000007 22.432639999999999
-93.534819999999996 98.648489999999995 22.02966
-93.578639999999993 98.308120000000002 21.61281
-93.623840000000001 97.984780000000001 21.182749999999999
-93.670349999999999 97.678989999999999 20.740179999999999
-93.718109999999996 97.391239999999996 20.285799999999998
-93.767030000000005 97.121989999999997 19.820350000000001
-93.817040000000006 96.871669999999995 19.344560000000001
-93.868049999999997 96.640680000000003 18.859190000000002
-93.919989999999999 96.429389999999998 18.365020000000001
-93.972769999999997 96.238140000000001 17.862850000000002
-94.026309999999995 96.067229999999995 17.353470000000002
-94.080520000000007 95.916939999999997 16.837710000000001
-94.135310000000004 95.787509999999997 16.316379999999999
-94.190600000000003 95.679150000000007 15.790319999999999
-94.246300000000005 95.592020000000005 15.26037
-94.302319999999995 95.526269999999997 14.72738
-94.35857 95.482010000000002 14.1922
-94.414959999999994 95.459299999999999 13.65569
-94.471400000000003 95.458169999999996 13.1187
-94.527799999999999 95.478639999999999 12.582100000000001
-94.584069999999997 95.520669999999996 12.046749999999999
-94.640119999999996 95.584199999999996 11.513489999999999
-93.438680000000005 101.86969999999999 22.94444
-94.369420000000005 102.785 23.655799999999999
-93.374899999999997 102.785 23.551269999999999
-94.235579999999999 101.9314 24.92914
-93.241060000000004 101.9314 24.82461
-95.845389999999995 107.2098 0.046065479999999999
-95.851650000000006 107.75490000000001 -0.01347809
-95.855590000000007 108.30200000000001 -0.050956609999999999
-95.857200000000006 108.8501 -0.066308259999999994
-95.856489999999994 109.3985 -0.059507690000000002
-95.853449999999995 109.9461 -0.030566139999999999
-95.848079999999996 110.492 0.020468650000000001
-95.840410000000006 111.0355 0.09351247
-95.830430000000007 111.57550000000001 0.1884448
-95.818169999999995 112.11109999999999 0.30510900000000002
-95.803640000000001 112.6416 0.4433126
-95.786869999999993 113.166 0.60282749999999996
-95.767899999999997 113.6835 0.78339049999999999
-95.746740000000003 114.1931 0.98470380000000002
-95.723429999999993 114.69410000000001 1.2064349999999999
-95.69802 115.18559999999999 1.4482189999999999
-95.670540000000003 115.6669 1.7096549999999999
-95.641040000000004 116.1371 1.9903139999999999
-95.609570000000005 116.5954 2.2897310000000002
-95.576179999999994 117.0411 2.6074139999999999
-95.540930000000003 117.4735 2.9428359999999998
-95.503870000000006 117.89190000000001 3.2954460000000001
-95.465059999999994 118.2955 3.6646619999999999
-95.424570000000003 118.6836 4.0498729999999998
-95.382469999999998 119.0558 4.4504450000000002
-95.338830000000002 119.41119999999999 4.8657159999999999
-95.293710000000004 119.74939999999999 5.2950020000000002
-95.247190000000003 120.0698 5.7375939999999996
-95.199349999999995 120.3719 6.1927620000000001
-95.150270000000006 120.6551 6.6597549999999996
-95.100020000000001 120.9191 7.1378019999999998
-95.048699999999997 121.16330000000001 7.6261140000000003
-94.996380000000002 121.3873 8.1238860000000006
-94.943150000000003 121.5908 8.6302970000000006
-94.889110000000002 121.7735 9.1445100000000004
-94.834329999999994 121.93510000000001 9.6656779999999998
-94.778909999999996 122.0752 10.19294
-94.722949999999997 122.19370000000001 10.725429999999999
-94.666520000000006 122.2903 11.262259999999999
-94.609740000000002 122.36490000000001 11.80255
-94.552679999999995 122.4175 12.345409999999999
-94.495450000000005 122.4478 12.889950000000001
-94.438130000000001 122.4558 13.43526
-94.380830000000003 122.44159999999999 13.98044
-94.323639999999997 122.4051 14.5246
-94.266649999999998 122.3464 15.066839999999999
-94.209950000000006 122.26560000000001 15.606249999999999
-94.153639999999996 122.16289999999999 16.141970000000001
-94.097819999999999 122.03830000000001 16.673089999999998
-94.042569999999998 121.8922 17.198740000000001
-93.987989999999996 121.7248 17.718060000000001
-93.934160000000006 121.5363 18.230180000000001
-93.881180000000001 121.327 18.734269999999999
-93.829130000000006 121.0973 19.229489999999998
-93.778099999999995 120.8476 19.715019999999999
-93.728170000000006 120.5782 20.190069999999999
-93.679429999999996 120.2897 20.653849999999998
-93.631950000000003 119.9825 21.105589999999999
-93.585809999999995 119.657 21.544550000000001
-93.541089999999997 119.31399999999999 21.969999999999999
-93.497870000000006 118.9538 22.381250000000001
-93.456209999999999 118.5771 22.777609999999999
-93.416179999999997 118.1846 23.158429999999999
-93.377859999999998 117.77679999999999 23.52308
-93.341290000000001 117.3545 23.87096
-93.306550000000001 116.9183 24.201499999999999
-93.273690000000002 116.46899999999999 24.514150000000001
-93.242769999999993 116.0073 24.808389999999999
-93.213830000000002 115.53400000000001 25.083739999999999
-93.186920000000001 115.0498 25.339749999999999
-93.162090000000006 114.55549999999999 25.575990000000001
-93.139380000000003 114.05200000000001 25.792069999999999
-93.118819999999999 113.5401 25.987629999999999
-93.100459999999998 113.02070000000001 26.16236
-93.084310000000002 112.4945 26.31597
-93.070419999999999 111.96250000000001 26.4482
-93.058790000000002 111.4255 26.55883
-93.049449999999993 110.8845 26.647680000000001
-93.042420000000007 110.3402 26.71461
-93.037700000000001 109.7937 26.759499999999999
-93.035300000000007 109.2458 26.78228
-93.035240000000002 108.69750000000001 26.782910000000001
-93.037499999999994 108.1495 26.761389999999999
-93.042079999999999 107.60290000000001 26.717759999999998
-93.048990000000003 107.0585 26.652090000000001
-94.052719999999994 106.51730000000001 26.66901
-93.058189999999996 106.51730000000001 26.56448
-94.064210000000003 105.98 26.559619999999999
-93.069689999999994 105.98 26.455089999999998
-94.077979999999997 105.4477 26.428619999999999
-93.083460000000002 105.4477 26.324090000000002
-94.093999999999994 104.9212 26.276230000000002
-93.09948 104.9212 26.171700000000001
-94.11224 104.40130000000001 26.102689999999999
-93.117720000000006 104.40130000000001 25.998159999999999
-94.132670000000005 103.889 25.90831
-93.138149999999996 103.889 25.80378
-94.155259999999998 103.38500000000001 25.693390000000001
-93.160740000000004 103.38500000000001 25.58886
-94.179969999999997 102.89019999999999 25.458290000000002
-93.185450000000003 102.89019999999999 25.353760000000001
-94.206760000000003 102.4054 25.203399999999998
-93.212239999999994 102.4054 25.098870000000002
-91.899990000000003 108.2647 15.10216
-92.003979999999999 108.9357 14.112769999999999
-90.723889999999997 108.37649999999999 14.81183
-93.649770000000004 97.736189999999993 30.981169999999999
-94.384450000000001 102.477 23.99109
-93.290480000000002 102.477 23.876110000000001
-97.412790000000001 106.3109 16.703620000000001
-97.433499999999995 106.1067 16.506550000000001
-96.418270000000007 106.3109 16.59909
-98.297569999999993 106.59399999999999 30.28905
-98.127359999999996 104.13160000000001 31.908560000000001
-98.149199999999993 103.937 31.70072
-98.172439999999995 103.7578 31.479600000000001
-98.196960000000004 103.595 31.24634
-98.222629999999995 103.44929999999999 31.002120000000001
-98.249319999999997 103.3215 30.748180000000001
-97.954170000000005 105.7884 33.556330000000003
-97.93383 105.9974 33.749839999999999
-95.532520000000005 106.02070000000001 3.0228069999999998
-95.532520000000005 106.02070000000001 3.0228069999999998
-95.836820000000003 106.66759999999999 0.12757579999999999
-95.836820000000003 106.66759999999999 0.12757579999999999
3 276 277 278
3 278 279 280
3 280 281 282
3 282 283 284
3 284 285 286
3 286 287 288
3 288 289 290
3 290 291 292
3 292 293 294
3 192 81 316
3 192 80 81
3 192 193 80
3 80 193 79
3 79 193 194
3 195 79 194
3 195 78 79
3 195 196 78
3 78 196 77
3 77 196 197
3 76 197 198
3 75 198 199
3 74 199 200
3 73 200 201
3 72 201 202
3 71 202 203
3 204 71 203
3 204 70 71
3 204 205 70
3 70 205 69
3 69 205 206
3 68 206 207
3 67 207 208
3 66 208 209
3 65 209 210
3 64 210 211
3 63 211 212
3 213 63 212
3 213 62 63
3 213 214 62
3 62 214 61
3 61 214 215
3 60 215 216
3 59 216 217
3 58 217 218
3 57 218 219
3 56 219 220
3 55 220 221
3 222 55 221
3 222 54 55
3 222 223 54
3 54 223 53
3 53 223 224
3 52 224 225
3 51 225 226
3 50 226 227
3 49 227 228
3 48 228 229
3 47 229 230
3 231 47 230
3 231 46 47
3 231 232 46
3 46 232 45
3 45 232 233
3 44 233 234
3 43 234 235
3 42 235 236
3 41 236 237
3 40 237 238
3 239 40 238
3 239 39 40
3 239 240 39
3 39 240 38
3 38 240 241
3 37 241 242
3 36 242 243
3 35 243 244
3 34 244 245
3 33 245 246
3 32 246 247
3 248 32 247
3 248 31 32
3 248 249 31
3 31 249 30
3 30 249 250
3 29 250 251
3 28 251 252
3 27 252 253
3 26 253 254
3 25 254 255
3 24 255 256
3 257 24 256
3 257 23 24
3 257 258 23
3 23 258 22
3 22 258 259
3 21 259 260
3 20 260 261
3 19 261 262
3 18 262 263
3 17 263 264
3 16 264 265
3 266 16 265
3 266 15 16
3 266 267 15
3 15 267 14
3 14 267 268
3 13 268 269
3 12 269 270
3 11 270 271
3 10 271 272
3 9 272 273
3 8 273 274
3 275 8 274
3 275 7 8
3 275 276 7
3 7 276 6
3 6 276 278
3 5 278 280
3 4 280 282
3 3 282 284
3 2 284 286
3 1 286 288
3 0 288 290
3 189 290 292
3 294 189 292
3 294 191 189
3 77 197 76
3 76 198 75
3 75 199 74
3 74 200 73
3 73 201 72
3 72 202 71
3 69 206 68
3 68 207 67
3 67 208 66
3 66 209 65
3 65 210 64
3 64 211 63
3 61 215 60
3 60 216 59
3 59 217 58
3 58 218 57
3 57 219 56
3 56 220 55
3 53 224 52
3 52 225 51
3 51 226 50
3 50 227 49
3 49 228 48
3 48 229 47
3 45 233 44
3 44 234 43
3 43 235 42
3 42 236 41
3 41 237 40
3 38 241 37
3 37 242 36
3 36 243 35
3 35 244 34
3 34 245 33
3 33 246 32
3 30 250 29
3 29 251 28
3 28 252 27
3 27 253 26
3 26 254 25
3 25 255 24
3 22 259 21
3 21 260 20
3 20 261 19
3 19 262 18
3 18 263 17
3 17 264 16
3 14 268 13
3 13 269 12
3 12 270 11
3 11 271 10
3 10 272 9
3 9 273 8
3 6 278 5
3 5 280 4
3 4 282 3
3 3 284 2
3 2 286 1
3 1 288 0
3 0 290 189
3 84 189 85
3 84 0 189
3 85 189 86
3 86 189 187
3 162 187 161
3 162 86 187
3 162 163 86
3 86 163 105
3 105 163 164
3 104 164 165
3 103 165 166
3 102 166 167
3 101 167 168
3 100 168 169
3 99 169 170
3 171 99 170
3 171 98 99
3 171 172 98
3 98 172 97
3 97 172 173
3 96 173 174
3 95 174 175
3 94 175 176
3 93 176 177
3 92 177 178
3 179 92 178
3 179 91 92
3 179 180 91
3 91 180 90
3 90 180 181
3 89 181 182
3 88 182 183
3 87 183 184
3 157 184 185
3 186 157 185
3 186 158 157
3 159 160 187
3 187 160 161
3 105 164 104
3 104 165 103
3 103 166 102
3 102 167 101
3 101 168 100
3 100 169 99
3 97 173 96
3 96 174 95
3 95 175 94
3 94 176 93
3 93 177 92
3 90 181 89
3 89 182 88
3 88 183 87
3 87 184 157
3 106 157 156
3 108 156 131
3 132 108 131
3 132 127 108
3 132 133 127
3 127 133 126
3 126 133 134
3 125 134 135
3 124 135 136
3 123 136 137
3 122 137 138
3 121 138 139
3 120 139 140
3 141 120 140
3 141 119 120
3 141 142 119
3 119 142 118
3 118 142 143
3 117 143 144
3 116 144 145
3 115 145 146
3 114 146 147
3 113 147 148
3 112 148 149
3 150 112 149
3 150 111 112
3 150 151 111
3 111 151 110
3 110 151 152
3 109 152 153
3 314 153 154
3 155 314 154
3 155 315 314
3 87 157 106
3 128 129 156
3 156 129 130
3 131 156 130
3 126 134 125
3 125 135 124
3 124 136 123
3 123 137 122
3 122 138 121
3 121 139 120
3 118 143 117
3 117 144 116
3 116 145 115
3 115 146 114
3 114 147 113
3 113 148 112
3 110 152 109
3 109 153 314
3 107 106 108
3 108 106 156
3 81 82 316
3 316 82 83
3 313 316 83
3 190 293 188
3 296 295 297
3 299 298 300
3 301 302 303
3 310 309 304
3 304 309 308
3 307 304 308
3 307 306 304
3 304 306 305
3 311 304 305
3 311 312 304

View File

@ -0,0 +1,32 @@
OFF
19 9 0
115.67936706542969 54.544418334960938 16.005954742431641
117.02452850341797 55.327865600585938 16.005954742431641
117.08579254150391 55.232322692871094 21.716342926025391
118.41075134277344 56.176109313964844 18.36799430847168
118.40937805175781 56.175144195556641 18.36907958984375
118.29840850830078 56.096733093261719 18.45704460144043
118.2930908203125 56.092964172363281 18.462148666381836
118.28346252441406 56.086151123046875 18.471376419067383
118.189697265625 56.019577026367188 18.57948112487793
118.18498229980469 56.016223907470703 18.584924697875977
117.99317169189453 55.876365661621094 19.02931022644043
117.99261474609375 55.875930786132812 19.032327651977539
117.98808288574219 55.872333526611328 19.057277679443359
117.97309875488281 55.859172821044922 19.2037353515625
137.00740051269531 110.97613525390625 65.796310424804688
137.4010009765625 109.5654296875 72.631622314453125
136.79624938964844 110.9072265625 90.521072387695312
117.02452850341797 55.327865600585938 16.005954742431641
117.02452850341797 55.327865600585938 16.005954742431641
3 0 1 2
3 17 3 4
3 17 4 5
3 5 6 17
3 17 6 7
3 8 9 17
3 18 10 11
3 12 13 18
3 15 14 16

View File

@ -0,0 +1,18 @@
OFF
10 4 0
-67.788215178655193 -78.899037523497398 24.1822920114423
-67.788215178655506 -82.368494747405407 20.2941771394655
-67.788215178655093 -78.768494747405398 22.3082058185067
-67.788215178655193 -78.8606722910787 24.164811049291199
-67.788215178655193 -77.913294154982495 23.733142053801899
-66.774775293321895 -83.668494747405404 20.2941771394655
-66.774775293321895 -87.017787227405407 20.2941771394655
-71.262809942886406 -85.979974786270603 20.2941771394655
-71.107949314941195 -84.254409627405394 20.2941771394655
-71.107949314941195 -83.754409627405394 20.2941771394655
3 3 2 4
3 4 0 3
3 6 1 5
3 8 7 9

View File

@ -0,0 +1,184 @@
OFF
131 49 0
-62.620407104492188 -9.4614181518554688 41.683597564697266
-62.620414733886719 -9.4511032104492188 41.735469818115234
-62.620414733886719 -9.3767547607421875 41.991233825683594
-62.620407104492188 9.4416522979736328 4.5261672312335577e-06
-62.620407104492188 -9.4427204132080078 4.5359465730143711e-06
-47.809219360351562 3.8510293960571289 1.6157564459717833e-06
-62.598831176757812 9.4465980529785156 3.6389292290550657e-06
-20.943687438964844 9.3370399475097656 4.2613187360984739e-06
-20.914989471435547 9.3493843078613281 4.2757633309520315e-06
-20.885889053344727 9.3612480163574219 4.1343832890561316e-06
-20.971958160400391 9.3242149353027344 4.4009525481669698e-06
20.884809494018555 -9.4510955810546875 125.24068450927734
22.457618713378906 9.3619651794433594 6.1448122323781718e-06
22.441225051879883 9.3684005737304688 1.7280481188208796e-06
21.514957427978516 9.4793128967285156 3.2127923077496234e-06
21.498519897460938 9.4772605895996094 3.4301033338124398e-06
20.953998565673828 -9.4645252227783203 125.24068450927734
20.742733001708984 9.2013893127441406 4.9752002269087825e-06
20.766811370849609 9.2175998687744141 5.0692037802946288e-06
20.62904167175293 -9.3767471313476562 125.24068450927734
20.645746231079102 -9.3829135894775391 125.24068450927734
62.620277404785156 -8.7213096618652344 123.39151763916016
62.620277404785156 -8.7151603698730469 123.389404296875
62.620277404785156 -8.6144905090332031 123.36297607421875
62.620277404785156 -8.5096912384033203 123.35044097900391
62.620277404785156 -8.5560245513916016 123.35414123535156
62.620277404785156 -8.5494556427001953 123.35344696044922
62.620277404785156 -8.5230045318603516 123.35118865966797
62.620277404785156 -8.6209068298339844 123.36423492431641
20.867586135864258 -9.4473285675048828 125.24068450927734
20.936674118041992 -9.4614124298095703 125.24068450927734
62.620277404785156 -8.5163536071777344 123.35079193115234
-51.99945068359375 3.1952471733093262 4.1346197576785926e-06
-43.269599914550781 -0.32817482948303223 4.1346211219206452e-06
-19.685705184936523 -9.4303531646728516 1.9637736841104925e-06
-19.701553344726562 -9.4344139099121094 2.3815769054635894e-06
-19.42884635925293 -9.3423709869384766 1.7326274246443063e-06
-19.443225860595703 -9.3484916687011719 1.6012136256904341e-06
-19.17613410949707 -9.2045211791992188 1.2267801139387302e-06
-19.188335418701172 -9.2127552032470703 1.5256782717187889e-06
-18.863229751586914 -8.8910446166992188 7.8400080383289605e-07
-18.856225967407227 -8.879791259765625 8.6185536929406226e-07
22.13054084777832 -9.4565982818603516 2.1357404875743669e-06
22.146760940551758 -9.4533329010009766 2.2930016712052748e-06
23.093219757080078 -8.848602294921875 7.369217200903222e-07
23.08673095703125 -8.8600540161132812 9.480672815698199e-07
23.192604064941406 -8.4715385437011719 6.3923471316229552e-07
-60.769142150878906 -8.7151699066162109 7.336057024076581e-07
-60.74151611328125 -8.6080684661865234 1.4775005183764733e-06
-60.742721557617188 -8.6144981384277344 7.0386249717557803e-07
-60.735458374023438 -8.5691280364990234 1.0365247362642549e-06
-60.73193359375 -8.5362129211425781 7.5862226367462426e-07
-60.732528686523438 -8.5428447723388672 1.4881388779031113e-06
-60.731399536132812 -8.5296192169189453 9.1052152129122987e-07
-60.73016357421875 -8.5097007751464844 6.7293058236828074e-07
-60.730514526367188 -8.5163631439208984 1.2532864275272004e-06
-60.76708984375 -8.7090129852294922 9.5696577773196623e-07
-60.736328125 -8.5756492614746094 6.0640104493359104e-07
-60.854118347167969 -8.8915023803710938 8.7522312242072076e-07
-60.861328125 -8.9026336669921875 7.8946050052763894e-07
-60.729110717773438 -8.4543952941894531 7.1729391493136063e-07
-60.876274108886719 -8.9246501922607422 8.3022769103990868e-07
-60.883995056152344 -8.9355392456054688 9.3188828031998128e-07
-60.729202270507812 -8.4245491027832031 6.4579762693028897e-07
-61.030609130859375 -9.0979843139648438 1.1536394595168531e-06
-61.041107177734375 -9.1073760986328125 1.031333340506535e-06
-61.118423461914062 -9.1705780029296875 1.2765494830091484e-06
-61.130058288574219 -9.1792144775390625 1.1604315659496933e-06
-60.72930908203125 -8.3959465026855469 5.9263948060106486e-07
-61.073394775390625 -9.135040283203125 1.2063428584951907e-06
-61.084426879882812 -9.1440582275390625 1.098478605854325e-06
-61.062492370605469 -9.1259078979492188 1.0930843927781098e-06
-61.334144592285156 -9.3026294708251953 1.4496454241452739e-06
-61.320480346679688 -9.2956562042236328 1.5968289517331868e-06
-61.389518737792969 -9.3292732238769531 1.5349778550444171e-06
-61.403656005859375 -9.3356666564941406 1.7014708646456711e-06
-61.375503540039062 -9.3227710723876953 1.6700305423000827e-06
-61.432220458984375 -9.3480777740478516 1.7529819160699844e-06
-61.446647644042969 -9.3541049957275391 1.6125777619890869e-06
-61.417877197265625 -9.3419284820556641 1.6077401596703567e-06
-61.475830078125 -9.3657875061035156 1.6983394743874669e-06
-61.834487915039062 -9.4651222229003906 2.3805814635124989e-06
-61.950363159179688 -9.4816017150878906 2.4013088477659039e-06
-61.280296325683594 -9.274261474609375 1.3925337043474428e-06
-61.611656188964844 -9.4122428894042969 2.077234057651367e-06
-61.850944519042969 -9.4679145812988281 2.2724693735654e-06
-61.190338134765625 -9.2207355499267578 1.3892249626223929e-06
-61.26702880859375 -9.2668933868408203 1.4124379958957434e-06
-61.177993774414062 -9.212646484375 1.2388636605464853e-06
-61.461189270019531 -9.3600082397460938 1.7946731531992555e-06
-61.596229553222656 -9.4075565338134766 1.8849377738661133e-06
-61.96710205078125 -9.4833869934082031 2.5995341275120154e-06
-60.72930908203125 -0.3698158860206604 5.9438934840727597e-07
0.94488376379013062 -8.5163593292236328 61.675388336181641
0.94445079565048218 -8.5296249389648438 61.675815582275391
62.620277404785156 -8.4543857574462891 123.34938812255859
-18.738601684570312 -8.4671974182128906 7.2904640546767041e-07
-60.729339599609375 -8.4828910827636719 6.0301135818008333e-07
-60.730934143066406 -8.5230140686035156 6.0507500165840611e-07
-60.733177185058594 -8.5494670867919922 6.2268281908473e-07
0.94390237331390381 -8.5428562164306641 61.676364898681641
20.309930801391602 8.4743385314941406 6.3502930061076768e-06
-21.621206283569336 8.4258155822753906 6.0075799410697073e-06
-60.72930908203125 6.7186679840087891 2.1500454749912024e-09
-60.735458374023438 -8.5691280364990234 1.0365247362642549e-06
-60.730514526367188 -8.5163631439208984 1.2532864275272004e-06
-60.729110717773438 -8.4543952941894531 7.1729391493136063e-07
-60.72930908203125 -8.3959465026855469 5.9263948060106486e-07
-60.72930908203125 -8.3959465026855469 5.9263948060106486e-07
-60.72930908203125 -8.3959465026855469 5.9263948060106486e-07
-60.72930908203125 -8.3959465026855469 5.9263948060106486e-07
-60.72930908203125 -8.3959465026855469 5.9263948060106486e-07
-60.72930908203125 -8.3959465026855469 5.9263948060106486e-07
-60.72930908203125 -8.3959465026855469 5.9263948060106486e-07
-60.72930908203125 -8.3959465026855469 5.9263948060106486e-07
-60.72930908203125 -8.3959465026855469 5.9263948060106486e-07
-60.72930908203125 -8.3959465026855469 5.9263948060106486e-07
0.94445079565048218 -8.5296249389648438 61.675815582275391
-18.738601684570312 -8.4671974182128906 7.2904640546767041e-07
-18.738601684570312 -8.4671974182128906 7.2904640546767041e-07
-18.738601684570312 -8.4671974182128906 7.2904640546767041e-07
0.94390237331390381 -8.5428562164306641 61.676364898681641
0.94390237331390381 -8.5428562164306641 61.676364898681641
0.94390237331390381 -8.5428562164306641 61.676364898681641
0.94390237331390381 -8.5428562164306641 61.676364898681641
0.94390237331390381 -8.5428562164306641 61.676364898681641
0.94390237331390381 -8.5428562164306641 61.676364898681641
20.309930801391602 8.4743385314941406 6.3502930061076768e-06
20.309930801391602 8.4743385314941406 6.3502930061076768e-06
-21.621206283569336 8.4258155822753906 6.0075799410697073e-06
23.192573547363281 -8.4420909881591797 5.9400190366432071e-07
3 46 44 45
3 50 57 97
3 41 40 96
3 58 59 60
3 55 98 104
3 61 62 106
3 64 65 63
3 66 67 68
3 107 69 70
3 17 18 101
3 38 39 118
3 107 71 69
3 108 73 72
3 109 74 75
3 10 7 102
3 109 76 74
3 77 78 110
3 36 37 119
3 110 79 77
3 8 9 129
3 13 12 127
3 111 87 83
3 89 80 112
3 1 11 29
3 113 90 84
3 34 35 120
3 0 16 30
3 82 91 114
3 88 86 115
3 130 43 42
3 128 15 14
3 116 81 85
3 92 4 3
3 103 92 3
3 103 3 6
3 33 32 5
3 100 23 28
3 49 48 121
3 122 22 21
3 47 56 123
3 124 26 25
3 99 52 125
3 52 51 125
3 125 51 53
3 24 31 126
3 31 27 126
3 94 93 95
3 117 105 54
3 2 20 19

View File

@ -0,0 +1,38 @@
OFF
24 10 0
-22.291719436645508 -33.0625 132.11399841308594
-15.280426979064941 -61.062496185302734 132.11399841308594
-15.280428886413574 -33.0625 132.11399841308594
-6.6061949729919434 -64.0625 119.71333312988281
-9.7316780090332031 -16.104564666748047 127.08177185058594
-9.7316789627075195 -7.02191162109375 127.08177185058594
-9.7316789627075195 -0.72058898210525513 127.08177185058594
-9.9586648941040039 -64.0625 126.97700500488281
92.889457702636719 7.335627555847168 17.32063102722168
92.185699462890625 7.335627555847168 16.567487716674805
-23.523859024047852 -39.552497863769531 128.30531311035156
-15.280428886413574 -39.552497863769531 128.30531311035156
-0.75768494606018066 -61.062496185302734 102.16104888916016
-17.72953987121582 -61.062496185302734 138.93341064453125
-22.269332885742188 -61.062496185302734 136.83811950683594
-19.280878067016602 -36.837699890136719 125.59051513671875
-0.75768661499023438 -33.062496185302734 102.16104888916016
-17.72953987121582 -33.0625 138.93341064453125
-22.26933479309082 -33.0625 136.83811950683594
88.691604614257812 7.3356270790100098 14.604374885559082
-6.1871428489685059 63.937496185302734 118.80537414550781
-6.6062026023864746 63.937496185302734 119.71333312988281
-9.9586725234985352 63.937496185302734 126.97700500488281
-9.9586648941040039 -64.0625 126.97700500488281
3 12 1 13
3 13 1 14
3 10 15 11
3 0 2 18
3 8 19 9
3 16 12 17
3 17 12 13
3 3 20 21
3 6 22 7
3 5 23 4

View File

@ -0,0 +1,16 @@
OFF
9 3 0
-22.291719436645508 -33.0625 132.11399841308594
-15.280428886413574 -33.0625 132.11399841308594
-9.7316780090332031 -16.104564666748047 127.08177185058594
-9.7316789627075195 -7.02191162109375 127.08177185058594
-0.75768494606018066 -61.062496185302734 102.16104888916016
-17.72953987121582 -61.062496185302734 138.93341064453125
-17.72953987121582 -33.0625 138.93341064453125
-22.26933479309082 -33.0625 136.83811950683594
-9.9586648941040039 -64.0625 126.97700500488281
3 0 1 7
3 6 4 5
3 3 8 2

View File

@ -0,0 +1,65 @@
OFF
36 25 0
37.944911956787109 -1.6982429027557373 8.5614194869995117
38.049285888671875 -1.7218705415725708 8.3133611679077148
38.145980834960938 -1.7387760877609253 8.0756645202636719
37.676181793212891 -0.08527558296918869 7.889103889465332
37.857818603515625 -0.093803025782108307 7.4036040306091309
37.735622406005859 -1.6400438547134399 8.8551864624023438
37.741683959960938 -1.6386662721633911 8.9242925643920898
37.965476989746094 -1.7082259654998779 8.3176441192626953
37.98175048828125 -1.7098640203475952 8.3662099838256836
38.196575164794922 -1.743979811668396 7.7442541122436523
38.217582702636719 -1.747313380241394 7.7797822952270508
37.731548309326172 -1.641355037689209 8.799250602722168
37.953289031982422 -1.7070465087890625 8.2782869338989258
38.180507659912109 -1.7413737773895264 7.7158145904541016
38.392875671386719 -1.7514817714691162 7.1327433586120605
37.931743621826172 -1.7050290107727051 8.2031984329223633
38.151718139648438 -1.73658287525177 7.6619982719421387
38.360618591308594 -1.7428750991821289 7.1007680892944336
37.916267395019531 -1.7036066055297852 8.1459512710571289
38.13104248046875 -1.7330515384674072 7.6210522651672363
38.337314605712891 -1.7364044189453125 7.0764865875244141
37.69573974609375 -1.6467151641845703 8.4192934036254883
37.877265930175781 -1.7001659870147705 7.9938459396362305
38.079837799072266 -1.7240269184112549 7.5114293098449707
38.279632568359375 -1.7194610834121704 7.0100784301757812
37.634693145751953 -1.6362926959991455 8.258021354675293
37.667015075683594 -1.6497268676757812 8.1850767135620117
37.831775665283203 -1.6966333389282227 7.8121175765991211
37.598911285400391 -1.6418465375900269 7.9809689521789551
37.62762451171875 -1.6536027193069458 7.9197196960449219
37.778202056884766 -1.6935627460479736 7.5982809066772461
37.596717834472656 -0.12143304198980331 8.034724235534668
37.98175048828125 -1.7098640203475952 8.3662099838256836
37.723155975341797 -1.6431630849838257 8.696990966796875
37.716129302978516 -1.6441534757614136 8.6208524703979492
37.877265930175781 -1.7001659870147705 7.9938459396362305
3 4 31 3
3 7 8 6
3 9 10 8
3 9 8 7
3 12 7 5
3 13 9 7
3 13 7 12
3 14 9 13
3 15 12 11
3 16 13 12
3 16 12 15
3 17 13 16
3 18 15 33
3 19 16 15
3 19 15 18
3 20 16 19
3 22 34 21
3 23 19 18
3 23 18 35
3 24 19 23
3 27 21 26
3 29 25 28
3 30 27 26
3 1 0 32
3 8 10 2

View File

@ -0,0 +1,27 @@
OFF
16 7 0
29.389250000000001 40.450850000000003 96.239999999999995
30.677409000000001 39.162691000000002 12.550000000000001
27.766093999999999 41.277892999999999 20.800000000000001
30.677409000000001 39.162691000000002 29.050000000000001
39.162691000000002 30.677409000000001 29.050000000000001
29.389250000000001 40.450850000000003 0
30.677409000000001 39.162691000000002 45.549999999999997
39.162691000000002 30.677409000000001 45.549999999999997
32.798729999999999 37.041370000000001 67.246151999999995
30.677409000000001 39.162691000000002 62.049999999999997
27.766093999999999 41.277892999999999 53.799999999999997
27.766093999999999 41.277892999999999 70.299999999999997
25.093077000000001 42.639868999999997 65.103847999999999
40.450850000000003 29.389250000000001 96.239999999999995
30.677409000000001 39.162691000000002 78.549999999999997
27.766093999999999 41.277892999999999 37.299999999999997
3 9 14 8
3 1 0 3
3 3 0 6
3 4 7 13
3 5 2 0
3 15 10 0
3 10 12 11

View File

@ -0,0 +1,227 @@
OFF
159 60 0
-37.902450000000002 27.537749999999999 98.304000000000002
-45.549999999999997 0 97.488
-40.289050000000003 29.271699999999999 96.959999999999994
-48.850000000000001 0 98.016000000000005
-47.3626 15.389049999999999 96.959999999999994
-49.149999999999999 0 97.775999999999996
-46.5 0 98.207999999999998
-50 0 96.239999999999995
-47.552849999999999 15.450850000000001 96.239999999999995
-45.100000000000001 0 96.719999999999999
-37.093449999999997 26.949950000000001 97.775999999999996
-48.649999999999999 0 98.159999999999997
-46.649299999999997 15.157299999999999 97.872
-46.744450000000001 15.1882 97.775999999999996
-43.177950000000003 14.029350000000001 97.296000000000006
-40.369950000000003 29.330500000000001 96.623999999999995
-42.892650000000003 13.93665 96.623999999999995
-37.700200000000002 27.390799999999999 98.256
-44.4619 14.44655 98.304000000000002
-44.319249999999997 14.4002 98.256
-47.25 0 98.400000000000006
-44.937399999999997 14.601050000000001 98.400000000000006
-45.412950000000002 14.755549999999999 98.400000000000006
-36.729349999999997 26.685449999999999 97.296000000000006
-43.60595 14.16845 97.775999999999996
-48.950000000000003 0 97.920000000000002
-46.554200000000002 15.1264 97.920000000000002
-49.649999999999999 0 97.200000000000003
-45 0 96.239999999999995
-44.557000000000002 14.477449999999999 98.304000000000002
-47 0 98.352000000000004
-46.850000000000001 0 98.304000000000002
-37.255249999999997 27.067499999999999 97.920000000000002
-48.399999999999999 0 98.256
-36.486649999999997 26.5091 96.623999999999995
-43.891249999999999 14.261150000000001 98.016000000000005
-39.601399999999998 28.772099999999998 97.920000000000002
-38.104700000000001 27.684699999999999 98.352000000000004
-45.100000000000001 0 96.623999999999995
-42.797550000000001 13.905749999999999 95.760000000000005
-46.839550000000003 15.219099999999999 97.680000000000007
-49.450000000000003 0 97.488
-49.25 0 97.680000000000007
-46.149999999999999 0 98.016000000000005
-39.358699999999999 28.595749999999999 98.159999999999997
-49.049999999999997 0 97.872
-47.75 0 98.400000000000006
-37.336150000000004 27.126300000000001 98.016000000000005
-40.167700000000004 29.18355 97.200000000000003
-40.127249999999997 29.154150000000001 97.296000000000006
-39.520499999999998 28.7133 98.016000000000005
-45.555599999999998 14.8019 98.352000000000004
-42.892650000000003 13.93665 96.719999999999999
-40.450850000000003 29.389250000000001 96.239999999999995
-38.751899999999999 28.154900000000001 98.352000000000004
-46.459099999999999 15.095499999999999 98.016000000000005
-39.439599999999999 28.65455 98.063999999999993
-44.794750000000001 14.5547 98.352000000000004
-39.682299999999998 28.830850000000002 97.872
-49.950000000000003 0 96.480000000000004
-47.505249999999997 15.4354 96.480000000000004
-36.405749999999998 26.45035 95.760000000000005
-42.797550000000001 13.905749999999999 96.239999999999995
-45.049999999999997 0 96.480000000000004
-39.763199999999998 28.88965 97.775999999999996
-45.75 0 97.680000000000007
-36.567549999999997 26.567900000000002 96.959999999999994
-36.527099999999997 26.538499999999999 96.864000000000004
-45.650700000000001 14.832800000000001 98.352000000000004
-48 0 98.352000000000004
-47.899999999999999 0 98.352000000000004
-37.174349999999997 27.008749999999999 97.872
-42.845100000000002 13.921200000000001 96.480000000000004
-45.850000000000001 0 97.775999999999996
-43.510849999999998 14.137549999999999 97.680000000000007
-45.25 0 97.103999999999999
-43.035299999999999 13.983000000000001 97.103999999999999
-49.850000000000001 0 96.864000000000004
-47.410150000000002 15.404500000000001 96.864000000000004
-45.888500000000001 14.91005 98.304000000000002
-45.793349999999997 14.879149999999999 98.304000000000002
-38.630549999999999 28.066749999999999 98.400000000000006
-39.844099999999997 28.948399999999999 97.680000000000007
-38.226050000000001 27.772849999999998 98.400000000000006
-45.950000000000003 0 97.872
-49.600000000000001 0 97.296000000000006
-40.046349999999997 29.09535 97.391999999999996
-48.5 0 98.207999999999998
-46.049999999999997 0 97.920000000000002
-43.701050000000002 14.199350000000001 97.872
-40.410400000000003 29.359850000000002 96.480000000000004
-36.446199999999997 26.479749999999999 96.480000000000004
-43.796149999999997 14.23025 97.920000000000002
-39.156399999999998 28.448799999999999 98.256
-46.126249999999999 14.987299999999999 98.207999999999998
-46.031149999999997 14.9564 98.256
-47.100000000000001 0 98.352000000000004
-49.950000000000003 0 96.384
-47.505249999999997 15.4354 96.384
-44.081449999999997 14.322950000000001 98.159999999999997
-40.329500000000003 29.301100000000002 96.864000000000004
-38.023800000000001 27.625900000000001 98.352000000000004
-44.224150000000002 14.369300000000001 98.207999999999998
-42.987749999999998 13.967549999999999 96.959999999999994
-45.200000000000003 0 96.959999999999994
-47.172400000000003 15.327249999999999 97.296000000000006
-49.899999999999999 0 96.623999999999995
-37.497950000000003 27.243849999999998 98.159999999999997
-37.417050000000003 27.18505 98.063999999999993
-46.268900000000002 15.0337 98.159999999999997
-48.75 0 98.063999999999993
-46.363999999999997 15.0646 98.063999999999993
-36.486649999999997 26.5091 96.719999999999999
-45 0 95.760000000000005
-45.5 0 97.391999999999996
-38.832799999999999 28.213699999999999 98.352000000000004
-40.248600000000003 29.2423 97.103999999999999
-46.25 0 98.063999999999993
-36.810250000000003 26.744250000000001 97.391999999999996
-38.954149999999998 28.301850000000002 98.304000000000002
-47.457700000000003 15.41995 96.719999999999999
-47.315049999999999 15.3736 97.103999999999999
-40.369950000000003 29.330500000000001 96.719999999999999
-37.012549999999997 26.891200000000001 97.680000000000007
-36.850700000000003 26.773599999999998 97.488
-44.699649999999998 14.5238 98.352000000000004
-46.75 0 98.304000000000002
-37.619300000000003 27.332000000000001 98.207999999999998
-49.799999999999997 0 96.959999999999994
-46.600000000000001 0 98.256
-43.320599999999999 14.075699999999999 97.488
-42.940199999999997 13.9521 96.864000000000004
-45.149999999999999 0 96.864000000000004
-48.149999999999999 0 98.304000000000002
-48.25 0 98.304000000000002
-40.410400000000003 29.359850000000002 96.384
-37.821550000000002 27.478950000000001 98.304000000000002
-47.219949999999997 15.342700000000001 97.200000000000003
-45.350000000000001 0 97.200000000000003
-43.273049999999998 14.06025 97.391999999999996
-39.035049999999998 28.36065 98.304000000000002
-45.399999999999999 0 97.296000000000006
-43.130400000000002 14.0139 97.200000000000003
-49.75 0 97.103999999999999
-36.405749999999998 26.45035 96.239999999999995
-40.005899999999997 29.065999999999999 97.488
-47.02975 15.280900000000001 97.488
-36.446199999999997 26.479749999999999 96.384
-36.607999999999997 26.597300000000001 97.103999999999999
-45.049999999999997 0 96.384
-42.845100000000002 13.921200000000001 96.384
-47.077300000000001 15.29635 97.391999999999996
-49.5 0 97.391999999999996
-43.986350000000002 14.29205 98.063999999999993
-39.237299999999998 28.5076 98.207999999999998
-47.457700000000003 15.41995 96.623999999999995
-49.899999999999999 0 96.719999999999999
-46.350000000000001 0 98.159999999999997
-36.688899999999997 26.65605 97.200000000000003
3 124 130 118
3 89 88 84
3 76 138 75
3 49 137 105
3 86 105 151
3 131 104 132
3 35 117 43
3 52 9 16
3 92 88 89
3 121 128 143
3 137 27 105
3 103 104 131
3 116 4 121
3 66 103 67
3 112 52 34
3 151 152 146
3 52 16 34
3 57 20 96
3 37 57 101
3 70 51 68
3 56 111 44
3 18 126 19
3 51 54 68
3 130 65 1
3 108 99 153
3 133 80 79
3 39 62 28
3 136 18 17
3 2 78 4
3 127 19 102
3 30 125 57
3 47 153 35
3 120 156 78
3 147 150 144
3 153 117 35
3 151 85 152
3 36 26 50
3 50 26 55
3 0 29 136
3 79 134 133
3 101 125 0
3 10 89 24
3 139 1 114
3 80 119 79
3 107 102 99
3 56 55 111
3 53 8 135
3 154 109 94
3 111 3 110
3 40 42 13
3 98 60 135
3 24 84 73
3 155 59 106
3 60 59 155
3 15 60 155
3 90 60 15
3 68 133 69
3 13 42 5
3 17 19 127
3 14 114 141

View File

@ -0,0 +1,214 @@
OFF
106 104 0
-37.902450000000002 27.537749999999999 98.304000000000002
-36.688899999999997 26.65605 97.200000000000003
-40.289050000000003 29.271699999999999 96.959999999999994
-47.457700000000003 15.41995 96.623999999999995
-47.3626 15.389049999999999 96.959999999999994
-39.237299999999998 28.5076 98.207999999999998
-43.986350000000002 14.29205 98.063999999999993
-47.077300000000001 15.29635 97.391999999999996
-47.552849999999999 15.450850000000001 96.239999999999995
-42.845100000000002 13.921200000000001 96.384
-37.093449999999997 26.949950000000001 97.775999999999996
-36.607999999999997 26.597300000000001 97.103999999999999
-46.649299999999997 15.157299999999999 97.872
-46.744450000000001 15.1882 97.775999999999996
-43.177950000000003 14.029350000000001 97.296000000000006
-40.369950000000003 29.330500000000001 96.623999999999995
-42.892650000000003 13.93665 96.623999999999995
-37.700200000000002 27.390799999999999 98.256
-44.4619 14.44655 98.304000000000002
-44.319249999999997 14.4002 98.256
-36.446199999999997 26.479749999999999 96.384
-44.937399999999997 14.601050000000001 98.400000000000006
-45.412950000000002 14.755549999999999 98.400000000000006
-36.729349999999997 26.685449999999999 97.296000000000006
-43.60595 14.16845 97.775999999999996
-47.02975 15.280900000000001 97.488
-46.554200000000002 15.1264 97.920000000000002
-40.005899999999997 29.065999999999999 97.488
-36.405749999999998 26.45035 96.239999999999995
-44.557000000000002 14.477449999999999 98.304000000000002
-43.130400000000002 14.0139 97.200000000000003
-39.035049999999998 28.36065 98.304000000000002
-37.255249999999997 27.067499999999999 97.920000000000002
-43.273049999999998 14.06025 97.391999999999996
-36.486649999999997 26.5091 96.623999999999995
-43.891249999999999 14.261150000000001 98.016000000000005
-39.601399999999998 28.772099999999998 97.920000000000002
-38.104700000000001 27.684699999999999 98.352000000000004
-47.219949999999997 15.342700000000001 97.200000000000003
-42.797550000000001 13.905749999999999 95.760000000000005
-46.839550000000003 15.219099999999999 97.680000000000007
-37.821550000000002 27.478950000000001 98.304000000000002
-40.410400000000003 29.359850000000002 96.384
-42.940199999999997 13.9521 96.864000000000004
-39.358699999999999 28.595749999999999 98.159999999999997
-43.320599999999999 14.075699999999999 97.488
-37.619300000000003 27.332000000000001 98.207999999999998
-37.336150000000004 27.126300000000001 98.016000000000005
-40.167700000000004 29.18355 97.200000000000003
-40.127249999999997 29.154150000000001 97.296000000000006
-39.520499999999998 28.7133 98.016000000000005
-45.555599999999998 14.8019 98.352000000000004
-42.892650000000003 13.93665 96.719999999999999
-40.450850000000003 29.389250000000001 96.239999999999995
-38.751899999999999 28.154900000000001 98.352000000000004
-46.459099999999999 15.095499999999999 98.016000000000005
-39.439599999999999 28.65455 98.063999999999993
-44.794750000000001 14.5547 98.352000000000004
-39.682299999999998 28.830850000000002 97.872
-44.699649999999998 14.5238 98.352000000000004
-47.505249999999997 15.4354 96.480000000000004
-36.405749999999998 26.45035 95.760000000000005
-42.797550000000001 13.905749999999999 96.239999999999995
-36.850700000000003 26.773599999999998 97.488
-39.763199999999998 28.88965 97.775999999999996
-37.012549999999997 26.891200000000001 97.680000000000007
-36.567549999999997 26.567900000000002 96.959999999999994
-36.527099999999997 26.538499999999999 96.864000000000004
-45.650700000000001 14.832800000000001 98.352000000000004
-40.369950000000003 29.330500000000001 96.719999999999999
-47.315049999999999 15.3736 97.103999999999999
-37.174349999999997 27.008749999999999 97.872
-42.845100000000002 13.921200000000001 96.480000000000004
-47.457700000000003 15.41995 96.719999999999999
-43.510849999999998 14.137549999999999 97.680000000000007
-38.954149999999998 28.301850000000002 98.304000000000002
-43.035299999999999 13.983000000000001 97.103999999999999
-36.810250000000003 26.744250000000001 97.391999999999996
-47.410150000000002 15.404500000000001 96.864000000000004
-45.888500000000001 14.91005 98.304000000000002
-45.793349999999997 14.879149999999999 98.304000000000002
-38.630549999999999 28.066749999999999 98.400000000000006
-39.844099999999997 28.948399999999999 97.680000000000007
-38.226050000000001 27.772849999999998 98.400000000000006
-40.248600000000003 29.2423 97.103999999999999
-38.832799999999999 28.213699999999999 98.352000000000004
-40.046349999999997 29.09535 97.391999999999996
-36.486649999999997 26.5091 96.719999999999999
-46.363999999999997 15.0646 98.063999999999993
-43.701050000000002 14.199350000000001 97.872
-40.410400000000003 29.359850000000002 96.480000000000004
-36.446199999999997 26.479749999999999 96.480000000000004
-43.796149999999997 14.23025 97.920000000000002
-39.156399999999998 28.448799999999999 98.256
-46.126249999999999 14.987299999999999 98.207999999999998
-46.031149999999997 14.9564 98.256
-46.268900000000002 15.0337 98.159999999999997
-37.417050000000003 27.18505 98.063999999999993
-47.505249999999997 15.4354 96.384
-44.081449999999997 14.322950000000001 98.159999999999997
-40.329500000000003 29.301100000000002 96.864000000000004
-38.023800000000001 27.625900000000001 98.352000000000004
-44.224150000000002 14.369300000000001 98.207999999999998
-42.987749999999998 13.967549999999999 96.959999999999994
-37.497950000000003 27.243849999999998 98.159999999999997
-47.172400000000003 15.327249999999999 97.296000000000006
3 17 19 46
3 59 101 57
3 90 60 15
3 65 74 63
3 15 60 3
3 84 70 48
3 17 18 19
3 98 60 42
3 5 96 94
3 21 83 22
3 97 6 47
3 53 8 42
3 56 55 88
3 63 45 77
3 93 95 31
3 77 33 23
3 104 102 99
3 80 75 79
3 10 89 24
3 44 96 5
3 67 43 87
3 81 22 83
3 23 14 1
3 75 80 85
3 1 30 11
3 15 73 69
3 44 88 96
3 11 30 76
3 39 61 62
3 37 21 57
3 0 59 29
3 50 55 56
3 77 45 33
3 85 80 68
3 101 59 0
3 31 79 75
3 54 51 81
3 42 60 90
3 0 29 41
3 27 7 25
3 104 99 97
3 48 38 49
3 50 26 55
3 47 35 32
3 32 92 71
3 93 94 95
3 81 51 22
3 11 76 66
3 72 9 20
3 66 76 103
3 36 26 50
3 49 38 105
3 63 74 45
3 64 40 13
3 83 21 37
3 65 24 74
3 18 41 29
3 86 105 7
3 61 28 62
3 58 13 12
3 1 14 30
3 42 8 98
3 82 40 64
3 49 105 86
3 86 7 27
3 67 103 43
3 31 95 79
3 100 78 2
3 91 16 72
3 69 73 100
3 3 73 15
3 85 68 54
3 84 4 70
3 36 12 26
3 87 43 52
3 28 9 62
3 20 9 28
3 48 70 38
3 23 33 14
3 27 25 82
3 5 94 93
3 32 35 92
3 91 72 20
3 66 103 67
3 87 52 34
3 47 6 35
3 52 16 34
3 46 19 102
3 82 25 40
3 2 78 4
3 41 18 17
3 58 12 36
3 2 4 84
3 34 16 91
3 97 99 6
3 46 102 104
3 10 24 65
3 71 92 89
3 64 13 58
3 71 89 10
3 37 57 101
3 100 73 78
3 56 88 44
3 51 54 68

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,137 @@
OFF
71 62 0
-0.043536998299999997 0.039117999399999999 -0.050000000699999998
-0.040624998500000002 0.042913000999999999 -0.050000000699999998
0.050000000699999998 0.050000000699999998 -0.050000000699999998
-0.041919000400000003 0.016919000100000001 -0.050000000699999998
-0.034375000699999998 0.042913000999999999 -0.050000000699999998
-0.033080998799999997 0.033080998799999997 -0.050000000699999998
-0.043749999300000002 0.0125000002 -0.050000000699999998
-0.037500001499999998 -0.043749999300000002 -0.050000000699999998
0.014117999900000001 0.031463000900000003 -0.050000000699999998
-0.039117999399999999 0.018536999799999999 -0.050000000699999998
0.031463000900000003 0.035881999900000003 -0.050000000699999998
-0.015625 0.042913000999999999 -0.050000000699999998
-0.035881999900000003 0.043536998299999997 -0.050000000699999998
-0.043749999300000002 0.037500001499999998 -0.050000000699999998
-0.037500001499999998 0.018750000700000002 -0.050000000699999998
-0.033080998799999997 0.016919000100000001 -0.050000000699999998
-0.041919000400000003 -0.041919000400000003 -0.050000000699999998
-0.034375000699999998 0.017913000700000001 -0.050000000699999998
-0.018536999799999999 0.039117999399999999 -0.050000000699999998
-0.043536998299999997 -0.014117999900000001 -0.050000000699999998
-0.031463000900000003 0.035881999900000003 -0.050000000699999998
-0.039117999399999999 0.031463000900000003 -0.050000000699999998
-0.03125 0.037500001499999998 -0.050000000699999998
-0.042913000999999999 0.015625 -0.050000000699999998
-0.032086998200000001 0.040624998500000002 -0.050000000699999998
-0.039117999399999999 0.043536998299999997 -0.050000000699999998
-0.041919000400000003 0.041919000400000003 -0.016919000100000001
-0.040624998500000002 0.032086998200000001 -0.050000000699999998
-0.0050889999000000002 -0.050000000699999998 -0.050000000699999998
-0.018750000700000002 0.037500001499999998 -0.050000000699999998
-0.032086998200000001 0.034375000699999998 -0.050000000699999998
-0.017913000700000001 0.040624998500000002 -0.050000000699999998
-0.016919000100000001 0.033080998799999997 -0.050000000699999998
-0.043536998299999997 -0.039117999399999999 -0.050000000699999998
0.032086998200000001 0.034375000699999998 -0.050000000699999998
-0.043536998299999997 0.014117999900000001 -0.050000000699999998
-0.035881999900000003 -0.050000000699999998 -0.043536998299999997
0.03125 0.037500001499999998 -0.050000000699999998
-0.016919000100000001 0.041919000400000003 -0.050000000699999998
-0.043749999300000002 -0.0125000002 -0.050000000699999998
-0.014117999900000001 0.043536998299999997 -0.050000000699999998
0.015625 0.017913000700000001 -0.050000000699999998
-0.037500001499999998 0.03125 -0.050000000699999998
-0.034375000699999998 0.032086998200000001 -0.050000000699999998
0.014117999900000001 0.018536999799999999 -0.050000000699999998
-0.041919000400000003 0.041919000400000003 -0.033080998799999997
-0.018536999799999999 0.035881999900000003 -0.050000000699999998
-0.042913000999999999 0.034375000699999998 -0.050000000699999998
-0.042913000999999999 0.040624998500000002 -0.050000000699999998
-0.040624998500000002 -0.042913000999999999 -0.050000000699999998
-0.037500001499999998 0.043749999300000002 -0.050000000699999998
-0.033080998799999997 0.041919000400000003 -0.050000000699999998
-0.041919000400000003 0.041919000400000003 -0.050000000699999998
0.018536999799999999 0.035881999900000003 -0.050000000699999998
-0.042913000999999999 -0.040624998500000002 -0.050000000699999998
0.050000000699999998 0.037500001499999998 -0.043749999300000002
-0.035881999900000003 -0.043536998299999997 -0.050000000699999998
-0.040972001899999999 0.042697001200000001 -0.0323030017
-0.035881999900000003 0.031463000900000003 -0.050000000699999998
-0.043536998299999997 -0.035881999900000003 -0.050000000699999998
-0.035881999900000003 0.018536999799999999 -0.050000000699999998
0.050000000699999998 0.035881999900000003 -0.043536998299999997
-0.040624998500000002 0.017913000700000001 -0.050000000699999998
-0.043749999300000002 -0.037500001499999998 -0.050000000699999998
-0.043536998299999997 0.035881999900000003 -0.050000000699999998
-0.031463000900000003 0.039117999399999999 -0.050000000699999998
-0.039117999399999999 -0.043536998299999997 -0.050000000699999998
-0.050000000699999998 0.050000000699999998 -0.050000000699999998
-0.017913000700000001 0.034375000699999998 -0.050000000699999998
-0.041919000400000003 0.033080998799999997 -0.050000000699999998
-0.050000000699999998 -0.050000000699999998 -0.050000000699999998
3 17 5 15
3 70 28 36
3 44 8 41
3 34 53 10
3 53 37 10
3 30 20 68
3 5 30 32
3 33 54 70
3 33 70 63
3 70 59 63
3 35 64 47
3 12 67 40
3 11 51 4
3 27 62 3
3 67 1 52
3 25 1 67
3 25 67 50
3 67 12 50
3 4 12 40
3 11 4 40
3 48 0 67
3 67 6 70
3 67 35 6
3 64 67 13
3 67 0 13
3 35 67 64
3 23 35 47
3 23 47 69
3 48 67 52
3 70 56 28
3 9 27 21
3 11 38 51
3 20 29 46
3 9 42 14
3 70 54 16
3 49 70 16
3 49 66 70
3 70 66 7
3 56 70 7
3 9 21 42
3 19 59 70
3 19 70 39
3 70 6 39
3 9 62 27
3 23 69 3
3 69 27 3
3 51 38 31
3 24 51 31
3 65 24 31
3 18 65 31
3 18 29 65
3 29 20 22
3 65 29 22
3 26 45 57
3 68 20 46
3 60 43 17
3 17 43 5
3 14 42 60
3 60 42 58
3 43 60 58
3 15 5 32
3 55 61 2

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,41 @@
OFF
25 12 0
-0.038718998400000003 0.031369999099999997 -0.018629999800000002
-0.038718998400000003 0.031369999099999997 -0.031369999099999997
0.015971999600000002 0.017697000899999999 -0.0323030017
0.015971999600000002 0.0323030017 -0.0323030017
0.016797000499999999 0.0170389991 -0.032930001600000001
0.016797000499999999 0.0170389991 -0.0170389991
0.016797000499999999 0.0307030007 -0.032930001600000001
0.016797000499999999 0.031846001700000001 -0.032841000699999998
0.016797000499999999 0.032960999800000002 -0.032575998500000002
0.016797000499999999 0.032960999800000002 -0.0170389991
0.016919000100000001 0.016919000100000001 -0.033080998799999997
0.016919000100000001 0.033080998799999997 -0.033080998799999997
0.0170699991 0.016762999800000001 -0.032930001600000001
0.017563000299999999 0.033835999700000001 -0.032232001400000002
0.017697000899999999 0.017697000899999999 -0.034028001099999997
0.017697000899999999 0.0323030017 -0.034028001099999997
0.018123999200000001 0.034775000100000002 -0.031706001599999999
-0.0398919992 0.031725998999999998 -0.018273999900000001
0.032024998200000002 0.034485999500000003 -0.031886998600000001
0.0323030017 0.017697000899999999 -0.034028001099999997
0.032680001100000002 0.0335219987 -0.032370001099999997
0.032930001600000001 0.016762999800000001 -0.032930001600000001
0.033236999099999998 0.0307030007 -0.032930001600000001
0.033309001499999998 0.031690999900000003 -0.032862998499999997
0.033539999299999998 0.032664000999999998 -0.032664000999999998
3 22 6 21
3 21 6 12
3 9 6 7
3 6 5 4
3 16 13 18
3 13 8 20
3 8 7 24
3 7 6 23
3 14 19 10
3 17 0 1
3 3 11 2
3 11 15 10

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,30 @@
OFF
19 7 0
22.597406387329102 -9.2975788116455078 1.4342140275402926e-06
22.583751678466797 -9.3044929504394531 1.8044247553916648e-06
22.70263671875 -9.2384414672851562 1.287946361117065e-06
22.68988037109375 -9.2462024688720703 1.6323538147844374e-06
22.677009582519531 -9.2538528442382812 1.3234302969067357e-06
23.117275238037109 -8.8020000457763672 7.0353144110413268e-07
23.122810363769531 -8.7901668548583984 7.8837638284312561e-07
23.189165115356445 -8.5451927185058594 9.3644030130235478e-07
23.183364868164062 -8.5910682678222656 6.0938327806070447e-07
23.185302734375 -8.5780525207519531 6.0787260736105964e-07
23.184362411499023 -8.5845699310302734 1.4671413737232797e-06
23.191577911376953 -8.5120143890380859 6.0525235312525183e-07
23.191884994506836 -8.5053310394287109 1.3624994608107954e-06
23.167057037353516 -8.6678066253662109 7.1637077780906111e-07
23.188507080078125 -8.5517463684082031 6.0315323935355991e-07
23.192604064941406 -8.4715385437011719 6.3923471316229552e-07
23.167057037353516 -8.6678066253662109 7.1637077780906111e-07
23.192573547363281 -8.4420909881591797 5.9400190366432071e-07
23.192573547363281 -8.4420909881591797 5.9400190366432071e-07
3 9 10 8
3 7 14 13
3 12 11 16
3 3 4 17
3 2 3 17
3 0 1 18
3 15 6 5

View File

@ -0,0 +1,39 @@
OFF
26 9 0
-10.02 0 -2.0080200000000001
-10 10 10
-3.9999799999999999 10.0281 -3.9999799999999999
-9.9999199999999995 10.040100000000001 -9.9999199999999995
-6.0047800000000002 6.0240600000000004 -10.007999999999999
-6.0192600000000001 6.0192600000000001 -6.0192600000000001
-5.9999599999999997 10.0321 -5.9999599999999997
-5.9999700000000002 10.0281 -3.9999799999999999
6.0047899999999998 6.0168400000000002 -4.00319
3.9999899999999999 10.012 3.9999899999999999
-5.9999900000000004 10.012 3.9999899999999999
-6 10 10
-6.0047899999999998 6.0192399999999999 4.00319
-6.0240600000000004 6.0192699999999997 -10.007999999999999
-10.0219 -0.92678099999999997 -0.92678099999999997
6.0048000000000004 6.0072099999999997 4.0031999999999996
-10.007999999999999 6.0240600000000004 -10.007999999999999
-6.0240600000000004 6.0240600000000004 -10.007999999999999
5.9999900000000004 10.007999999999999 -3.9999899999999999
-10.007999999999999 6.0240600000000004 5.0200500000000003
-5.9999599999999997 10.0321 -9.9999400000000005
-4.00319 6.0168400000000002 -4.00319
-6.0047899999999998 6.0192399999999999 -6.0047899999999998
-6.0047899999999998 6.0192399999999999 -4.00319
5.9999900000000004 10.007999999999999 3.9999899999999999
4.0031999999999996 6.0072099999999997 4.0031999999999996
6 23 7 2 18 8 21
3 13 17 5
4 17 4 22 5
6 16 3 20 4 17 13
3 3 6 20
6 22 4 20 6 7 23
6 12 25 15 24 9 10
8 1 11 10 9 2 7 6 3
6 14 1 3 16 19 0

View File

@ -0,0 +1,10 @@
#!/bin/sh
NUM_MAX=${1:-10000}
IDS=$(jq -c "[input_filename, .num_vertices < $NUM_MAX and .solid == 1]" json/* | awk -F'[/.,]' '/,true/ { print $2 }')
pushd solid-max_10k_vertices > /dev/null
for i in $IDS; do
ln -s ../raw_meshes/$i.* ./
done

View File

@ -0,0 +1,67 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polygon_mesh_processing/autorefinement.h>
#include <CGAL/Polygon_mesh_processing/repair_polygon_soup.h>
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
#include <CGAL/Polygon_mesh_processing/triangulate_faces.h>
#include <CGAL/IO/polygon_soup_io.h>
#include <CGAL/make_conforming_constrained_Delaunay_triangulation_3.h>
#include <CGAL/Real_timer.h>
#include <boost/container/small_vector.hpp>
#include <CGAL/Polygon_mesh_processing/repair.h>
#include <CGAL/Surface_mesh.h>
#include <iostream>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef Kernel::Point_3 Point;
namespace PMP = CGAL::Polygon_mesh_processing;
int main(int argc, char** argv)
{
const std::string filename = argc == 1 ? CGAL::data_file_path("meshes/elephant.off")
: std::string(argv[1]);
std::vector<Point> input_points;
std::vector<boost::container::small_vector<std::size_t, 3>> input_triangles;
if (!CGAL::IO::read_polygon_soup(filename, input_points, input_triangles))
{
std::cerr << "Cannot read " << filename << "\n";
return 1;
}
PMP::repair_polygon_soup(input_points, input_triangles);
PMP::triangulate_polygons(input_points, input_triangles);
CGAL::Real_timer t;
t.start();
PMP::autorefine_triangle_soup(input_points, input_triangles,
CGAL::parameters::concurrency_tag(CGAL::Parallel_if_available_tag())
/* .apply_iterative_snap_rounding(true) */);
t.stop();
std::cout << "#points = " << input_points.size() << " and #triangles = " << input_triangles.size() << " in " << t.time() << " sec." << std::endl;
// CGAL::IO::write_polygon_soup("autorefined.off", input_points, input_triangles, CGAL::parameters::stream_precision(17));
#if 1
PMP::orient_polygon_soup(input_points, input_triangles);
CGAL::Surface_mesh<Kernel::Point_3> mesh;
PMP::polygon_soup_to_polygon_mesh(input_points, input_triangles, mesh);
PMP::remove_almost_degenerate_faces(mesh);
input_points.clear();
input_triangles.clear();
PMP::polygon_mesh_to_polygon_soup(mesh, input_points, input_triangles);
#endif
t.reset();
t.start();
auto ccdt = CGAL::make_conforming_constrained_Delaunay_triangulation_3(input_points, input_triangles);\
t.stop();
std::cout << "call to CDT3 done in " << t.time() << " sec." << std::endl;
return ccdt.triangulation().dimension() == 3 ? 0 : 1;
}

View File

@ -0,0 +1,37 @@
#include <CGAL/make_conforming_constrained_Delaunay_triangulation_3.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/boost/graph/Euler_operations.h>
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
using Point = K::Point_3;
using PolygonMesh = CGAL::Surface_mesh<Point>;
using vertex_descriptor = PolygonMesh::Vertex_index;
int main()
{
const std::array<Point, 4> points{ {
{0., 0., 0.},
{1., 0., 0.},
{1., 1., 0.},
{0., 1., 0.}
} };
const std::array<std::array<std::size_t, 4>, 1> polygons{ {
{0, 1, 2, 3}
} };
std::array<vertex_descriptor, 4> vertices;
PolygonMesh mesh;
std::transform(points.begin(), points.end(), vertices.begin(),
[&mesh](const Point& p) { return mesh.add_vertex(p); });
[[maybe_unused]] auto fd = CGAL::Euler::add_face(vertices, mesh);
assert(CGAL::is_valid_polygon_mesh(mesh));
auto cdt = CGAL::make_conforming_constrained_Delaunay_triangulation_3(mesh);
assert(cdt.is_valid());
assert(cdt.constrained_facets().size() == 2);
cdt = CGAL::make_conforming_constrained_Delaunay_triangulation_3(points, polygons);
assert(cdt.is_valid());
assert(cdt.constrained_facets().size() == 2);
}

View File

@ -0,0 +1,69 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Tetrahedral_remeshing/Remeshing_vertex_base_3.h>
#include <CGAL/Tetrahedral_remeshing/Remeshing_cell_base_3.h>
#include <CGAL/Conforming_constrained_Delaunay_triangulation_vertex_base_3.h>
#include <CGAL/Conforming_constrained_Delaunay_triangulation_cell_base_3.h>
#include <CGAL/make_conforming_constrained_Delaunay_triangulation_3.h>
#include <CGAL/Conforming_constrained_Delaunay_triangulation_3.h>
#include <CGAL/tetrahedral_remeshing.h>
#include <algorithm>
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
using Vbb = CGAL::Tetrahedral_remeshing::Remeshing_vertex_base_3<K>;
using Vb = CGAL::Conforming_constrained_Delaunay_triangulation_vertex_base_3<K, Vbb>;
using Cbb = CGAL::Tetrahedral_remeshing::Remeshing_cell_base_3<K>;
using Cb = CGAL::Conforming_constrained_Delaunay_triangulation_cell_base_3<K, Cbb>;
using Tds = CGAL::Triangulation_data_structure_3<Vb, Cb>;
using Tr = CGAL::Triangulation_3<K, Tds>;
using CDT = CGAL::Conforming_constrained_Delaunay_triangulation_3<K, Tr>;
int main(int argc, char* argv[])
{
CGAL::Surface_mesh<K::Point_3> mesh;
auto filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/mpi.off");
std::ifstream in(filename);
if(!in || !CGAL::IO::read_OFF(in, mesh)) {
std::cerr << "Error: cannot read file " << filename << std::endl;
return EXIT_FAILURE;
}
std::cout << "Read " << mesh.number_of_vertices() << " vertices and "
<< mesh.number_of_faces() << " faces" << std::endl;
auto cdt = CGAL::make_conforming_constrained_Delaunay_triangulation_3<CDT>(mesh);
static_assert(std::is_same_v<decltype(cdt), CDT>);
CDT cdt2(mesh);
const auto nb_cstr_facets = cdt2.number_of_constrained_facets();
assert(cdt.triangulation().number_of_vertices() == cdt2.triangulation().number_of_vertices());
assert(cdt.number_of_constrained_facets() == cdt2.number_of_constrained_facets());
assert(cdt.number_of_constrained_facets() > mesh.num_faces());
namespace Tet_remesh = CGAL::Tetrahedral_remeshing;
Tr tr = Tet_remesh::get_remeshing_triangulation(std::move(cdt));
CGAL::tetrahedral_isotropic_remeshing(tr, 2.,
CGAL::parameters::number_of_iterations(3)
.remesh_boundaries(false));
std::cout << "Number of vertices in tr: " << tr.number_of_vertices() << std::endl;
auto nb = 0u;
for(auto f : tr.finite_facets())
{
const Tr::Cell_handle c = f.first;
const int i = f.second;
if(c->is_facet_on_surface(i))
++nb;
}
assert(nb == nb_cstr_facets);
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,47 @@
#include <CGAL/make_conforming_constrained_Delaunay_triangulation_3.h>
#include <CGAL/Conforming_constrained_Delaunay_triangulation_3.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <algorithm>
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
using CDT = CGAL::Conforming_constrained_Delaunay_triangulation_3<K>;
static_assert(CGAL::cdt_3_msvc_2019_or_older() || CGAL::is_nothrow_movable_v<CDT>);
int main(int argc, char* argv[])
{
CGAL::Surface_mesh<K::Point_3> mesh;
auto filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/mpi.off");
std::ifstream in(filename);
if(!in || !CGAL::IO::read_OFF(in, mesh)) {
std::cerr << "Error: cannot read file " << filename << std::endl;
return EXIT_FAILURE;
}
std::cout << "Read " << mesh.number_of_vertices() << " vertices and "
<< mesh.number_of_faces() << " faces" << std::endl;
auto cdt = CGAL::make_conforming_constrained_Delaunay_triangulation_3<CDT>(mesh);
static_assert(std::is_same_v<decltype(cdt), CDT>);
CDT cdt2(mesh);
const auto nb_cstr_facets = cdt2.number_of_constrained_facets();
assert(cdt.triangulation().number_of_vertices() == cdt2.triangulation().number_of_vertices());
assert(cdt.number_of_constrained_facets() == cdt2.number_of_constrained_facets());
assert(cdt.number_of_constrained_facets() > mesh.num_faces());
auto tr = std::move(cdt).triangulation();
assert(0 == cdt.triangulation().number_of_vertices());
assert(tr.number_of_vertices() == cdt2.triangulation().number_of_vertices());
std::size_t nb = 0;
for([[maybe_unused]] auto _ : cdt2.constrained_facets()) {
++nb;
}
assert(nb == nb_cstr_facets);
int dist = static_cast<int>(std::distance(cdt2.constrained_facets_begin(),
cdt2.constrained_facets_end()));
assert(dist >= 0);
assert(dist == static_cast<int>(nb));
}

Some files were not shown because too many files have changed in this diff Show More