Kinetic Shape Partition (#7198)

PR for Kinetic Partitioning and Reconstruction feature.

* Affected package(s): Kinetic Partitioning and Reconstruction
* Issue(s) solved (if any): 
* Feature/Small Feature (if any):
[link](https://cgal.geometryfactory.com/CGAL/Members/wiki/Features/Kinetic_Shape_Partition_3)
* Link to compiled documentation:
[link](https://cgal.github.io/7198/v0/Manual/packages.html#PkgKineticSpacePartition)
* License and copyright ownership:  GeometryFactory/Inria

**TODO:**
- [x] check branch size (for @sloriot)
This commit is contained in:
Sebastien Loriot 2024-05-26 17:51:49 +02:00 committed by GitHub
commit 596fa09e20
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
117 changed files with 14086 additions and 9 deletions

View File

@ -566,6 +566,7 @@ double alpha_expansion_graphcut (const InputGraph& input_graph,
std::size_t number_of_labels = get(vertex_label_cost_map, *(vertices(input_graph).first)).size();
bool success;
bool full_loop = false;
do {
success = false;
@ -644,6 +645,8 @@ double alpha_expansion_graphcut (const InputGraph& input_graph,
#ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT
cut_time += timer.time();
#endif
if (full_loop && flow >= min_cut)
continue;
if(min_cut - flow <= flow * tolerance) {
continue;
@ -656,18 +659,158 @@ double alpha_expansion_graphcut (const InputGraph& input_graph,
std::size_t vertex_i = get (vertex_index_map, vd);
alpha_expansion.update(vertex_label_map, inserted_vertices, vd, vertex_i, alpha);
}
}
} while(success);
}
full_loop = true;
} while (success);
#ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT
CGAL_TRACE_STREAM << "vertex creation time: " << vertex_creation_time <<
std::endl;
CGAL_TRACE_STREAM << "edge creation time: " << edge_creation_time << std::endl;
CGAL_TRACE_STREAM << "max flow algorithm time: " << cut_time << std::endl;
CGAL_TRACE_STREAM << "vertex creation time: " << vertex_creation_time <<
std::endl;
CGAL_TRACE_STREAM << "edge creation time: " << edge_creation_time << std::endl;
CGAL_TRACE_STREAM << "max flow algorithm time: " << cut_time << std::endl;
#endif
return min_cut;
}
return min_cut;
}
template <typename InputGraph,
typename EdgeCostMap,
typename VertexLabelCostMap,
typename VertexLabelMap,
typename NamedParameters>
double min_cut(const InputGraph& input_graph,
EdgeCostMap edge_cost_map,
VertexLabelCostMap vertex_label_cost_map,
VertexLabelMap vertex_label_map,
const NamedParameters& np)
{
using parameters::choose_parameter;
using parameters::get_parameter;
typedef boost::graph_traits<InputGraph> GT;
typedef typename GT::edge_descriptor input_edge_descriptor;
typedef typename GT::vertex_descriptor input_vertex_descriptor;
typedef typename GetInitializedVertexIndexMap<InputGraph, NamedParameters>::type VertexIndexMap;
VertexIndexMap vertex_index_map = CGAL::get_initialized_vertex_index_map(input_graph, np);
typedef typename GetImplementationTag<NamedParameters>::type Impl_tag;
// select implementation
typedef typename std::conditional
<std::is_same<Impl_tag, Alpha_expansion_boost_adjacency_list_tag>::value,
Alpha_expansion_boost_adjacency_list_impl,
typename std::conditional
<std::is_same<Impl_tag, Alpha_expansion_boost_compressed_sparse_row_tag>::value,
Alpha_expansion_boost_compressed_sparse_row_impl,
Alpha_expansion_MaxFlow_impl>::type>::type
Alpha_expansion;
typedef typename Alpha_expansion::Vertex_descriptor Vertex_descriptor;
Alpha_expansion graph;
double min_cut = (std::numeric_limits<double>::max)();
#ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT
double vertex_creation_time, edge_creation_time, cut_time;
vertex_creation_time = edge_creation_time = cut_time = 0.0;
#endif
std::vector<Vertex_descriptor> inserted_vertices;
inserted_vertices.resize(num_vertices(input_graph));
graph.clear_graph();
#ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT
Timer timer;
timer.start();
#endif
// For E-Data
// add every input vertex as a vertex to the graph, put edges to source & sink vertices
for (input_vertex_descriptor vd : CGAL::make_range(vertices(input_graph)))
{
std::size_t vertex_i = get(vertex_index_map, vd);
Vertex_descriptor new_vertex = graph.add_vertex();
inserted_vertices[vertex_i] = new_vertex;
double source_weight = get(vertex_label_cost_map, vd)[0];
double sink_weight = get(vertex_label_cost_map, vd)[0];
graph.add_tweight(new_vertex, source_weight, sink_weight);
}
#ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT
vertex_creation_time += timer.time();
timer.reset();
#endif
// For E-Smooth
// add edge between every vertex,
for (input_edge_descriptor ed : CGAL::make_range(edges(input_graph)))
{
input_vertex_descriptor vd1 = source(ed, input_graph);
input_vertex_descriptor vd2 = target(ed, input_graph);
std::size_t idx1 = get(vertex_index_map, vd1);
std::size_t idx2 = get(vertex_index_map, vd2);
double weight = get(edge_cost_map, ed);
Vertex_descriptor v1 = inserted_vertices[idx1],
v2 = inserted_vertices[idx2];
graph.add_edge(v1, v2, weight, weight);
}
#ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT
edge_creation_time += timer.time();
#endif
graph.init_vertices();
#ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT
timer.reset();
#endif
min_cut = graph.max_flow();
#ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT
cut_time += timer.time();
#endif
/*
//update labeling
for (input_vertex_descriptor vd : CGAL::make_range(vertices(input_graph)))
{
std::size_t vertex_i = get(vertex_index_map, vd);
alpha_expansion.update(vertex_label_map, inserted_vertices, vd, vertex_i, alpha);
}*/
graph.get_labels(vertex_index_map, vertex_label_map, inserted_vertices, CGAL::make_range(vertices(input_graph)));
/*
//update labeling
for (auto vd : vertices(input_graph)) {
std::size_t idx = get(vertex_index_map, vd);
int label = graph.get_label(inserted_vertices[idx]);
put(vertex_label_map, vd, label);
}
for (input_vertex_descriptor vd : CGAL::make_range(vertices(input_graph)))
{
std::size_t vertex_i = get(vertex_index_map, vd);
graph.update(vertex_label_map, inserted_vertices, vd, vertex_i, alpha);
}*/
#ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT
CGAL_TRACE_STREAM << "vertex creation time: " << vertex_creation_time <<
std::endl;
CGAL_TRACE_STREAM << "edge creation time: " << edge_creation_time << std::endl;
CGAL_TRACE_STREAM << "max flow algorithm time: " << cut_time << std::endl;
#endif
return min_cut;
}
/// \cond SKIP_IN_MANUAL
@ -701,6 +844,23 @@ double alpha_expansion_graphcut (const std::vector<std::pair<std::size_t, std::s
CGAL::parameters::vertex_index_map (graph.vertex_index_map()).
implementation_tag (AlphaExpansionImplementationTag()));
}
template <typename AlphaExpansionImplementationTag>
double min_cut(const std::vector<std::pair<std::size_t, std::size_t> >& edges,
const std::vector<double>& edge_costs,
const std::vector<std::vector<double> >& cost_matrix,
std::vector<std::size_t>& labels,
const AlphaExpansionImplementationTag&)
{
internal::Alpha_expansion_old_API_wrapper_graph graph(edges, edge_costs, cost_matrix, labels);
return min_cut(graph,
graph.edge_cost_map(),
graph.vertex_label_cost_map(),
graph.vertex_label_map(),
CGAL::parameters::vertex_index_map(graph.vertex_index_map()).
implementation_tag(AlphaExpansionImplementationTag()));
}
/// \endcond
}//namespace CGAL

View File

@ -29,6 +29,18 @@ endif()
find_package(Doxygen REQUIRED)
find_package(Python3 REQUIRED COMPONENTS Interpreter)
if (NOT Python3_EXECUTABLE)
message(FATAL_ERROR "Cannot build the documentation without Python3!")
return()
endif()
message(STATUS ${Python3_EXECUTABLE})
if(NOT DOXYGEN_FOUND)
message(WARNING "Cannot build the documentation without Doxygen!")
return()
endif()
#starting from cmake 3.9 the usage of DOXYGEN_EXECUTABLE is deprecated
if(TARGET Doxygen::doxygen)
get_property(

View File

@ -103,6 +103,7 @@
\package_listing{Scale_space_reconstruction_3}
\package_listing{Advancing_front_surface_reconstruction}
\package_listing{Polygonal_surface_reconstruction}
\package_listing{Kinetic_space_partition}
\package_listing{Optimal_transportation_reconstruction_2}
\cgalPackageSection{PartGeometryProcessing,Geometry Processing}

View File

@ -151974,6 +151974,25 @@ pages = {179--189}
organization={Wiley Online Library}
}
@article{bauchet2020kinetic,
author = {Bauchet, Jean-Philippe and Lafarge, Florent},
title = {Kinetic Shape Reconstruction},
journal = "ACM Transactions on Graphics",
year = {2020},
issue_date = {October 2020},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
volume = {39},
number = {5},
issn = {0730-0301},
url = {https://doi.org/10.1145/3376918},
doi = {10.1145/3376918},
month = {jun},
articleno = {156},
numpages = {14},
keywords = {polygonal surface mesh, Surface reconstruction, kinetic framework, surface approximation}
}
@article{levismooth,
title={Smooth Rotation Enhanced As-Rigid-As-Possible Mesh Animation},
author={Levi, Zohar and Gotsman, Craig},

View File

@ -0,0 +1,54 @@
// Copyright (c) 2016 GeometryFactory SARL (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org)
//
// $URL$
// $Id$
// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s) : Andreas Fabri
//
// Warning: this file is generated, see include/CGAL/license/README.md
#ifndef CGAL_LICENSE_KINETIC_SPACE_PARTITION_H
#define CGAL_LICENSE_KINETIC_SPACE_PARTITION_H
#include <CGAL/config.h>
#include <CGAL/license.h>
#ifdef CGAL_KINETIC_SPACE_PARTITION_COMMERCIAL_LICENSE
# if CGAL_KINETIC_SPACE_PARTITION_COMMERCIAL_LICENSE < CGAL_RELEASE_DATE
# if defined(CGAL_LICENSE_WARNING)
CGAL_pragma_warning("Your commercial license for CGAL does not cover "
"this release of the Kinetic Space Partition package.")
# endif
# ifdef CGAL_LICENSE_ERROR
# error "Your commercial license for CGAL does not cover this release \
of the Kinetic Space Partition package. \
You get this error, as you defined CGAL_LICENSE_ERROR."
# endif // CGAL_LICENSE_ERROR
# endif // CGAL_KINETIC_SPACE_PARTITION_COMMERCIAL_LICENSE < CGAL_RELEASE_DATE
#else // no CGAL_KINETIC_SPACE_PARTITION_COMMERCIAL_LICENSE
# if defined(CGAL_LICENSE_WARNING)
CGAL_pragma_warning("\nThe macro CGAL_KINETIC_SPACE_PARTITION_COMMERCIAL_LICENSE is not defined."
"\nYou use the CGAL Kinetic Space Partition package under "
"the terms of the GPLv3+.")
# endif // CGAL_LICENSE_WARNING
# ifdef CGAL_LICENSE_ERROR
# error "The macro CGAL_KINETIC_SPACE_PARTITION_COMMERCIAL_LICENSE is not defined.\
You use the CGAL Kinetic Space Partition package under the terms of \
the GPLv3+. You get this error, as you defined CGAL_LICENSE_ERROR."
# endif // CGAL_LICENSE_ERROR
#endif // no CGAL_KINETIC_SPACE_PARTITION_COMMERCIAL_LICENSE
#endif // CGAL_LICENSE_KINETIC_SPACE_PARTITION_H

View File

@ -26,6 +26,7 @@ Inscribed_areas Inscribed Areas
Interpolation 2D and Surface Function Interpolation
Interval_skip_list Interval Skip List
Jet_fitting_3 Estimation of Local Differential Properties of Point-Sampled Surfaces
Kinetic_space_partition Kinetic Space Partition
Matrix_search Monotone and Sorted Matrix Search
Mesh_2 2D Conforming Triangulations and Meshes
Mesh_3 3D Mesh Generation

View File

@ -0,0 +1,39 @@
// Copyright (c) 2023 GeometryFactory Sarl (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s) : Sven Oesau, Florent Lafarge, Dmitry Anisimov, Simon Giraudot
/*!
\ingroup PkgKineticSpacePartitionConcepts
\cgalConcept
The concept `KineticLCCFaceAttribute` refines `CellAttribute` to store additional information for each face related to its associated input polygon.
\cgalHasModelsBegin
\cgalHasModelsBare{\link CGAL::Cell_attribute `Cell_attribute<LCC, CGAL::Kinetic_space_partition_3::Linear_cell_complex_min_items::Face_attribute>`\endlink}
\cgalHasModelsEnd
\sa `CGAL::Kinetic_space_partition_3`
\sa `LinearCellComplexItems`
*/
struct KineticLCCFaceAttribute {
/// \name Access Members
/// @{
/// 3D plane type compatible with `Kinetic_space_partition_3::Intersection_kernel`
typedef unspecified_type Plane_3;
/// Stores the index of the input polygon the provided that support plane of this face. Negative numbers correspond to the values defined in the enum `Kinetic_space_partition_3::Face_support`.
int input_polygon_index;
/// Support plane of the face derived from the corresponding input polygon or from octree nodes used for subdivision.
Plane_3 plane;
/// Does this face overlap with the corresponding input polygon.
bool part_of_initial_polygon;
/// @}
};

View File

@ -0,0 +1,40 @@
// Copyright (c) 2023 GeometryFactory Sarl (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s) : Sven Oesau, Florent Lafarge, Dmitry Anisimov, Simon Giraudot
/*!
\ingroup PkgKineticSpacePartitionConcepts
\cgalConcept
The concept `KineticLCCItems` refines the concept of `LinearCellComplexItems` by adding 2-attributes and 3-attributes to store information from the kinetic partition.
The first type in Attributes tuple must be a model of the `CellAttributeWithPoint` concept.
The third type in Attributes tuple must be a model of the `KineticLCCFaceAttribute` concept.
The fourth type in Attributes tuple must be a model of the `KineticLCCVolumeAttribute` concept.
\cgalHasModelsBegin
\cgalHasModelsBare{`CGAL::Kinetic_space_partition_3::Linear_cell_complex_min_items`}
\cgalHasModelsEnd
\sa `CGAL::Kinetic_space_partition_3`
\sa `KineticLCCFaceAttribute`
\sa `KineticLCCVolumeAttribute`
\sa `LinearCellComplexItems`
*/
struct KineticLCCItems {
/// \name Types
/// @{
/// Using the index-based version of the `LinearCellComplex` concept.
typedef CGAL::Tag_true Use_index;
typedef std::uint32_t Index_type;
/// @}
};

View File

@ -0,0 +1,37 @@
// Copyright (c) 2023 GeometryFactory Sarl (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s) : Sven Oesau, Florent Lafarge, Dmitry Anisimov, Simon Giraudot
/*!
\ingroup PkgKineticSpacePartitionConcepts
\cgalConcept
The concept `KineticLCCVolumeAttribute` refines `CellAttribute` to store the barycenter and an id.
\cgalHasModelsBegin
\cgalHasModelsBare{\link CGAL::Cell_attribute `Cell_attribute<LCC, CGAL::Kinetic_space_partition_3::Linear_cell_complex_min_items::Volume_attribute>`\endlink}
\cgalHasModelsEnd
\sa `CGAL::Kinetic_space_partition_3`
\sa `LinearCellComplexItems`
*/
struct KineticLCCVolumeAttribute {
/// \name Access Members
/// @{
/// 3D point type compatible with `Kinetic_space_partition_3::Intersection_kernel`
typedef unspecified_type Point_3;
/// Contains the barycenter of the volume.
Point_3 barycenter;
/// 0-based volume id.
std::size_t volume_id;
/// @}
};

View File

@ -0,0 +1,253 @@
/*!
\ingroup PkgKineticSpacePartitionConcepts
\cgalConcept
A concept that describes the set of types required by the `CGAL::Kinetic_space_partition_3`.
\cgalHasModelsBegin
\cgalHasModelsBare{All models of the concept `Kernel`}
\cgalHasModelsEnd
\sa `CGAL::Kinetic_space_partition_3`
*/
class KineticSpacePartitionTraits_3 {
public:
/// \name Types
/// @{
/// The 3D point type
typedef unspecified_type Point_3;
/// The 2D point type
typedef unspecified_type Point_2;
/// The 3D vector type
typedef unspecified_type Vector_3;
/// The 2D line type
typedef unspecified_type Line_2;
/// The 2D direction type
typedef unspecified_type Direction_2;
/// The plane type
typedef unspecified_type Plane_3;
/// The 2D vector type
typedef unspecified_type Vector_2;
/// The 2D segment type
typedef unspecified_type Segment_2;
/// The 3d tetrahedron type
typedef unspecified_type Tetrahedron_3;
/// The 3d transformation type
typedef unspecified_type Transform_3;
/// The number type of the Cartesian coordinates
typedef unspecified_type FT;
/*!
* Function object type that provides
* `Point_3 operator()(Origin p)`
* returning the point with 0, 0, 0 as Cartesian coordinates
* and `Point_3 operator()(FT x, FT y, FT z)`
* returning the point with `x`, `y` and `z` as Cartesian coordinates.
*/
typedef unspecified_type Construct_point_3;
/*!
* Function object type that provides
* `Vector_3 operator()(Point_3 p1, Point_3 p2)` and
* `Vector_3 operator()(Origin p1, Point_3 p2)`
* returning the vector `p1p2`, `Vector_3 operator()(NULL_VECTOR)` returning
* the null vector, and `Vector_3 operator()(Line_3 l)` returning
* a vector having the same direction as `l`
*/
typedef unspecified_type Construct_vector_3;
/*!
* Function object type that provides `Line_2 operator()(Point_2 p, Vector_2 d)`
* returning the line going through `p` in the direction of `d`.
*/
typedef unspecified_type Construct_line_2;
/*!
* Function object type that provides
* `Point_2 operator()(Line_2 l, int i)`
* returning an arbitrary point on `l`. `i` is not used and can be of
* any value.
*/
typedef unspecified_type Construct_point_on_2;
/*!
* Function object type that provides
* `Point_2 operator()(FT x, FT y)`
* returning the 2D point with `x` and `y` as Cartesian coordinates.
*/
typedef unspecified_type Construct_point_2;
/*!
* Function object type that provides
* `Vector_2 operator()(Point_2 p1, Point_2 p2)`
* returning the vector `p1p2`, `Vector_2 operator()(NULL_VECTOR)` returning
* the null vector.
*/
typedef unspecified_type Construct_vector_2;
/*!
* Function object type that provides
* `Tetrahedron_3 operator(Point_3 p, Point_3 q, Point_3 r, Point_3 s)`
* returning the tetrahedron with the points `p`, `q`, `r` and `s`.
*/
typedef unspecified_type ConstructTetrahedron_3;
/*!
* Function object type that provides
* `FT operator()(Point_3 p)` and `FT operator()(Vector_3 v)`
* returning the `x` coordinate of a point and a vector respectively.
*/
typedef unspecified_type Compute_x_3;
/*!
* Function object type that provides
* `FT operator()(Point_3 p)` and `FT operator()(Vector_3 v)`
* returning the `y` coordinate of a point and a vector respectively.
*/
typedef unspecified_type Compute_y_3;
/*!
* Function object type that provides
* `FT operator()(Point_3 p)` and `FT operator()(Vector_3 v)`
* returning the `z` coordinate of a point and a vector respectively.
*/
typedef unspecified_type Compute_z_3;
/*!
* Function object type that provides
* `FT operator()(Point_2 p)` and `FT operator()(Vector_2 v)`
* returning the `x` coordinate of a point and a vector respectively.
*/
typedef unspecified_type Compute_x_2;
/*!
* Function object type that provides
* `FT operator()(Point_2 p)` and `FT operator()(Vector_2 v)`
* returning the `y` coordinate of a point and a vector respectively.
*/
typedef unspecified_type Compute_y_2;
/*!
* Function object type that provides
* `FT operator()(Vector_2 v)`
* returning the squared length of `v`.
*/
typedef unspecified_type Compute_squared_length_2;
/*!
* Function object type that provides
* `FT operator()(Vector_3 v1, Vector_3 v2)`
* returning the scalar product of `v1` and `v2`.
*/
typedef unspecified_type Compute_scalar_product_3;
/*!
* Function object type that provides
* `Vector_3 operator() (Vector_3 v1, Vector_3 v2)`
* returning the `v1+v2`.
*/
typedef unspecified_type Construct_sum_of_vectors_3;
/*!
* Function object type that provides
* `Vector_3 operator()(Plane_3 p)`
* returns a vector that is orthogonal to the plane `p` and directed to the positive side of `p`.
*/
typedef unspecified_type Construct_orthogonal_vector_3;
/*!
* Function object type that provides
* `Plane_3 operator()(Point_3 p, Point_3 q, Point_3 r)`
* creates a plane passing through the points `p`, `q` and `r` and
* `Plane_3 operator()(Point_3 p, Vector_3 v)`
* introduces a plane that passes through point `p` and that is orthogonal to `V`.
*/
typedef unspecified_type Construct_plane_3;
/*!
* Function object type that provides
* `Vector_3 operator()(Plane_3 h, Point_3 p)
* returns the orthogonal projection of `p` onto `h`.
*/
typedef unspecified_type Construct_projected_point_3;
/*!
* Function object type that provides
* `bool operator()(Point_3 p, Point_3 q, Point_3 r)`
* returning true if the points `p`, `q`, and `r` are collinear and
* false otherwise.
*/
typedef unspecified_type Collinear_3;
/*!
* Function object type that provides
* `Oriented_size operator()(Plane_3 h, Point_3 p)`
* returns `CGAL::ON_ORIENTED_BOUNDARY`, `CGAL::ON_NEGATIVE_SIDE`, or `CGAL::ON_POSITIVE_SIDE`, depending on the position of `p` relative to the oriented plane `h`.
*/
typedef unspecified_type Oriented_side_3;
/// @}
/// \name Access to Function Objects
/// @{
Construct_point_3
construct_point_3_object();
Construct_vector_3
construct_vector_3_object();
Construct_line_2
construct_line_2_object();
Construct_point_on_3
construct_point_on_3_object();
Construct_point_2
construct_point_2_object();
Construct_vector_2
construct_vector_2_object();
Construct_tetrahedron_3
construct_tetrahedron_3_object();
Compute_x_3
compute_x_3_object();
Compute_y_3
compute_y_3_object();
Compute_z_3
compute_z_3_object();
Compute_x_2
compute_x_2_object();
Compute_y_2
compute_y_2_object();
Compute_squared_length_2
compute_squared_length_2_object();
Construct_sum_of_vectors_3
construct_sum_of_vectors_3_object();
Construct_projected_point_3
construct_projected_point_3_object();
Compute_scalar_product_3
compute_scalar_product_3_object();
Collinear_3
collinear_3_object();
Oriented_side_3
oriented_side_3_object();
/// @}
};

View File

@ -0,0 +1,7 @@
@INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS}
PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - Kinetic Space Partition"
EXTRACT_ALL = NO
HIDE_UNDOC_CLASSES = YES
WARN_IF_UNDOCUMENTED = NO
PREDEFINED += DOXYGEN_RUNNING

View File

@ -0,0 +1,71 @@
namespace CGAL {
/*!
\mainpage User Manual
\anchor Chapter_Kinetic_Space_Partition
\cgalAutoToc
\authors Sven Oesau and Florent Lafarge
\section Ksp_introduction Introduction
This \cgal component implements the kinetic space partition proposed by Bauchet et. al \cgalCite{bauchet2020kinetic}. It takes as input a set of non-coplanar convex polygons and partitions the bounding box of the input into polyhedra, where each polyhedron and its facets are convex. Each facet of the partition is part of the input polygons or an extension of them.
The kinetic partition homogeneously expands each input polygon along its support plane until collisions occur between them. At each collision, their expansion may stop, they may restrict the propagation along an intersection line between two support planes or they may continue beyond the intersection line. The polygons are split at the beginning of the kinetic simulation and at each collision along the intersection line to be intersection-free.
Whether a polygon is expanded beyond an intersection with another polygon, depends on the user specified `k` parameter. Choosing `k = 1` will cause the expansion of polygons to locally stop at the first intersection with another polygon and choosing `k` equal to the number of input polygons will lead to a full expansion of each polygon to the bounding box.
\section Ksp_algorithm Algorithm
The first step of the method creates a plane arrangement between the support planes of the input polygons. The computation of a plane arrangement has \cgalBigO{n^3}. To speed up the computation the input data can be decomposed into separate kinetic partitions using an octree. The decomposition limits the expansion of an input polygon to octree nodes it initially intersects. The usage of an octree significantly speeds up the creation of the kinetic partition. However, it adds facets to the partition which originate from the octree and do not belong to an input polygon.
The plane arrangement contains all points and lines of the intersections between the support planes and the bounding box. For each support plane, all intersections with the bounding box and other support planes are given by lines, edges and vertices of the arrangement. The kinetic partition created in the second step is a subset of faces of the arrangement depending on the `k` parameter.
\cgalFigureBegin{Ksp_introductionfig,intersection_graph.png}
Plane arrangement.\n
Left: 4 convex polygons as input. Right: plane arrangement and bounding box together with input.
\cgalFigureEnd
The kinetic partition for a chosen `k` is obtained by propagating each polygon within its support plane. As intersections with other polygons can only occur at the known edges in the plane arrangement, the 3D collision problem can be solved as separate 2D polygon edge collisions.
\cgalFigureBegin{Ksp_algorithmfig,k_variation.png}
Impact of `k` parameter on partition.\n
Left: Arrangement with 4 input polygons. Right: three columns with propagated polygons on top and volumes of kinetic partition on bottom for `k` = 1, `k` = 2 and `k` = 3 from left to right with 5, 8 and 12 created volumes respectively.
\cgalFigureEnd
\subsection Ksp_parameters Parameters
The algorithm has five parameters:
- `k`: unsigned int\n
The main parameter of this method is `k`, the maximum number of intersections that can occur for a polygon before its expansion stops. The initial intersections of the original input polygons are not considered. Thus increasing the `k` leads to a higher complexity of the partitioning, i.e., a higher number of facets and a higher number of volumes. For a certain `k` the partition can be considered to be complete and an increase in `k` will not further increase the complexity of the partition. A typical choice of `k` is in the range of 1 to 3.
- `reorient_bbox`: boolean\n
The default bounding box of the partition is axis-aligned. Setting `reorient_bbox` to true aligns the x-axis of the bounding box with the direction of the largest variation in horizontal direction of the input data while maintaining the z-axis.
- `bbox_dilation_ratio`: FT\n
By default the size bounding box of the input data is increased by 10% to avoid that input polygons are coplanar with the sides of the bounding box.
- `max_octree_node_size`: unsigned int\n
A kinetic partition is split into 8 subpartitions using an octree if the number of intersecting polygons is larger than specified. The default value is 40 polygons.
- `max_octree_depth`: unsigned int\n
Limits the maximum depth of the octree decomposition. A limitation is necessary as arbitrary dense polygon configurations exist, e.g., a star. The default value is set to 3.
\section Ksp_result Result
The kinetic partition can be accessed as a `LinearCellComplex` via `CGAL::Kinetic_space_partition_3::get_linear_cell_complex()`.
\subsection Ksp_examples Examples
The following example reads a set of polygons from a file and creates a kinetic partition. Increasing the `k` parameter to 2 or 3 leads to a more detailed kinetic partition.
\cgalExample{Kinetic_space_partition/kinetic_partition.cpp}
\section Ksp_history Design and Implementation History
This package is an implementation of Bauchet et. al \cgalCite{bauchet2020kinetic} with an octree replacing the grid subdivision.
A proof of concept of the kinetic partition was developed by Simon Giraudot and Dmitry Anisimov.
*/
} /* namespace CGAL */

View File

@ -0,0 +1,39 @@
/*!
\defgroup PkgKineticSpacePartitionRef Kinetic Space Partition Reference
\defgroup PkgKineticSpacePartitionConcepts Concepts
\ingroup PkgKineticSpacePartitionRef
\addtogroup PkgKineticSpacePartitionRef
\cgalPkgDescriptionBegin{Kinetic Space Partition, PkgKineticSpacePartition}
\cgalPkgPicture{kinetic_logo.png}
\cgalPkgSummaryBegin
\cgalPkgAuthors{Sven Oesau and Florent Lafarge}
\cgalPkgDesc{This package implements kinetic space partition. Based on a set of planar input shapes the bounding box of the input data is split into convex volumes. The complexity of the partition can be adjusted with a single parameter.}
\cgalPkgManuals{Chapter_Kinetic_Space_Partition, PkgKineticSpacePartitionRef}
\cgalPkgSummaryEnd
\cgalPkgShortInfoBegin
\cgalPkgSince{6.0}
\cgalPkgDependsOn{\ref PkgSurfaceMesh, \ref PkgLinearCellComplex}
\cgalPkgBib{cgal:ol-kinetic}
\cgalPkgLicense{\ref licensesGPL "GPL"}
\cgalPkgShortInfoEnd
\cgalPkgDescriptionEnd
\cgalClassifedRefPages
\cgalCRPSection{Concepts}
- `KineticSpacePartitionTraits_3`
- `KineticLCCItems`
- `KineticLCCFaceAttribute`
- `KineticLCCVolumeAttribute`
\cgalCRPSection{Classes}
- `CGAL::Kinetic_space_partition_3`
*/

View File

@ -0,0 +1,9 @@
Manual
Kernel_23
BGL
Circulator
Linear_cell_complex
Combinatorial_map
Surface_mesh
Property_map
Polygon_mesh_processing

View File

@ -0,0 +1,3 @@
/*!
\example Kinetic_space_partition/kinetic_partition.cpp
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,24 @@
# Created by the script cgal_create_CMakeLists.
# This is the CMake script for compiling a set of CGAL applications.
cmake_minimum_required(VERSION 3.1...3.23)
project(Kinetic_space_partition_Examples)
find_package(CGAL REQUIRED)
include(CGAL_CreateSingleSourceCGALProgram)
find_package(Eigen3 3.1.0 REQUIRED)
if(Eigen3_FOUND)
message(STATUS "Found Eigen")
include(CGAL_Eigen_support)
set(targets kinetic_partition)
foreach(target ${targets})
create_single_source_cgal_program("${target}.cpp")
target_link_libraries(${target} PUBLIC CGAL::Eigen_support)
endforeach()
else()
message(ERROR "This program requires the Eigen library, and will not be compiled.")
endif()

View File

@ -0,0 +1,31 @@
OFF
25 4 0
0.42368054211531858133 0.18253980947404854773 0.43361217636176885293
0.022479024642939653134 0.54906919604958193126 0.18056913227494222896
0.2811647526241494166 0.52705548769951282573 0.022668645874355360104
0.56065427807169632146 0.47592798567162025725 -0.10696848363655320213
0.97118185417342761667 0.3697089496003267417 -0.25076552479989255851
0.53714992649207637943 0.15247177832828684441 0.39492925062101502665
0.47254430936410363184 -0.4706882612609787353 -0.2428207513377572957
0.072755785530830729968 -0.01774935447864991328 -0.65160096675580014836
-0.13624087681667856886 -0.19828919510256182157 -1
0.050171309138895309188 -1 -1
0.42195670307475763305 -1 -0.48389499638253974378
0.49191328462142458466 -0.78271514577943301916 -0.31664816804310136344
0.49305113818899937161 -0.56550106370623254293 -0.24495695687290242049
-1 0.038516275072130623514 0.26001089527408571822
-0.0052646635725682039419 0.32175247304120269121 -1
0.79946300115531654384 1 -1
-0.24605339821785221499 1 1
-0.9554579135068776985 0.40209355388461098801 1
-1 0.32023017788244112491 0.89940428108662362483
0.71260765954572424796 -1 0.060430338155497906327
0.93155151560319460202 -0.69967629334922809559 0.098656503647987087158
1 -0.4563157020167216138 0.27001739515231759636
1 0.14422055985065576622 0.91048995874330840294
0.94048661719673254389 0.15625792052012871247 1
-0.016691632221610047671 -1 1
6 0 1 2 3 4 5
7 6 7 8 9 10 11 12
6 13 14 15 16 17 18
6 19 20 21 22 23 24

View File

@ -0,0 +1,78 @@
// Copyright (c) 2023 GeometryFactory Sarl (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s) : Sven Oesau, Florent Lafarge, Dmitry Anisimov, Simon Giraudot
#ifndef CGAL_KSR_ALL_PARAMETERS_EXAMPLES_H
#define CGAL_KSR_ALL_PARAMETERS_EXAMPLES_H
// STL includes.
#include <string>
namespace CGAL {
namespace KSR {
template<typename FT>
struct All_parameters {
// Path to the input data file.
std::string data; // required!
// Boolean tags.
bool with_normals; // do we use normals
bool verbose;// verbose basic info
bool debug; // verbose more info
// Shape detection / Shape regularization.
// See the corresponding CGAL packages.
std::size_t k_neighbors;
FT distance_threshold;
FT angle_threshold;
std::size_t min_region_size;
bool regularize;
// Partitioning.
// See KSR/parameters.h
unsigned int k_intersections;
FT enlarge_bbox_ratio;
bool reorient;
std::size_t max_octree_depth;
std::size_t max_octree_node_size;
// Reconstruction.
FT graphcut_beta; // magic parameter between 0 and 1
// Constructor.
All_parameters() :
data(""),
// boolean tags
with_normals(true),
verbose(false),
debug(false),
// shape detection / shape regularization
k_neighbors(12),
min_region_size(100),
max_octree_node_size(40),
max_octree_depth(3),
// partition
k_intersections(1),
enlarge_bbox_ratio(FT(11) / FT(10)),
reorient(false),
// reconstruction
graphcut_beta(FT(1) / FT(2))
{ }
};
} // KSR
} // CGAL
#endif // CGAL_KSR_ALL_PARAMETERS_EXAMPLES_H

View File

@ -0,0 +1,340 @@
#ifndef CGAL_KSR_TERMINAL_PARSER_H
#define CGAL_KSR_TERMINAL_PARSER_H
#if defined(WIN32) || defined(_WIN32)
#define _SR_ "\\"
#else
#define _SR_ "/"
#endif
// STL includes.
#include <vector>
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#include <unordered_map>
namespace CGAL {
namespace KSR {
template<typename FT>
class Terminal_parser {
public:
using Input_parameters = std::unordered_map<std::string, std::string>;
Terminal_parser(
const int num_parameters,
const char** parameters,
const std::string path_to_save = "") :
m_path_to_save(path_to_save) {
// Help.
show_help(num_parameters, parameters);
// Handle all input parameters.
Input_parameters input_parameters;
set_input_parameters(num_parameters, parameters, input_parameters);
// Set here all required parameters.
std::vector<std::string> required(1);
required[0] = "-data";
// Set parameters.
set_parameters(input_parameters, required);
// Set here all parameters that should not be saved.
std::vector<std::string> exceptions(2);
exceptions[0] = "-data";
exceptions[1] = "-params";
// Save parameters.
save_parameters_to_file(input_parameters, exceptions);
}
inline Input_parameters& get_input_parameters() {
return m_parameters;
}
inline const Input_parameters& get_input_parameters() const {
return m_parameters;
}
template<typename Scalar>
void add_val_parameter(
const std::string parameter_name,
Scalar& variable_value) {
if (!does_parameter_exist(parameter_name, m_parameters))
return;
const std::string parameter_value = m_parameters.at(parameter_name);
if (parameter_value != "default")
variable_value = static_cast<Scalar>(std::stod(parameter_value.c_str()));
std::cout << parameter_name << " : " << variable_value << std::endl;
}
void add_str_parameter(
const std::string parameter_name,
std::string& variable_value) {
if (!does_parameter_exist(parameter_name, m_parameters))
return;
const std::string parameter_value = m_parameters.at(parameter_name);
if (parameter_value != "default")
variable_value = parameter_value;
std::cout << parameter_name << " : " << variable_value << std::endl;
}
void add_bool_parameter(
const std::string parameter_name,
bool& variable_value) {
if (!does_parameter_exist(parameter_name, m_parameters))
return;
variable_value = true;
std::cout << parameter_name << " : " << (variable_value ? "true" : "false") << std::endl;
}
private:
const std::string m_path_to_save;
Input_parameters m_parameters;
// Help.
void show_help(
const int num_parameters,
const char** parameters) {
if (!is_asked_for_help(num_parameters, parameters))
return;
print_help();
exit(EXIT_SUCCESS);
}
bool is_asked_for_help(
const int num_parameters,
const char** parameters) {
for (int i = 0; i < num_parameters; ++i)
if (std::strcmp(parameters[i], "-help") == 0)
return true;
return false;
}
void print_help() {
std::cout << std::endl << "* HELP:" << std::endl;
std::cout << std::endl << "* EXAMPLE:" << std::endl;
std::cout <<
"your terminal name $ ."
<< std::string(_SR_) <<
"kinetic_reconstruction_example -data path_to_data"
<< std::string(_SR_) <<
"data_name.ply -other_param_name -other_param_value"
<< std::endl << std::endl;
std::cout << std::endl << "REQUIRED PARAMETERS:" << std::endl << std::endl;
std::cout <<
"parameter name: -data" << std::endl <<
"parameter value: path_to_data" << std::string(_SR_) << "data_name.ply" << std::endl <<
"description: path to the file with input data" << std::endl << std::endl;
std::cout << std::endl << "OPTIONAL PARAMETERS:" << std::endl << std::endl;
std::cout <<
"parameter name: -silent" << std::endl <<
"description: supress any intermediate output except for the final result" << std::endl << std::endl;
std::cout <<
"parameter name: -params" << std::endl <<
"parameter value: path_to" << std::string(_SR_) << "parameters.ksr" << std::endl <<
"description: load parameters from the file" << std::endl << std::endl;
}
// Setting parameters.
void set_input_parameters(
const int num_parameters,
const char** parameters,
Input_parameters& input_parameters) {
assert(num_parameters > 0);
for (int i = 1; i < num_parameters; ++i) {
std::string str = static_cast<std::string>(parameters[i]);
auto first_letter = str[0];
if (first_letter == '-') {
if (i + 1 < num_parameters) {
str = static_cast<std::string>(parameters[i + 1]);
first_letter = str[0];
if (first_letter != '-')
input_parameters[parameters[i]] = parameters[i + 1];
else
input_parameters[parameters[i]] = "default";
} else input_parameters[parameters[i]] = "default";
}
}
}
void set_parameters(
Input_parameters& input_parameters,
const std::vector<std::string>& required) {
if (!are_required_parameters_set(input_parameters, required)) {
std::cerr << std::endl <<
"ERROR: SEVERAL REQUIRED PARAMETERS ARE MISSING!"
<< std::endl << std::endl;
exit(EXIT_FAILURE);
}
if (parameters_should_be_loaded(input_parameters))
load_parameters_from_file(input_parameters);
m_parameters = input_parameters;
}
bool are_required_parameters_set(
const Input_parameters& input_parameters,
const std::vector<std::string>& required) {
bool are_all_set = true;
for (std::size_t i = 0; i < required.size(); ++i)
if (!is_required_parameter_set(required[i], input_parameters))
are_all_set = false;
return are_all_set;
}
bool is_required_parameter_set(
const std::string parameter_name,
const Input_parameters& input_parameters) {
const bool is_set = does_parameter_exist(parameter_name, input_parameters) &&
!does_parameter_have_default_value(parameter_name, input_parameters);
if (!is_set)
std::cerr << std::endl <<
parameter_name << " PARAMETER IS REQUIRED!"
<< std::endl;
return is_set;
}
bool does_parameter_exist(
const std::string parameter_name,
const Input_parameters& input_parameters) {
for (Input_parameters::const_iterator parameter = input_parameters.begin();
parameter != input_parameters.end(); ++parameter)
if ((*parameter).first == parameter_name)
return true;
return false;
}
bool does_parameter_have_default_value(
const std::string parameter_name,
const Input_parameters& input_parameters) {
assert(does_parameter_exist(parameter_name, input_parameters));
return input_parameters.at(parameter_name) == "default";
}
// Loading from a file.
bool parameters_should_be_loaded(const Input_parameters& input_parameters) {
if (does_parameter_exist("-params", input_parameters))
return true;
return false;
}
void load_parameters_from_file(Input_parameters& input_parameters) {
const std::string filePath = input_parameters.at("-params");
if (filePath == "default") {
std::cerr << std::endl <<
"ERROR: PATH TO THE FILE WITH PARAMETERS IS NOT DEFINED!"
<< std::endl << std::endl;
exit(EXIT_FAILURE);
}
std::ifstream file(filePath.c_str(), std::ios_base::in);
if (!file) {
std::cerr << std::endl <<
"ERROR: ERROR LOADING FILE WITH PARAMETERS!"
<< std::endl << std::endl;
exit(EXIT_FAILURE);
}
Input_parameters tmp_parameters;
while (!file.eof()) {
std::string parameter_name, parameter_value;
file >> parameter_name >> parameter_value;
if (parameter_name == "" || parameter_value == "")
continue;
tmp_parameters[parameter_name] = parameter_value;
}
for (Input_parameters::const_iterator pit = tmp_parameters.begin();
pit != tmp_parameters.end(); ++pit)
input_parameters[(*pit).first] = (*pit).second;
file.close();
}
// Saving to a file.
void save_parameters_to_file(
const Input_parameters& input_parameters,
const std::vector<std::string>& exceptions) {
save_input_parameters(m_path_to_save, input_parameters, exceptions);
return;
}
void save_input_parameters(
const std::string path_to_save,
const Input_parameters& input_parameters,
const std::vector<std::string>& exceptions) {
const std::string file_path = path_to_save + "parameters.ksr";
save_parameters(file_path, input_parameters, exceptions);
std::cout << "* parameters are saved in: " << file_path << std::endl;
}
void save_parameters(
const std::string file_path,
const Input_parameters& input_parameters,
const std::vector<std::string>& exceptions) {
std::ofstream file(file_path.c_str(), std::ios_base::out);
if (!file) {
std::cerr << std::endl <<
"ERROR: SAVING FILE WITH THE NAME " << file_path
<< std::endl << std::endl;
exit(EXIT_FAILURE);
}
for (Input_parameters::const_iterator parameter = input_parameters.begin();
parameter != input_parameters.end(); ++parameter)
if (parameter_should_be_saved((*parameter).first, exceptions))
file << (*parameter).first << " " << (*parameter).second << std::endl;
file.close();
}
bool parameter_should_be_saved(
const std::string parameter_name,
const std::vector<std::string>& exceptions) {
for (std::size_t i = 0; i < exceptions.size(); ++i)
if (exceptions[i] == parameter_name)
return false;
return true;
}
};
} // KSR
} // CGAL
#endif // CGAL_KSR_TERMINAL_PARSER_H

View File

@ -0,0 +1,73 @@
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Kinetic_space_partition_3.h>
#include <CGAL/Real_timer.h>
#include <CGAL/IO/polygon_soup_io.h>
using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel;
using EPECK = CGAL::Exact_predicates_exact_constructions_kernel;
using Kernel = EPICK;
using FT = typename Kernel::FT;
using Point_3 = typename Kernel::Point_3;
using Surface_mesh = CGAL::Surface_mesh<Point_3>;
using KSP = CGAL::Kinetic_space_partition_3<EPICK>;
using Timer = CGAL::Real_timer;
int main(int argc, char** argv)
{
// Reading polygons from file
std::string input_filename = (argc > 1 ? argv[1] : "data/test-4-rnd-polygons-4-6.off");
std::ifstream input_file(input_filename);
std::vector<Point_3> input_vertices;
std::vector<std::vector<std::size_t> > input_faces;
if (CGAL::IO::read_polygon_soup(input_filename, input_vertices, input_faces)) {
std::cout << "* reading the file: " << input_filename << "!" << std::endl;
input_file.close();
} else {
std::cerr << "ERROR: can't read the file " << input_filename << "!" << std::endl;
return EXIT_FAILURE;
}
std::cout << "--- INPUT STATS: \n* number of polygons: " << input_faces.size() << std::endl;
// Parameters.
const unsigned int k = (argc > 2 ? std::atoi(argv[2]) : 1);
// Initialization of Kinetic_space_partition_3 object.
// 'debug' set to true exports intermediate results into files in the working directory.
// The resulting volumes are exported into a volumes folder, if the folder already exists.
KSP ksp(CGAL::parameters::verbose(true).debug(true));
// Providing input polygons.
ksp.insert(input_vertices, input_faces);
Timer timer;
timer.start();
// 'initialize' creates the intersection graph that is used for the partition.
ksp.initialize(CGAL::parameters::bbox_dilation_ratio(1.1).reorient_bbox(false));
// Creating the partition with allowing up to 'k' intersections for each kinetic polygon.
ksp.partition(k);
timer.stop();
const FT time = static_cast<FT>(timer.time());
// Access the kinetic partition via linear cell complex.
typedef CGAL::Linear_cell_complex_traits<3, EPECK> LCC_Traits;
CGAL::Linear_cell_complex_for_combinatorial_map<3, 3, LCC_Traits, typename KSP::Linear_cell_complex_min_items> lcc;
ksp.get_linear_cell_complex(lcc);
std::vector<unsigned int> cells = { 0, 2, 3 }, count;
count = lcc.count_cells(cells);
std::cout << "For k = " << k << ":\n" << " vertices: " << count[0] << "\n faces: " << count[2] << "\n volumes: " << count[3] << std::endl;
std::cout << "\n3D kinetic partition created in " << time << " seconds!" << std::endl;
return EXIT_SUCCESS;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,48 @@
// Copyright (c) 2021 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) : Simon Giraudot, Dmitry Anisimov
#ifndef CGAL_KSP_PARAMETERS_H
#define CGAL_KSP_PARAMETERS_H
#include <CGAL/license/Kinetic_space_partition.h>
namespace CGAL {
namespace KSP {
namespace internal {
template<typename FT>
struct Parameters_3 {
unsigned int k = 1; // k intersections
// Octree parameters for subdivison of input data into kinetic subpartitions
unsigned int max_octree_depth = 3;
unsigned int max_octree_node_size = 40;
FT bbox_dilation_ratio = FT(11) / FT(10); // ratio to enlarge bbox
bool reorient_bbox = false; // true - optimal bounding box, false - axis aligned
// All files are saved in the current build directory.
bool verbose = false; // print basic verbose information
bool debug = false; // print all steps and substeps + export initial and final configurations
// See also global tolerance inside utils.h! (set to 0)
Parameters_3(const bool v = true, const bool d = false) :
verbose(v), debug(d) { }
};
} // namespace internal
} // namespace KSP
} // namespace CGAL
#endif // CGAL_KSP_PARAMETERS_H

View File

@ -0,0 +1,310 @@
// Copyright (c) 2020 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) : Simon Giraudot, Dmitry Anisimov
#ifndef CGAL_KSP_UTILS_H
#define CGAL_KSP_UTILS_H
#include <CGAL/license/Kinetic_space_partition.h>
// STL includes.
#include <set>
#include <cmath>
#include <array>
#include <string>
#include <sstream>
#include <functional>
#include <fstream>
#include <vector>
#include <deque>
#include <queue>
#include <map>
// CGAL includes.
#include <CGAL/Bbox_3.h>
#include <CGAL/centroid.h>
#include <CGAL/Polygon_2.h>
#include <CGAL/Iterator_range.h>
#include <CGAL/convex_hull_2.h>
#include <CGAL/number_utils.h>
#include <CGAL/assertions.h>
#include <CGAL/Cartesian_converter.h>
#include <CGAL/linear_least_squares_fitting_2.h>
#include <CGAL/linear_least_squares_fitting_3.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polygon_2_algorithms.h>
// Boost includes.
#include <boost/iterator/function_output_iterator.hpp>
namespace CGAL {
namespace KSP {
namespace internal {
#ifdef DOXYGEN_RUNNING
#else
// Convert point to string.
template<typename Point_d>
const std::string to_string(const Point_d& p) {
std::ostringstream oss;
oss.precision(20);
oss << p;
return oss.str();
}
// Distance between two points.
template<typename Point_d>
decltype(auto) distance(const Point_d& p, const Point_d& q) {
using Traits = typename Kernel_traits<Point_d>::Kernel;
using FT = typename Traits::FT;
const FT sq_dist = CGAL::squared_distance(p, q);
return static_cast<FT>(CGAL::approximate_sqrt(sq_dist));
}
// Project 3D point onto 2D plane.
template<typename Point_3>
typename Kernel_traits<Point_3>::Kernel::Point_2
point_2_from_point_3(const Point_3& point_3) {
return typename Kernel_traits<Point_3>::Kernel::Point_2(
point_3.x(), point_3.y());
}
// Get 3D point from a 2D point.
template<typename Point_2>
typename Kernel_traits<Point_2>::Kernel::Point_3
point_3_from_point_2(const Point_2& point_2) {
return typename Kernel_traits<Point_2>::Kernel::Point_3(
point_2.x(), point_2.y(), typename Kernel_traits<Point_2>::Kernel::FT(0));
}
// Normalize vector.
template<typename Vector_d>
inline const Vector_d normalize(const Vector_d& v) {
using Traits = typename Kernel_traits<Vector_d>::Kernel;
using FT = typename Traits::FT;
const FT dot_product = CGAL::abs(v * v);
//CGAL_assertion(dot_product != FT(0));
return v / static_cast<FT>(CGAL::approximate_sqrt(dot_product));
}
// Intersections. Used only in the 2D version.
// For the 3D version, see conversions.h!
template<typename Type1, typename Type2, typename ResultType>
inline bool intersection(
const Type1& t1, const Type2& t2, ResultType& result) {
const auto inter = intersection(t1, t2);
if (!inter) return false;
if (CGAL::assign(result, inter))
return true;
return false;
}
template<typename ResultType, typename Type1, typename Type2>
inline const ResultType intersection(const Type1& t1, const Type2& t2) {
ResultType out;
CGAL_assertion_code(const bool is_intersection_found =) intersection(t1, t2, out);
CGAL_assertion(is_intersection_found);
return out;
}
// Get boundary points from a set of points.
template<typename Point_2, typename Line_2>
void boundary_points_on_line_2(
const std::vector<Point_2>& input_range,
const std::vector<std::size_t>& indices,
const Line_2& line, Point_2& p, Point_2& q) {
using Traits = typename Kernel_traits<Point_2>::Kernel;
using FT = typename Traits::FT;
using Vector_2 = typename Traits::Vector_2;
FT min_proj_value = (std::numeric_limits<FT>::max)();
FT max_proj_value = -min_proj_value;
const auto ref_vector = line.to_vector();
const auto& ref_point = input_range[indices.front()];
for (const std::size_t index : indices) {
const auto& query = input_range[index];
const auto point = line.projection(query);
const Vector_2 curr_vector(ref_point, point);
const FT value = CGAL::scalar_product(curr_vector, ref_vector);
if (value < min_proj_value) {
min_proj_value = value;
p = point;
}
if (value > max_proj_value) {
max_proj_value = value;
q = point;
}
}
}
// Angles.
// Converts radians to degrees.
template<typename FT>
const FT degrees_2(const FT angle_rad) {
return angle_rad * FT(180) / static_cast<FT>(CGAL_PI);
}
// Computes an angle in degrees between two directions.
template<typename Direction_2>
const typename Kernel_traits<Direction_2>::Kernel::FT
compute_angle_2(const Direction_2& dir1, const Direction_2& dir2) {
using Traits = typename Kernel_traits<Direction_2>::Kernel;
using FT = typename Traits::FT;
const auto v1 = dir2.to_vector();
const auto v2 = -dir1.to_vector();
const FT det = CGAL::determinant(v1, v2);
const FT dot = CGAL::scalar_product(v1, v2);
const FT angle_rad = static_cast<FT>(
std::atan2(CGAL::to_double(det), CGAL::to_double(dot)));
const FT angle_deg = degrees_2(angle_rad);
return angle_deg;
}
// Converts an angle in degrees from the range [-180, 180]
// into the mod 90 angle.
template<typename FT>
const FT convert_angle_2(const FT angle_2) {
FT angle = angle_2;
if (angle > FT(90)) angle = FT(180) - angle;
else if (angle < -FT(90)) angle = FT(180) + angle;
return angle;
}
// Computes a positive angle in degrees that
// is always in the range [0, 90].
template<typename Direction_2>
const typename Kernel_traits<Direction_2>::Kernel::FT
angle_2(const Direction_2& dir1, const Direction_2& dir2) {
const auto angle_2 = compute_angle_2(dir1, dir2);
return CGAL::abs(convert_angle_2(angle_2));
}
// Classes.
template<typename IVertex>
class Indexer {
public:
std::size_t operator()(const IVertex& ivertex) {
const auto pair = m_indices.insert(
std::make_pair(ivertex, m_indices.size()));
const auto& item = pair.first;
const std::size_t idx = item->second;
return idx;
}
void clear() { m_indices.clear(); }
private:
std::map<IVertex, std::size_t> m_indices;
};
template<
typename GeomTraits,
typename InputRange,
typename NeighborQuery>
class Estimate_normals_2 {
public:
using Traits = GeomTraits;
using Input_range = InputRange;
using Neighbor_query = NeighborQuery;
using Kernel = Traits;
using FT = typename Kernel::FT;
using Vector_2 = typename Kernel::Vector_2;
using Line_2 = typename Kernel::Line_2;
using Indices = std::vector<std::size_t>;
using IK = CGAL::Exact_predicates_inexact_constructions_kernel;
using IPoint_2 = typename IK::Point_2;
using ILine_2 = typename IK::Line_2;
using Converter = CGAL::Cartesian_converter<Kernel, IK>;
Estimate_normals_2(
const Input_range& input_range,
const Neighbor_query& neighbor_query) :
m_input_range(input_range),
m_neighbor_query(neighbor_query) {
CGAL_precondition(input_range.size() > 0);
}
void get_normals(std::vector<Vector_2>& normals) const {
normals.clear();
normals.reserve(m_input_range.size());
Indices neighbors;
for (std::size_t i = 0; i < m_input_range.size(); ++i) {
neighbors.clear();
m_neighbor_query(i, neighbors);
const auto line = fit_line(neighbors);
auto normal = line.to_vector();
normal = normal.perpendicular(CGAL::COUNTERCLOCKWISE);
normal = normalize(normal);
normals.push_back(normal);
}
CGAL_assertion(normals.size() == m_input_range.size());
}
private:
const Input_range& m_input_range;
const Neighbor_query& m_neighbor_query;
const Converter m_converter;
const Line_2 fit_line(const Indices& indices) const {
CGAL_assertion(indices.size() > 0);
std::vector<IPoint_2> points;
points.reserve(indices.size());
for (const std::size_t index : indices) {
const auto& point = get(m_neighbor_query.point_map(), index);
points.push_back(m_converter(point));
}
CGAL_assertion(points.size() == indices.size());
ILine_2 fitted_line;
IPoint_2 fitted_centroid;
CGAL::linear_least_squares_fitting_2(
points.begin(), points.end(),
fitted_line, fitted_centroid,
CGAL::Dimension_tag<0>());
const Line_2 line(
static_cast<FT>(fitted_line.a()),
static_cast<FT>(fitted_line.b()),
static_cast<FT>(fitted_line.c()));
return line;
}
};
#endif
} // namespace internal
} // namespace KSP
} // namespace CGAL
#endif // CGAL_KSP_UTILS_H

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -0,0 +1,181 @@
// Copyright (c) 2023 GeometryFactory Sarl (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s) : Sven Oesau, Florent Lafarge, Dmitry Anisimov, Simon Giraudot
#ifndef CGAL_KSR_3_GRAPHCUT_H
#define CGAL_KSR_3_GRAPHCUT_H
// #include <CGAL/license/Kinetic_shape_reconstruction.h>
// CGAL includes.
#include <CGAL/assertions.h>
//#define CGAL_DO_NOT_USE_BOYKOV_KOLMOGOROV_MAXFLOW_SOFTWARE
#include <CGAL/boost/graph/alpha_expansion_graphcut.h>
#include <CGAL/boost/graph/Alpha_expansion_MaxFlow_tag.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Delaunay_triangulation_2.h>
#include <CGAL/Delaunay_triangulation_3.h>
#include <CGAL/Cartesian_converter.h>
// Internal includes.
#include <CGAL/KSP/utils.h>
#include <CGAL/KSP/debug.h>
#include <CGAL/KSP_3/Data_structure.h>
namespace CGAL {
namespace KSR_3 {
#ifdef DOXYGEN_RUNNING
#else
template<typename GeomTraits>
class Graphcut {
public:
using Kernel = GeomTraits;
using FT = typename Kernel::FT;
using Point_3 = typename Kernel::Point_3;
using Triangle_2 = typename Kernel::Triangle_2;
using Triangle_3 = typename Kernel::Triangle_3;
using Indices = std::vector<std::size_t>;
using Delaunay_2 = CGAL::Delaunay_triangulation_2<Kernel>;
using Delaunay_3 = CGAL::Delaunay_triangulation_3<Kernel>;
Graphcut(
const FT lambda) : m_lambda(lambda) { }
void solve(const std::vector<std::pair<std::size_t, std::size_t> >& edges, const std::vector<FT>& edge_weights, const std::vector<std::vector<double> > &cost_matrix, std::vector<std::size_t> &labels) {
assert(edges.size() == edge_weights.size());
assert(!cost_matrix.empty());
labels.resize(cost_matrix[0].size());
for (std::size_t i = 0; i < cost_matrix[0].size(); i++) {
// Verify quadratic size
assert(cost_matrix[0].size() == cost_matrix[1].size());
labels[i] = (cost_matrix[0][i] > cost_matrix[1][0]) ? 1 : 0;
}
compute_graphcut(edges, edge_weights, cost_matrix, labels);
}
private:
const FT m_lambda;
double get_face_cost(
const FT face_prob, const FT face_weight) const {
CGAL_assertion(face_prob >= FT(0) && face_prob <= FT(1));
CGAL_assertion(face_weight >= FT(0) && face_weight <= FT(1));
const double weight = CGAL::to_double(face_weight);
const double value = (1.0 - CGAL::to_double(face_prob));
return weight * value;
}
void compute_graphcut(
const std::vector< std::pair<std::size_t, std::size_t> >& edges,
const std::vector<FT>& edge_costs,
const std::vector< std::vector<double> >& cost_matrix,
std::vector<std::size_t>& labels) const {
std::vector<std::size_t> tmp = labels;
std::cout << std::endl << "beta" << m_lambda << std::endl;
std::vector<FT> edge_costs_lambda(edge_costs.size());
std::vector<std::vector<double> > cost_matrix_lambda(2);
for (std::size_t i = 0; i < edge_costs.size(); i++)
edge_costs_lambda[i] = edge_costs[i] * m_lambda;
for (std::size_t i = 0; i < cost_matrix.size(); i++) {
cost_matrix_lambda[i].resize(cost_matrix[i].size());
for (std::size_t j = 0; j < cost_matrix[i].size(); j++)
cost_matrix_lambda[i][j] = cost_matrix[i][j] * (1.0 - m_lambda);
}
double min = 10000000000;
double max = -min;
double edge_sum = 0;
for (std::size_t i = 0; i < edge_costs.size(); i++) {
min = (std::min<double>)(edge_costs[i], min);
max = (std::max<double>)(edge_costs[i], max);
edge_sum += edge_costs[i] * m_lambda;
}
std::cout << "edge costs" << std::endl;
std::cout << "sum: " << edge_sum << std::endl;
std::cout << "min: " << min << std::endl;
std::cout << "max: " << max << std::endl;
min = 1000000000;
max = -min;
std::size_t sum_inside = 0;
std::size_t sum_outside = 0;
for (std::size_t i = 6; i < cost_matrix[0].size(); i++) {
sum_inside += cost_matrix[0][i];
sum_outside += cost_matrix[1][i];
min = (std::min<double>)(cost_matrix[0][i], min);
min = (std::min<double>)(cost_matrix[1][i], min);
max = (std::max<double>)(cost_matrix[0][i], max);
max = (std::max<double>)(cost_matrix[1][i], max);
}
std::cout << "label costs" << std::endl;
std::cout << "min: " << min << std::endl;
std::cout << "max: " << max << std::endl;
std::cout << "sum inside: " << sum_inside << std::endl;
std::cout << "sum outside: " << sum_outside << std::endl;
CGAL::alpha_expansion_graphcut(edges, edge_costs_lambda, cost_matrix_lambda, labels);
/*
CGAL::min_cut(
edges, edge_costs, cost_matrix, labels, CGAL::parameters::implementation_tag(CGAL::Alpha_expansion_MaxFlow_tag()));*/
/*
bool difference = false;
for (std::size_t i = 0; i < labels.size(); i++) {
if (tmp[i] != labels[i]) {
difference = true;
break;
}
}
std::cout << "Labels changed: " << difference << std::endl;*/
}
/*
void apply_new_labels(
const std::vector<std::size_t>& labels,
std::vector<Volume_cell>& volumes) const {
CGAL_assertion((volumes.size() + 6) == labels.size());
for (std::size_t i = 0; i < volumes.size(); ++i) {
const std::size_t label = labels[i + 6];
auto& volume = volumes[i];
if (label == 0) {
volume.visibility = Visibility_label::INSIDE;
} else {
volume.visibility = Visibility_label::OUTSIDE;
}
}
}*/
};
#endif //DOXYGEN_RUNNING
} // KSR_3
} // CGAL
#endif // CGAL_KSR_3_GRAPHCUT_H

View File

@ -0,0 +1,874 @@
// Copyright (c) 2023 GeometryFactory Sarl (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s) : Sven Oesau, Florent Lafarge, Dmitry Anisimov, Simon Giraudot
#ifndef CGAL_KSP_3_INITIALIZER_H
#define CGAL_KSP_3_INITIALIZER_H
#include <CGAL/license/Kinetic_space_partition.h>
// CGAL includes.
#include <CGAL/Timer.h>
#include <CGAL/optimal_bounding_box.h>
#include <CGAL/Boolean_set_operations_2.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/intersections.h>
#include <CGAL/min_quadrilateral_2.h>
#include <CGAL/Aff_transformation_2.h>
#include <boost/optional/optional_io.hpp>
// Internal includes.
#include <CGAL/KSP/utils.h>
#include <CGAL/KSP/debug.h>
#include <CGAL/KSP/parameters.h>
#include <CGAL/KSP_3/Data_structure.h>
#include <CGAL/Real_timer.h>
namespace CGAL {
namespace KSP_3 {
namespace internal {
#ifdef DOXYGEN_RUNNING
#else
template<typename GeomTraits, typename IntersectionKernel>
class Initializer {
public:
using Kernel = GeomTraits;
using Intersection_kernel = IntersectionKernel;
private:
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
using Point_3 = typename Kernel::Point_3;
using Vector_2 = typename Kernel::Vector_2;
using Segment_2 = typename Kernel::Segment_2;
using Segment_3 = typename Kernel::Segment_3;
using Line_2 = typename Kernel::Line_2;
using Transform_3 = CGAL::Aff_transformation_3<Kernel>;
using Direction_2 = typename Kernel::Direction_2;
using Data_structure = KSP_3::internal::Data_structure<Kernel, Intersection_kernel>;
using Support_plane = typename Data_structure::Support_plane;
using IEdge = typename Data_structure::IEdge;
using IFace = typename Data_structure::IFace;
using Face_property = typename Data_structure::Intersection_graph::Face_property;
using Intersection_graph = typename Data_structure::Intersection_graph;
using IEdge_set = typename Data_structure::IEdge_set;
using IVertex = typename Data_structure::IVertex;
using To_exact = CGAL::Cartesian_converter<Kernel, Intersection_kernel>;
using From_exact = CGAL::Cartesian_converter<Intersection_kernel, Kernel>;
using Bbox_3 = CGAL::Bbox_3;
using OBB_traits = CGAL::Oriented_bounding_box_traits_3<Kernel>;
using Parameters = KSP::internal::Parameters_3<FT>;
using Timer = CGAL::Real_timer;
public:
Initializer(std::vector<std::vector<Point_3> >& input_polygons, Data_structure& data, const Parameters& parameters) :
m_input_polygons(input_polygons), m_data(data), m_parameters(parameters)
{ }
Initializer(std::vector<std::vector<Point_3> >& input_polygons, std::vector<typename Intersection_kernel::Plane_3>& input_planes, Data_structure& data, const Parameters& parameters) :
m_input_polygons(input_polygons), m_input_planes(input_planes), m_data(data), m_parameters(parameters)
{ }
void initialize(const std::array<typename Intersection_kernel::Point_3, 8>& bbox, std::vector<std::size_t>& input_polygons) {
Timer timer;
timer.reset();
timer.start();
std::vector< std::vector<typename Intersection_kernel::Point_3> > bbox_faces;
bounding_box_to_polygons(bbox, bbox_faces);
add_polygons(bbox_faces, input_polygons);
m_data.igraph().finished_bbox();
if (m_parameters.verbose)
std::cout << "* intersecting input polygons ... ";
// Fills in the ivertices on support plane intersections inside the bbox.
make_polygons_intersection_free();
// Generation of ifaces
create_ifaces();
// Splitting the input polygons along intersection lines.
initial_polygon_iedge_intersections();
create_bbox_meshes();
// Starting from here the intersection graph is const, it won't change anymore.
if (m_parameters.verbose)
std::cout << "done" << std::endl;
if (m_parameters.debug)
KSP_3::internal::dump(m_data, m_data.prefix() + "intersected");
CGAL_assertion(m_data.check_bbox());
//m_data.set_limit_lines();
m_data.precompute_iedge_data();
m_data.initialization_done();
if (m_parameters.debug) {
for (std::size_t sp = 0; sp < m_data.number_of_support_planes(); sp++)
dump_2d_surface_mesh(m_data, sp, m_data.prefix() + "before-partition-sp" + std::to_string(sp));
}
if (m_parameters.verbose) {
std::cout << "v: " << m_data.igraph().number_of_vertices() << " f: " << m_data.igraph().number_of_faces() << std::endl;
}
}
void clear() {
// to be added
}
private:
std::vector<std::vector<Point_3> >& m_input_polygons;
std::vector<typename Intersection_kernel::Plane_3>& m_input_planes;
Data_structure& m_data;
const Parameters& m_parameters;
void add_iface_from_iedge(std::size_t sp_idx, IEdge edge, IEdge next, bool cw) {
IVertex s = m_data.source(edge);
IVertex t = m_data.target(edge);
IFace face_idx = m_data.add_iface(sp_idx);
Face_property& face = m_data.igraph().face(face_idx);
face.pts.push_back(m_data.support_plane(sp_idx).to_2d(m_data.igraph().point_3(s)));
face.pts.push_back(m_data.support_plane(sp_idx).to_2d(m_data.igraph().point_3(t)));
face.vertices.push_back(s);
face.vertices.push_back(t);
face.edges.push_back(edge);
m_data.igraph().add_face(sp_idx, edge, face_idx);
face.edges.push_back(next);
m_data.igraph().add_face(sp_idx, next, face_idx);
std::size_t iterations = 0;
int dir = (cw) ? -1 : 1;
const std::size_t uninitialized = static_cast<std::size_t>(-1);
std::size_t inext;
while (s != m_data.target(next) && iterations < 10000) {
face.vertices.push_back(m_data.target(next));
face.pts.push_back(m_data.support_plane(sp_idx).to_2d(m_data.igraph().point_3(m_data.target(next))));
IEdge enext, eprev;
get_prev_next(sp_idx, next, eprev, enext);
std::vector<std::pair<IEdge, Direction_2> > connected;
m_data.get_and_sort_all_connected_iedges(sp_idx, m_data.target(next), connected);
inext = uninitialized;
for (std::size_t idx = 0; idx < connected.size(); idx++) {
if (connected[idx].first == next) {
inext = (idx + dir + connected.size()) % connected.size();
break;
}
}
CGAL_assertion(inext != uninitialized);
next = connected[inext].first;
face.edges.push_back(next);
m_data.igraph().add_face(sp_idx, next, face_idx);
iterations++;
}
// Loop complete, connecting face with all edges.
for (IEdge edge : face.edges) {
m_data.support_plane(sp_idx).add_neighbor(edge, face_idx);
CGAL_assertion_code(IFace f1 = m_data.support_plane(sp_idx).iface(edge);)
CGAL_assertion_code(IFace f2 = m_data.support_plane(sp_idx).other(edge, f1);)
CGAL_assertion(f1 == face_idx || f2 == face_idx);
}
std::vector<typename Intersection_kernel::Point_2> pts;
pts.reserve(face.pts.size());
for (auto p : face.pts)
pts.push_back(p);
face.poly = Polygon_2<Intersection_kernel>(pts.begin(), pts.end());
if (face.poly.orientation() != CGAL::COUNTERCLOCKWISE) {
face.poly.reverse_orientation();
std::reverse(face.pts.begin(), face.pts.end());
std::reverse(face.vertices.begin(), face.vertices.end());
std::reverse(face.edges.begin(), face.edges.end());
}
CGAL_assertion(face.poly.orientation() == CGAL::COUNTERCLOCKWISE);
CGAL_assertion(face.poly.is_convex());
CGAL_assertion(face.poly.is_simple());
}
void get_prev_next(std::size_t sp_idx, IEdge edge, IEdge& prev, IEdge& next) {
CGAL_assertion(edge != Intersection_graph::null_iedge());
CGAL_assertion(sp_idx != static_cast<std::size_t>(-1));
std::vector<std::pair<IEdge, Direction_2> > connected;
m_data.get_and_sort_all_connected_iedges(sp_idx, m_data.target(edge), connected);
//if (connected.size() <= 2) ivertex is on bbox edge
std::size_t inext = static_cast<std::size_t>(-1), iprev = static_cast<std::size_t>(-1);
for (std::size_t idx = 0; idx < connected.size(); idx++) {
if (connected[idx].first == edge) {
iprev = (idx - 1 + connected.size()) % connected.size();
inext = (idx + 1) % connected.size();
break;
}
}
CGAL_assertion(inext != static_cast<std::size_t>(-1));
CGAL_assertion(iprev != static_cast<std::size_t>(-1));
prev = connected[iprev].first;
next = connected[inext].first;
}
void create_ifaces() {
for (std::size_t sp_idx = 0; sp_idx < m_data.number_of_support_planes(); sp_idx++) {
const IEdge_set& uiedges = m_data.support_plane(sp_idx).unique_iedges();
// Special case bbox without splits
if (sp_idx < 6 && uiedges.size() == 4) {
// Get first edge
IEdge first = *uiedges.begin();
IEdge edge = first;
IVertex s = m_data.source(edge);
IVertex t = m_data.target(edge);
// Create single IFace for unsplit bbox face
IFace face_idx = m_data.add_iface(sp_idx);
Face_property& face = m_data.igraph().face(face_idx);
// Add first edge, vertices and points to face properties
face.pts.push_back(m_data.support_plane(sp_idx).to_2d(m_data.igraph().point_3(s)));
face.pts.push_back(m_data.support_plane(sp_idx).to_2d(m_data.igraph().point_3(t)));
face.vertices.push_back(s);
face.vertices.push_back(t);
face.edges.push_back(edge);
// Link edge and face
m_data.igraph().add_face(sp_idx, edge, face_idx);
// Walk around bbox face
while (s != t) {
auto inc_iedges = m_data.incident_iedges(t);
for (auto next : inc_iedges) {
// Filter edges that are not in this bbox face
const auto iplanes = m_data.intersected_planes(next);
if (iplanes.find(sp_idx) == iplanes.end()) {
continue;
}
// Skip current edge
if (edge == next)
continue;
// The only left edge is the next one.
edge = next;
break;
}
t = (m_data.target(edge) == t) ? m_data.source(edge) : m_data.target(edge);
face.vertices.push_back(t);
face.pts.push_back(m_data.support_plane(sp_idx).to_2d(m_data.igraph().point_3(t)));
face.edges.push_back(edge);
m_data.igraph().add_face(sp_idx, edge, face_idx);
}
// create polygon in proper order
}
bool all_on_bbox = true;
for (auto edge : uiedges) {
bool on_edge = m_data.igraph().iedge_is_on_bbox(edge);
//if (m_data.igraph().iedge_is_on_bbox(edge))
// continue;
//
//Note the number of bbox lines during creation and skip all those.
// If non-bbox support plane is treated, skip all edges on bbox as they only have one face.
if (sp_idx >= 6 && on_edge)
continue;
// If bbox support plane is treated, skip edges on bbox edge.
if (sp_idx < 6 && m_data.igraph().line_is_bbox_edge(m_data.line_idx(edge)))
continue;
all_on_bbox = false;
IFace n1 = m_data.support_plane(sp_idx).iface(edge);
IFace n2 = m_data.support_plane(sp_idx).other(edge, n1);
if (n1 != Intersection_graph::null_iface() && n2 != Intersection_graph::null_iface())
continue;
Face_property np1, np2;
if (n1 != Intersection_graph::null_iface())
np1 = m_data.igraph().face(n1);
if (n2 != Intersection_graph::null_iface())
np2 = m_data.igraph().face(n2);
IEdge next, prev;
get_prev_next(sp_idx, edge, prev, next);
// Check if cw face already exists.
bool skip = false;
if (n1 != Intersection_graph::null_iface()) {
if (np1.is_part(edge, next))
skip = true;
}
if (!skip && n2 != Intersection_graph::null_iface()) {
if (np2.is_part(edge, next))
skip = true;
}
if (!skip) {
add_iface_from_iedge(sp_idx, edge, next, false);
}
// Check if cw face already exists.
skip = false;
if (n1 != Intersection_graph::null_iface()) {
if (np1.is_part(edge, prev))
skip = true;
}
if (!skip && n2 != Intersection_graph::null_iface()) {
if (np2.is_part(edge, prev))
skip = true;
}
if (!skip) {
add_iface_from_iedge(sp_idx, edge, prev, true);
}
}
// Special case if the input polygon only intersects with the bbox.
if (all_on_bbox) {
IEdge next, prev;
get_prev_next(sp_idx, *uiedges.begin(), prev, next);
add_iface_from_iedge(sp_idx, *uiedges.begin(), prev, true);
}
}
}
void initial_polygon_iedge_intersections() {
To_exact to_exact;
From_exact from_exact;
for (std::size_t sp_idx = 0; sp_idx < m_data.number_of_support_planes(); sp_idx++) {
bool polygons_assigned = false;
Support_plane& sp = m_data.support_plane(sp_idx);
if (sp.is_bbox())
continue;
sp.mesh().clear_without_removing_property_maps();
std::map<std::size_t, std::vector<IEdge> > line2edges;
// Get all iedges, sort into lines and test intersection per line?
for (const IEdge& edge : sp.unique_iedges()) {
if (m_data.is_bbox_iedge(edge))
continue;
std::size_t line = m_data.igraph().line(edge);
line2edges[line].push_back(edge);
}
for (auto pair : line2edges) {
// Get line
//Line_2 l(sp.to_2d(m_data.point_3(m_data.source(pair.second[0]))),sp.to_2d(m_data.point_3(m_data.target(pair.second[0]))));
typename Intersection_kernel::Point_2 a(sp.to_2d(m_data.point_3(m_data.source(pair.second[0]))));
typename Intersection_kernel::Point_2 b(sp.to_2d(m_data.point_3(m_data.target(pair.second[0]))));
typename Intersection_kernel::Line_2 exact_line(a, b);
Line_2 l = from_exact(exact_line);
typename Intersection_kernel::Vector_2 ldir = exact_line.to_vector();
ldir = (typename Intersection_kernel::FT(1.0) / CGAL::approximate_sqrt(ldir * ldir)) * ldir;
Vector_2 dir = from_exact(ldir);
std::vector<typename Intersection_kernel::Segment_2> crossing_polygon_segments;
std::vector<IEdge> crossing_iedges;
typename Intersection_kernel::FT emin = (std::numeric_limits<double>::max)();
typename Intersection_kernel::FT emax = -(std::numeric_limits<double>::max)();
FT min_speed = (std::numeric_limits<double>::max)(), max_speed = -(std::numeric_limits<double>::max)();
CGAL::Oriented_side last_side = l.oriented_side(sp.data().original_vertices.back());
Point_2 minp, maxp;
typename Intersection_kernel::Point_2 eminp, emaxp;
// Map polygon to line and get min&max projection
for (std::size_t v = 0; v < sp.data().original_vertices.size(); v++) {
const Point_2& p = sp.data().original_vertices[v];
CGAL::Oriented_side s = l.oriented_side(p);
if (last_side != s) {
// Fetch former point to add segment.
const Point_2& prev = sp.data().original_vertices[(v + sp.data().original_vertices.size() - 1) % sp.data().original_vertices.size()];
const Vector_2 edge_dir = sp.original_edge_direction((v + sp.data().original_vertices.size() - 1) % sp.data().original_vertices.size(), v);
typename Intersection_kernel::Segment_2 seg(to_exact(prev), to_exact(p));
const auto result = CGAL::intersection(seg, exact_line);
typename Intersection_kernel::Point_2 intersection;
if (result && CGAL::assign(intersection, result)) {
typename Intersection_kernel::FT eproj = (intersection - exact_line.point()) * ldir;
//FT proj = to_inexact(eproj);
if (eproj < emin) {
eminp = intersection;
emin = eproj;
minp = from_exact(intersection);
//min = proj;
typename Intersection_kernel::FT p = dir * edge_dir;
assert(p != 0);
min_speed = CGAL::approximate_sqrt(edge_dir * edge_dir) / from_exact(p);
}
if (emax < eproj) {
emaxp = intersection;
emax = eproj;
maxp = from_exact(intersection);
//max = proj;
typename Intersection_kernel::FT p = dir * edge_dir;
assert(p != 0);
max_speed = CGAL::approximate_sqrt(edge_dir * edge_dir) / from_exact(p);
}
}
else std::cout << "crossing segment does not intersect line" << std::endl;
crossing_polygon_segments.push_back(seg);
}
last_side = s;
}
// Is there any intersection?
// As the polygon is convex there can only be one line segment on the inside of the polygon
if (emin < emax) {
m_data.support_plane(sp_idx).set_crossed_line(pair.first);
// Collect crossing edges by overlapping min/max barycentric coordinates on line
for (IEdge e : pair.second) {
std::pair<IFace, IFace> faces;
m_data.igraph().get_faces(sp_idx, e, faces);
IVertex lower = m_data.source(e);
IVertex upper = m_data.target(e);
if (lower > upper) {
IVertex tmp = upper;
upper = lower;
lower = tmp;
}
typename Intersection_kernel::FT s = (sp.to_2d(m_data.point_3(lower)) - exact_line.point()) * ldir;
typename Intersection_kernel::FT t = (sp.to_2d(m_data.point_3(upper)) - exact_line.point()) * ldir;
if (s < t) {
if (s < emax && emin < t) {
std::pair<IFace, IFace> faces;
m_data.igraph().get_faces(sp_idx, e, faces);
polygons_assigned = true;
if (!m_data.igraph().face(faces.first).part_of_partition) {
auto pface = m_data.add_iface_to_mesh(sp_idx, faces.first);
sp.data().initial_ifaces.push_back(faces.first);
sp.set_initial(pface.second);
}
if (!m_data.igraph().face(faces.second).part_of_partition) {
auto pface = m_data.add_iface_to_mesh(sp_idx, faces.second);
sp.data().initial_ifaces.push_back(faces.second);
sp.set_initial(pface.second);
}
typename Intersection_graph::Kinetic_interval& kinetic_interval = m_data.igraph().kinetic_interval(e, sp_idx);
crossing_iedges.push_back(e);
if (emin > s) {
typename Intersection_kernel::FT bary_edge_exact = (emin - s) / (t - s);
FT bary_edge = from_exact((emin - s) / (t - s));
CGAL_assertion(bary_edge_exact >= 0);
FT time = CGAL::abs(from_exact(s - emin) / min_speed);
kinetic_interval.push_back(std::pair<FT, FT>(0, time)); // border barycentric coordinate
kinetic_interval.push_back(std::pair<FT, FT>(bary_edge, 0));
}
else {
kinetic_interval.push_back(std::pair<FT, FT>(0, 0));
}
if (t > emax) {
typename Intersection_kernel::FT bary_edge_exact = (emax - s) / (t - s);
FT bary_edge = from_exact((emax - s) / (t - s));
CGAL_assertion(0 <= bary_edge_exact && bary_edge_exact <= 1);
FT time = CGAL::abs(from_exact(emax - t) / max_speed);
kinetic_interval.push_back(std::pair<FT, FT>(bary_edge, 0));
kinetic_interval.push_back(std::pair<FT, FT>(1, time)); // border barycentric coordinate
}
else
kinetic_interval.push_back(std::pair<FT, FT>(1, 0));
}
}
else if (t < emax && emin < s) {
std::pair<IFace, IFace> faces;
m_data.igraph().get_faces(sp_idx, e, faces);
polygons_assigned = true;
if (!m_data.igraph().face(faces.first).part_of_partition) {
auto pface = m_data.add_iface_to_mesh(sp_idx, faces.first);
sp.data().initial_ifaces.push_back(faces.first);
sp.set_initial(pface.second);
}
if (!m_data.igraph().face(faces.second).part_of_partition) {
auto pface = m_data.add_iface_to_mesh(sp_idx, faces.second);
sp.data().initial_ifaces.push_back(faces.second);
sp.set_initial(pface.second);
}
typename Intersection_graph::Kinetic_interval& kinetic_interval = m_data.igraph().kinetic_interval(e, sp_idx);
crossing_iedges.push_back(e);
if (s > emax) {
typename Intersection_kernel::FT bary_edge_exact = (s - emax) / (s - t);
FT bary_edge = from_exact((s - emax) / (s - t));
CGAL_assertion(0 <= bary_edge_exact && bary_edge_exact <= 1);
FT time = CGAL::abs(from_exact(emax - s) / max_speed);
kinetic_interval.push_back(std::pair<FT, FT>(0, time)); // border barycentric coordinate
kinetic_interval.push_back(std::pair<FT, FT>(bary_edge, 0));
}
else
kinetic_interval.push_back(std::pair<FT, FT>(0, 0));
if (emin > t) {
typename Intersection_kernel::FT bary_edge_exact = (s - emin) / (s - t);
FT bary_edge = from_exact(bary_edge_exact);
CGAL_assertion(0 <= bary_edge_exact && bary_edge_exact <= 1);
FT time = CGAL::abs(from_exact(t - emin) / min_speed);
kinetic_interval.push_back(std::pair<FT, FT>(bary_edge, 0));
kinetic_interval.push_back(std::pair<FT, FT>(1, time)); // border barycentric coordinate
}
else
kinetic_interval.push_back(std::pair<FT, FT>(1, 0));
}
}
}
}
// If no faces have been assigned, the input polygon lies completely inside a face.
// Every IFace is checked whether the polygon, or just a single vertex, lies inside.
// The implementation takes advantage of the IFace being convex.
if (!polygons_assigned) {
IFace face = IFace(-1);
for (auto& f : sp.ifaces()) {
Face_property& fp = m_data.igraph().face(f);
typename Intersection_kernel::Point_2 p = to_exact(sp.data().centroid);
bool outside = false;
// poly, vertices and edges in IFace are oriented ccw
for (std::size_t i = 0; i < fp.pts.size(); i++) {
typename Intersection_kernel::Vector_2 ts = fp.pts[(i + fp.pts.size() - 1) % fp.pts.size()] - p;
typename Intersection_kernel::Vector_2 tt = fp.pts[i] - p;
bool ccw = (tt.x() * ts.y() - tt.y() * ts.x()) <= 0;
if (!ccw) {
outside = true;
break;
}
}
if (!outside) {
if (face == IFace(-1))
face = f;
else {
std::cout << "Two faces found for " << sp_idx << " sp, f1 " << face << " f2 " << f << std::endl;
}
}
}
if (face != IFace(-1)) {
if (!m_data.igraph().face(face).part_of_partition) {
auto pface = m_data.add_iface_to_mesh(sp_idx, face);
sp.data().initial_ifaces.push_back(face);
sp.set_initial(pface.second);
}
}
else
std::cout << "No IFace found for sp " << sp_idx << std::endl;
}
}
}
void bounding_box_to_polygons(const std::array<typename Intersection_kernel::Point_3, 8>& bbox, std::vector<std::vector<typename Intersection_kernel::Point_3> >& bbox_faces) const {
bbox_faces.clear();
bbox_faces.reserve(6);
bbox_faces.push_back({ bbox[0], bbox[1], bbox[2], bbox[3] }); // zmin
bbox_faces.push_back({ bbox[0], bbox[5], bbox[6], bbox[1] }); // ymin
bbox_faces.push_back({ bbox[1], bbox[6], bbox[7], bbox[2] }); // xmax
bbox_faces.push_back({ bbox[2], bbox[7], bbox[4], bbox[3] }); // ymax
bbox_faces.push_back({ bbox[3], bbox[4], bbox[5], bbox[0] }); // xmin
bbox_faces.push_back({ bbox[5], bbox[4], bbox[7], bbox[6] }); // zmax
CGAL_assertion(bbox_faces.size() == 6);
}
void add_polygons(const std::vector<std::vector<typename Intersection_kernel::Point_3> >& bbox_faces, std::vector<std::size_t>& input_polygons) {
add_bbox_faces(bbox_faces);
// Filter input polygons
std::vector<bool> remove(input_polygons.size(), false);
for (std::size_t i = 0; i < 6; i++)
for (std::size_t j = 0; j < m_input_planes.size(); j++)
if (m_data.support_plane(i).exact_plane() == m_input_planes[j] || m_data.support_plane(i).exact_plane() == m_input_planes[j].opposite()) {
m_data.support_plane(i).set_input_polygon(j);
remove[j] = true;
}
std::size_t write = 0;
for (std::size_t i = 0; i < input_polygons.size(); i++)
if (!remove[i]) {
m_input_polygons[write] = m_input_polygons[i];
m_input_planes[write] = m_input_planes[i];
input_polygons[write] = input_polygons[i];
write++;
}
m_input_polygons.resize(write);
m_input_planes.resize(write);
input_polygons.resize(write);
add_input_polygons();
}
void add_bbox_faces(const std::vector< std::vector<typename Intersection_kernel::Point_3> >& bbox_faces) {
for (const auto& bbox_face : bbox_faces)
m_data.add_bbox_polygon(bbox_face);
CGAL_assertion(m_data.number_of_support_planes() == 6);
CGAL_assertion(m_data.ivertices().size() == 8);
CGAL_assertion(m_data.iedges().size() == 12);
if (m_parameters.verbose) {
std::cout << "* inserted bbox faces: " << bbox_faces.size() << std::endl;
}
}
void add_input_polygons() {
using Polygon_2 = std::vector<Point_2>;
using Indices = std::vector<std::size_t>;
std::map< std::size_t, std::pair<Polygon_2, Indices> > polygons;
preprocess_polygons(polygons);
for (const auto& item : polygons) {
const std::size_t support_plane_idx = item.first;
const auto& pair = item.second;
const Polygon_2& polygon = pair.first;
const Indices& input_indices = pair.second;
m_data.add_input_polygon(support_plane_idx, input_indices, polygon);
m_data.support_plane(support_plane_idx).set_input_polygon(static_cast<int>(item.first) - 6);
}
//dump_polygons(m_data, polygons, m_data.prefix() + "inserted-polygons");
CGAL_assertion(m_data.number_of_support_planes() >= 6);
if (m_parameters.verbose) {
std::cout << "* provided input polygons: " << m_data.input_polygons().size() << std::endl;
std::cout << "* inserted input polygons: " << polygons.size() << std::endl;
}
}
template<typename PointRange>
void convert_polygon(const std::size_t support_plane_idx, const PointRange& polygon_3, std::vector<Point_2>& polygon_2) {
polygon_2.clear();
polygon_2.reserve(polygon_3.size());
for (const auto& point : polygon_3) {
const Point_3 converted(static_cast<FT>(point.x()), static_cast<FT>(point.y()), static_cast<FT>(point.z()));
polygon_2.push_back(m_data.support_plane(support_plane_idx).to_2d(converted));
}
CGAL_assertion(polygon_2.size() == polygon_3.size());
}
void preprocess_polygons(std::map< std::size_t, std::pair<std::vector<Point_2>, std::vector<std::size_t> > >& polygons) {
std::size_t input_index = 0;
std::vector<Point_2> polygon_2;
std::vector<std::size_t> input_indices;
for (std::size_t i = 0; i < m_input_polygons.size(); i++) {
bool is_added = true;
std::size_t support_plane_idx = std::size_t(-1);
std::tie(support_plane_idx, is_added) = m_data.add_support_plane(m_input_polygons[i], false, m_input_planes[i]);
CGAL_assertion(support_plane_idx != std::size_t(-1));
convert_polygon(support_plane_idx, m_input_polygons[i], polygon_2);
if (is_added) {
input_indices.clear();
input_indices.push_back(input_index);
polygons[support_plane_idx] = std::make_pair(polygon_2, input_indices);
}
else {
CGAL_assertion(polygons.find(support_plane_idx) != polygons.end());
auto& pair = polygons.at(support_plane_idx);
auto& other_polygon = pair.first;
auto& other_indices = pair.second;
other_indices.push_back(input_index);
merge_polygons(support_plane_idx, polygon_2, other_polygon);
}
++input_index;
}
}
void merge_polygons(const std::size_t support_plane_idx, const std::vector<Point_2>& polygon_a, std::vector<Point_2>& polygon_b) {
const bool is_debug = false;
CGAL_assertion(support_plane_idx >= 6);
if (is_debug) {
std::cout << std::endl << "support plane idx: " << support_plane_idx << std::endl;
}
// Add points from a to b.
auto& points = polygon_b;
for (const auto& point : polygon_a) {
points.push_back(point);
}
// Create the merged polygon.
std::vector<Point_2> merged;
create_merged_polygon(points, merged);
if (is_debug) {
std::cout << "merged polygon: " << std::endl;
for (std::size_t i = 0; i < merged.size(); ++i) {
const std::size_t ip = (i + 1) % merged.size();
const auto& p = merged[i];
const auto& q = merged[ip];
std::cout << "2 " <<
m_data.to_3d(support_plane_idx, p) << " " <<
m_data.to_3d(support_plane_idx, q) << std::endl;
}
}
// Update b with the new merged polygon.
polygon_b = merged;
}
void create_merged_polygon(const std::vector<Point_2>& points, std::vector<Point_2>& merged) const {
merged.clear();
CGAL::convex_hull_2(points.begin(), points.end(), std::back_inserter(merged));
CGAL_assertion(merged.size() >= 3);
}
void create_bbox_meshes() {
for (std::size_t i = 0; i < 6; i++) {
m_data.clear_pfaces(i);
std::set<IFace> ifaces = m_data.support_plane(i).ifaces();
for (auto iface : ifaces) {
m_data.add_iface_to_mesh(i, iface);
}
}
}
void make_polygons_intersection_free() {
// First, create all transverse intersection lines.
using Map_p2vv = std::map<std::set<std::size_t>, std::pair<IVertex, IVertex> >;
Map_p2vv map_p2vv;
for (const auto ivertex : m_data.ivertices()) {
const auto key = m_data.intersected_planes(ivertex, false);
if (key.size() < 2) {
continue;
}
const auto pair = map_p2vv.insert(
std::make_pair(key, std::make_pair(ivertex, IVertex())));
const bool is_inserted = pair.second;
if (!is_inserted) {
pair.first->second.second = ivertex;
}
}
// Then, intersect these lines to find internal intersection vertices.
using Pair_pv = std::pair< std::set<std::size_t>, std::vector<IVertex> >;
std::vector<Pair_pv> todo;
for (auto it_a = map_p2vv.begin(); it_a != map_p2vv.end(); ++it_a) {
const auto& set_a = it_a->first;
todo.push_back(std::make_pair(set_a, std::vector<IVertex>()));
auto& crossed_vertices = todo.back().second;
crossed_vertices.push_back(it_a->second.first);
std::set<std::set<std::size_t>> done;
for (auto it_b = map_p2vv.begin(); it_b != map_p2vv.end(); ++it_b) {
const auto& set_b = it_b->first;
std::size_t common_plane_idx = std::size_t(-1);
const std::function<void(const std::size_t idx)> lambda =
[&](const std::size_t idx) {
common_plane_idx = idx;
};
std::set_intersection(
set_a.begin(), set_a.end(),
set_b.begin(), set_b.end(),
boost::make_function_output_iterator(lambda)
);
if (common_plane_idx != std::size_t(-1)) {
auto union_set = set_a;
union_set.insert(set_b.begin(), set_b.end());
if (!done.insert(union_set).second) {
continue;
}
typename Intersection_kernel::Point_2 point;
typename Intersection_kernel::Segment_3 seg_a(m_data.point_3(it_a->second.first), m_data.point_3(it_a->second.second));
typename Intersection_kernel::Segment_3 seg_b(m_data.point_3(it_b->second.first), m_data.point_3(it_b->second.second));
if (!intersection(m_data.support_plane(common_plane_idx).to_2d(seg_a), m_data.support_plane(common_plane_idx).to_2d(seg_b), point))
continue;
crossed_vertices.push_back(
m_data.add_ivertex(m_data.support_plane(common_plane_idx).to_3d(point), union_set));
}
}
crossed_vertices.push_back(it_a->second.second);
}
for (auto& t : todo) {
m_data.add_iedge(t.first, t.second);
}
return;
}
template<typename Type1, typename Type2, typename ResultType>
inline bool intersection(const Type1& t1, const Type2& t2, ResultType& result) const {
const auto inter = CGAL::intersection(t1, t2);
if (!inter) return false;
if (CGAL::assign(result, inter))
return true;
return false;
}
};
#endif //DOXYGEN_RUNNING
} // namespace internal
} // namespace KSP_3
} // namespace CGAL
#endif // CGAL_KSP_3_INITIALIZER_H

View File

@ -0,0 +1,436 @@
// Copyright (c) 2023 GeometryFactory Sarl (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s) : Sven Oesau, Florent Lafarge, Dmitry Anisimov, Simon Giraudot
#ifndef CGAL_KSP_3_INTERSECTION_GRAPH_H
#define CGAL_KSP_3_INTERSECTION_GRAPH_H
#include <CGAL/license/Kinetic_space_partition.h>
// Boost includes.
#include <boost/graph/adjacency_list.hpp>
// CGAL includes.
#include <CGAL/Cartesian_converter.h>
#include <CGAL/Polygon_2.h>
// Internal includes.
#include <CGAL/KSP/utils.h>
namespace CGAL {
namespace KSP_3 {
namespace internal {
#ifdef DOXYGEN_RUNNING
#else
template<typename GeomTraits, typename IntersectionKernel>
class Intersection_graph {
public:
using Kernel = GeomTraits;
using Intersection_kernel = IntersectionKernel;
using IkFT = typename Intersection_kernel::FT;
using Point_2 = typename Intersection_kernel::Point_2;
using Point_3 = typename Intersection_kernel::Point_3;
using Segment_3 = typename Intersection_kernel::Segment_3;
using Line_3 = typename Intersection_kernel::Line_3;
using Polygon_2 = typename CGAL::Polygon_2<Intersection_kernel>;
struct Vertex_property {
Point_3 point;
Vertex_property() {}
Vertex_property(const Point_3& point) : point(point) {}
};
using Kinetic_interval = std::vector<std::pair<IkFT, IkFT> >;
struct Edge_property {
std::size_t line;
std::size_t order;
std::map<std::size_t, std::pair<std::size_t, std::size_t> > faces; // For each intersecting support plane there is one pair of adjacent faces (or less if the edge is on the bbox)
std::set<std::size_t> planes;
std::set<std::size_t> crossed;
std::map<std::size_t, Kinetic_interval> intervals; // Maps support plane index to the kinetic interval. std::pair<FT, FT> is the barycentric coordinate and intersection time.
Edge_property() : line(std::size_t(-1)), order(edge_counter++) { }
Edge_property(const Edge_property& e) = default;
const Edge_property& operator=(const Edge_property& other) {
line = other.line;
// order = other.order;
faces = other.faces;
planes = other.planes;
crossed = other.crossed;
intervals = other.intervals;
return *this;
}
private:
static std::size_t edge_counter;
};
using Kinetic_interval_iterator = typename std::map<std::size_t, Kinetic_interval>::const_iterator;
using Graph = boost::adjacency_list<
boost::setS, boost::vecS, boost::undirectedS,
Vertex_property, Edge_property>;
using Vertex_descriptor = typename boost::graph_traits<Graph>::vertex_descriptor;
using Edge_descriptor = typename boost::graph_traits<Graph>::edge_descriptor;
using Face_descriptor = std::size_t;
struct lex {
bool operator()(const Edge_descriptor& a, const Edge_descriptor& b) const {
Edge_property* pa = (Edge_property*)a.get_property();
Edge_property* pb = (Edge_property*)b.get_property();
return pa->order < pb->order;
}
};
using IEdge_set = std::set<Edge_descriptor, lex>;
struct Face_property {
Face_property() : support_plane(static_cast<std::size_t>(-1)), part_of_partition(false) {}
Face_property(std::size_t support_plane_idx) : support_plane(support_plane_idx), part_of_partition(false) {}
std::size_t support_plane;
bool part_of_partition;
CGAL::Polygon_2<Intersection_kernel> poly;
std::vector<Point_2> pts;
std::vector<Edge_descriptor> edges;
std::vector<Vertex_descriptor> vertices;
bool is_part(Edge_descriptor a, Edge_descriptor b) {
std::size_t aidx = std::size_t(-1);
for (std::size_t i = 0; i < edges.size(); i++) {
if (edges[i] == a) {
aidx = i;
break;
}
}
if (aidx == std::size_t(-1))
return false;
if (edges[(aidx + 1) % edges.size()] == b || edges[(aidx + edges.size() - 1) % edges.size()] == b)
return true;
return false;
}
};
private:
Graph m_graph;
std::vector<Line_3> m_lines;
std::size_t m_nb_lines_on_bbox;
std::map<Point_3, Vertex_descriptor> m_map_points;
std::map<std::vector<std::size_t>, Vertex_descriptor> m_map_vertices;
std::vector<Face_property> m_ifaces;
std::vector<bool> m_initial_part_of_partition;
std::vector<std::map<std::size_t, Kinetic_interval> > m_initial_intervals;
std::vector<std::set<std::size_t> > m_initial_crossed;
public:
Intersection_graph() :
m_nb_lines_on_bbox(0)
{ }
void clear() {
m_graph.clear();
m_map_points.clear();
m_map_vertices.clear();
}
std::size_t number_of_vertices() const {
return static_cast<std::size_t>(boost::num_vertices(m_graph));
}
std::size_t number_of_faces() const {
return m_ifaces.size();
}
static Vertex_descriptor null_ivertex() {
return boost::graph_traits<Graph>::null_vertex();
}
static Edge_descriptor null_iedge() {
return Edge_descriptor(null_ivertex(), null_ivertex(), nullptr);
}
static Face_descriptor null_iface() {
return std::size_t(-1);
}
std::size_t add_line(const Line_3& line) {
m_lines.push_back(line);
return m_lines.size() - 1;
}
std::size_t nb_lines() const { return m_lines.size(); }
const std::pair<Vertex_descriptor, bool> add_vertex(const Point_3& point) {
const auto pair = m_map_points.insert(std::make_pair(point, Vertex_descriptor()));
const auto is_inserted = pair.second;
if (is_inserted) {
pair.first->second = boost::add_vertex(m_graph);
m_graph[pair.first->second].point = point;
}
return std::make_pair(pair.first->second, is_inserted);
}
const std::pair<Vertex_descriptor, bool> add_vertex(
const Point_3& point, const std::vector<std::size_t>& intersected_planes) {
const auto pair = m_map_vertices.insert(std::make_pair(intersected_planes, Vertex_descriptor()));
const auto is_inserted = pair.second;
if (is_inserted) {
pair.first->second = boost::add_vertex(m_graph);
m_graph[pair.first->second].point = point;
}
return std::make_pair(pair.first->second, is_inserted);
}
const std::pair<Edge_descriptor, bool> add_edge(
const Vertex_descriptor& source, const Vertex_descriptor& target,
const std::size_t support_plane_idx) {
const auto out = boost::add_edge(source, target, m_graph);
m_graph[out.first].planes.insert(support_plane_idx);
return out;
}
template<typename IndexContainer>
const std::pair<Edge_descriptor, bool> add_edge(
const Vertex_descriptor& source, const Vertex_descriptor& target,
const IndexContainer& support_planes_idx) {
const auto out = boost::add_edge(source, target, m_graph);
for (const auto support_plane_idx : support_planes_idx) {
m_graph[out.first].planes.insert(support_plane_idx);
}
return out;
}
const std::pair<Edge_descriptor, bool> add_edge(const Point_3& source, const Point_3& target) {
return add_edge(add_vertex(source).first, add_vertex(target).first);
}
std::size_t add_face(std::size_t support_plane_idx) {
m_ifaces.push_back(Face_property(support_plane_idx));
return std::size_t(m_ifaces.size() - 1);
}
bool add_face(std::size_t sp_idx, const Edge_descriptor& edge, const Face_descriptor& idx) {
auto pair = m_graph[edge].faces.insert(std::make_pair(sp_idx, std::pair<Face_descriptor, Face_descriptor>(-1, -1)));
if (pair.first->second.first == static_cast<std::size_t>(-1)) {
pair.first->second.first = idx;
return true;
}
else if (pair.first->second.second == static_cast<std::size_t>(-1)) {
pair.first->second.second = idx;
return true;
}
return false;
}
void get_faces(std::size_t sp_idx, const Edge_descriptor& edge, std::pair<Face_descriptor, Face_descriptor>& pair) const {
auto it = m_graph[edge].faces.find(sp_idx);
if (it != m_graph[edge].faces.end())
pair = it->second;
}
const Face_property& face(Face_descriptor idx) const {
CGAL_assertion(idx < m_ifaces.size());
return m_ifaces[idx];
}
Face_property& face(Face_descriptor idx) {
CGAL_assertion(idx < m_ifaces.size());
return m_ifaces[idx];
}
const Edge_property& edge(Edge_descriptor idx) const {
return m_graph[idx];
}
void set_line(const Edge_descriptor& edge, const std::size_t line_idx) {
m_graph[edge].line = line_idx;
}
std::size_t line(const Edge_descriptor& edge) const {
return m_graph[edge].line;
}
const Line_3& line(std::size_t line_idx) const {
return m_lines[line_idx];
}
bool line_is_on_bbox(std::size_t line_idx) const {
return line_idx < m_nb_lines_on_bbox;
}
bool line_is_bbox_edge(std::size_t line_idx) const {
return line_idx < 12;
}
bool iedge_is_on_bbox(Edge_descriptor e) {
return line(e) < m_nb_lines_on_bbox;
}
void finished_bbox() {
m_nb_lines_on_bbox = m_lines.size();
}
void initialization_done() {
auto e = edges();
m_initial_crossed.resize(e.size());
m_initial_intervals.resize(e.size());
std::size_t idx = 0;
for (const auto& edge : e) {
m_initial_intervals[idx] = m_graph[edge].intervals;
m_initial_crossed[idx++] = m_graph[edge].crossed;
}
m_initial_part_of_partition.resize(m_ifaces.size());
for (idx = 0; idx < m_ifaces.size(); idx++)
m_initial_part_of_partition[idx] = m_ifaces[idx].part_of_partition;
}
void reset_to_initialization() {
auto e = edges();
CGAL_assertion(e.size() == m_initial_crossed.size());
CGAL_assertion(e.size() == m_initial_intervals.size());
std::size_t idx = 0;
for (auto edge : e) {
m_graph[edge].intervals = m_initial_intervals[idx];
m_graph[edge].crossed = m_initial_crossed[idx++];
}
CGAL_assertion(m_ifaces.size() == m_initial_part_of_partition.size());
for (idx = 0; idx < m_ifaces.size(); idx++)
m_ifaces[idx].part_of_partition = m_initial_part_of_partition[idx];
}
const std::pair<Edge_descriptor, Edge_descriptor>
split_edge(const Edge_descriptor& edge, const Vertex_descriptor& vertex) {
const auto source = boost::source(edge, m_graph);
const auto target = boost::target(edge, m_graph);
const auto prop = m_graph[edge];
boost::remove_edge(edge, m_graph);
bool is_inserted;
Edge_descriptor sedge;
std::tie(sedge, is_inserted) = boost::add_edge(source, vertex, m_graph);
if (!is_inserted) {
std::cerr << "WARNING: " << segment_3(edge) << " " << point_3(vertex) << std::endl;
}
CGAL_assertion(is_inserted);
m_graph[sedge] = prop;
Edge_descriptor tedge;
std::tie(tedge, is_inserted) = boost::add_edge(vertex, target, m_graph);
if (!is_inserted) {
std::cerr << "WARNING: " << segment_3(edge) << " " << point_3(vertex) << std::endl;
}
CGAL_assertion(is_inserted);
m_graph[tedge] = prop;
return std::make_pair(sedge, tedge);
}
decltype(auto) vertices() const {
return CGAL::make_range(boost::vertices(m_graph));
}
decltype(auto) edges() const {
return CGAL::make_range(boost::edges(m_graph));
}
std::vector<Face_descriptor>& faces() {
return m_ifaces;
}
const std::vector<Face_descriptor>& faces() const {
return m_ifaces;
}
const Vertex_descriptor source(const Edge_descriptor& edge) const {
return boost::source(edge, m_graph);
}
const Vertex_descriptor target(const Edge_descriptor& edge) const {
return boost::target(edge, m_graph);
}
bool is_edge(const Vertex_descriptor& source, const Vertex_descriptor& target) const {
return boost::edge(source, target, m_graph).second;
}
const Edge_descriptor edge(const Vertex_descriptor& source, const Vertex_descriptor& target) const {
return boost::edge(source, target, m_graph).first;
}
decltype(auto) incident_edges(const Vertex_descriptor& vertex) const {
return CGAL::make_range(boost::out_edges(vertex, m_graph));
}
const std::set<std::size_t>& intersected_planes(const Edge_descriptor& edge) const {
return m_graph[edge].planes;
}
std::set<std::size_t>& intersected_planes(const Edge_descriptor& edge) {
return m_graph[edge].planes;
}
const std::pair<Kinetic_interval_iterator, Kinetic_interval_iterator> kinetic_intervals(const Edge_descriptor& edge) {
return std::pair<Kinetic_interval_iterator, Kinetic_interval_iterator>(m_graph[edge].intervals.begin(), m_graph[edge].intervals.end());
}
Kinetic_interval& kinetic_interval(const Edge_descriptor& edge, std::size_t sp_idx) {
return m_graph[edge].intervals[sp_idx];
}
const Point_3& point_3(const Vertex_descriptor& vertex) const {
return m_graph[vertex].point;
}
const Segment_3 segment_3(const Edge_descriptor& edge) const {
return Segment_3(
m_graph[boost::source(edge, m_graph)].point,
m_graph[boost::target(edge, m_graph)].point);
}
const Line_3 line_3(const Edge_descriptor& edge) const {
return Line_3(
m_graph[boost::source(edge, m_graph)].point,
m_graph[boost::target(edge, m_graph)].point);
}
bool has_crossed(const Edge_descriptor& edge, std::size_t sp_idx) {
return m_graph[edge].crossed.count(sp_idx) == 1;
}
void set_crossed(const Edge_descriptor& edge, std::size_t sp_idx) {
CGAL_assertion(false);
m_graph[edge].crossed.insert(sp_idx);
}
};
template<typename GeomTraits, typename IntersectionKernel> std::size_t Intersection_graph<GeomTraits, IntersectionKernel>::Edge_property::edge_counter = 0;
#endif //DOXYGEN_RUNNING
} // namespace internal
} // namespace KSP_3
} // namespace CGAL
#endif // CGAL_KSP_3_INTERSECTION_GRAPH_H

View File

@ -0,0 +1,232 @@
// Copyright (c) 2023 GeometryFactory Sarl (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s) : Sven Oesau, Florent Lafarge, Dmitry Anisimov, Simon Giraudot
#ifndef CGAL_KSP_3_FACEPROPAGATION_H
#define CGAL_KSP_3_FACEPROPAGATION_H
#include <CGAL/license/Kinetic_space_partition.h>
// Internal includes.
#include <CGAL/KSP/utils.h>
#include <CGAL/KSP/debug.h>
#include <CGAL/KSP/parameters.h>
#include <CGAL/KSP_3/Data_structure.h>
namespace CGAL {
namespace KSP_3 {
namespace internal {
#ifdef DOXYGEN_RUNNING
#else
template<typename GeomTraits, typename IntersectionKernel>
class Propagation {
public:
using Kernel = GeomTraits;
using Intersection_kernel = IntersectionKernel;
private:
using FT = typename Kernel::FT;
using IkFT = typename Intersection_kernel::FT;
using Point_2 = typename Kernel::Point_2;
using Vector_2 = typename Kernel::Vector_2;
using Segment_2 = typename Kernel::Segment_2;
using Direction_2 = typename Kernel::Direction_2;
using Line_2 = typename Kernel::Line_2;
using Data_structure = CGAL::KSP_3::internal::Data_structure<Kernel, Intersection_kernel>;
using IVertex = typename Data_structure::IVertex;
using IEdge = typename Data_structure::IEdge;
using IFace = typename Data_structure::IFace;
using PVertex = typename Data_structure::PVertex;
using PEdge = typename Data_structure::PEdge;
using PFace = typename Data_structure::PFace;
using Bbox_2 = CGAL::Bbox_2;
using Face_index = typename Data_structure::Face_index;
using Parameters = KSP::internal::Parameters_3<FT>;
using Face_event = typename Data_structure::Support_plane::Face_event;
using From_exact = CGAL::Cartesian_converter<Intersection_kernel, Kernel>;
struct Face_event_order {
bool operator()(const Face_event& a, const Face_event& b) {
return a.time > b.time;
}
};
public:
Propagation(Data_structure& data, const Parameters& parameters) :
m_data(data), m_parameters(parameters),
m_min_time(-FT(1)), m_max_time(-FT(1))
{ }
std::size_t propagate(std::size_t k) {
std::size_t num_events = 0;
m_data.reset_to_initialization();
for (std::size_t i = 0; i < m_data.number_of_support_planes(); ++i)
m_data.k(i) = static_cast<int>(k);
initialize_queue();
while (!m_face_queue.empty())
run(num_events);
return num_events;
}
void clear() {
m_face_queue.clear();
m_min_time = -FT(1);
m_max_time = -FT(1);
}
private:
Data_structure& m_data;
const Parameters& m_parameters;
FT m_min_time;
FT m_max_time;
std::priority_queue<typename Data_structure::Support_plane::Face_event, std::vector<Face_event>, Face_event_order> m_face_queue;
/*******************************
** IDENTIFY EVENTS **
********************************/
void initialize_queue() {
m_data.fill_event_queue(m_face_queue);
}
/*******************************
** RUNNING **
********************************/
std::size_t run(
const std::size_t initial_iteration) {
std::size_t iteration = initial_iteration;
while (!m_face_queue.empty()) {
// m_queue.print();
const Face_event event = m_face_queue.top();
m_face_queue.pop();
++iteration;
apply(event);
}
return iteration;
}
/*******************************
** HANDLE EVENTS **
********************************/
void apply(const Face_event& event) {
if (m_data.igraph().face(event.face).part_of_partition) {
return;
}
std::size_t line = m_data.line_idx(event.crossed_edge);
auto& sp = m_data.support_plane(event.support_plane);
int& k = sp.k();
const typename Data_structure::Intersection_graph::Face_property& face = m_data.igraph().face(event.face);
From_exact from_exact;
Point_2 on_edge = sp.to_2d(from_exact(m_data.point_3(m_data.source(event.crossed_edge))));
Point_2 center = from_exact(CGAL::centroid(face.pts.begin(), face.pts.end()));
Point_2 origin = sp.centroid();
Vector_2 dir = sp.to_2d(from_exact(m_data.igraph().line(line).to_vector()));
dir = Vector_2(-dir.y(), dir.x()); // Vector orthogonal to line.
bool need_check = false;
// Only allow crossing edges away from the input polygon centroid.
if (((center - on_edge) * dir) * ((origin - on_edge) * dir) > 0)
need_check = true;
if (need_check || !sp.has_crossed_line(line)) {
// Check intersection against kinetic intervals from other support planes
int crossing = 0;
auto kis = m_data.igraph().kinetic_intervals(event.crossed_edge);
for (auto ki = kis.first; ki != kis.second; ki++) {
if (ki->first == event.support_plane)
continue;
for (std::size_t i = 0; i < ki->second.size(); i++) {
// Exactly on one
if (ki->second[i].first == event.intersection_bary) {
if (ki->second[i].second < event.time) {
crossing++;
}
break;
}
// Within an interval
if (ki->second[i].first > event.intersection_bary && ki->second[i - 1].first < event.intersection_bary) {
IkFT interval_pos = (event.intersection_bary - ki->second[i - 1].first) / (ki->second[i].first - ki->second[i - 1].first);
IkFT interval_time = interval_pos * (ki->second[i].second - ki->second[i - 1].second) + ki->second[i - 1].second;
if (event.time > interval_time) {
crossing++;
}
break;
}
}
}
// Check if the k value is sufficient for crossing the edge.
if (k <= crossing)
return;
// The edge can be crossed.
// Adjust k value
k -= crossing;
m_data.support_plane(event.support_plane).set_crossed_line(line);
}
// Associate IFace to mesh.
PFace f = m_data.add_iface_to_mesh(event.support_plane, event.face);
// Calculate events for new border edges.
// Iterate inside of this face, check if each opposite edge is border and on bbox and then calculate intersection times.
std::vector<IEdge> border;
m_data.support_plane(event.support_plane).get_border(m_data.igraph(), f.second, border);
for (IEdge edge : border) {
Face_event fe;
IkFT t = m_data.calculate_edge_intersection_time(event.support_plane, edge, fe);
if (t > 0)
m_face_queue.push(fe);
}
}
};
#endif //DOXYGEN_RUNNING
} // namespace internal
} // namespace KSP_3
} // namespace CGAL
#endif // CGAL_KSP_3_FACEPROPAGATION_H

View File

@ -0,0 +1,771 @@
// Copyright (c) 2023 GeometryFactory Sarl (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s) : Sven Oesau, Florent Lafarge, Dmitry Anisimov, Simon Giraudot
#ifndef CGAL_KSP_3_SUPPORT_PLANE_H
#define CGAL_KSP_3_SUPPORT_PLANE_H
#include <CGAL/license/Kinetic_space_partition.h>
// CGAL includes.
#include <CGAL/Surface_mesh.h>
#include <CGAL/centroid.h>
// Internal includes.
#include <CGAL/KSP/utils.h>
#include <CGAL/KSP_3/Intersection_graph.h>
namespace CGAL {
namespace KSP_3 {
namespace internal {
#ifdef DOXYGEN_RUNNING
#else
template<typename GeomTraits, typename IntersectionKernel>
class Support_plane {
public:
using Kernel = GeomTraits;
using Intersection_kernel = IntersectionKernel;
using To_exact = CGAL::Cartesian_converter<Kernel, Intersection_kernel>;
using From_exact = CGAL::Cartesian_converter<Intersection_kernel, Kernel>;
using FT = typename Kernel::FT;
using Point_2 = typename Kernel::Point_2;
using Point_3 = typename Kernel::Point_3;
using Vector_2 = typename Kernel::Vector_2;
using Vector_3 = typename Kernel::Vector_3;
using Direction_2 = typename Kernel::Direction_2;
using Segment_2 = typename Kernel::Segment_2;
using Segment_3 = typename Kernel::Segment_3;
using Line_2 = typename Kernel::Line_2;
using Line_3 = typename Kernel::Line_3;
using Plane_3 = typename Kernel::Plane_3;
using Triangle_2 = typename Kernel::Triangle_2;
using Mesh = CGAL::Surface_mesh<Point_2>;
using Intersection_graph = CGAL::KSP_3::internal::Intersection_graph<Kernel, Intersection_kernel>;
using Bbox_2 = CGAL::Bbox_2;
using IVertex = typename Intersection_graph::Vertex_descriptor;
using IEdge = typename Intersection_graph::Edge_descriptor;
using IFace = typename Intersection_graph::Face_descriptor;
using IEdge_set = typename Intersection_graph::IEdge_set;
using Vertex_index = typename Mesh::Vertex_index;
using Face_index = typename Mesh::Face_index;
using Edge_index = typename Mesh::Edge_index;
using Halfedge_index = typename Mesh::Halfedge_index;
using V_vector_map = typename Mesh::template Property_map<Vertex_index, Vector_2>;
using V_ivertex_map = typename Mesh::template Property_map<Vertex_index, IVertex>;
using V_iedge_map = typename Mesh::template Property_map<Vertex_index, IEdge>;
using V_bool_map = typename Mesh::template Property_map<Vertex_index, bool>;
using E_iedge_map = typename Mesh::template Property_map<Edge_index, IEdge>;
using F_index_map = typename Mesh::template Property_map<Face_index, std::vector<std::size_t> >;
using F_uint_map = typename Mesh::template Property_map<Face_index, unsigned int>;
using F_bool_map = typename Mesh::template Property_map<Face_index, bool>;
using V_original_map = typename Mesh::template Property_map<Vertex_index, bool>;
using V_time_map = typename Mesh::template Property_map<Vertex_index, std::vector<FT> >;
struct Face_event {
Face_event() {}
Face_event(std::size_t sp_idx, FT time, IEdge edge, IFace face) : support_plane(sp_idx), time(time), crossed_edge(edge), face(face) {}
std::size_t support_plane;
typename Intersection_kernel::FT time;
typename Intersection_kernel::FT intersection_bary;
IEdge crossed_edge;
IFace face; // The face that does not yet belong to the region.
};
struct Data {
Data() : mesh(),
v_ivertex_map(mesh.template add_property_map<Vertex_index, IVertex>("v:ivertex", Intersection_graph::null_ivertex()).first),
v_iedge_map(mesh.template add_property_map<Vertex_index, IEdge>("v:iedge", Intersection_graph::null_iedge()).first),
e_iedge_map(mesh.template add_property_map<Edge_index, IEdge>("e:iedge", Intersection_graph::null_iedge()).first),
input_map(mesh.template add_property_map<Face_index, std::vector<std::size_t> >("f:input", std::vector<std::size_t>()).first),
f_initial_map(mesh.template add_property_map<Face_index, bool >("f:initial", false).first),
v_original_map(mesh.template add_property_map<Vertex_index, bool>("v:original", false).first) {}
bool is_bbox;
Point_2 centroid;
Plane_3 plane;
typename Intersection_kernel::Plane_3 exact_plane;
Mesh mesh;
V_ivertex_map v_ivertex_map;
V_iedge_map v_iedge_map;
E_iedge_map e_iedge_map;
F_index_map input_map;
F_bool_map f_initial_map;
V_original_map v_original_map;
std::map<IEdge, std::pair<IFace, IFace> > iedge2ifaces;
std::set<IFace> ifaces; // All ifaces in the support plane
std::vector<IFace> initial_ifaces; // IFaces which intersect with the input polygon and are thus part of the mesh before the propagation starts.
std::vector<Face_index> initial_pfaces;
std::map<IVertex, Vertex_index> ivertex2pvertex;
IEdge_set unique_iedges;
std::set<std::size_t> crossed_lines;
std::vector<IEdge> iedges;
std::vector<Point_2> original_vertices;
std::vector<Vector_2> original_vectors;
std::vector<Direction_2> original_directions;
std::vector<typename Intersection_kernel::Ray_2> original_rays;
FT distance_tolerance;
FT angle_tolerance;
std::size_t actual_input_polygon;
int k;
};
private:
static constexpr bool identical_kernel = !std::is_same_v<Kernel, Intersection_kernel>;
std::shared_ptr<Data> m_data;
public:
Support_plane() : m_data(std::make_shared<Data>()) {}
template<typename PointRange>
Support_plane(const PointRange& polygon, const bool is_bbox, typename Intersection_kernel::Plane_3 plane) :
m_data(std::make_shared<Data>()) {
std::vector<Point_3> points;
points.reserve(polygon.size());
for (const auto& point : polygon) {
points.push_back(Point_3(
static_cast<FT>(point.x()),
static_cast<FT>(point.y()),
static_cast<FT>(point.z())));
}
CGAL_assertion(points.size() == polygon.size());
From_exact from_exact;
m_data->k = 0;
m_data->plane = from_exact(plane);
m_data->exact_plane = plane;
m_data->is_bbox = is_bbox;
m_data->distance_tolerance = 0;
m_data->angle_tolerance = 0;
m_data->actual_input_polygon = static_cast<std::size_t>(- 1);
std::vector<Triangle_2> tris(points.size() - 2);
for (std::size_t i = 2; i < points.size(); i++) {
tris[i - 2] = Triangle_2(to_2d(points[0]), to_2d(points[i - 1]), to_2d(points[i]));
}
m_data->centroid = CGAL::centroid(tris.begin(), tris.end(), CGAL::Dimension_tag<2>());
add_property_maps();
}
Support_plane(const std::vector<typename Intersection_kernel::Point_3>& polygon, const bool is_bbox) :
m_data(std::make_shared<Data>()) {
From_exact from_exact;
std::vector<Point_3> points;
points.reserve(polygon.size());
for (const auto& point : polygon) {
points.push_back(Point_3(
from_exact(point.x()),
from_exact(point.y()),
from_exact(point.z())));
}
CGAL_assertion_code(const std::size_t n = points.size();)
CGAL_assertion(n == polygon.size());
CGAL_assertion(n != 0);
m_data->k = 0;
m_data->exact_plane = typename Intersection_kernel::Plane_3(polygon[0], polygon[1], polygon[2]);
m_data->plane = from_exact(m_data->exact_plane);
m_data->is_bbox = is_bbox;
m_data->distance_tolerance = 0;
m_data->angle_tolerance = 0;
m_data->actual_input_polygon = static_cast<std::size_t>(- 1);
std::vector<Triangle_2> tris(points.size() - 2);
for (std::size_t i = 2; i < points.size(); i++) {
tris[i - 2] = Triangle_2(to_2d(points[0]), to_2d(points[i - 1]), to_2d(points[i]));
}
m_data->centroid = CGAL::centroid(tris.begin(), tris.end(), CGAL::Dimension_tag<2>());
add_property_maps();
}
void add_property_maps() {
m_data->v_ivertex_map = m_data->mesh.template add_property_map<Vertex_index, IVertex>("v:ivertex", Intersection_graph::null_ivertex()).first;
m_data->v_iedge_map = m_data->mesh.template add_property_map<Vertex_index, IEdge>("v:iedge", Intersection_graph::null_iedge()).first;
m_data->e_iedge_map = m_data->mesh.template add_property_map<Edge_index, IEdge>("e:iedge", Intersection_graph::null_iedge()).first;
m_data->input_map = m_data->mesh.template add_property_map<Face_index, std::vector<std::size_t> >("f:input", std::vector<std::size_t>()).first;
m_data->v_original_map = m_data->mesh.template add_property_map<Vertex_index, bool>("v:original", false).first;
m_data->f_initial_map = m_data->mesh.template add_property_map<Face_index, bool >("f:initial", false).first;
}
void link_property_maps() {
m_data->v_ivertex_map = m_data->mesh.template property_map<Vertex_index, IVertex>("v:ivertex").value();
m_data->v_iedge_map = m_data->mesh.template property_map<Vertex_index, IEdge>("v:iedge").value();
m_data->e_iedge_map = m_data->mesh.template property_map<Edge_index, IEdge>("e:iedge").value();
m_data->input_map = m_data->mesh.template property_map<Face_index, std::vector<std::size_t> >("f:input").value();
m_data->v_original_map = m_data->mesh.template property_map<Vertex_index, bool>("v:original").value();
m_data->f_initial_map = m_data->mesh.template property_map<Face_index, bool >("f:initial").value();
}
void centroid(Point_2& c) {
if (m_data->original_vertices.size() < 2)
return;
std::vector<Triangle_2> tris(m_data->original_vertices.size() - 2);
for (std::size_t i = 2; i < m_data->original_vertices.size(); i++) {
tris[i - 2] = Triangle_2(m_data->original_vertices[0], m_data->original_vertices[i - 1], m_data->original_vertices[i]);
}
c = CGAL::centroid(tris.begin(), tris.end(), CGAL::Dimension_tag<2>());
}
void get_border(Intersection_graph& igraph, std::vector<IEdge>& border) {
border.clear();
auto m = mesh();
Vertex_index s = Mesh::null_vertex();
for (auto v : m_data->mesh.vertices()) {
if (m_data->mesh.is_border(v)) {
s = v;
break;
}
}
if (s == Mesh::null_vertex()) {
std::cout << "Support plane does not have border vertices" << std::endl;
return;
}
auto h = m.halfedge(s);
if (!m.is_border(h))
h = m.opposite(h);
auto n = h;
IVertex last = ivertex(s);
do {
n = m.next(n);
IVertex current = ivertex(m.target(n));
border.push_back(igraph.edge(last, current));
last = current;
} while (n != h && n != Mesh::null_halfedge());
if (n == Mesh::null_halfedge()) {
std::cout << " NULL_HALFEDGE!";
}
//std::cout << "edges: " << border.size() << std::endl;
}
void get_border(Intersection_graph& igraph, const Face_index& fi, std::vector<IEdge>& border) {
border.clear();
auto m = mesh();
auto first = m.halfedge(fi);
auto h = first;
do {
auto o = m.opposite(h);
if (m.is_border(o))
border.push_back(igraph.edge(ivertex(m.target(h)), ivertex(m.target(o))));
h = m.next(h);
} while (h != first && h != Mesh::null_halfedge());
if (h == Mesh::null_halfedge()) {
std::cout << " NULL_HALFEDGE!";
}
//std::cout << "edges: " << border.size() << std::endl;
}
Data& data() { return *m_data; }
FT distance_tolerance() const {
return m_data->distance_tolerance;
}
FT angle_tolerance() const {
return m_data->angle_tolerance;
}
void clear_pfaces() {
m_data->mesh.clear();
add_property_maps();
}
const std::array<Vertex_index, 4>
add_bbox_polygon(
const std::array<Point_2, 4>& points,
const std::array<IVertex, 4>& ivertices) {
CGAL_assertion(CGAL::is_simple_2(points.begin(), points.end()));
CGAL_assertion(CGAL::is_convex_2(points.begin(), points.end()));
std::array<Vertex_index, 4> vertices;
for (std::size_t i = 0; i < 4; ++i) {
const auto vi = m_data->mesh.add_vertex(points[i]);
m_data->v_ivertex_map[vi] = ivertices[i];
vertices[i] = vi;
}
const auto fi = m_data->mesh.add_face(vertices);
CGAL_assertion(fi != Mesh::null_face());
auto& input_vec = m_data->input_map[fi];
CGAL_assertion(input_vec.empty());
input_vec.push_back(std::size_t(-1));
return vertices;
}
template<typename Pair>
std::size_t add_input_polygon(
const std::vector<Pair>& points,
const std::vector<std::size_t>& input_indices) {
CGAL_assertion(is_simple_polygon(points));
CGAL_assertion(is_convex_polygon(points));
CGAL_assertion(is_valid_polygon(points));
To_exact to_exact;
CGAL_assertion(points.size() >= 3);
std::vector<Triangle_2> tris(points.size() - 2);
for (std::size_t i = 2; i < points.size(); i++)
tris[i - 2] = Triangle_2(points[0].first, points[i - 1].first, points[i].first);
m_data->centroid = CGAL::centroid(tris.begin(), tris.end(), CGAL::Dimension_tag<2>());
std::vector<Vertex_index> vertices;
const std::size_t n = points.size();
CGAL_assertion(n >= 3);
vertices.reserve(n);
m_data->original_vertices.resize(n);
m_data->original_vectors.resize(n);
m_data->original_directions.resize(n);
m_data->original_rays.resize(n);
FT sum_length = FT(0);
std::vector<Vector_2> directions;
directions.reserve(n);
std::vector<std::pair<std::size_t, Direction_2> > dir_vec;
FT num = 0;
for (const auto& pair : points) {
const auto& point = pair.first;
directions.push_back(Vector_2(m_data->centroid, point));
const FT length = static_cast<FT>(
CGAL::approximate_sqrt(CGAL::abs(directions.back() * directions.back())));
sum_length += length;
num += 1;
}
CGAL_assertion(directions.size() == n);
sum_length /= num;
dir_vec.reserve(n);
for (std::size_t i = 0; i < n; i++)
dir_vec.push_back(std::pair<std::size_t, Direction_2>(i, directions[i]));
std::sort(dir_vec.begin(), dir_vec.end(),
[&](const std::pair<std::size_t, Direction_2>& a,
const std::pair<std::size_t, Direction_2>& b) -> bool {
return a.second < b.second;
});
for (std::size_t i = 0; i < n; ++i) {
const auto& point = points[dir_vec[i].first].first;
const auto vi = m_data->mesh.add_vertex(point);
m_data->original_vertices[i] = point;
m_data->original_vectors[i] = directions[dir_vec[i].first] / sum_length;
m_data->original_directions[i] = Direction_2(directions[dir_vec[i].first]);
m_data->original_rays[i] = typename Intersection_kernel::Ray_2(to_exact(point), to_exact(m_data->original_vectors[i]));
m_data->v_original_map[vi] = true;
vertices.push_back(vi);
}
for (std::size_t i = 0; i < m_data->original_directions.size(); i++) {
for (std::size_t j = 0; j < m_data->original_directions.size(); j++) {
if (j < i)
assert(m_data->original_directions[j] < m_data->original_directions[i]);
if (j > i)
assert(m_data->original_directions[i] < m_data->original_directions[j]);
}
}
const auto fi = m_data->mesh.add_face(vertices);
CGAL_assertion(fi != Mesh::null_face());
auto& input_vec = m_data->input_map[fi];
CGAL_assertion(input_vec.empty());
for (const std::size_t input_index : input_indices) {
input_vec.push_back(input_index);
}
return static_cast<std::size_t>(fi);
}
bool has_crossed_line(std::size_t line) const {
return m_data->crossed_lines.find(line) != m_data->crossed_lines.end();
}
void set_crossed_line(std::size_t line) {
m_data->crossed_lines.insert(line);
}
void set_input_polygon(std::size_t input_polygon_idx) {
m_data->actual_input_polygon = input_polygon_idx;
}
template<typename Pair>
bool is_valid_polygon(const std::vector<Pair>& polygon) const {
for (std::size_t i = 0; i < polygon.size(); ++i) {
const std::size_t ip = (i + 1) % polygon.size();
const auto& p = polygon[i].first;
const auto& q = polygon[ip].first;
const bool is_equal_zero = (KSP::internal::distance(p, q) == 0);
CGAL_assertion_msg(!is_equal_zero,
"ERROR: WE HAVE EQUAL POINTS IN THE INPUT POLYGON!");
if (is_equal_zero) return false;
}
return true;
}
template<typename Pair>
bool is_simple_polygon(const std::vector<Pair>& points) const {
std::vector<Point_2> polygon;
polygon.reserve(points.size());
for (const auto& pair : points)
polygon.push_back(pair.first);
CGAL_assertion(polygon.size() == points.size());
return CGAL::is_simple_2(polygon.begin(), polygon.end());
}
template<typename Pair>
bool is_convex_polygon(const std::vector<Pair>& points) const {
std::vector<Point_2> polygon;
polygon.reserve(points.size());
for (const auto& pair : points)
polygon.push_back(pair.first);
CGAL_assertion(polygon.size() == points.size());
return CGAL::is_convex_2(polygon.begin(), polygon.end());
}
const Plane_3& plane() const { return m_data->plane; }
const typename Intersection_kernel::Plane_3& exact_plane() const { return m_data->exact_plane; }
const Point_2& centroid() const { return m_data->centroid; }
bool is_bbox() const { return m_data->is_bbox; }
std::map<IVertex, Vertex_index>& ivertex2pvertex() { return m_data->ivertex2pvertex; }
const Mesh& mesh() const { return m_data->mesh; }
Mesh& mesh() { return m_data->mesh; }
const Point_2& get_point(const Vertex_index& vi) const {
return m_data->mesh.point(vi);
}
void set_point(const Vertex_index& vi, const Point_2& point) {
m_data->mesh.point(vi) = point;
}
void add_neighbor(IEdge edge, IFace face) {
std::pair<IEdge, std::pair<IFace, IFace>> neighbor(edge, std::pair<IFace, IFace>(face, Intersection_graph::null_iface()));
auto pair = m_data->iedge2ifaces.insert(neighbor);
m_data->ifaces.insert(face);
if (!pair.second) {
CGAL_assertion(pair.first->second.first != Intersection_graph::null_iface());
CGAL_assertion(pair.first->second.second == Intersection_graph::null_iface());
pair.first->second.second = face;
}
}
IFace iface(IEdge edge) {
auto it = m_data->iedge2ifaces.find(edge);
if (it == m_data->iedge2ifaces.end())
return Intersection_graph::null_iface();
else return it->second.first;
}
IFace other(IEdge edge, IFace face) {
auto it = m_data->iedge2ifaces.find(edge);
if (it == m_data->iedge2ifaces.end())
return Intersection_graph::null_iface();
if (it->second.first == face)
return it->second.second;
else
return it->second.first;
}
std::size_t has_ifaces(IEdge edge) const {
auto it = m_data->iedge2ifaces.find(edge);
if (it == m_data->iedge2ifaces.end())
return 0;
if (it->second.second != Intersection_graph::null_iface())
return 2;
else
return 1;
}
const Vertex_index prev(const Vertex_index& vi) const {
return m_data->mesh.source(m_data->mesh.halfedge(vi));
}
const Vertex_index next(const Vertex_index& vi) const {
return m_data->mesh.target(m_data->mesh.next(m_data->mesh.halfedge(vi)));
}
const Face_index face(const Vertex_index& vi) const {
auto out = m_data->mesh.face(m_data->mesh.halfedge(vi));
if (out == Face_index()) {
out = m_data->mesh.face(m_data->mesh.opposite(m_data->mesh.halfedge(vi)));
}
CGAL_assertion(out != Face_index());
return out;
}
const std::pair<Face_index, Face_index> faces(const Vertex_index& vi) const {
for (const auto& he : halfedges_around_target(halfedge(vi, m_data->mesh), m_data->mesh)) {
if (has_iedge(m_data->mesh.edge(he))) {
return std::make_pair(
m_data->mesh.face(he), m_data->mesh.face(m_data->mesh.opposite(he)));
}
}
CGAL_assertion_msg(false, "ERROR: NO CONSTRAINED EDGE FOUND!");
return std::make_pair(Face_index(), Face_index());
}
const std::pair<Face_index, Face_index> faces(const Halfedge_index& he) const {
if (has_iedge(m_data->mesh.edge(he))) {
return std::make_pair(
m_data->mesh.face(he), m_data->mesh.face(m_data->mesh.opposite(he)));
}
CGAL_assertion_msg(false, "ERROR: NO CONSTRAINED EDGE FOUND!");
return std::make_pair(Face_index(), Face_index());
}
const Point_2 point_2(const Vertex_index& vi) const {
return m_data->mesh.point(vi);
}
const Point_3 point_3(const Vertex_index& vi) const {
return to_3d(m_data->mesh.point(vi));
}
const Segment_2 segment_2(const Edge_index& ei) const {
return Segment_2(m_data->mesh.point(m_data->mesh.source(m_data->mesh.halfedge(ei))), m_data->mesh.point(m_data->mesh.target(m_data->mesh.halfedge(ei))));
}
const Segment_3 segment_3(const Edge_index& ei) const {
return Segment_3(
point_3(m_data->mesh.source(m_data->mesh.halfedge(ei))),
point_3(m_data->mesh.target(m_data->mesh.halfedge(ei))));
}
void set_iedge(
const Vertex_index& v0, const Vertex_index& v1, const IEdge& iedge) const {
const auto he = m_data->mesh.halfedge(v0, v1);
CGAL_assertion(he != Halfedge_index());
const auto ei = m_data->mesh.edge(he);
m_data->e_iedge_map[ei] = iedge;
}
void set_ivertex(const Vertex_index& vi, const IVertex& ivertex) const {
m_data->v_ivertex_map[vi] = ivertex;
}
void set_iedge(const Vertex_index& vi, const IEdge& iedge) const {
m_data->v_iedge_map[vi] = iedge;
}
void set_iedge(const Edge_index& ei, const IEdge& iedge) const {
m_data->e_iedge_map[ei] = iedge;
}
const IEdge& iedge(const Edge_index& ei) const {
return m_data->e_iedge_map[ei];
}
const IEdge& iedge(const Vertex_index& vi) const {
return m_data->v_iedge_map[vi];
}
const IVertex& ivertex(const Vertex_index& vi) const {
return m_data->v_ivertex_map[vi];
}
bool has_iedge(const Edge_index& ei) const {
return (m_data->e_iedge_map[ei] != Intersection_graph::null_iedge());
}
bool has_iedge(const Vertex_index& vi) const {
return (m_data->v_iedge_map[vi] != Intersection_graph::null_iedge());
}
bool has_ivertex(const Vertex_index& vi) const {
return (m_data->v_ivertex_map[vi] != Intersection_graph::null_ivertex());
}
const Vector_2 original_edge_direction(std::size_t v1, std::size_t v2) const {
const Vector_2 edge = m_data->original_vertices[v1] - m_data->original_vertices[v2];
Vector_2 orth = Vector_2(-edge.y(), edge.x());
orth = (1.0 / (CGAL::approximate_sqrt(orth * orth))) * orth;
FT s1 = orth * m_data->original_vectors[v1];
FT s2 = orth * m_data->original_vectors[v2];
if (abs(s1 - s2) > 0.0001)
std::cout << "edge speed seems inconsistent" << std::endl;
return s1 * orth;
}
const FT speed(const Vertex_index& vi) const {
return static_cast<FT>(CGAL::approximate_sqrt((CGAL::abs(m_data->direction[vi].squared_length()))));
}
const std::vector<std::size_t>& input(const Face_index& fi) const { return m_data->input_map[fi]; }
std::vector<std::size_t>& input(const Face_index& fi) { return m_data->input_map[fi]; }
bool is_original(const Vertex_index& vi) const { return m_data->v_original_map[vi]; }
bool is_initial(const Face_index& fi) const { return m_data->f_initial_map[fi]; }
void set_initial(const Face_index& fi) { m_data->f_initial_map[fi] = true; }
const int& k() const { return m_data->k; }
int& k() { return m_data->k; }
const std::set<IFace>& ifaces() const { return m_data->ifaces; }
const IEdge_set& unique_iedges() const { return m_data->unique_iedges; }
IEdge_set& unique_iedges() { return m_data->unique_iedges; }
const std::vector<IEdge>& iedges() const { return m_data->iedges; }
std::vector<IEdge>& iedges() { return m_data->iedges; }
const Point_2 to_2d(const Point_3& point) const {
return m_data->plane.to_2d(point);
}
const Vector_2 to_2d(const Vector_3& vec) const {
return Vector_2(
m_data->plane.to_2d(Point_3(0, 0, 0)),
m_data->plane.to_2d(Point_3(0, 0, 0) + vec));
}
template<typename = typename std::enable_if<identical_kernel>::type >
const typename Intersection_kernel::Point_2 to_2d(const typename Intersection_kernel::Point_3& point) const {
return m_data->exact_plane.to_2d(point);
}
const Line_2 to_2d(const Line_3& line) const {
return Line_2(
m_data->plane.to_2d(line.point()),
m_data->plane.to_2d(line.point() + line.to_vector()));
}
template<typename = typename std::enable_if<identical_kernel>::type >
const typename Intersection_kernel::Line_2 to_2d(const typename Intersection_kernel::Line_3& line) const {
return typename Intersection_kernel::Line_2(
m_data->exact_plane.to_2d(line.point()),
m_data->exact_plane.to_2d(line.point() + line.to_vector()));
}
const Segment_2 to_2d(const Segment_3& segment) const {
return Segment_2(
m_data->plane.to_2d(segment.source()),
m_data->plane.to_2d(segment.target()));
}
template<typename = typename std::enable_if<identical_kernel>::type >
const typename Intersection_kernel::Segment_2 to_2d(const typename Intersection_kernel::Segment_3& segment) const {
return typename Intersection_kernel::Segment_2(
m_data->exact_plane.to_2d(segment.source()),
m_data->exact_plane.to_2d(segment.target()));
}
const Vector_3 to_3d(const Vector_2& vec) const {
return Vector_3(
m_data->plane.to_3d(Point_2(FT(0), FT(0))),
m_data->plane.to_3d(Point_2(FT(0), FT(0)) + vec));
}
const Point_3 to_3d(const Point_2& point) const {
return m_data->plane.to_3d(point);
}
template<typename = typename std::enable_if<identical_kernel>::type >
const typename Intersection_kernel::Point_3 to_3d(const typename Intersection_kernel::Point_2& point) const {
return m_data->exact_plane.to_3d(point);
}
const Edge_index edge(const Vertex_index& v0, const Vertex_index& v1) {
return m_data->mesh.edge(m_data->mesh.halfedge(v0, v1));
}
const Edge_index add_edge(
const Vertex_index& v0, const Vertex_index& v1, const IEdge& iedge) {
const auto out = m_data->mesh.edge(m_data->mesh.add_edge(v0, v1));
m_data->e_iedge_map[out] = iedge;
return out;
}
const Vertex_index add_vertex(const Point_2& point) {
return m_data->mesh.add_vertex(point);
}
void remove_vertex(const Vertex_index& vi) {
m_data->mesh.remove_vertex(vi);
}
const Edge_index split_vertex(const Vertex_index& vi) {
return m_data->mesh.edge(
CGAL::Euler::split_vertex(
m_data->mesh.halfedge(vi),
m_data->mesh.opposite(m_data->mesh.next(m_data->mesh.halfedge(vi))),
m_data->mesh));
}
const Vertex_index split_edge(
const Vertex_index& v0, const Vertex_index& v1) {
return m_data->mesh.target(
CGAL::Euler::split_edge(m_data->mesh.halfedge(v0, v1), m_data->mesh));
}
};
template<typename GeomTraits, typename IntersectionKernel>
bool operator==(const Support_plane<GeomTraits, IntersectionKernel>& a, const Support_plane<GeomTraits, IntersectionKernel>& b) {
if (a.is_bbox() || b.is_bbox()) {
return false;
}
if (a.exact_plane() == b.exact_plane())
return true;
else return false;
}
#endif //DOXYGEN_RUNNING
} // namespace internal
} // namespace KSP_3
} // namespace CGAL
#endif // CGAL_KSP_3_SUPPORT_LINE_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
GeometryFactory SARL (France)
INRIA Sophia-Antipolis

View File

@ -0,0 +1,49 @@
Algebraic_foundations
Arithmetic_kernel
Arrangement_on_surface_2
BGL
Boolean_set_operations_2
Bounding_volumes
CGAL_Core
Cartesian_kernel
Circulator
Combinatorial_map
Convex_hull_2
Convex_hull_3
Distance_2
Distance_3
Filtered_kernel
Generalized_map
HalfedgeDS
Hash_map
Homogeneous_kernel
Installation
Intersections_2
Intersections_3
Interval_support
Kernel_23
Kernel_d
Kinetic_space_partition
Linear_cell_complex
Modular_arithmetic
Number_types
Optimal_bounding_box
Orthtree
Point_set_3
Point_set_processing_3
Polygon
Polygon_mesh_processing
Principal_component_analysis
Principal_component_analysis_LGPL
Profiling_tools
Property_map
Random_numbers
STL_Extension
Solver_interface
Spatial_sorting
Stream_support
Surface_mesh
Surface_sweep_2
TDS_2
Triangulation_2
Union_find

View File

@ -0,0 +1,4 @@
Kinetic Shape Partition
This CGAL package provides a way to partition 2D or 3D space by propagating
2D segments or 3D polygons until they interesect the predefined number of times.

View File

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

View File

@ -0,0 +1,6 @@
Kinetic Shape Partition
This CGAL package provides a way to partition 2D or 3D space by propagating
2D segments or 3D polygons until they intersect the predefined number of times.
Once the partion is found, a 3D shape can be reconstructed by utilizing a graph cut approach.
The final result is a piece-wise linear approximation of the given smooth shape.

View File

@ -0,0 +1 @@
Sven Oesau <sven.oesau@geometryfactory.com>

View File

@ -0,0 +1,193 @@
** GENERAL **
All the test data are placed in the folder examples/data. The name of the folder indicates the type of data or its complexity.
The complexity 1 is low while the complexity 6 is high.
Examples are the entry point to the package:
- kinetic_2d_example: 2D kinetic algorithm on a random set of segments
- kinetic_precomputed_shapes_example: 3D kinetic algorithm on a set of user-defined polygons read from a file
- kinetic_random_shapes_example: 3D kinetic algorithm on a set of random polygons, you can provide the number
of input polygons to be generated and the number of vertices each polygon should have
- kinetic_reconstruction_example: given a ply with classified point cloud, it first fits polygons using Region Growing,
then runs 3D kinetic, and finally filters out exterior volumes creating a reconstructed model
Tests:
- kinetic_2d_stress_test: tests the 2D kinetic algorithm
- kinetic_3d_test_all: tests the 3D kinetic algorithm on all data from the examples folder
The 3D algorithm is running on macOS with zero warnings. On Windows and Linux, it compiles and works,
but can generate warnings related to conversions between std:size_t and int e.g. or similar warnings.
The 2D algorithm should work on all platforms as well.
** CURRENT ISSUES **
-- EPICK versus EPECK --
By running:
./kinetic_precomputed_shapes_example data/stress-test-4/test-9-rnd-polygons-12-4.off
first with EPICK and then with EPECK shows a huge difference in runtime. This
data set contains 12 random polygons, each having 4 vertices. The time for EPICK is
milliseconds while for EPECK is about 3 minutes. It can also be noticed that most of
the time is spent at the last iterations while the first iterations are very fast.
It is even slower for `Simple_cartesian<Gmpq>`.
It is probably happening because at the latest iterations the computation involves
all operations carried out from the first iteration. Evaluating these cascading operations
is a slow process. E.g. computing an intersection between two lines at the first iterations
takes seconds while at the higher iterations it takes minutes.
Another issue shows up when running the code with the following data set:
./kinetic_precomputed_shapes_example data/real-data-test/test-40-polygons.ply 6
that is with k = 6, where k is the number of allowed intersections between support planes
of the input polygons. Here, at about 5300 iteration, assertions start failing but not because
some parts of the algorithm are not implemented (as indicated in the TODO) but because
the scaling is no longer uniform due to the accumulating errors. It leads to wrong configurations
and hence to the wrong events, which in practice with the correct precision cannot happen.
-- Possible solutions --
- Use EPICK and compute always with respect to the input. E.g. intersections between lines should be
done not between lines at the current and previous iterations but between lines at the current
and first iterations. The reason for this optimization is that if we use simply EPICK,
when the number of input polygons grow, we bump into an issue of the accumulating error that gets
bigger and bigger with each iteration and at some point can break the results. It does not happen
with EPECK but we lose speed there.
- Use EPECK only when it is absolutely necessary like when computing intersections and
use EPICK for all other computations. This way we avoid accumulating errors and should
preserve speed.
A few ideas shortly after the meeting with Sebastien:
- speed up kinetic with EPECK by optimizing the parts, which are slow, vtune it
- use different speed for each polygon edge during the uniform scaling, a similar
trick to what is used in the straight skeleton now
- point_2() wrapper inside Support_plane.h: it can compute point not based on the point constructed
at the previous event (which is cascading) but based on the interesting polygon edge and intersection graph edge
- avoid certain events in the queue or keep track only of the last 10 events instead of all of them
** INTERNALS **
-- File descriptions --
- KSR sub-folder, used in both KSR2 and KSR3:
- conversions.h: the kinetic_traits class that performs intersections, all intersections
in the code are called from this class, here the hybrid mode could be potentially implemented.
- debug.h: a file with a bunch of functions which enable to export / dump to a file
different intermediate steps, events, data structure, etc.
- enum.h: enumerations used by the reconstruction code from the Reconstruction.h.
- parameters.h: internal parameters used in the code.
- property_map.h: property maps used by the reconstruction code from the Reconstruction.h.
- utils.h: different internal utilities such as normals, distances, angles, etc.
the most important one is tolerance(): this is a global tolerance used everywhere in the code
- KSR_2 sub-folder, 2D kinetic algorithm, works and tested, fully implemented by Simon.
- KSR_3 sub-folder, 3D kinetic algorithm + 3D reconstruction algorithm:
- Data_structure.h: a class with all conversions and all basic operations, which are
performed on the polygons and during events, it also stores the main intersection graph
and all support planes.
- Event_queue.h: a wrapper around boost multi_index_container that represents a queue of
all events, which can be sorted by time or vertex type. It is based on doubles so even
when we use EPECK globally, we need to convert to double here because boost multi_index_container
fails to work with EPECK due to the fact that values represented by EPECK can vary when converted
to_double() or to_interval().
- Event.h: represents different event types used in the propagation.
- Intersection_graph.h: a boost adjacency_list graph that stores an initial intersection graph
fully in 3D that is used as constraints for kinetic propagation.
- Support_plane.h: a wrapper around a support plane for each input polygon that stores a 2D
surface mesh that represents an input polygon. So, initially this mesh has only 1 face that is
input polygon while during propagation it is updated and new vertices, faces are added. We preserve
the uniform scaling of the input polygon and validity of this mesh after each event. If any of these
two fails, the whole algorithm fails. At the end of the support plane header, there is an overload
of the operator()== that is used to compare if two planes are equal. It heavily depends on the
tolerance parameters and in case two planes are found to be equal, they are merged at the initialization step.
- Initializer.h: a class that gets input polygons, creates an enlarged bounding box, removes all
near-collinear vertices of each polygon, merges all near-coplanar support planes, then inserts each
support plane and intersects it with the bounding box. At the final step, it calls Polygon_splitter
class in order to intersect all polygons within the bounding box, one support plane at a time.
- Polygon_splitter.h: given a support plane, its original polygon, and a set of intersection edges created
by intersecting all support planes with the bounding box (see Initializer.h), it builds a CDT, inserts all these edges
in the CDT, marks all interior and exterior faces and creates a proper 2D surface mesh. This class can be parameterized
by any kernel independently of the input kernel.
- Propagation.h: is called after Initializer.h has done its work, this class creates an initial queue of
events for all vertices of all polygons and handles these events one by one. This is the most time-consuming
part of the total algorithm. The queue is updated and rebuilt until no valid events show up. This class can be parameterized
by any kernel independently of the input kernel.
- Finalizer.h: is called after Propagation.h, this class receives a set of 2D surface meshes from the propagation
and an initial intersection graph and does three things: first it checks the validity of the results, it then searches
for dangling / hanging faces if any and fills the gaps; and finally creates 3D volumes bounded by all faces of the 2D surface meshes.
This class can be parameterized by any kernel independently of the input kernel.
- Reconstruction.h: this class first gets a point cloud with or without semantic labels. If the labels are missing, some
parts of the code have to be still finished. However, if labels are present, it assumes they come from urban areas that is they are
walls, roofs, ground, and trees. It removes all tree points, fits a polygon to the ground points, runs region growing
separately for wall and roof points and detects convex planar polygons, which approximate the input point cloud.
Next, it runs 3D kinetic on these input polygons + ground polygon and gets a set of volumes, which partition the 3D space.
It then estimates a probability of each volume to be inside or outside the point cloud using Visibility.h and runs the graph cut
algorithm on these estimations using Graphcut.h. At the end, it removes all volumes, which have been labeled as exterior volumes
and returns a reconstructed model: 3D surface mesh that approximates the input point cloud (or buildings in case labels are urban related).
- Visibility.h: estimates a probability of each partition volume to be inside or outside a point cloud by using normals
of the input points and sampling each volume.
- Graphcut.h: takes initially estimated probabilities for each volume to be interior or exterior with respect to the point cloud,
computed by Visibility.h, creates a graph where each node is a volume and each edge connects volumes to their neighboring volumes,
and runs the mincut - maxflow Boykov optimization algorithm to define which volumes are inside and outside the point cloud.
All edges are weighted by the face area that adjacent to two incident volumes and all nodes are weighted by the volume itself.
- Kinetic_shape_reconstruction_2.h: is an entry point to the 2D kinetic propagation.
- Kinetic_shape_reconstruction_3.h: is an entry point to the 3D kinetic propagation and 3D kinetic reconstruction algorithms.
-- Epsilon usage inside the code --
Note that epsilon tolerance is used throughout the code. It is defined inside utils.h
in the function tolerance(). It is used everywhere where we expect a result of certain precision
but it may not be the case. We check if we outside this tolerance and apply different sub-algorithms
in order to be sure we that will generate the right results. This is mostly used for EPICK. When using EPECK,
the precision is higher and the tolerance should be satisfied until there is a bug.
-- Important parts of the code --
- point_2() wrapper from the Support_plane.h. Currently all original points are 3D,
this wrapper takes a point, and constructs the corresponding 2D point for the chosen
2D support plane and at the chosen time step. That means all these points are constructed
that is a weak point when using EPECK because it is slow.
- operator==() at the bottom of the Support_plane.h: controls if two near-coplanar planes should
be merged. If we merge too many planes because our input parameters are very weak, we fail to create
a valid partition. If we do not merge at all, the near-coplanar support planes may lead to intricated
errors in the kinetic propagation due to numerical instabilities.
- parameters: all all explained in the parameters.h. The parameters used in the Reconstruction.h are defined
in the file examples/include/Parameters.h.
- FUTURE POINTS AND DIRECTIONS section at the bottom of the Data_structure.h, this is where the future points
are computed and this is a part of the code that leads to multiple precision issues, identifying a future
point from the previous event is hard, so instead we simply translate the lines and intersect them at the next
time step to get the point at which our current vertex will be later, but these intersections are imprecise.
If we lose precision here, we fail to scale polygon uniformly so our point can end up behind the bounding box
or in the direction opposite to the one we need, especially if the lines that we intersect are near parallel.

View File

@ -0,0 +1,24 @@
# Created by the script cgal_create_CMakeLists.
# This is the CMake script for compiling a set of CGAL applications.
cmake_minimum_required(VERSION 3.1...3.23)
project(Kinetic_space_partition_Tests)
find_package(CGAL REQUIRED)
include(CGAL_CreateSingleSourceCGALProgram)
find_package(Eigen3 3.1.0 REQUIRED)
if(Eigen3_FOUND)
message(STATUS "Found Eigen")
include(CGAL_Eigen_support)
set(targets kinetic_3d_test_all)
foreach(target ${targets})
create_single_source_cgal_program("${target}.cpp")
target_link_libraries(${target} PUBLIC CGAL::Eigen_support)
endforeach()
else()
message(ERROR "This program requires the Eigen library, and will not be compiled.")
endif()

View File

@ -0,0 +1,12 @@
OFF
8 2 0
0.0 0.0 0.0
1.0 0.0 0.0
1.0 1.0 0.0
0.0 1.0 0.0
0.5 0.5 0.0
0.5 1.5 0.0
0.5 1.5 1.0
0.5 0.5 1.0
4 0 1 2 3
4 4 5 6 7

View File

@ -0,0 +1,22 @@
OFF
16 4 0
0.0 0.0 0.0
1.0 0.0 0.0
1.0 1.0 0.0
0.0 1.0 0.0
2.0 0.0 0.0
3.0 0.0 0.0
3.0 1.0 0.0
2.0 1.0 0.0
0.5 0.5 0.0
0.5 1.5 0.0
0.5 1.5 1.0
0.5 0.5 1.0
2.5 0.5 0.0
2.5 1.5 0.0
2.5 1.5 1.0
2.5 0.5 1.0
4 0 1 2 3
4 4 5 6 7
4 8 9 10 11
4 12 13 14 15

View File

@ -0,0 +1,27 @@
OFF
20 5 0
0.0 0.0 0.0
1.0 0.0 0.0
1.0 1.0 0.0
0.0 1.0 0.0
2.0 0.0 0.0
3.0 0.0 0.0
3.0 1.0 0.0
2.0 1.0 0.0
0.5 0.5 0.0
0.5 1.5 0.0
0.5 1.5 1.0
0.5 0.5 1.0
2.5 0.5 0.0
2.5 1.5 0.0
2.5 1.5 1.0
2.5 0.5 1.0
1.5 1.5 0.0
2.5 2.5 0.0
1.5 3.5 0.0
0.5 2.5 0.0
4 0 1 2 3
4 4 5 6 7
4 8 9 10 11
4 12 13 14 15
4 16 17 18 19

View File

@ -0,0 +1,32 @@
OFF
28 2 0
1.00 1.00 0.0
1.40 0.96 0.0
1.80 0.96 0.0
2.20 0.96 0.0
2.60 1.00 0.0
2.64 1.40 0.0
2.64 1.60 0.0
2.60 2.00 0.0
2.20 2.03 0.0
1.80 2.04 0.0
1.40 2.03 0.0
1.00 2.00 0.0
0.96 1.60 0.0
0.96 1.40 0.0
1.8 1.00 1.00
1.8 1.40 0.96
1.8 1.80 0.96
1.8 2.20 0.96
1.8 2.60 1.00
1.8 2.64 1.40
1.8 2.64 1.60
1.8 2.60 2.00
1.8 2.20 2.03
1.8 1.80 2.04
1.8 1.40 2.03
1.8 1.00 2.00
1.8 0.96 1.60
1.8 0.96 1.40
14 0 1 2 3 4 5 6 7 8 9 10 11 12 13
14 14 15 16 17 18 19 20 21 22 23 24 25 26 27

View File

@ -0,0 +1,12 @@
OFF
8 2 0
0.0 0.0 0.0
1.0 0.0 0.0
1.0 1.0 0.0
0.0 1.0 0.0
2.0 0.0 0.0
3.0 0.0 0.0
3.0 1.0 0.0
2.0 1.0 0.0
4 0 1 2 3
4 4 5 6 7

View File

@ -0,0 +1,12 @@
OFF
8 2 0
0.0 0.0 0.0
1.0 0.0 0.0
1.0 0.0 1.0
0.0 0.0 1.0
2.0 0.0 0.0
3.0 0.0 0.0
3.0 0.0 1.0
2.0 0.0 1.0
4 0 1 2 3
4 4 5 6 7

View File

@ -0,0 +1,12 @@
OFF
8 2 0
0.0 0.0 0.0
0.0 1.0 0.0
0.0 1.0 1.0
0.0 0.0 1.0
0.0 2.0 0.0
0.0 3.0 0.0
0.0 3.0 1.0
0.0 2.0 1.0
4 0 1 2 3
4 4 5 6 7

View File

@ -0,0 +1,33 @@
OFF
25 6 0
0.0 0.0 0.0
2.3 0.0 0.0
2.4 0.0 0.9
0.0 0.0 1.0
0.5 0.1 0.0
0.5 1.1 0.0
0.5 1.1 1.0
0.5 0.2 1.0
1.5 0.1 0.0
1.5 0.01 0.75
1.5 1.1 0.0
1.5 1.1 1.0
1.5 0.2 1.0
1.52 0.01 0.5
3.0 0.1 0.5
3.0 1.1 0.5
2.0 1.1 0.5
2.5 0.2 0.0
2.5 1.1 0.0
2.5 1.1 1.0
2.5 0.3 1.0
-0.5 -0.1 0.0
-0.5 -1.1 0.0
-0.5 -1.1 1.0
-0.5 -0.2 1.0
4 0 1 2 3
4 4 5 6 7
5 8 9 10 11 12
4 13 14 15 16
4 17 18 19 20
4 21 22 23 24

View File

@ -0,0 +1,34 @@
OFF
26 6 0
0.0 0.0 0.0
2.12 0.0 0.0
2.1 0.0 1.0
0.0 0.0 1.0
0.5 0.1 0.0
0.5 1.1 0.0
0.5 1.1 1.0
0.5 0.2 1.0
1.5 0.1 0.0
1.5 0.01 0.75
1.5 1.1 0.0
1.5 1.1 1.0
1.5 0.2 1.0
1.52 0.01 0.5
2.4 0.02 0.5
3.0 0.1 0.5
3.0 1.1 0.5
2.0 1.1 0.5
2.5 0.2 0.0
2.5 1.1 0.0
2.5 1.1 1.0
2.5 0.3 1.0
-0.5 -0.1 0.0
-0.5 -1.1 0.0
-0.5 -1.1 1.0
-0.5 -0.2 1.0
4 0 1 2 3
4 4 5 6 7
5 8 9 10 11 12
5 13 14 15 16 17
4 18 19 20 21
4 22 23 24 25

View File

@ -0,0 +1,32 @@
OFF
24 6 0
0.0 0.0 0.0
1.8 0.0 0.0
1.8 0.0 1.0
0.0 0.0 1.0
0.5 0.1 0.0
0.5 1.1 0.0
0.5 1.1 1.0
0.5 0.1 1.0
1.5 0.1 0.0
1.5 1.1 0.0
1.5 1.1 1.0
1.5 0.1 1.0
2.0 0.1 0.5
3.0 0.1 0.5
3.0 1.1 0.5
2.0 1.1 0.5
2.5 0.2 0.0
2.5 1.1 0.0
2.5 1.1 1.0
2.5 0.2 1.0
-0.5 -0.1 0.0
-0.5 -1.1 0.0
-0.5 -1.1 1.0
-0.5 -0.1 1.0
4 0 1 2 3
4 4 5 6 7
4 8 9 10 11
4 12 13 14 15
4 16 17 18 19
4 20 21 22 23

View File

@ -0,0 +1,204 @@
OFF
192 10 0
57.531272273 -38.894738065 -35.596646778
57.542283368 -38.844672176 -35.872878527
57.525259607 -38.457749173 -36.063844298
57.309433067 -35.185838007 -36.31068148
57.278886643 -34.784009496 -36.264087737
57.223961763 -34.229772196 -35.956319563
57.213142302 -34.1271718 -35.88693934
57.197355531 -33.980950024 -35.781069563
56.835054126 -33.518038801 -29.500936982
56.822186495 -33.5023233 -29.276923905
56.782262006 -33.52500907 -28.486779477
56.772038172 -33.537489246 -28.275560785
56.74007394 -33.700228795 -27.45052095
56.74883641 -33.868830364 -27.392897338
56.760741645 -34.097490701 -27.315154974
56.775635836 -34.38025532 -27.222291796
56.86101069 -35.599486435 -27.224536151
56.974032333 -37.160980714 -27.297464138
57.003835374 -37.559711778 -27.334030629
57.008376036 -37.601628591 -27.364667914
57.022042641 -37.664297958 -27.54139207
57.10159686 -38.011201507 -28.593939257
57.117882487 -38.077558339 -28.815607442
57.490167508 -38.845650839 -34.879568422
42.253287429 -39.865616399 -31.237740888
42.806859459 -39.862105064 -35.452286882
55.963165304 -38.015477551 -35.586781968
56.985742173 -37.871604326 -35.577737756
57.738751973 -37.756941856 -35.076572432
57.428147075 -37.701406838 -29.449414785
57.10765397 -37.717459648 -27.804766993
57.043844053 -37.724096157 -27.672501577
56.886459473 -37.742399843 -27.456051834
56.745254072 -37.76068526 -27.367565821
54.434557148 -38.077610247 -26.923788537
53.62273993 -38.192259119 -26.955297862
51.519007624 -38.490283284 -27.089405984
51.041035593 -38.558734221 -27.161816326
43.652535913 -39.622649519 -28.610160152
42.962518738 -39.723852122 -28.849968836
42.822882989 -39.747250381 -29.064061915
47.000499188 -35.234184742 -29.040005574
45.895175246 -35.268729967 -29.043512226
43.587628554 -35.544352585 -29.043779406
42.800055626 -35.658244342 -29.043183594
42.378480026 -35.922894586 -29.03580482
42.285497234 -36.056257892 -29.031578098
42.292757421 -37.1193097 -28.994701319
42.316216892 -37.672691045 -28.97542102
42.609786853 -39.623153218 -28.906567708
42.614482601 -39.649419494 -28.905637322
42.924698978 -39.748732042 -28.900874892
47.896795672 -39.244873792 -28.897178752
48.926042655 -39.115852504 -28.897270452
49.450279791 -39.039096965 -28.897699802
49.854899943 -38.905160008 -28.90062015
49.882777712 -38.855018495 -28.902239437
49.981469988 -38.653269337 -28.908812147
49.980088857 -38.502483414 -28.914044336
49.947923596 -38.052603825 -28.929774265
49.940673002 -37.952070039 -28.93328967
49.89992045 -37.719303268 -28.941530911
49.825008126 -37.38654781 -28.953383183
49.77949503 -37.201398751 -28.959994231
49.727861971 -37.113947313 -28.963245078
47.848756981 -35.32002063 -29.03342047
54.265306757 -34.103364498 -27.113141535
53.721774897 -34.125133725 -27.11470015
52.413377462 -34.290611196 -27.114532863
52.15333392 -34.346475909 -27.113703251
51.93331568 -34.578144065 -27.10660987
49.926663344 -36.877441888 -27.035454967
49.874111701 -36.980011218 -27.032123514
49.897113929 -37.369631221 -27.018521207
49.93186478 -37.761344318 -27.004796353
49.973492985 -38.134909449 -26.991671248
50.087458109 -38.674522576 -26.972482991
50.14565235 -38.813605533 -26.967414648
50.176350715 -38.842887341 -26.966269081
50.250349934 -38.879800929 -26.964674715
50.707150327 -38.87594795 -26.962864223
52.74603026 -38.547842016 -26.965559524
54.687001875 -38.135281849 -26.971598725
54.78696727 -37.993390618 -26.976091312
54.835992246 -37.868400281 -26.980214898
54.735796783 -36.241060289 -27.037045684
54.608228321 -34.785247097 -27.088047764
54.515437673 -34.284584947 -27.105795848
54.474854136 -34.11546577 -27.111830313
55.923822759 -33.664281314 -28.322139157
55.722586035 -33.667770077 -28.322874652
55.492954851 -33.689049159 -28.323114367
54.86419354 -33.767457977 -28.323072544
54.819295053 -33.775362284 -28.322989655
54.71102404 -33.807792156 -28.322326396
54.611777017 -33.932343977 -28.318431744
54.583243756 -33.984974951 -28.31672896
54.694458689 -36.580456214 -28.226295041
54.795985989 -37.08864217 -28.208248997
55.581340015 -38.212524896 -28.1659524
55.593730692 -38.227459862 -28.165382015
56.487247173 -38.171368135 -28.163523579
57.386880343 -36.191007863 -28.228335175
57.361119523 -35.463080613 -28.253675112
57.007486583 -34.89396626 -28.274905866
56.204379523 -33.727971076 -28.318737654
56.186585668 -33.707464744 -28.31952414
56.025936658 -33.67549225 -28.321316007
47.673527497 -37.258152339 -28.943716842
48.460379254 -37.218450814 -32.965096698
48.631076073 -37.209601384 -33.824050677
49.409802969 -37.099558218 -33.789997706
51.997764733 -36.729433519 -33.426320666
52.039742638 -36.723041799 -33.398402043
52.067048183 -36.711870392 -32.982334272
52.107962588 -36.691314729 -32.142381352
52.135326441 -36.621565734 -28.403530209
52.137858496 -36.609575931 -27.743508288
52.122377578 -36.605190204 -27.371262616
51.956172049 -36.624223033 -27.12584749
51.886345547 -36.632422985 -27.034308431
51.701268643 -36.657542773 -26.983755649
50.4121345 -36.836863717 -26.878514502
50.277447215 -36.856978968 -26.945809772
41.877465015 -35.851440127 -30.592808864
41.923677909 -35.908076721 -34.17440555
41.940406964 -35.907431467 -34.271183189
42.025604039 -35.903782326 -34.743453769
42.062310135 -35.899744698 -34.80705518
43.512668819 -35.706079983 -35.384018491
53.159859484 -34.361532156 -36.023756048
53.277170459 -34.345067363 -36.025014927
57.644071036 -33.72902896 -35.893931785
57.648678984 -33.721155009 -35.483963678
56.931660035 -33.68463196 -27.694971754
56.8486508 -33.690424494 -27.361745204
56.508200107 -33.73702152 -27.290818417
54.42017874 -34.023895846 -26.917635946
52.064163854 -34.354728026 -26.901517227
42.524994666 -35.727148928 -28.704365814
42.455746646 -35.738491034 -28.795701139
42.229805701 -35.776814747 -29.168420893
46.876721268 -33.239537256 -35.621461937
46.492795466 -33.247957592 -35.622803985
40.653703301 -33.469130718 -35.63998786
40.020108346 -33.507231688 -35.641363697
39.812041902 -33.520394431 -35.641792953
39.682888847 -33.650876864 -35.637820016
39.702423524 -33.986843814 -35.626092107
39.901546086 -35.379963464 -35.576958497
40.203231484 -37.37603646 -35.506489754
40.435606938 -38.909783567 -35.452340414
40.721729784 -40.792734814 -35.385858755
40.729242519 -40.824732829 -35.384717716
40.827788938 -40.94321962 -35.380191517
41.119563663 -40.941591443 -35.379006224
43.047060946 -40.708410522 -35.378885398
49.627807131 -34.883451456 -35.552775097
49.662974769 -34.068063074 -35.580887181
49.416058321 -33.896831182 -35.58787298
49.072521599 -33.69196466 -35.596435764
48.780684313 -33.549379856 -35.602619814
47.257799282 -33.287669019 -35.618171887
50.747147014 -34.301446963 -33.621374373
48.39332418 -34.496065983 -33.624646117
47.796061756 -35.32506094 -33.59845457
48.534725225 -37.190845964 -33.530641992
49.496062846 -37.189099561 -33.52661129
51.296538024 -36.918693268 -33.52832129
52.031681164 -36.755359665 -33.530853909
52.061689351 -36.650183637 -33.534371652
52.118608023 -36.29753705 -33.546352318
52.146930592 -34.787363034 -33.598575133
52.035091708 -34.587787497 -33.605968476
50.802981154 -34.309398356 -33.620861157
56.539844906 -33.010313866 -36.129063109
50.319876907 -33.392999639 -36.184965116
45.88724082 -33.711979158 -36.175316545
43.95672484 -34.126644218 -35.876137504
43.856398211 -34.204652087 -35.80019298
43.79684445 -34.255366314 -35.750396006
43.186684534 -35.071025864 -34.923481727
43.245222997 -35.826956259 -34.110441344
43.39505417 -36.000206058 -33.913898179
44.285809078 -36.089397364 -33.751852585
48.35754716 -35.77235409 -33.786426698
57.607003782 -34.230814444 -34.743596259
57.977757991 -33.859025306 -35.113585953
57.976231145 -33.84942716 -35.123967847
57.828904198 -33.275580509 -35.748864758
57.751802126 -33.002660651 -36.046590138
57.706962082 -32.976358416 -36.078081368
24 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
17 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
25 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
23 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
19 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
16 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
18 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
21 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
12 163 164 165 166 167 168 169 170 171 172 173 174
17 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191

View File

@ -0,0 +1,77 @@
OFF
60 15 0
60.449881891 -13.692214483 -35.548420038
60.455828185 -25.202689488 -35.743109903
51.889312672 -25.202689488 -36.004751788
51.883366377 -13.692214483 -35.810061923
56.013494661 -22.286077626 -35.794136224
58.289635919 -22.325795715 -35.794829611
58.289635919 -22.50061645 -25.78088178
56.013494661 -22.460898362 -25.780188393
58.070892334 -21.905636832 -26.101082072
58.070892334 -21.746641653 -35.20990978
60.258117676 -21.746641653 -35.20990978
60.258117676 -21.905636832 -26.101082072
54.42440687 -22.038030512 -35.922176361
54.5656494 -13.946251373 -35.922176361
54.5656494 -13.946251373 -26.198154449
54.42440687 -22.038030512 -26.198154449
54.363766926 -21.719850201 -35.365935422
56.286979614 -21.753409775 -35.366521296
56.286979614 -21.912655257 -26.244743216
54.363766926 -21.879095683 -26.244157342
60.129200355 -20.359792807 -33.698807129
60.084094816 -23.886993185 -27.588442933
59.959713735 -19.368804043 -24.981241847
60.004819274 -15.841603666 -31.091606043
54.23652352 -17.103515563 -26.393317799
60.056166552 -17.100143515 -26.273959576
60.056166552 -22.551184854 -26.119959504
54.23652352 -22.554556902 -26.239317727
61.530666604 -12.199937334 -35.495003395
61.531466955 -19.059906537 -35.545595555
58.888261444 -19.059906537 -35.587410171
58.887461093 -12.199937334 -35.536818011
60.840349073 -16.766832339 -30.09282598
58.391720941 -17.199597832 -30.266048756
57.771486126 -13.698842248 -30.244583924
60.220114259 -13.266076754 -30.071361148
60.28580042 -17.233539007 -27.986459964
55.030055 -18.643526755 -28.248937225
54.6463142 -17.207471362 -28.279326743
59.90205962 -15.797483614 -28.016849482
54.840260431 -13.995359805 -35.871916394
58.620622307 -14.078480263 -35.54503007
58.010141023 -14.433155993 -28.575147284
54.229779147 -14.350035535 -28.902033608
58.291708667 -16.784192496 -30.1751564
54.730118484 -17.412277855 -30.279859285
54.229931677 -14.58528037 -30.223846608
57.79152186 -13.95719501 -30.119143724
54.276560845 -14.161177028 -35.658184052
56.497525215 -13.769561087 -35.658184052
56.497525215 -13.769561087 -30.807325363
54.276560845 -14.161177028 -30.807325363
53.832898877 -17.601506764 -29.896430592
54.485161788 -17.323940082 -27.303531189
58.745918537 -16.480552413 -28.465639489
58.093655627 -16.758119095 -31.058538892
56.32506092 -17.944488816 -30.541171077
56.552264209 -17.928601214 -26.195287696
56.675291733 -19.687976768 -26.195287696
56.448088443 -19.703864369 -30.541171077
4 0 1 2 3
4 4 5 6 7
4 8 9 10 11
4 12 13 14 15
4 16 17 18 19
4 20 21 22 23
4 24 25 26 27
4 28 29 30 31
4 32 33 34 35
4 36 37 38 39
4 40 41 42 43
4 44 45 46 47
4 48 49 50 51
4 52 53 54 55
4 56 57 58 59

View File

@ -0,0 +1,364 @@
OFF
342 20 0
55.950882754 -37.928082671 -35.581798553
42.794903635 -39.777034958 -35.45759964
42.251569898 -39.853395535 -31.244499207
42.826445923 -39.772601979 -29.065000534
42.966601764 -39.75290436 -28.851200104
43.6572015 -39.655846896 -28.613300323
51.049219606 -38.616966502 -27.15929985
51.527367542 -38.549767192 -27.087600708
53.631425634 -38.254061111 -26.961799622
54.443319396 -38.139956884 -26.928699493
54.598729575 -38.118115408 -26.958000183
56.752938263 -37.815361121 -27.371799469
56.893928708 -37.795546206 -27.456899643
57.050787471 -37.773501144 -27.673999786
57.114276079 -37.764578403 -27.805400848
57.430773881 -37.720097537 -29.454500198
57.727708863 -37.678366047 -35.071098328
56.973481595 -37.784365777 -35.571998596
57.128820972 -32.859865572 -35.906313471
56.092315122 -32.892757908 -35.905423667
53.531802824 -32.987599673 -35.902988434
53.131890005 -33.003318504 -35.902592276
52.819892251 -33.019272131 -35.902218802
52.437676335 -33.04156052 -35.90171338
51.104816846 -33.119385678 -35.899949111
50.631366206 -33.182880448 -35.898696746
50.103776353 -33.534752172 -35.892395016
49.973149294 -33.864405921 -35.886601972
57.482314445 -38.834862269 -35.802143086
57.69151585 -38.865082743 -35.801679396
57.837295652 -38.853311529 -35.801929241
58.559815723 -38.773554864 -35.80354129
58.704485889 -38.703309098 -35.804811319
58.708489114 -38.535816637 -35.807735685
57.787589473 -33.050587428 -35.903185598
57.745416344 -32.910132062 -35.905624035
57.358696184 -32.878205408 -35.906063424
57.549169901 -38.891850328 -35.595699785
57.55940112 -38.842665597 -35.871975904
57.536271527 -38.46325276 -36.063291575
57.263485222 -35.19506617 -36.313120119
57.226027082 -34.794714242 -36.266893573
57.160783644 -34.23373546 -35.959636804
57.148060442 -34.129970819 -35.89035126
57.129676171 -33.983417599 -35.784615835
57.011143085 -33.650359576 -34.085362143
57.013319039 -33.900879099 -33.710102186
57.192708085 -37.129491008 -31.750743202
57.258400756 -37.774804486 -31.926288052
57.341129536 -38.143731871 -32.885302769
57.507379014 -38.845338077 -34.878667866
57.522193082 -38.863107997 -35.130709564
57.264980486 -38.214204621 -31.320623138
57.288425206 -37.948030345 -32.208928703
57.287080503 -37.823286617 -32.390820517
57.196480988 -36.453127805 -32.947273272
57.02663775 -34.066527894 -33.687792241
57.001642392 -33.761342931 -33.720199243
56.746145765 -33.510388201 -29.280930061
56.706377891 -33.530972625 -28.490768809
56.696329231 -33.543130521 -28.279539639
56.667237863 -33.706808808 -27.454353562
56.678920134 -33.875080094 -27.396576038
56.694961153 -34.105303251 -27.318624013
56.71459904 -34.385759797 -27.225503494
56.821085171 -35.60222239 -27.226633438
56.95737403 -37.158665107 -27.228872001
56.989584173 -37.490400298 -27.289453592
56.998025406 -37.559896345 -27.334334948
57.003277529 -37.601528877 -27.364933891
57.018006121 -37.663645853 -27.54160029
57.121448083 -38.080023244 -28.815431281
57.133951738 -38.095045529 -29.028124843
47.002115986 -35.239691152 -29.008054086
45.896392951 -35.27264826 -29.007142065
43.586753084 -35.540267622 -29.001767871
42.799260438 -35.65443187 -28.999535534
42.377440867 -35.91821076 -28.994803458
42.284869891 -36.053162284 -28.992420029
42.292381154 -37.117501978 -28.973847028
42.315879554 -37.671209237 -28.964190662
42.610114998 -39.624830755 -28.930184899
42.614814845 -39.651121295 -28.929727498
42.924370649 -39.748000151 -28.928131029
47.896036652 -39.242777696 -28.93846291
48.925127866 -39.113214823 -28.941037589
49.449108697 -39.035512361 -28.942553306
49.854457666 -38.904316063 -28.944966479
49.882143572 -38.853437131 -28.945862873
49.979988182 -38.648434262 -28.949470473
49.978698001 -38.497943048 -28.952096514
49.939299924 -37.94741726 -28.961692512
49.898070522 -37.712773801 -28.965775045
49.823707065 -37.381975918 -28.971525611
49.778530145 -37.198031854 -28.974722115
49.727364842 -37.112312494 -28.976202537
47.849130712 -35.320937413 -29.006894172
54.26455998 -34.099825659 -27.074383193
53.721493205 -34.123310125 -27.073807897
52.412665605 -34.287106944 -27.070550536
52.152666502 -34.34313511 -27.069493506
51.932559307 -34.574523192 -27.065388175
49.926489914 -36.876585559 -27.024600532
49.873889152 -36.978997973 -27.022797165
49.896991927 -37.369123881 -27.015995567
49.932436726 -37.763582944 -27.009122105
49.974567419 -38.139166608 -27.0025801
50.087817155 -38.676263527 -26.993240966
50.146446877 -38.817038623 -26.990801963
50.177147531 -38.846341036 -26.990299918
50.251144609 -38.883264635 -26.989678054
50.709017518 -38.883494849 -26.989813519
52.746738903 -38.55108662 -26.9962356
53.246180841 -38.443949259 -26.998257551
54.68645476 -38.133822136 -27.00410877
54.787314654 -37.995268154 -27.006557596
54.83620694 -37.86974145 -27.008763233
54.735802015 -36.241082096 -27.037156671
54.607993255 -34.783885789 -27.062549319
54.514765965 -34.281408482 -27.071290357
54.473949263 -34.11135221 -27.074245814
55.923165863 -33.66107997 -28.283291704
55.721970278 -33.664707966 -28.283167096
55.492263419 -33.685688925 -28.282730951
54.863461713 -33.763917137 -28.281174127
54.818562711 -33.771818225 -28.281022556
54.710261414 -33.804135058 -28.280425558
54.611704769 -33.93132252 -28.278175807
54.583101055 -33.983702159 -28.277252942
54.694607482 -36.581122961 -28.231955667
54.796423144 -37.09056789 -28.223095644
55.581930246 -38.215454284 -28.203702961
55.594434708 -38.230824294 -28.203438526
56.488686116 -38.177562648 -28.204640488
57.387112538 -36.192092922 -28.239565403
57.360885648 -35.462172467 -28.252296282
57.005468788 -34.886116803 -28.262241568
56.203446905 -33.723773843 -28.282282928
56.185970204 -33.704455788 -28.28261475
56.025265067 -33.672247719 -28.283127902
54.684527406 -33.405158995 -35.980499268
52.840355313 -33.66434048 -35.914600372
50.298316373 -34.021600755 -35.712200165
50.146575643 -34.042926524 -35.076698303
50.370312712 -34.011482329 -34.942100525
51.262307794 -33.886120596 -34.847198486
52.830671248 -33.665701487 -34.76720047
53.840559009 -33.523771018 -34.772800446
54.863497296 -33.380006417 -34.784301758
56.736435872 -33.116782066 -34.813899994
57.426618099 -33.01978328 -34.91519928
57.500343966 -33.009421785 -35.022499084
57.700497213 -32.981292081 -35.795200348
57.555580771 -33.001658759 -35.905601501
57.354935111 -33.029857667 -35.915100098
48.621807078 -37.143649056 -33.825000763
48.453196895 -37.167345672 -32.962799072
47.676114177 -37.276557526 -28.9416008
50.284887362 -36.909918365 -26.945100784
50.419738126 -36.890966326 -26.876800537
51.708616609 -36.709826268 -26.983400345
51.893570707 -36.683832665 -27.034000397
51.963174835 -36.674050443 -27.123699188
52.128784183 -36.650775567 -27.368600845
52.143360813 -36.648726955 -27.742700577
52.139225384 -36.649308152 -28.404499054
52.102778831 -36.654430381 -32.141799927
52.059823949 -36.660467296 -32.981399536
52.031507661 -36.66444689 -33.396701813
51.989461934 -36.670356032 -33.424999237
49.400616698 -37.034194502 -33.790901184
54.847522127 -37.880426744 -27.399305551
54.85031915 -37.867861825 -27.473368671
54.852811172 -37.356894894 -28.370492016
54.650619323 -35.05622795 -28.353202364
54.57671536 -34.256962345 -28.277600067
54.569426129 -34.202223857 -28.230074308
54.546248122 -34.045962873 -28.049361343
54.515949058 -33.996877595 -27.55505107
54.489578231 -34.055981064 -26.955489613
54.49483039 -34.127315902 -26.936693406
54.698040786 -36.803295332 -26.349187954
54.716661484 -36.992775423 -26.388028794
54.810810057 -37.682582026 -27.030485
54.825884175 -37.79117579 -27.136427008
54.837217706 -37.849216246 -27.255337687
52.270104944 -36.663452907 -31.464909093
52.288022124 -36.647458038 -31.832088264
52.290647983 -35.833925923 -33.234930813
52.278240147 -35.621162718 -33.35290785
52.25634166 -35.347465261 -33.391817125
52.207511364 -34.885963856 -33.231116094
52.129996107 -34.513199939 -32.377583041
51.932532886 -34.298351798 -28.981397062
51.926823013 -34.307349379 -28.857897536
51.878144637 -34.474756851 -27.654189546
51.875272659 -34.499319078 -27.558749733
51.865033235 -34.723745628 -26.990885148
51.86987027 -34.857496907 -26.860397573
51.945569366 -35.741117834 -26.829837227
52.021187622 -36.559065746 -26.906956455
52.021717251 -36.562402006 -26.911475626
52.029800786 -36.598189883 -27.005615396
52.159895054 -36.65660194 -29.381374784
53.272587686 -34.312459236 -36.021499634
53.155279768 -34.328945789 -36.020301819
48.236363368 -35.020254406 -35.69549942
43.509643203 -35.684551603 -35.380001068
42.060686121 -35.888189241 -34.805400848
42.024134531 -35.893326232 -34.742099762
41.940084731 -35.905138661 -34.275398254
41.923590776 -35.907456735 -34.177600861
41.886078567 -35.912728732 -30.591600418
42.241879483 -35.862724175 -29.165800095
42.468725868 -35.830842994 -28.797199249
42.538195766 -35.821079637 -28.70359993
52.08174457 -34.479821322 -26.901399612
54.437720299 -34.148710526 -26.915300369
56.524835103 -33.85538567 -27.285400391
56.865113496 -33.80756266 -27.357700348
56.947313231 -33.796010241 -27.690200806
57.20307568 -33.760065173 -34.630599976
56.745283131 -33.82440372 -35.604698181
56.551115716 -33.85169217 -35.701999664
56.307211646 -33.885970652 -35.776199341
54.048506984 -34.107919643 -26.88419453
53.435539667 -34.142989269 -26.883395751
53.148640165 -34.23628539 -26.88168011
53.107026406 -34.279697629 -26.880909785
52.014043437 -35.423708842 -26.860611078
52.017528895 -35.449648107 -26.860159437
52.106516786 -35.695864315 -26.85588948
54.055891262 -36.763616145 -26.837848485
54.149787907 -36.760537486 -26.837930819
54.644929079 -36.178907179 -26.848232504
54.808367061 -35.839739525 -26.854201584
54.849399206 -35.600721058 -26.858385531
54.789216537 -35.349243484 -26.862756086
54.446620706 -34.14542041 -26.88366133
54.405826667 -34.124166437 -26.884019836
46.877026038 -33.240243725 -35.597618036
46.493664215 -33.250761605 -35.597317689
44.860252214 -33.308532609 -35.595811855
41.160443187 -33.445529225 -35.59229385
40.652964208 -33.46544531 -35.591791672
40.019685171 -33.504696429 -35.590913728
39.811689952 -33.518114411 -35.59061619
39.682486106 -33.648436055 -35.588302404
39.702088904 -33.98476913 -35.582438554
39.901609464 -35.379852771 -35.558151768
40.203300265 -37.376610473 -35.523395446
40.436121799 -38.912550764 -35.496660516
40.722424925 -40.796808858 -35.463862896
40.730029792 -40.829165284 -35.463300515
40.828524958 -40.947505018 -35.461265207
41.120434212 -40.946408526 -35.461373268
43.047611811 -40.712110551 -35.466049413
49.627900582 -34.884120151 -35.569766443
49.663215579 -34.069029687 -35.584002491
49.417067517 -33.900620528 -35.586866651
49.073356938 -33.695003503 -35.590350458
48.78145292 -33.552096599 -35.592755604
47.258020227 -33.288107003 -35.59689877
45.874309551 -34.103482743 -36.284841027
45.50593772 -34.18070125 -36.167047034
45.28352959 -34.266823595 -36.030197208
43.891696914 -34.918597898 -34.986054548
43.40947237 -35.243231059 -34.459863066
43.25024795 -35.514035983 -34.013861413
43.283364397 -35.562341827 -33.932517342
43.297995817 -35.576325205 -33.908823657
48.071461161 -35.886309982 -33.254352373
48.093970501 -35.887215932 -33.252191049
48.293644006 -35.882679074 -33.253940845
48.359288153 -35.862993936 -33.284790747
49.646109547 -35.411392954 -33.99888992
49.674696536 -35.399374122 -34.018059241
49.707414035 -35.334335857 -34.125334303
49.688880312 -34.594244518 -35.357403861
49.387565521 -34.367885533 -35.742822992
49.336677794 -34.338091606 -35.79387893
47.746391014 -34.224585655 -36.028946749
50.746568926 -34.298844377 -33.598728875
48.39437748 -34.499479894 -33.594510747
47.796427895 -35.326097404 -33.579902127
48.535766514 -37.195088885 -33.547508955
49.496845728 -37.192448701 -33.547847809
51.295096998 -36.91371966 -33.553260108
52.031481191 -36.75507757 -33.556253121
52.061263151 -36.649017069 -33.558113205
52.118163609 -36.296193756 -33.564288154
52.146148257 -34.784264198 -33.590683488
52.035426114 -34.588827823 -33.594060594
50.802580422 -34.307472062 -33.598595364
42.577020952 -39.712280814 -29.994361271
42.645009404 -39.677842411 -31.34399374
42.644146754 -39.512930198 -31.601850093
42.614794924 -38.996867633 -31.902142899
42.449592898 -37.070307491 -31.96582423
42.358460978 -36.209902453 -31.664420589
42.347386369 -36.118674119 -31.605624105
42.286667964 -35.940886966 -30.747122127
42.212877334 -35.787845942 -29.598982847
42.204807741 -35.798259404 -29.428273862
42.175471418 -35.854647203 -28.776858931
42.261131036 -36.819055257 -28.801281978
42.508925649 -39.593889466 -28.896858298
42.517963046 -39.674388985 -28.934772742
50.479128095 -34.489653521 -36.117099762
49.559203245 -34.618940527 -36.019298553
49.215978733 -34.667177586 -35.766998291
51.823892678 -34.300659183 -33.381500244
52.32782742 -34.229835774 -32.95059967
56.618600397 -33.626806959 -32.68119812
56.704400354 -33.614748561 -32.768501282
57.158519766 -33.55092624 -35.709899902
57.104038137 -33.558583133 -35.803699493
56.844790113 -33.595018067 -35.915100098
45.201472802 -37.420135825 -29.443658898
44.977567342 -37.534059397 -29.441602449
44.895485249 -37.836337344 -29.436301966
44.780444694 -39.492700935 -29.407359391
47.689734032 -39.315811642 -29.4113328
47.756801393 -38.706010298 -29.421995732
47.62025001 -38.377997535 -29.427678746
46.721910546 -37.761351532 -29.438167039
46.401313699 -37.657333053 -29.439884747
50.027475079 -38.881402168 -27.196314018
50.04901985 -38.847262143 -27.6626243
50.052157219 -38.837699445 -27.738164125
50.076362793 -38.753314375 -28.338611382
50.083638819 -38.586046034 -28.755090297
49.971326334 -37.240820381 -28.857346856
49.937385717 -36.925713151 -28.736218688
49.872856653 -36.881041196 -27.583907055
49.842798308 -36.884004765 -27.007613678
49.844252387 -36.952592731 -26.921189687
49.898274547 -37.576538401 -26.910430627
49.912719925 -37.73481283 -26.921800765
49.94587104 -38.088289863 -26.964111497
49.986832834 -38.50125305 -27.055964437
50.012154863 -38.738710185 -27.142400422
18 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
19 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
15 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
21 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
24 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
24 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
19 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
15 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
16 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
15 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
18 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
21 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
15 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
23 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
19 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
12 282 283 284 285 286 287 288 289 290 291 292 293
14 294 295 296 297 298 299 300 301 302 303 304 305 306 307
10 308 309 310 311 312 313 314 315 316 317
9 318 319 320 321 322 323 324 325 326
15 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341

View File

@ -0,0 +1,437 @@
ply
format ascii 1.0
element vertex 384
property double x
property double y
property double z
element face 40
property list uchar int vertex_indices
property uchar red
property uchar green
property uchar blue
property uchar alpha
end_header
03.29868532018736 393.5299726324156 21.80889597269399971
30.43944002094213 393.5381471058354 23.139587462290656106
30.44661123899277 417.4042486362159 22.993328973862105613
03.305856538238 417.3960741627961 21.662637484265449217
11.95831769844517 396.2328201103956 28.294205721002068543
11.66516924533062 396.60181907285 27.354071180336173796
10.76777932967525 398.8796317894012 26.740174206905063414
08.42645351670217 405.7806099280715 27.027606581919823014
08.28849473420996 406.6192669896409 27.896408416272603148
08.30528880935162 407.0143724363297 28.771011472796086395
08.78512516152114 406.7632776526734 31.005693720187988305
09.66730323340744 405.4849680690095 33.503852504771195697
10.36672410042956 403.4724039435387 33.514516960131004453
11.97644138394389 398.8209823416546 33.500601977983016866
12.04349625611212 396.3510358324274 29.01188164949416759
31.64162581833079 400.4716062713414 26.308578369288170506
31.49925644847099 400.8870130516589 24.812041777679500143
31.20062054879963 401.7728082975373 24.432037617953483277
29.51961759175174 406.768534982577 24.136002432398530715
26.2994834567653 416.3442336115986 24.691150469599055128
26.12519626447465 416.8626198163256 24.742064757566822664
26.11351498460863 416.9352899761871 31.996124021056861153
26.14477371040266 416.84700401593 32.883146439811760331
26.29246082087047 416.4139445247129 34.027120597282646486
26.48416705231648 415.8475459283218 34.69710175926366702
27.16982886847109 413.8172330595553 36.23002493464264262
30.71549923007842 403.27671257779 36.235069503100930888
31.51127581414767 400.8956648781896 33.29705406037010107
09.96794394159224 393.5845419801772 25.462325656801109375
09.59196252713446 394.2652920279652 23.067847779602740133
08.92245784553234 396.0885722236708 22.294101987077734606
06.58075666427612 402.9881431758404 22.571126564842412421
06.33355139067862 403.7206296017393 22.623916850192472339
05.15569547971245 407.2313878554851 22.993608996053804816
05.25199429236818 407.1149218408391 23.937538957325156019
05.54563176503871 406.5454468391836 25.591618494727295996
09.17227164178621 396.1855942578986 27.022489921495431275
09.30650311848149 395.7963385824114 27.042265768744979226
05.29625984642189 407.2173816617578 21.984000000000001762
09.19340264739003 395.4237939463928 21.984000000000001762
09.19340264739003 395.4237939463928 37.783000000000001251
05.29625984642189 407.2173816617578 37.783000000000001251
26.92756963765714 414.7398494333029 21.984000000000001762
30.84900872863363 402.8770360965282 21.984000000000001762
30.84900872863363 402.8770360965282 37.783000000000001251
26.92756963765714 414.7398494333029 37.783000000000001251
12.30659139517229 394.2659373255447 21.984000000000001762
16.94558888243046 395.7719450648874 21.984000000000001762
16.94558888243046 395.7719450648874 37.783000000000001251
12.30659139517229 394.2659373255447 37.783000000000001251
07.65509033796843 410.8346233814955 21.984000000000001762
23.74691606254783 416.1841798843816 21.984000000000001762
23.74691606254783 416.1841798843816 37.783000000000001251
07.65509033796843 410.8346233814955 37.783000000000001251
25.3789191886317 398.2966175414622 21.984000000000001762
29.0789147822652 399.4067870927975 21.984000000000001762
29.0789147822652 399.4067870927975 37.783000000000001251
25.3789191886317 398.2966175414622 37.783000000000001251
24.69913416169584 398.4604729581624 21.984000000000001762
23.12052289722487 394.1485271891579 21.984000000000001762
23.12052289722487 394.1485271891579 37.783000000000001251
24.69913416169584 398.4604729581624 37.783000000000001251
18.10733771696687 396.8055807435885 21.984000000000001762
21.71259050397202 393.9449482774362 21.984000000000001762
21.71259050397202 393.9449482774362 37.783000000000001251
18.10733771696687 396.8055807435885 37.783000000000001251
04.78117047424894 409.3697847705334 21.984000000000001762
07.15099212841596 410.804912161082 21.984000000000001762
07.15099212841596 410.804912161082 37.783000000000001251
04.78117047424894 409.3697847705334 37.783000000000001251
29.41121161729097 399.4289674684405 21.984000000000001762
31.64206198463216 400.8534375298768 21.984000000000001762
31.64206198463216 400.8534375298768 37.783000000000001251
29.41121161729097 399.4289674684405 37.783000000000001251
12.03708795062266 396.1833529956639 27.527064983727544956
11.87521254806779 396.6743886657059 27.320104436654222724
11.10706354642753 398.9980785492808 26.669613906293310635
09.34694079356268 404.2894131932408 26.888329679975871755
08.80703900079243 405.9123338526115 26.963083993558033313
08.53773760295007 406.7052164496854 27.858198002959401407
08.41652493539732 407.0524781392887 28.757203074732391457
08.52758264017757 406.6738497940823 31.053074612094864193
08.87626182776876 405.6203993018717 31.280143404408594421
10.76737655617762 399.9344173343852 31.089279419526068438
11.45857777469791 397.866535901092 30.485932862964549628
11.71811043436173 397.0950137358159 30.005155312608625451
11.97905500605702 396.329041252844 29.018845753107601837
12.00984423828777 396.2506101094186 28.286031205214388962
20.80346840480343 394.5582724893466 26.252471478292136453
20.83452274359297 394.8729069987312 26.251258579730347265
21.21817035262939 396.8859696928412 26.252499568428902421
21.90245209762361 397.704919565469 26.27871021963073872
23.85032996872906 398.3787633813918 26.367671578067529481
23.96741564373951 398.021926051937 26.376459282197174616
23.94998104381375 396.9036475494504 26.385293174258549698
23.86145736963954 395.9195628045127 26.389505548962915782
23.64098852674942 395.0058241290972 26.386687586549669504
23.41368229675572 394.7338786125183 26.37798005106014898
22.96944047545549 394.2039216337726 26.360949034067743924
21.23965976189356 393.663882621564 26.281443101899640169
20.93250534043182 393.7542659183964 26.265712519874796271
20.80562129733153 394.2402452873066 26.255329819316102657
31.64227601420134 400.4718440799043 26.308601208285377737
31.50001746148337 400.8872672170401 24.812038144656522576
31.20135234494228 401.7730528181419 24.43203426939589562
29.52001882740296 406.7686698706821 24.136001628475238334
26.29915334878024 416.3441157452762 24.69114248169261927
26.1248234231025 416.8624914428219 24.742061702833776593
26.11248733312823 416.9349386068061 31.996118691243278676
26.14367313263938 416.8466270966455 32.883139959273187003
26.2912882338278 416.4135445440188 34.027115702246646833
27.16864283324685 413.8168328749016 36.230025488649516774
30.71506579651032 403.276563603431 36.235066279730283156
31.51127557805739 400.8956623580307 33.297050994041001104
09.59564814821351 394.266237183474 23.070070483572628461
08.98182004480623 396.1089351205155 22.289983680547717171
06.67405171331484 403.0197057845071 22.568072978725464139
06.42911847715732 403.7530555464327 22.620053542422731141
05.25583994959015 407.2653012191877 22.990070824541810168
05.29908964328934 407.1307168509811 23.937067712976382694
05.49721778184175 406.5284369569272 25.598103495304709298
05.84691255330108 405.4806289412081 25.672971400915226781
06.42916319612414 403.7363835535944 25.72598606830432999
08.9856130204862 396.0788739966229 25.802009978416201363
09.36904216720723 394.9336818093434 25.190003630415041869
10.15165971359238 405.6163990423083 33.789335982455668272
10.37148975417949 405.8803684646264 33.783077011044952087
12.0010716955876 406.6901133377105 33.741511563301173737
12.18241358699743 406.7261748481542 33.737113780531217344
12.83117339585442 406.8113115467131 33.721565309762809193
13.29875867755618 406.5559565816075 33.711693086032028077
14.01517911243718 405.8388523114845 33.697939795762067661
14.22524788940791 405.1608646875247 33.695877275138627738
14.36998980306089 404.6679981648922 33.69456449045901536
15.31806151068304 401.2138311527669 33.686917011214973172
15.15848043421283 399.4771868744865 33.697968835818755906
14.88845390093047 399.1793620120734 33.705545703567622695
13.67576247791294 398.7554175294936 33.735725071888737148
13.11592012050096 398.8444458916783 33.748458006175496848
12.13463081652299 399.6353938421234 33.768101786241459195
11.4907712014392 400.6725990129635 33.778807767852413235
11.08449465525337 401.6680889939889 33.78412678188396967
10.92545679805335 402.1549022719264 33.785799785619019531
10.17481925431639 405.2944277450442 33.790150006148905959
24.09592059464194 404.9350041607395 33.655649064670797088
23.65366482082754 405.4009651616216 33.652468012895042193
23.34727713791654 405.5039854748175 33.650342741070289776
22.69172771042213 405.3030142690986 33.645946016562902514
21.12705132400151 404.7709973100573 33.635470535049989849
20.15541586140171 404.367978207767 33.628991401805706118
20.16600559651852 404.0449997065589 33.62917895499504084
20.71686616842635 402.0860070129856 33.633633620165710454
23.80807237396948 402.9879962084815 33.654382442354744853
24.04598021658603 403.2730010366067 33.655902321814664901
16.30886730283964 399.0485838828608 37.383880029374267906
16.05505506973714 400.1230322783813 35.79329137405147776
16.0527517106384 400.952840895392 34.653746826661517844
17.05309022823349 401.3127794396132 34.618906202565995045
18.81781646446325 401.9174010902643 34.599091457086615264
28.4063362107845 405.1661522341892 34.541396368062123656
29.48844987957273 405.5208615828305 34.551251482829684392
29.82462588546332 405.4303451469168 34.82968846065341495
29.82834649470169 404.3090427070856 36.369809397161589004
28.90587378223427 403.4075349662453 37.183404198702191934
28.10649059840944 403.0420899093151 37.31800513707275968
24.80114753521048 401.763745944947 37.55526928196195513
17.45412425382528 399.3909041853622 37.439705149954534136
27.98403379356023 406.2150578461587 33.63974307622629567
25.19196529197507 405.2919405875728 33.644397773788682571
25.1099953004159 404.6879919553176 33.645585668971989435
26.1279870554572 404.5019778413698 33.644840912326117177
26.54101301380433 404.5990222766995 33.644224354813331956
28.26099261292256 405.3299873536453 33.641061172168406301
28.63200675719418 405.5050115659833 33.640347230511792986
28.5250124570448 405.9220213247463 33.63970118112365526
31.12578812881839 401.010466940701 34.20225403872973402
30.14019220275804 400.7123725209385 34.206975818688079016
28.70433963113464 400.2712514922023 34.213637897622902528
19.82130767614581 397.3515258030966 34.248815124050452141
17.51184840325732 396.3741302173585 34.25104779755929485
18.54598947521299 396.3450231952593 34.235267489064426627
20.44490156602114 396.9060130519792 34.225747853592110914
31.02429209568072 400.3983562542126 34.184329492580218357
12.83047490182798 409.8276979653165 37.301745028627919964
12.73099151055794 410.045077146031 36.951467148057417944
12.52860016422346 410.9702720120549 35.564028248874819838
12.43777488998603 411.5709831416607 34.682175228430423886
13.68894848483615 412.2667654724792 34.295242627704283223
16.75632934679743 413.3165501356125 34.263254940509796143
25.46423992456403 416.198115022853 34.310277851545833983
25.64967550383881 416.2474643588066 34.328065942827379331
26.02825473761186 416.2724462365732 34.470251434337114915
26.98133600945584 414.3280222164467 37.633006195974303409
17.86789295799099 411.3135268893093 37.582024828647263348
16.00925202551298 410.7091953996569 37.557012233199202456
12.70881285716314 409.8854826185852 26.35909908402027213
12.41799780190922 411.1971517587081 26.35149347089100047
12.20115599653218 411.415824951604 26.34760929249387118
11.32714918686543 411.9581288369372 26.332751546116924146
08.39887227059808 411.120019341819 26.289219659567606868
07.64131585264113 410.8741035982966 26.278026254425640218
05.80604593711905 410.2209930438548 26.251043859687342774
05.20725857350044 409.95696084667 26.242360280382854398
04.75450039946008 409.606075652875 26.236150299191649538
04.75511477189139 409.4129826202989 26.236614212870335905
04.90493087808136 409.1980104669929 26.239448134670965374
11.0074223926058 408.6692388914526 26.335522219618724193
11.81245074269827 408.8112345989794 26.347697850069380365
16.00699518492911 398.2397992908955 31.834022784569242503
15.72654205991421 398.2219329783693 31.838362203432552633
15.5600269655697 398.2038861773908 31.840274126569056534
12.10687672731001 397.4350782707334 31.844675920816371217
12.31434874620754 396.5305279297754 31.75947836552222725
12.50871230196208 396.486214382574 31.751406192868671496
14.80485285457689 396.9366211052984 31.743047333889990114
15.5045500950655 397.2696780292317 31.757992157066837535
16.11687475908548 397.9250865774229 31.803582919941618457
21.24400211707689 415.3649937966838 34.221373337626516786
20.76801680563949 415.2369507532567 34.221335424372284706
16.20199049613439 413.7480278508738 34.221259107364630836
15.49297467514407 413.4630742128938 34.221306525021873313
14.1950002212543 412.9239993542433 34.221412537064963999
13.96100540529005 412.6429841602221 34.221634344188714749
14.22299253626261 412.5490218736231 34.221836523627530369
14.54503161227331 412.5259073628113 34.221983180784320666
16.68394469725899 413.2591620618477 34.221979476686499311
21.62200755579397 414.9989778585732 34.221919139503370388
16.27177142922301 397.4098354214802 26.380329670930223074
15.84392641438171 397.373166853562 26.390112569297343725
09.51510503934696 394.58267401997 26.349077292325091548
09.62544953019824 394.0914825974032 26.305188237547554309
10.31081303523388 393.6737568015233 26.250149228435475379
11.51109791407362 393.9939708076417 26.240662030762905488
13.8091999044409 394.7544484538957 26.234675300740491366
16.21612889831886 395.6586443642154 26.237304228587163379
16.4901551468065 395.7685719421133 26.238180734042543918
17.11912527575623 397.0248591434211 26.323145475311321206
16.68454568006564 397.262458274141 26.355791359703289345
11.93014367308933 408.4866758985445 31.791276846430264413
09.28215536219068 407.7173936963081 31.75605983633431606
08.74757413670886 407.4797048456967 31.756121767808508594
08.61412482708693 407.3882067482919 31.75893703239489696
09.05170323851053 406.4516927711666 31.857346675777080236
09.24047374923248 406.1099546290934 31.894379589299205691
12.85623958439101 407.9982270346954 31.869533666489587631
13.01573631656356 408.1685248427093 31.860864001089794328
12.60696733568329 408.3685853499919 31.827674651693087071
12.5537612909684 408.3870501527563 31.824014113088196609
26.09436442807782 411.1418359261006 33.613139895019230607
22.76559876604006 411.1088722394779 33.5871516782008257
22.39167954714503 411.0465058833361 33.593537769562317408
21.11190716864076 410.6785701820627 33.639898887096933322
18.3858401379548 409.5416183434427 33.794679730270217988
18.06981709052343 409.3120081759989 33.828137343183698249
18.49924923444632 408.2976987315342 33.993056488779984647
18.78232809819747 408.2413648013026 34.004646997062991431
21.8356563069392 408.9238989697769 33.92501590406664036
22.96910176088568 409.2903643958271 33.877515758465051476
25.84534085320774 410.4831490581855 33.715286898040176311
09.43863188964315 405.3597322963178 31.762247841048520058
09.89115137478802 405.181680591777 31.777478949341457337
10.17826394399162 404.8533465769142 31.783940076376893558
10.37235267239157 404.6110035898164 31.788004619942512363
10.6306758383289 403.8708779914305 31.787204625259619206
11.89047611737624 399.4353083558381 31.771018473955336958
12.08489108085632 398.7040881551802 31.767825920222094283
12.03206044901162 398.6396463699639 31.764780301818973385
11.79601710278075 398.3573828209192 31.751256837189430371
11.32837305520661 398.8131404118612 31.739557866923860274
11.09322579344735 399.108837752603 31.734664536954369396
09.60053287597839 404.0732005666941 31.749512755166506395
22.38012466148939 410.2717697853222 35.512808224564651027
21.76535009255167 410.0583534762263 35.515353234756730672
21.16399827261921 409.8030031882226 35.516850234233061201
18.70914852875285 408.7097257077694 35.521877533411498007
18.88266742322594 408.4416141761467 35.514165402064463706
19.26781342586037 408.446344550699 35.509823906101701141
19.40485333825927 408.4482708433643 35.508284325263048231
19.52424465096556 408.4625481972471 35.507211379973341536
22.42845231422689 409.3311647009104 35.492215933744773793
22.51874839002267 409.519464654848 35.495185251558723394
07.66731075686403 405.9165781596676 26.045615110313519835
08.10775040811859 406.0468677319586 26.191244866116903722
08.62209367996547 406.193032508716 26.360687590204179287
09.38062976091169 405.1466660387814 26.479257534607313573
12.16817101661582 396.1632100148126 26.380301777506247163
12.08555225213058 395.6253103781492 26.299552559503354132
11.72506791120395 395.4042473118752 26.168452374171465635
10.39215060905553 396.7210750905797 25.905791483819484711
07.82994132814929 405.2935093222186 26.029544222517870367
05.59181280178018 406.697630411014 26.130241364473477006
05.71033744746819 406.9111389126629 26.420678965747356415
05.96792367403395 406.9424428027123 26.832983688684180379
06.0822843904607 406.6657573990524 26.869036767864599824
06.74939188617282 404.9568413356319 27.031335173873230815
07.58642286830582 401.6892724130303 26.666695349849760532
07.28482181811705 401.8848574198782 26.301421149633824825
06.01104488689452 405.4353220108896 26.136943868827074766
05.81004733382724 405.9985682694241 26.112505188211798668
15.04790493741166 404.5012912554666 37.477588657115120441
14.64020109013654 404.7543642893434 36.944467098408495076
14.30796716420446 405.8120464729145 35.34264787744905334
14.3989862886956 406.265977458097 34.761845092114526778
16.45279925479554 407.2056752191857 34.411195288426824845
16.91541617247276 407.3847473813221 34.376898264425108209
24.54904939758126 409.8888082224876 34.429038441114244051
27.74898807646241 410.9189691953361 34.477650952685507946
28.57294878060929 409.8112522726879 36.372579813105403446
28.25475491897669 409.1197040956467 37.175444313543266617
27.10640522127505 408.5657455120236 37.410639556153910235
25.8794661895372 408.1435740171 37.429266919891233556
24.60806292074267 407.7107849912718 37.44215016947418917
07.38440433656797 400.9313132427633 26.120383454253897071
07.23960742726922 401.7865592353046 26.324537456966936588
07.53420250117779 401.793233146891 26.672524284571409225
07.71495402022265 401.7367494972423 26.859560764161869884
08.1986744357273 401.4598023481667 27.305128939915448427
08.89351076516323 399.9361853413284 27.453164787497371435
10.28881558030844 394.8661671532318 26.871835514204576612
10.03704017028213 394.8759261742234 26.581186483148485422
09.8059262256138 394.8859698623419 26.31486340262927115
09.43845552066341 395.0240454431623 25.944773125695064664
09.66778644081205 403.4647348187864 26.337618718344856461
08.8997445841087 403.6956828441471 26.333443882085703081
07.06372075318359 406.8888949789107 26.351912883042132307
06.69527769403066 407.9611030938104 26.360266123376277392
06.77834777918179 408.258190119639 26.364186821667317417
08.09166745655239 408.8183453446254 26.381613183995796135
09.03677122516092 409.2159576499835 26.394094553454124252
09.20211365749128 409.2243828577921 26.395619596917640592
09.61370015062857 409.0736276702955 26.397566103636563639
15.4682791858213 399.2634038729593 31.479548230476211756
14.80883755779359 399.1494604777545 31.433286550818593241
13.06189165648539 398.7613800894469 31.341803388626431115
12.61210298794322 398.039652923122 31.542296807514503598
12.4739399967948 397.646624116227 31.665617381368065253
12.69228640513029 397.4224994368851 31.775284764531534165
13.17649666231591 397.4756484823301 31.820249089199933223
15.15018516452983 398.2171205608174 31.814422523078974336
15.87209138239268 398.5394725268707 31.793862094564246945
28.47482733079232 408.4539570054039 37.457226056736544706
26.64186012768187 408.0440455144271 37.7303463601419935
23.92127509554848 407.1618094155565 37.758067429982475005
15.51361764210742 404.209619323723 37.532395488669862971
14.98390895675402 403.7298531495035 37.113011360270320438
15.13680858619045 402.519411591813 35.373628491215640679
15.62003063806333 402.1724260812625 34.674021707149222493
15.90693419333547 402.0676498580724 34.398273581871762872
17.4373902019579 402.5643118815497 34.383179915108485147
28.92778840148821 406.4030059529468 34.421353010489838198
29.09841634274926 406.6933534517884 34.74374837864888832
29.10631852201186 406.7495580958202 34.817651046163518913
29.11926705250517 407.2447516089305 35.494698643509764224
28.85813654132653 407.9069043342024 36.527390699804527685
30.26375315745827 403.1060210810974 37.419273910403717309
29.72118894685991 402.9403491765261 37.441250081261387095
27.37703220266849 402.1986325215548 37.500426853759563528
17.94393827067688 399.0468268487602 37.508064336841925979
16.60063665651251 398.3095294414088 37.111174231307813898
16.5283750644885 397.9964971924201 36.712679079661029391
16.83965840144083 396.9415150415152 35.11347122787265107
17.02770575834438 396.5115091884509 34.433395225365529768
17.30162658600602 396.5665348675102 34.38282125124533195
19.63386923552025 397.2746989382431 34.282856459161848761
22.84955634304788 398.3093501618132 34.225370394095079973
25.13768680905923 399.1083672726527 34.271117255120771006
30.171610408579 400.8717804849148 34.379435587601619773
30.79252238594927 401.1926858900115 34.535442984124529175
30.86113985301927 401.2371999248862 34.565169709268957376
30.44147782737855 402.8868560073897 37.034843245564843528
25.9360053059645 413.545240602456 37.482975690931198187
21.52375787985511 412.1020318977535 37.510900912238867022
14.98278726264834 409.9073546351865 37.475929563224781305
13.82306245248765 409.5052645234391 37.451778643415309489
13.1865007460583 409.1682788869366 37.27759104227880016
12.9883900124114 408.9704278400168 37.094706454357947223
13.51134748803452 407.5007107844576 34.820614318232401274
13.54922282882035 407.4640715504065 34.752521318441722542
13.82592372654472 407.5069092074409 34.684797710651764646
14.86175025370903 407.6851303623989 34.455991395661840215
15.38129317294806 407.8126745382324 34.394031222997000441
26.57955647364724 411.2675523795187 34.035319927003001794
27.20708823762834 412.062462261878 34.84740668126323726
26.88831128063612 413.6024539070204 37.125035057601053268
4 0 1 2 3 151 47 171 255
11 4 5 6 7 8 9 10 11 12 13 14 104 165 85 255
13 15 16 17 18 19 20 21 22 23 24 25 26 27 57 123 160 255
10 28 29 30 31 32 33 34 35 36 37 170 81 74 255
4 38 39 40 41 122 40 149 255
4 42 43 44 45 75 158 63 255
4 46 47 48 49 188 116 138 255
4 50 51 52 53 141 74 52 255
4 54 55 56 57 93 32 127 255
4 58 59 60 61 46 150 41 255
4 62 63 64 65 159 108 116 255
4 66 67 68 69 112 67 190 255
4 70 71 72 73 64 185 105 255
14 74 75 76 77 78 79 80 81 82 83 84 85 86 87 177 143 179 255
14 88 89 90 91 92 93 94 95 96 97 98 99 100 101 130 101 94 255
12 102 103 104 105 106 107 108 109 110 111 112 113 83 59 168 255
11 114 115 116 117 118 119 120 121 122 123 124 35 177 83 255
19 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 148 135 157 255
10 144 145 146 147 148 149 150 151 152 153 101 94 71 255
13 154 155 156 157 158 159 160 161 162 163 164 165 166 53 52 146 255
8 167 168 169 170 171 172 173 174 166 170 60 255
8 175 176 177 178 179 180 181 182 119 128 135 255
12 183 184 185 186 187 188 189 190 191 192 193 194 72 86 49 255
13 195 196 197 198 199 200 201 202 203 204 205 206 207 184 44 124 255
9 208 209 210 211 212 213 214 215 216 137 163 38 255
10 217 218 219 220 221 222 223 224 225 226 90 121 113 255
11 227 228 229 230 231 232 233 234 235 236 237 43 79 187 255
10 238 239 240 241 242 243 244 245 246 247 155 37 102 255
11 248 249 250 251 252 253 254 255 256 257 258 108 155 176 255
12 259 260 261 262 263 264 265 266 267 268 269 270 61 113 91 255
10 271 272 273 274 275 276 277 278 279 280 174 71 165 255
9 281 282 283 284 285 286 287 288 289 126 190 80 255
9 290 291 292 293 294 295 296 297 298 79 148 154 255
13 299 300 301 302 303 304 305 306 307 308 309 310 311 32 106 69 255
10 312 313 314 315 316 317 318 319 320 321 144 64 143 255
9 322 323 324 325 326 327 328 329 330 97 182 58 255
9 331 332 333 334 335 336 337 338 339 50 140 132 255
14 340 341 342 343 344 345 346 347 348 349 350 351 352 353 163 99 47 255
16 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 115 57 121 255
14 370 371 372 373 374 375 376 377 378 379 380 381 382 383 68 175 36 255

View File

@ -0,0 +1,7 @@
OFF
4 1 0
-1.2075642347335815 1.220183253288269 0.28014403581619263
-1.3879016637802124 0.81997495889663696 1.1786493062973022
-0.41184309124946594 0.860099196434021 1.3924243450164795
-0.2315056324005127 1.2603075504302979 0.4939190149307251
4 0 1 2 3

View File

@ -0,0 +1,7 @@
OFF
4 1 0
-1.2339011430740356 0.63655495643615723 1.7042105197906494
-0.2479671835899353 0.68639802932739258 1.544680118560791
-0.22395908832550049 -0.30043560266494751 1.3847332000732422
-1.2098929882049561 -0.35027864575386047 1.5442636013031006
4 0 1 2 3

View File

@ -0,0 +1,7 @@
OFF
4 1 0
-1.9354726076126099 0.71762150526046753 -0.19198636710643768
-1.175773024559021 1.3678698539733887 -0.1861824095249176
-0.56098687648773193 0.64675056934356689 0.13323120772838593
-1.3206864595413208 -0.0034976601600646973 0.12742726504802704
4 0 1 2 3

View File

@ -0,0 +1,7 @@
OFF
4 1 0
-1.6605820655822754 0.72791147232055664 1.1093254089355469
-1.6906610727310181 0.74579495191574097 0.10993790626525879
-1.5139358043670654 -0.23819819092750549 0.087010979652404785
-1.4838567972183228 -0.25608164072036743 1.0863984823226929
4 0 1 2 3

View File

@ -0,0 +1,12 @@
OFF
8 2 0
-1.2075642347335815 1.220183253288269 0.28014403581619263
-1.3879016637802124 0.81997495889663696 1.1786493062973022
-0.41184309124946594 0.860099196434021 1.3924243450164795
-0.2315056324005127 1.2603075504302979 0.4939190149307251
-1.2339011430740356 0.63655495643615723 1.7042105197906494
-0.2479671835899353 0.68639802932739258 1.544680118560791
-0.22395908832550049 -0.30043560266494751 1.3847332000732422
-1.2098929882049561 -0.35027864575386047 1.5442636013031006
4 0 1 2 3
4 4 5 6 7

View File

@ -0,0 +1,12 @@
OFF
8 2 0
-1.2075642347335815 1.220183253288269 0.28014403581619263
-1.3879016637802124 0.81997495889663696 1.1786493062973022
-0.41184309124946594 0.860099196434021 1.3924243450164795
-0.2315056324005127 1.2603075504302979 0.4939190149307251
-1.9354726076126099 0.71762150526046753 -0.19198636710643768
-1.175773024559021 1.3678698539733887 -0.1861824095249176
-0.56098687648773193 0.64675056934356689 0.13323120772838593
-1.3206864595413208 -0.0034976601600646973 0.12742726504802704
4 0 1 2 3
4 4 5 6 7

View File

@ -0,0 +1,12 @@
OFF
8 2 0
-1.2075642347335815 1.220183253288269 0.28014403581619263
-1.3879016637802124 0.81997495889663696 1.1786493062973022
-0.41184309124946594 0.860099196434021 1.3924243450164795
-0.2315056324005127 1.2603075504302979 0.4939190149307251
-1.6605820655822754 0.72791147232055664 1.1093254089355469
-1.6906610727310181 0.74579495191574097 0.10993790626525879
-1.5139358043670654 -0.23819819092750549 0.087010979652404785
-1.4838567972183228 -0.25608164072036743 1.0863984823226929
4 0 1 2 3
4 4 5 6 7

View File

@ -0,0 +1,12 @@
OFF
8 2 0
-1.2339011430740356 0.63655495643615723 1.7042105197906494
-0.2479671835899353 0.68639802932739258 1.544680118560791
-0.22395908832550049 -0.30043560266494751 1.3847332000732422
-1.2098929882049561 -0.35027864575386047 1.5442636013031006
-1.9354726076126099 0.71762150526046753 -0.19198636710643768
-1.175773024559021 1.3678698539733887 -0.1861824095249176
-0.56098687648773193 0.64675056934356689 0.13323120772838593
-1.3206864595413208 -0.0034976601600646973 0.12742726504802704
4 0 1 2 3
4 4 5 6 7

View File

@ -0,0 +1,12 @@
OFF
8 2 0
-1.2339011430740356 0.63655495643615723 1.7042105197906494
-0.2479671835899353 0.68639802932739258 1.544680118560791
-0.22395908832550049 -0.30043560266494751 1.3847332000732422
-1.2098929882049561 -0.35027864575386047 1.5442636013031006
-1.6605820655822754 0.72791147232055664 1.1093254089355469
-1.6906610727310181 0.74579495191574097 0.10993790626525879
-1.5139358043670654 -0.23819819092750549 0.087010979652404785
-1.4838567972183228 -0.25608164072036743 1.0863984823226929
4 0 1 2 3
4 4 5 6 7

View File

@ -0,0 +1,12 @@
OFF
8 2 0
-1.9354726076126099 0.71762150526046753 -0.19198636710643768
-1.175773024559021 1.3678698539733887 -0.1861824095249176
-0.56098687648773193 0.64675056934356689 0.13323120772838593
-1.3206864595413208 -0.0034976601600646973 0.12742726504802704
-1.6605820655822754 0.72791147232055664 1.1093254089355469
-1.6906610727310181 0.74579495191574097 0.10993790626525879
-1.5139358043670654 -0.23819819092750549 0.087010979652404785
-1.4838567972183228 -0.25608164072036743 1.0863984823226929
4 0 1 2 3
4 4 5 6 7

View File

@ -0,0 +1,17 @@
OFF
12 3 0
-1.2075642347335815 1.220183253288269 0.28014403581619263
-1.3879016637802124 0.81997495889663696 1.1786493062973022
-0.41184309124946594 0.860099196434021 1.3924243450164795
-0.2315056324005127 1.2603075504302979 0.4939190149307251
-1.2339011430740356 0.63655495643615723 1.7042105197906494
-0.2479671835899353 0.68639802932739258 1.544680118560791
-0.22395908832550049 -0.30043560266494751 1.3847332000732422
-1.2098929882049561 -0.35027864575386047 1.5442636013031006
-1.9354726076126099 0.71762150526046753 -0.19198636710643768
-1.175773024559021 1.3678698539733887 -0.1861824095249176
-0.56098687648773193 0.64675056934356689 0.13323120772838593
-1.3206864595413208 -0.0034976601600646973 0.12742726504802704
4 0 1 2 3
4 4 5 6 7
4 8 9 10 11

View File

@ -0,0 +1,17 @@
OFF
12 3 0
-1.2075642347335815 1.220183253288269 0.28014403581619263
-1.3879016637802124 0.81997495889663696 1.1786493062973022
-0.41184309124946594 0.860099196434021 1.3924243450164795
-0.2315056324005127 1.2603075504302979 0.4939190149307251
-1.2339011430740356 0.63655495643615723 1.7042105197906494
-0.2479671835899353 0.68639802932739258 1.544680118560791
-0.22395908832550049 -0.30043560266494751 1.3847332000732422
-1.2098929882049561 -0.35027864575386047 1.5442636013031006
-1.6605820655822754 0.72791147232055664 1.1093254089355469
-1.6906610727310181 0.74579495191574097 0.10993790626525879
-1.5139358043670654 -0.23819819092750549 0.087010979652404785
-1.4838567972183228 -0.25608164072036743 1.0863984823226929
4 0 1 2 3
4 4 5 6 7
4 8 9 10 11

View File

@ -0,0 +1,17 @@
OFF
12 3 0
-1.2075642347335815 1.220183253288269 0.28014403581619263
-1.3879016637802124 0.81997495889663696 1.1786493062973022
-0.41184309124946594 0.860099196434021 1.3924243450164795
-0.2315056324005127 1.2603075504302979 0.4939190149307251
-1.9354726076126099 0.71762150526046753 -0.19198636710643768
-1.175773024559021 1.3678698539733887 -0.1861824095249176
-0.56098687648773193 0.64675056934356689 0.13323120772838593
-1.3206864595413208 -0.0034976601600646973 0.12742726504802704
-1.6605820655822754 0.72791147232055664 1.1093254089355469
-1.6906610727310181 0.74579495191574097 0.10993790626525879
-1.5139358043670654 -0.23819819092750549 0.087010979652404785
-1.4838567972183228 -0.25608164072036743 1.0863984823226929
4 0 1 2 3
4 4 5 6 7
4 8 9 10 11

View File

@ -0,0 +1,17 @@
OFF
12 3 0
-1.2339011430740356 0.63655495643615723 1.7042105197906494
-0.2479671835899353 0.68639802932739258 1.544680118560791
-0.22395908832550049 -0.30043560266494751 1.3847332000732422
-1.2098929882049561 -0.35027864575386047 1.5442636013031006
-1.9354726076126099 0.71762150526046753 -0.19198636710643768
-1.175773024559021 1.3678698539733887 -0.1861824095249176
-0.56098687648773193 0.64675056934356689 0.13323120772838593
-1.3206864595413208 -0.0034976601600646973 0.12742726504802704
-1.6605820655822754 0.72791147232055664 1.1093254089355469
-1.6906610727310181 0.74579495191574097 0.10993790626525879
-1.5139358043670654 -0.23819819092750549 0.087010979652404785
-1.4838567972183228 -0.25608164072036743 1.0863984823226929
4 0 1 2 3
4 4 5 6 7
4 8 9 10 11

View File

@ -0,0 +1,22 @@
OFF
16 4 0
-1.2075642347335815 1.220183253288269 0.28014403581619263
-1.3879016637802124 0.81997495889663696 1.1786493062973022
-0.41184309124946594 0.860099196434021 1.3924243450164795
-0.2315056324005127 1.2603075504302979 0.4939190149307251
-1.2339011430740356 0.63655495643615723 1.7042105197906494
-0.2479671835899353 0.68639802932739258 1.544680118560791
-0.22395908832550049 -0.30043560266494751 1.3847332000732422
-1.2098929882049561 -0.35027864575386047 1.5442636013031006
-1.9354726076126099 0.71762150526046753 -0.19198636710643768
-1.175773024559021 1.3678698539733887 -0.1861824095249176
-0.56098687648773193 0.64675056934356689 0.13323120772838593
-1.3206864595413208 -0.0034976601600646973 0.12742726504802704
-1.6605820655822754 0.72791147232055664 1.1093254089355469
-1.6906610727310181 0.74579495191574097 0.10993790626525879
-1.5139358043670654 -0.23819819092750549 0.087010979652404785
-1.4838567972183228 -0.25608164072036743 1.0863984823226929
4 0 1 2 3
4 4 5 6 7
4 8 9 10 11
4 12 13 14 15

View File

@ -0,0 +1,32 @@
OFF
24 6 0
0 0 0
0 0 1
0 1 1
0 1 0
-0.41184309124946594 0.860099196434021 1.3924243450164795
-0.2315056324005127 1.2603075504302979 0.4939190149307251
-1.2075642347335815 1.220183253288269 0.28014403581619263
-1.3879016637802124 0.81997495889663696 1.1786493062973022
-1.5139358043670654 -0.23819819092750549 0.087010979652404785
-1.4838567972183228 -0.25608164072036743 1.0863984823226929
-1.6605820655822754 0.72791147232055664 1.1093254089355469
-1.6906610727310181 0.74579495191574097 0.10993790626525879
-0.22395908832550049 -0.30043560266494751 1.3847332000732422
-1.2098929882049561 -0.35027864575386047 1.5442636013031006
-1.2339011430740356 0.63655495643615723 1.7042105197906494
-0.2479671835899353 0.68639802932739258 1.544680118560791
-0.56098687648773193 0.64675056934356689 0.13323120772838593
-1.3206864595413208 -0.0034976601600646973 0.12742726504802704
-1.9354726076126099 0.71762150526046753 -0.19198636710643768
-1.175773024559021 1.3678698539733887 -0.1861824095249176
0.14283668994903564 -0.38020235300064087 0.41061314940452576
-0.5413280725479126 -0.38269975781440735 1.1399362087249756
-1.2631766796112061 -0.52322244644165039 0.46230223774909973
-0.57901191711425781 -0.52072501182556152 -0.26702100038528442
4 3 0 1 2
4 6 7 4 5
4 10 11 8 9
4 14 15 12 13
4 18 19 16 17
4 22 23 20 21

View File

@ -0,0 +1,7 @@
OFF
4 1 0
0.15610991754831515799 0.24730995207178518847 0.75806682915926948407
0.16496709986145913218 0.18982328490637073726 0.70621064409094358449
0.20220499043408207696 0.20800513642778320489 0.71143331797136521999
0.16105327970812033378 0.2435329721365592226 0.75344213477059684969
4 0 1 2 3

View File

@ -0,0 +1,7 @@
OFF
4 1 0
-0.27848521200095188721 -0.17362065704985990555 -0.48022333651203907845
-0.20910287047394182647 -0.20831641286430413462 -0.53964773517772191003
-0.2134526329908462694 -0.33174234165134169894 -0.5522901251270546652
-0.31416959136675037811 -0.26137648424083415044 -0.46342192033603812895
4 0 1 2 3

View File

@ -0,0 +1,7 @@
OFF
4 1 0
0.6058591159546817817 -0.56965501181620259441 -0.29382108215188745826
0.71027544352893468016 -0.63504842208265066539 -0.24922269226626372896
0.63486095745100423748 -0.62410903622118019118 -0.27462002163464782623
0.5875997816478347735 -0.61217025573008843065 -0.29149056082363838938
4 0 1 2 3

View File

@ -0,0 +1,7 @@
OFF
4 1 0
0.65774115146030065482 -0.66588743441438869031 0.27077692585696377936
0.65483141298809188768 -0.64671837624026129454 0.22277241439658501676
0.66734344616877083745 -0.63810531966829220352 0.20761055052755555961
0.66426225766577595699 -0.65297632519401094253 0.24237630508545701669
4 0 1 2 3

View File

@ -0,0 +1,11 @@
OFF
7 2 0
-0.75586385340622519458 1 -0.23672909840242375989
-0.89154339425773576622 1 -0.37805786411577557704
-0.88250651732241514047 0.78626208915370920938 -0.36670801179005296788
-0.59348480160629835112 -0.74056950060417792159 -0.2050942961146551835
-0.58102610551155020602 -0.83393195226800997943 -0.15380965246301198102
-0.56262801769830761422 -0.86362049050797007332 -0.13372370148604620366
-0.55289320769567962266 -0.81624109559911572909 -0.15554750188514177012
3 0 1 2
4 3 4 5 6

View File

@ -0,0 +1,12 @@
OFF
8 2 0
0.3277950810341435095 -0.71572519555741642705 1
0.44583319221311557001 -0.55783953841437650123 1.000000000000000222
0.49792905888093180744 -0.56261138383018360898 0.95733761618227708468
0.35294980452977714469 -0.7302358935204456003 0.97240589188409809474
-0.2346615724500573652 0.2359083322962581275 -0.86630763542970434798
-0.17518768997739564419 0.25326623936895609202 -0.82117173852959646219
-0.20624273406363702321 0.24040269900916738655 -0.83899000079087826531
-0.31450806115767204751 0.2041965275375098865 -0.91418165777420656859
4 0 1 2 3
4 4 5 6 7

View File

@ -0,0 +1,12 @@
OFF
8 2 0
0.42664258079485539721 -0.64018711292148333669 -0.8515757057538347885
0.47128039048102887687 -0.69117967554445147726 -0.63642214593465173955
0.46521352858576825451 -0.6847858061697209564 -0.59387621079444419259
0.39467662025607563869 -0.60557076789227659575 -0.75145416500478101618
-0.61128424188107577386 -0.0065553514480266306119 0.12041273689470853581
-0.60307712410684555238 -0.015813165312354077185 0.10456127944179721689
-0.52365439684733239289 -0.036618358574756329493 0.10431565172664833407
-0.56077586454367644997 0.0030584121888515419796 0.17112120108282907749
4 0 1 2 3
4 4 5 6 7

View File

@ -0,0 +1,17 @@
OFF
12 3 0
0.41186711452985735882 -0.84447415301680273103 0.61971905157646700602
0.3726672154490638933 -0.82005456375273910741 0.57833709628588936269
0.43379958596908135826 -0.77821018652431572793 0.53965070351019428507
0.45953807659864553958 -0.85201997202192947256 0.64143687269748617119
0.56524011840538745943 -0.9269854143534064228 -0.93732252277842953436
0.53001528833590116907 -0.88197115046793839177 -0.80305925948165446382
0.5343169664938652863 -0.8813615647683934462 -0.76975034913882212084
0.58688152844940888464 -0.93327180618593374994 -0.84587700839017732068
0.84617929803296143554 -0.075606934264453112826 -0.45154586920481221135
0.84257074360400219248 -0.049825902960862020197 -0.49619010373477290265
0.84551728503647649582 -0.043649377875797881754 -0.5046759664229832909
0.85188434368617060866 -0.057012532939158339085 -0.47892803304073455761
4 0 1 2 3
4 4 5 6 7
4 8 9 10 11

View File

@ -0,0 +1,8 @@
OFF
5 1 0
-0.66508574846829815463 1.000000000000000222 1
-1 1.000000000000000222 0.29568263728738397589
-1 0.49897796883298217718 -0.11978312174973984594
-0.55660743371016518921 -0.16149819747870888809 0.26497082067774524461
-0.6084138559251720535 0.85627772973828997216 1
5 0 1 2 3 4 151 47 171 255

View File

@ -0,0 +1,8 @@
OFF
5 1 0
0.22199248907356433635 0.38234721541755123386 -0.40145544621769113647
-0.16310117977085047958 0.33839070297855550207 -0.67649397701646463155
0.54142965320095326476 0.11968280838115723241 -0.59326324340446001671
1.000000000000000222 0.053701914675631429175 -0.43186632245303618882
1.000000000000000222 0.14056359227637085785 -0.30991834223072101118
5 0 1 2 3 4 151 47 171 255

View File

@ -0,0 +1,8 @@
OFF
5 1 0
-0.55990680617290000676 -0.31687801053428754638 -1.000000000000000222
-0.29966590024503708678 -0.68176527196857317215 -0.6438155321085256011
-0.23989802816964495014 -0.68926183670651930413 -0.61289408431803793498
-0.22826065730695549449 -0.44086473021631966684 -0.77348164105171091087
-0.52861453414917458637 -0.29652433388882448728 -1.000000000000000222
5 0 1 2 3 4 151 47 171 255

View File

@ -0,0 +1,7 @@
OFF
4 1 0
-0.48289007545713447112 -0.61238641185731346184 -0.86469797977928597454
-0.80950276482308491932 -1 -0.16276171016941476388
-0.83915985241456136912 -1.000000000000000222 -0.0096588397408848350456
-0.092097972413576381645 0.45417658189572263083 -0.17405046706790600064
4 0 1 2 3 151 47 171 255

View File

@ -0,0 +1,12 @@
OFF
8 2 0
0.82247803392408824763 -0.38723958396306096263 0.71483223598611078664
0.4371395289299538911 0.41385223399823467538 1
0.7768629544902037054 -0.067486358150455449945 1
-0.52490807106371173418 0.70202499736418966236 -0.75131148071949449552
-1 0.14887624923234760166 -0.97841040882785967892
-1 0.12973035450051789708 -0.9952241441222685614
-0.40768718176727042346 0.58297646639260447543 -0.91968136980637482658
-0.44764602127526076369 0.75497110588690130584 -0.74688119809810737948
3 0 1 2
5 3 4 5 6 7

View File

@ -0,0 +1,19 @@
OFF
14 3 0
-1 -0.1308931904985670136 -0.33516365222314192795
-1 -0.6714824653370352614 -0.46095306936059748937
-0.53132660948762833186 -1 -0.49330740560611507917
0.057623176177123244801 -1 -0.43790474847955712656
0.12944421667994215897 -0.59071019190416351741 -0.33591110742787727572
-0.42911398666181577166 0.07466508181731418281 -0.23362901893928705865
0.7067437544383333714 0.37344944179068317869 -0.036158806509220278724
0.91313952580497859124 -0.012979918910328319681 -0.69007115976925992307
0.77787066773300050926 -0.78971672987692920209 -0.62288835681625798202
0.48017540496484628632 0.47262447393840950616 0.5676285342638573983
0.30269300459096654121 0.65614771078703726381 -0.4545514736489389418
-0.15148149240820321659 -0.053039606746919661096 -1
0.64392628567208598511 0.48307410642363313169 -1
0.6903559188209320574 0.92904220194979114655 -0.43884663379571980935
6 0 1 2 3 4 5
4 6 7 8 9
4 10 11 12 13

View File

@ -0,0 +1,13 @@
OFF
9 2 0
-0.76493907384012427286 1.000000000000000222 0.77243260997077778374
-0.45140411323920476283 1.000000000000000222 0.43172219910796783005
-0.13431377427363866417 0.7907631996323489787 -0.092352722151927968408
-1 0.34026961369270730673 0.46189493876274467787
-1 0.76006911279018973815 0.82203434977762390723
-1 0.11657020090119979416 -0.065554122581789067703
-1 -0.0054723979577700077731 0.014022945467475884593
-0.95051199506759909141 -0.56363750391437550391 0.34491514379739784957
0.37343330580552425157 -0.6855170465483748643 -0.45994733771416640433
5 0 1 2 3 4
4 5 6 7 8

View File

@ -0,0 +1,32 @@
OFF
25 5 0
-0.35937495820139497837 1 -1.000000000000000222
-0.56168365686147958549 1 -0.88079113017468313451
-0.51765631751223795121 0.71236096709012564077 -0.73280279939922654542
-0.32201075440850046583 0.41207376422586394771 -0.66650619669355770647
-0.24485654838122550281 0.81187983031110944054 -0.95372559705916493122
-0.30870221019199428625 0.95062137083057174358 -1
-0.92634822138016548188 0.32081332010057639348 -0.89166385180227825114
-0.65046206980728782376 -0.14506803516911384588 0.1349183917820096501
-0.90687582408461064887 -0.55696720534041499473 -0.020160895165416414798
-1 -0.49262635780542279873 -0.27880641108149650798
-1 -0.085148195619931407729 -0.6641707630162561049
-1 -0.41326725544380638055 -0.36778153643664412975
-0.82904255842645513397 -0.65731436296472334213 -0.072082412778263660336
-0.88802569218386917527 -1 0.094015865785319951975
-1 -1 0.00073472792471934722514
-0.28493565917010799105 -0.81230567244099638469 0.3037669232785958906
0.27084247530824534511 0.24191842674958360937 -1
0.4820710256500247981 0.409847753407221016 -1
-0.23633151774247695975 -0.82319359663389457538 0.40921569357703219127
-1 -0.28251564915131083255 0.11078989440908593167
-1 0.75867918699380421099 0.82693688741610604787
-0.85576724261093495283 0.74232635514938138943 1.000000000000000222
-0.71833701121485060703 0.486998038301589109 1
-0.75383346694464692384 0.096118113082173745054 0.68578796121348406523
-0.89033230213133696118 -0.5108053106028178636 0.093910499149422357879
6 0 1 2 3 4 5
5 6 7 8 9 10
4 11 12 13 14
4 15 16 17 18
6 19 20 21 22 23 24

View File

@ -0,0 +1,14 @@
OFF
10 2 0
0.065178480926116666438 1 -0.20264199478017669298
-0.10659096293787867493 1 -0.99999999999999988898
-0.082990164860711909678 0.73167804164683836188 -1
-0.066163058435701305182 0.61774449366182171417 -0.96840706757492145407
0.39731238540939262105 0.095576446776173107356 0.96985690312632977239
-1 -0.55362335819508701196 0.96499401999985501277
-0.73419017118209328743 -1.000000000000000222 0.42819404217768131105
-0.35962148959437645335 -1.000000000000000222 0.67195886059086873399
-0.74144277975571681871 -0.637429111257047043 1
-1 -0.53160847565811986115 1
5 0 1 2 3 4
5 5 6 7 8 9

View File

@ -0,0 +1,13 @@
OFF
9 2 0
-0.84361004019701402168 -0.65130254444935298253 1
-0.95423896077224912293 -0.50938438578589595451 0.69781052627559669865
-1 -0.55430483193732538183 0.64158495303585083569
-1 -0.69336060090569506809 0.73387372724361377152
-0.86883463768205282385 -0.72276222859077143834 1
-0.25796910527514543832 1.000000000000000222 0.054658922434833369375
-0.066746659167777860899 -0.16823609427621460943 0.36926899081650771395
-0.24439833879466932309 0.92947306772009130604 1
-0.25590097132239597588 1.000000000000000222 1
5 0 1 2 3 4
4 5 6 7 8

View File

@ -0,0 +1,14 @@
OFF
10 2 0
0.99395946670045409732 -0.60484981715831964699 1
0.082000690456150326924 0.12862242584613770013 1
0.11251197455782492585 1 -0.07986612560595388044
0.40482642634579113494 1 -0.36324063342911483421
1 0.63472626351812611034 -0.49994044031475315393
1 -0.59792896677548235118 0.98580237350131394436
0.38334599303813665649 -0.046781085286543885871 -1
0.82632443446521053332 0.40968638946239865906 -0.57274494339167314472
1 0.31767600361817982524 -0.69552390095880878285
1 0.033459285003822773763 -1
6 0 1 2 3 4 5
4 6 7 8 9

View File

@ -0,0 +1,7 @@
OFF
4 1 0
-1 0.050837722027102974498 0.54505759599567538132
0.16811988199649041675 0.80151514898576281531 -0.40109844110374870407
-0.58818491486273871693 0.31521669829332493729 0.21741677816456833616
-1 0.050730632041863517323 0.54741721763029804748
4 0 1 2 3

View File

@ -0,0 +1,14 @@
OFF
10 2 0
-1 0.030045090724081961048 -0.13245310112330005436
-0.15988174854623449228 -0.053663372801941000567 -0.24395348495693491842
0.20856755520811867677 -0.20955867925969323684 1
-0.42251405587732343561 -0.13895714421134214289 1
-1 -0.045583618398000957939 0.68793685321320441339
0.34148803330388094457 0.45290081304230628279 -1
-0.0019946781954731521505 -0.54523601993875181471 -0.45249651637909066304
1 -0.73388441054823971843 0.46529063840734929958
1 0.12075133383802641451 -0.22796759125806756452
0.62913514637166012555 0.72354959198817503374 -1.000000000000000222
5 0 1 2 3 4
5 5 6 7 8 9

View File

@ -0,0 +1,13 @@
OFF
9 2 0
0.21014951485263619335 1 0.88322417841893186008
-0.80833788025653663389 1 -0.3903756888948147763
-0.91308199916295884613 0.46390556084368345102 -1
-0.17215345807450577187 -0.57382025641791567505 -1
0.46663273709075830942 0.48797826485366435634 0.74680099826306656219
-1 1 0.57313255164517351581
-1 0.54861250776213532632 0.01191340639967708448
-0.51534558532781971074 0.83882461852285628012 0.65873530482115905116
-0.64253052892012707531 1 0.78407585863525697256
5 0 1 2 3 4
4 5 6 7 8

View File

@ -0,0 +1,12 @@
OFF
8 2 0
1 -0.18032788943244038027 -1
1 -1 -0.62043922092690551029
0.46774636865127655616 -1 -1
-0.60803313547406756534 0.92278262536235888813 0.31030950273307777998
-0.074786157769665961847 0.69086312870825294929 0.42312887536103316322
0.88258564703627984116 0.4189583808595845893 1
-0.2422025097226233048 1 1
-0.68251042711498965954 1 0.41069115005829937015
3 0 1 2
5 3 4 5 6 7

View File

@ -0,0 +1,23 @@
OFF
17 4 0
-0.59927801135997582627 0.054423761118229552203 -0.12361202917057637074
0.33832899450009945586 0.34180695431538510309 -0.88320153093211084538
0.55809619306034807806 0.2161012005285471993 -1
0.97496969797187027496 -0.72078719735808149949 -1
0.9163996679067800688 -1 -0.86967515528330574526
0.34195859898220892781 -1 -0.46015125039554727326
0.92125003698889240678 -1 0.080719083865852497839
1 -0.90164933058389584719 0.013373242912551153161
1 -1 0.13705569297706979293
-0.14178338317944827462 -0.3652723792320966556 0.83367757030327360734
-0.43643519781711792582 -0.99999999999999988898 0.50479077088635593284
-0.56007763899565277121 -1 0.91840637866040641946
-0.51072334963738752478 -0.8808852428280217195 1
-0.14009353589600781476 -0.28223585199477130292 1
-0.9705898296613374443 0.072163451161779190723 0.11837352551837125592
-1 0.2796136648060683072 0.23899121544953688678
-0.99999999999999988898 0.22652888809555332683 0.21314102147935104492
6 0 1 2 3 4 5
3 6 7 8
5 9 10 11 12 13
3 14 15 16

View File

@ -0,0 +1,13 @@
OFF
9 2 0
0.04592020327577803207 -0.92473712134298158283 -0.10204495476006578136
0.93580989228502475807 0.45036944155644575982 -0.23927111881850338104
1 0.4913677706800226308 -0.27229956184809461783
0.99999999999999988898 -1 -0.86508570849756161181
0.016483382794031872787 -1 -0.10934073037520947169
-0.43938321811258473915 -0.41058405164472389082 0.67650709033740252796
-1.000000000000000222 0.95898111697777688178 -0.15663746870248965171
-1 -1.000000000000000222 0.43012998993532880476
-0.34980136587230392653 -1.000000000000000222 0.92063254112484982361
5 0 1 2 3 4
4 5 6 7 8

View File

@ -0,0 +1,25 @@
OFF
20 3 0
-0.041136787858694082165 0.10169415400705132668 -1
0.66024800308791609105 -1 -1
1 -1 0.076373139665307260282
1 -0.76733277287752810203 0.54565238478165234426
0.36711295045044056717 0.051890849170681388469 0.19293080246793398169
0.021476723311123921412 0.11318570011392727059 -0.7784553162836537199
-1 0.26140655961523528994 0.90242863773611692313
-1 1 0.026632628846286948709
0.39992008604188844512 1 0.049046679766118733701
0.096003555953658198385 0.779182155085341277 0.30601808890623055648
-0.42651436860343072688 0.43436319760769237508 0.70652528482593535131
-0.44986205244852928153 1 0.42111059568436093326
0.076153314839546334958 1 -0.8878228055594648005
0.097224068626571957807 0.90798112203163694467 -1
0.065770681947035863901 0.78743231566586968651 -1
-0.15393413642935208085 0.4289275000565445084 -0.68605338312455188543
-0.34100876443073602218 0.21180397163506028968 -0.36150926602606126004
-0.42114289714411812238 0.37635121319937747675 -0.055268818856361895397
-0.45654739430498381125 0.65162035359132197687 0.21155480135414206355
-0.47307071660059962781 0.9466016728407951053 0.44419309799809181261
6 0 1 2 3 4 5
5 6 7 8 9 10
9 11 12 13 14 15 16 17 18 19

View File

@ -0,0 +1,23 @@
OFF
17 4 0
0.84488390377294519951 -0.68284952340081683797 1
0.86641741714329612023 -0.77082927236669984694 0.60352060686775832465
0.9121288410152565973 -0.72952161179574059879 0.76350747230545468192
0.89566568009956504248 -0.71539523907881052978 0.83326393608111781752
0.85168505448751063991 -0.6821236357081662538 1
0.79682933348847195809 -1.000000000000000222 1
-0.34791801239959296854 -0.28764156042861477314 1
-0.299327523245019822 0.60093975914093267221 0.11142468738453970012
1 -0.25852980189441154835 0.16066698604451506993
1 -1 0.87773152880361726691
-0.17838891690765840137 0.83420160017091160576 -0.0018095684143322131841
-0.89030839343594470048 -0.53933548311667733888 0.025633427611447195255
-0.48343258035503122727 -0.15341484353582110489 0.80649160552105070288
-0.12894874426582667026 0.59311027229066026756 0.66787334512323059954
-0.029780379789940497615 0.38045922161658057847 -1
-0.09773715311854092036 0.77676442434007964 -1
-0.070770042277002398468 0.67434228105399462994 -0.93293408971343372293
5 0 1 2 3 4
5 5 6 7 8 9
4 10 11 12 13
3 14 15 16

View File

@ -0,0 +1,31 @@
OFF
25 4 0
0.42368054211531858133 0.18253980947404854773 0.43361217636176885293
0.022479024642939653134 0.54906919604958193126 0.18056913227494222896
0.2811647526241494166 0.52705548769951282573 0.022668645874355360104
0.56065427807169632146 0.47592798567162025725 -0.10696848363655320213
0.97118185417342761667 0.3697089496003267417 -0.25076552479989255851
0.53714992649207637943 0.15247177832828684441 0.39492925062101502665
0.47254430936410363184 -0.4706882612609787353 -0.2428207513377572957
0.072755785530830729968 -0.01774935447864991328 -0.65160096675580014836
-0.13624087681667856886 -0.19828919510256182157 -1
0.050171309138895309188 -1 -1
0.42195670307475763305 -1 -0.48389499638253974378
0.49191328462142458466 -0.78271514577943301916 -0.31664816804310136344
0.49305113818899937161 -0.56550106370623254293 -0.24495695687290242049
-1 0.038516275072130623514 0.26001089527408571822
-0.0052646635725682039419 0.32175247304120269121 -1
0.79946300115531654384 1 -1
-0.24605339821785221499 1 1
-0.9554579135068776985 0.40209355388461098801 1
-1 0.32023017788244112491 0.89940428108662362483
0.71260765954572424796 -1 0.060430338155497906327
0.93155151560319460202 -0.69967629334922809559 0.098656503647987087158
1 -0.4563157020167216138 0.27001739515231759636
1 0.14422055985065576622 0.91048995874330840294
0.94048661719673254389 0.15625792052012871247 1
-0.016691632221610047671 -1 1
6 0 1 2 3 4 5
7 6 7 8 9 10 11 12
6 13 14 15 16 17 18
6 19 20 21 22 23 24

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