diff --git a/Advancing_front_surface_reconstruction/doc/Advancing_front_surface_reconstruction/Advancing_front_surface_reconstruction.txt b/Advancing_front_surface_reconstruction/doc/Advancing_front_surface_reconstruction/Advancing_front_surface_reconstruction.txt index 5d996b0ceed..90502e1bdf6 100644 --- a/Advancing_front_surface_reconstruction/doc/Advancing_front_surface_reconstruction/Advancing_front_surface_reconstruction.txt +++ b/Advancing_front_surface_reconstruction/doc/Advancing_front_surface_reconstruction/Advancing_front_surface_reconstruction.txt @@ -47,6 +47,9 @@ not provide any guarantee on the topology of the surface. We describe next the algorithm and provide examples. +\note A \ref tuto_reconstruction "detailed tutorial on surface reconstruction" +is provided with a guide to choose the most appropriate method along +with pre- and post-processing. \section AFSR_Definitions Definitions and the Algorithm diff --git a/BGL/doc/BGL/BGL.txt b/BGL/doc/BGL/BGL.txt index 23c63b9f37d..cfa68697da9 100644 --- a/BGL/doc/BGL/BGL.txt +++ b/BGL/doc/BGL/BGL.txt @@ -653,6 +653,74 @@ can be done directly using `Face_filtered_graph`. Using \ref BGLNamedParameters some of the many options of METIS can be customized, as shown in \ref BGL_polyhedron_3/polyhedron_partition.cpp "this example". + +\section BGLGraphcut Graph Cut + +An optimal partition from a set of labels can be computed through a +graph cut approach called alpha expansion +\cgalCite{Boykov2001FastApproximate}. \cgal provides +`CGAL::alpha_expansion_graphcut()` which, for a graph \f$(V,E)\f$, +computes the partition `f` that minimizes the following cost function: + +\f[ + \mathrm{C}(f) = \sum_{\{v0,v1\} \in E} C_E(v0,v1) + \sum_{v \in V} C_V(f_v) + \f] + +where \f$C_E(v0,v1)\f$ is the edge cost of assigning a different label +to \f$v0\f$ and \f$v1\f$, and \f$C_V(f_v)\f$ is the vertex cost of +assigning the label \f$f\f$ to the vertex \f$v\f$. + +Three different implementations are provided and can be selected by +using one of the following tags: + +- `CGAL::Alpha_expansion_boost_adjacency_list_tag` (default) +- `CGAL::Alpha_expansion_boost_compressed_sparse_raw_tag` +- `CGAL::Alpha_expansion_MaxFlow_tag`, released under GPL + license and provided by the \ref PkgSurfaceMeshSegmentationRef + package + +All these implementations produce the exact same result but behave +differently in terms of timing and memory (see +\cgalFigureRef{alpha_exp}). The _MaxFlow_ implementation is the +fastest, but it grows rapidly in memory when increasing the complexity +of the input graph and labeling; the _compressed sparse raw_ (CSR) is very +efficient from a memory point of view but becomes very slow as the +complexity of the input graph and labeling increases; the _adjacency +list_ version provides a good compromise and is therefore the default +implementation. + +\cgalFigureBegin{alpha_exp, alpha_expansion.png} +Comparison of time and memory consumed by the different alpha +expansion implementations. +\cgalFigureEnd + +The following example shows how to apply the alpha expansion algorithm +to a `boost::adjacency_list` describing a 2D array with 3 labels "X", +" " and "O": + +\cgalExample{BGL_graphcut/alpha_expansion_example.cpp} + +The output of this program shows how the initial 2D array is +regularized spatially: + +``` +Input: +XOX +XX X O +OX OO +X OOX + OXOO + +Alpha expansion... + +Output: +XXX +XX O +XX OO +X OOO + OOOO +``` + + */ } /* namespace CGAL */ - diff --git a/BGL/doc/BGL/Doxyfile.in b/BGL/doc/BGL/Doxyfile.in index c676a95fc13..8984e5b8082 100644 --- a/BGL/doc/BGL/Doxyfile.in +++ b/BGL/doc/BGL/Doxyfile.in @@ -15,7 +15,8 @@ INPUT += ${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/boost/graph/Euler_operations.h \ ${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/boost/graph/io.h \ ${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/boost/graph/partition.h \ ${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/boost/graph/METIS/partition_graph.h \ - ${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/boost/graph/METIS/partition_dual_graph.h + ${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/boost/graph/METIS/partition_dual_graph.h \ + ${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/boost/graph/alpha_expansion_graphcut.h EXAMPLE_PATH = ${CGAL_Surface_mesh_skeletonization_EXAMPLE_DIR} \ diff --git a/BGL/doc/BGL/NamedParameters.txt b/BGL/doc/BGL/NamedParameters.txt index 1b388ebe66d..49ae2710d46 100644 --- a/BGL/doc/BGL/NamedParameters.txt +++ b/BGL/doc/BGL/NamedParameters.txt @@ -193,6 +193,11 @@ operation.\n Default: None. \cgalNPEnd +\cgalNPBegin{implementation_tag} \anchor BGL_implementation_tag +tag used to select the implementation to be used among an algorithm-specific list.\n +Type:a tag class\n +Default: algorithm-specific. +\cgalNPEnd \cgalNPTableEnd diff --git a/BGL/doc/BGL/PackageDescription.txt b/BGL/doc/BGL/PackageDescription.txt index 6651c00b6e2..848b5ea95a5 100644 --- a/BGL/doc/BGL/PackageDescription.txt +++ b/BGL/doc/BGL/PackageDescription.txt @@ -533,7 +533,8 @@ both in term of time and memory. \addtogroup PkgBGLPartition Methods to split a mesh into subdomains, using the library -METIS. +METIS or a graphcut +implementation. */ /*! @@ -715,11 +716,12 @@ user might encounter. \cgalCRPSection{Partitioning Methods} - `CGAL::METIS::partition_graph()` - `CGAL::METIS::partition_dual_graph()` +- `CGAL::alpha_expansion_graphcut()` \cgalCRPSection{I/O Functions} -- \link PkgBGLIOFct CGAL::read_off() \endlink -- \link PkgBGLIOFct CGAL::write_off() \endlink -- \link PkgBGLIOFct CGAL::write_wrl() \endlink +- \link PkgBGLIOFct `CGAL::read_off()` \endlink +- \link PkgBGLIOFct `CGAL::write_off()` \endlink +- \link PkgBGLIOFct `CGAL::write_wrl()` \endlink - `CGAL::write_vtp()` */ diff --git a/BGL/doc/BGL/examples.txt b/BGL/doc/BGL/examples.txt index f9768039d04..2f871f5d369 100644 --- a/BGL/doc/BGL/examples.txt +++ b/BGL/doc/BGL/examples.txt @@ -3,6 +3,7 @@ \example BGL_arrangement_2/arr_rational_nt.h \example BGL_arrangement_2/arrangement_dual.cpp \example BGL_arrangement_2/primal.cpp +\example BGL_graphcut/alpha_expansion_example.cpp \example BGL_polyhedron_3/copy_polyhedron.cpp \example BGL_polyhedron_3/cube.off \example BGL_polyhedron_3/distance.cpp diff --git a/BGL/doc/BGL/fig/alpha_expansion.png b/BGL/doc/BGL/fig/alpha_expansion.png new file mode 100644 index 00000000000..afa8e5f457f Binary files /dev/null and b/BGL/doc/BGL/fig/alpha_expansion.png differ diff --git a/BGL/examples/BGL_graphcut/CMakeLists.txt b/BGL/examples/BGL_graphcut/CMakeLists.txt new file mode 100644 index 00000000000..30469f9d3b2 --- /dev/null +++ b/BGL/examples/BGL_graphcut/CMakeLists.txt @@ -0,0 +1,42 @@ +# 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.15) + +project( BGL_graphcut_Examples ) + + +# CGAL and its components +find_package( CGAL QUIET COMPONENTS ) + +if ( NOT CGAL_FOUND ) + + message(STATUS "This project requires the CGAL library, and will not be compiled.") + return() + +endif() + + +# Boost and its components +find_package( Boost REQUIRED ) + +if ( NOT Boost_FOUND ) + + message(STATUS "This project requires the Boost library, and will not be compiled.") + + return() + +endif() + +# include for local directory + +# include for local package + + +# Creating entries for all C++ files with "main" routine +# ########################################################## + + +create_single_source_cgal_program( "alpha_expansion_example.cpp" ) + + diff --git a/BGL/examples/BGL_graphcut/alpha_expansion_example.cpp b/BGL/examples/BGL_graphcut/alpha_expansion_example.cpp new file mode 100644 index 00000000000..a086be418e3 --- /dev/null +++ b/BGL/examples/BGL_graphcut/alpha_expansion_example.cpp @@ -0,0 +1,96 @@ +#include +#include + +struct Vertex_property +{ + int label; + std::vector cost; +}; + +struct Edge_property +{ + double weight; +}; + +using Graph = boost::adjacency_list ; +using GT = boost::graph_traits; +using vertex_descriptor = GT::vertex_descriptor; +using edge_descriptor = GT::edge_descriptor; + +int main() +{ + std::array labels = { 'X', ' ', 'O' }; + + std::array, 5> input + = { { { 0, 2, 0, 1, 1, 1 }, + { 0, 0, 1, 0, 1, 2 }, + { 2, 0, 1, 1, 2, 2 }, + { 0, 1, 1, 2, 2, 0 }, + { 1, 1, 2, 0, 2, 2 } } }; + + std::array, 5> vertices; + + // Init vertices from values + Graph g; + for (std::size_t i = 0; i < input.size(); ++ i) + for (std::size_t j = 0; j < input[i].size(); ++ j) + { + vertices[i][j] = boost::add_vertex(g); + g[vertices[i][j]].label = input[i][j]; + + // Cost of assigning this vertex to any label is positive except + // for current label which is 0 (favor init solution) + g[vertices[i][j]].cost.resize(3, 1); + g[vertices[i][j]].cost[std::size_t(input[i][j])] = 0; + } + + // Display input values + std::cerr << "Input:" << std::endl; + for (std::size_t i = 0; i < vertices.size(); ++ i) + { + for (std::size_t j = 0; j < vertices[i].size(); ++ j) + std::cerr << labels[std::size_t(g[vertices[i][j]].label)]; + std::cerr << std::endl; + } + + // Init adjacency + double weight = 0.5; + for (std::size_t i = 0; i < vertices.size(); ++ i) + for (std::size_t j = 0; j < vertices[i].size(); ++ j) + { + // Neighbor vertices are connected + if (i < vertices.size() - 1) + { + edge_descriptor ed = boost::add_edge (vertices[i][j], vertices[i+1][j], g).first; + g[ed].weight = weight; + } + if (j < vertices[i].size() - 1) + { + edge_descriptor ed = boost::add_edge (vertices[i][j], vertices[i][j+1], g).first; + g[ed].weight = weight; + } + } + + std::cerr << std::endl << "Alpha expansion..." << std::endl << std::endl; + CGAL::alpha_expansion_graphcut (g, + get (&Edge_property::weight, g), + get (&Vertex_property::cost, g), + get (&Vertex_property::label, g), + CGAL::parameters::vertex_index_map (get (boost::vertex_index, g))); + + + // Display output graph + std::cerr << "Output:" << std::endl; + for (std::size_t i = 0; i < vertices.size(); ++ i) + { + for (std::size_t j = 0; j < vertices[i].size(); ++ j) + std::cerr << labels[std::size_t(g[vertices[i][j]].label)]; + std::cerr << std::endl; + } + + return 0; +} diff --git a/BGL/include/CGAL/boost/graph/alpha_expansion_graphcut.h b/BGL/include/CGAL/boost/graph/alpha_expansion_graphcut.h new file mode 100644 index 00000000000..088c3472201 --- /dev/null +++ b/BGL/include/CGAL/boost/graph/alpha_expansion_graphcut.h @@ -0,0 +1,748 @@ +#ifndef CGAL_BOOST_GRAPH_ALPHA_EXPANSION_GRAPHCUT_H +// Copyright (c) 2014 GeometryFactory (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) : Ilker O. Yaz, Simon Giraudot + +#define CGAL_BOOST_GRAPH_ALPHA_EXPANSION_GRAPHCUT_H + +#include +#include +#include +#ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT +#include +#endif +#include + +#include +#include + +#include + +#include +#include + +#if BOOST_VERSION >= 104400 // at this version kolmogorov_max_flow become depricated. +# include +#else +# include +#endif + +#include + + + + +namespace CGAL +{ + +/// \cond SKIP_IN_MANUAL +namespace internal +{ + +struct Alpha_expansion_old_API_wrapper_graph +{ + typedef std::size_t vertex_descriptor; + typedef std::size_t edge_descriptor; + typedef boost::directed_tag directed_category; + typedef boost::disallow_parallel_edge_tag edge_parallel_category; + typedef boost::edge_list_graph_tag traversal_category; + + typedef boost::counting_iterator counting_iterator; + typedef CGAL::Iterator_range counting_range; + + typedef CGAL::Identity_property_map Vertex_index_map; + typedef CGAL::Pointer_property_map::type Vertex_label_map; + + struct Vertex_label_cost_map + { + typedef std::size_t key_type; + typedef std::vector value_type; + typedef value_type reference; + typedef boost::readable_property_map_tag category; + + const std::vector >* cost_matrix; + + Vertex_label_cost_map (const std::vector >* cost_matrix) + : cost_matrix (cost_matrix) + { } + + friend reference get (const Vertex_label_cost_map& pmap, key_type idx) + { + std::vector out; + out.reserve (pmap.cost_matrix->size()); + for (std::size_t i = 0; i < pmap.cost_matrix->size(); ++ i) + out.push_back ((*pmap.cost_matrix)[i][idx]); + return out; + } + + }; + + typedef CGAL::Pointer_property_map::const_type Edge_cost_map; + + const std::vector >& edges; + const std::vector& edge_costs; + const std::vector >& cost_matrix; + std::vector& labels; + + Alpha_expansion_old_API_wrapper_graph (const std::vector >& edges, + const std::vector& edge_costs, + const std::vector >& cost_matrix, + std::vector& labels) + : edges (edges), edge_costs (edge_costs), cost_matrix (cost_matrix), labels (labels) + { } + + friend counting_range vertices (const Alpha_expansion_old_API_wrapper_graph& graph) + { + return CGAL::make_range (boost::counting_iterator(0), + boost::counting_iterator(graph.labels.size())); + } + + friend std::size_t num_vertices (const Alpha_expansion_old_API_wrapper_graph& graph) { return graph.labels.size(); } + + friend counting_range edges (const Alpha_expansion_old_API_wrapper_graph& graph) + { + return CGAL::make_range (boost::counting_iterator(0), + boost::counting_iterator(graph.edges.size())); + } + + friend vertex_descriptor source (edge_descriptor ed, const Alpha_expansion_old_API_wrapper_graph& graph) + { return graph.edges[ed].first; } + friend vertex_descriptor target (edge_descriptor ed, const Alpha_expansion_old_API_wrapper_graph& graph) + { return graph.edges[ed].second; } + + Vertex_index_map vertex_index_map() const { return Vertex_index_map(); } + Vertex_label_map vertex_label_map() { return CGAL::make_property_map(labels); } + Vertex_label_cost_map vertex_label_cost_map() const + { return Vertex_label_cost_map(&cost_matrix); } + Edge_cost_map edge_cost_map() const { return CGAL::make_property_map(edge_costs); } +}; + +//////////////////////////////////////////////////////////////////////////////////////// +// Comments about performance: +// +// 1) With BGL: +// * Using adjacency_list: +// ** Without pre-allocating vertex-list +// | OutEdgeList | VertexList | Performance | +// | listS | listS | 25.2 | +// | vecS | listS | 22.7 | +// | listS | vecS | 30.7 | +// | vecS | vecS | 26.1 | +// +// ** With pre-allocating vertex-list with max-node size +// (Note: exact number of vertices are not certain at the beginning) +// | OutEdgeList | VertexList | Performance | +// | listS | vecS | 25.2 | +// | vecS | vecS | 23.4 | +// +// * Didn't try adjacency_matrix since our graph is sparse +// ( Also one can check BGL book, performance section ) +// +// Decision: +// * Alpha_expansion_graph_cut_boost: use adjacency_list without +// pre-allocating vertex-list. +// +// 2) With Boykov-Kolmogorov MAXFLOW software: +// (http://pub.ist.ac.at/~vnk/software/maxflow-v2.21.src.tar.gz) +// | Performance | +// | 3.1 | +// * Alpha_expansion_graph_cut_boykov_kolmogorov provides an implementation. +// MAXFLOW does not provide any option for pre-allocation (It is possible with v_3.02 though). +// +// Typical Benchmark result provided by Ilker +// | construction of vertices | construction of edges | graph cut | Total +// ----------------------------------------------------------------------------------------------------------- +// boost with an adjacency list | 1.53 | 1.51 | 3.00 | 6.04 +// boost with CSR | 0.11 (gather in a vector) | 0.15 (gather in a vector) | 2.67 | 2.93 +// MaxFlow | 0.042 | 0.076 | 1.043 | 1.161 +// +// The main issue for now with CSR is the construction of the opposite edge map that is too costly, +// since it is done by exploring all edges to find opposite +//////////////////////////////////////////////////////////////////////////////////////// + +} // namespace internal + +/** + * @brief Implements alpha-expansion graph cut algorithm. + * + * For representing graph, it uses adjacency_list with OutEdgeList = vecS, VertexList = listS. + * Also no pre-allocation is made for vertex-list. + */ +class Alpha_expansion_boost_adjacency_list_impl +{ +private: + typedef boost::adjacency_list_traits + Adjacency_list_traits; + + typedef boost::adjacency_list + > > >, + // 3 edge properties + boost::property > + > > Graph; + + typedef boost::graph_traits Traits; + typedef boost::color_traits ColorTraits; + +public: + + typedef Traits::vertex_descriptor Vertex_descriptor; + typedef Traits::vertex_iterator Vertex_iterator; + typedef Traits::edge_descriptor Edge_descriptor; + typedef Traits::edge_iterator Edge_iterator; + +private: + + Graph graph; + Vertex_descriptor cluster_source; + Vertex_descriptor cluster_sink; + +public: + + void clear_graph() + { + graph.clear(); + cluster_source = boost::add_vertex(graph); + cluster_sink = boost::add_vertex(graph); + } + + Vertex_descriptor add_vertex() + { + return boost::add_vertex(graph); + } + + void add_tweight (Vertex_descriptor& v, double w1, double w2) + { + add_edge (cluster_source, v, w1, 0); + add_edge (v, cluster_sink, w2, 0); + } + + void init_vertices() + { + // initialize vertex indices, it is necessary since we are using VertexList = listS + Vertex_iterator v_begin, v_end; + Traits::vertices_size_type index = 0; + for(boost::tie(v_begin, v_end) = vertices(graph); v_begin != v_end; ++v_begin) { + boost::put(boost::vertex_index, graph, *v_begin, index++); + } + } + + double max_flow() + { +#if BOOST_VERSION >= 104400 + return boost::boykov_kolmogorov_max_flow(graph, cluster_source, + cluster_sink); +#else + return boost::kolmogorov_max_flow(graph, cluster_source, cluster_sink); +#endif + } + + template + void update(VertexLabelMap vertex_label_map, + const std::vector& inserted_vertices, + InputVertexDescriptor vd, + std::size_t vertex_i, + std::size_t alpha) + { + boost::default_color_type color = boost::get(boost::vertex_color, graph, + inserted_vertices[vertex_i]); + if(std::size_t(get (vertex_label_map, vd)) != alpha + && color == ColorTraits::white()) //new comers (expansion occurs) + put (vertex_label_map, vd, + static_cast::value_type>(alpha)); + } + + void add_edge (Vertex_descriptor& v1, Vertex_descriptor& v2, double w1, double w2) + { + Edge_descriptor v1_v2, v2_v1; + bool v1_v2_added, v2_v1_added; + + boost::tie(v1_v2, v1_v2_added) = boost::add_edge(v1, v2, graph); + boost::tie(v2_v1, v2_v1_added) = boost::add_edge(v2, v1, graph); + + CGAL_assertion(v1_v2_added && v2_v1_added); + //put edge capacities + boost::put(boost::edge_reverse, graph, v1_v2, v2_v1); + boost::put(boost::edge_reverse, graph, v2_v1, v1_v2); + + //map reverse edges + boost::put(boost::edge_capacity, graph, v1_v2, w1); + boost::put(boost::edge_capacity, graph, v2_v1, w2); + + } +}; + +// another implementation using compressed_sparse_row_graph +// for now there is a performance problem while setting reverse edges +// if that can be solved, it is faster than Alpha_expansion_graph_cut_boost +class Alpha_expansion_boost_compressed_sparse_row_impl +{ +private: + // CSR only accepts bundled props + struct VertexP { + boost::default_color_type vertex_color; + double vertex_distance_t; + // ? do not now there is another way to take it, I think since edge_descriptor does not rely on properties + // this should be fine... + boost::compressed_sparse_row_graph::edge_descriptor + vertex_predecessor; + }; + + struct EdgeP { + double edge_capacity; + double edge_residual_capacity; + boost::compressed_sparse_row_graph::edge_descriptor + edge_reverse; + }; + + typedef boost::compressed_sparse_row_graph Graph; + + typedef boost::graph_traits Traits; + typedef boost::color_traits ColorTraits; + +public: + + typedef Traits::vertex_descriptor Vertex_descriptor; + typedef Traits::vertex_iterator Vertex_iterator; + typedef Traits::edge_descriptor Edge_descriptor; + typedef Traits::edge_iterator Edge_iterator; + +private: + + Graph graph; + std::size_t nb_vertices; + std::vector > edge_map; + std::vector edge_map_weights; + +public: + void clear_graph() + { + nb_vertices = 2; + edge_map.clear(); + edge_map_weights.clear(); + // edge_map.reserve(labels.size() * + // 8); // there is no way to know exact edge count, it is a heuristic value + // edge_map_weights.reserve(labels.size() * 8); + } + + Vertex_descriptor add_vertex() + { + return (nb_vertices ++); + } + + void add_tweight (Vertex_descriptor& v, double w1, double w2) + { + add_edge (0, v, w1, 0); + add_edge (v, 1, w2, 0); + } + + void init_vertices() + { +#if BOOST_VERSION >= 104000 + graph = Graph(boost::edges_are_unsorted, edge_map.begin(), edge_map.end(), + edge_map_weights.begin(), nb_vertices); +#else + graph= Graph(edge_map.begin(), edge_map.end(), + edge_map_weights.begin(), nb_vertices); +#endif + + // PERFORMANCE PROBLEM + // need to set reverse edge map, I guess there is no way to do that before creating the graph + // since we do not have edge_descs + // however from our edge_map, we know that each (2i, 2i + 1) is reverse pairs, how to facilitate that ? + // will look it back + Graph::edge_iterator ei, ee; + for(boost::tie(ei, ee) = boost::edges(graph); ei != ee; ++ei) { + Graph::vertex_descriptor v1 = boost::source(*ei, graph); + Graph::vertex_descriptor v2 = boost::target(*ei, graph); + std::pair opp_edge = boost::edge(v2, v1, graph); + + CGAL_assertion(opp_edge.second); + graph[opp_edge.first].edge_reverse = + *ei; // and edge_reverse of *ei will be (or already have been) set by the opp_edge + } + } + + double max_flow() + { +#if BOOST_VERSION >= 104400 + // since properties are bundled, defaults does not work need to specify them + return boost::boykov_kolmogorov_max_flow + (graph, + boost::get(&EdgeP::edge_capacity, graph), + boost::get(&EdgeP::edge_residual_capacity, graph), + boost::get(&EdgeP::edge_reverse, graph), + boost::get(&VertexP::vertex_predecessor, graph), + boost::get(&VertexP::vertex_color, graph), + boost::get(&VertexP::vertex_distance_t, graph), + boost::get(boost::vertex_index, + graph), // this is not bundled, get it from graph (CRS provides one) + 0, 1); +#else + return boost::kolmogorov_max_flow + (graph, + boost::get(&EdgeP::edge_capacity, graph), + boost::get(&EdgeP::edge_residual_capacity, graph), + boost::get(&EdgeP::edge_reverse, graph), + boost::get(&VertexP::vertex_predecessor, graph), + boost::get(&VertexP::vertex_color, graph), + boost::get(&VertexP::vertex_distance_t, graph), + boost::get(boost::vertex_index, + graph), // this is not bundled, get it from graph + 0, 1); +#endif + } + + template + void update(VertexLabelMap vertex_label_map, + const std::vector&, + InputVertexDescriptor vd, + std::size_t vertex_i, + std::size_t alpha) + { + boost::default_color_type color = graph[vertex_i + 2].vertex_color; + if(get(vertex_label_map, vd)!= alpha + && color == ColorTraits::white()) //new comers (expansion occurs) + put(vertex_label_map, vd, alpha); + } + + void add_edge(Vertex_descriptor v1, Vertex_descriptor v2, double w1, double w2) + { + edge_map.push_back(std::make_pair(v1, v2)); + EdgeP p1; + p1.edge_capacity = w1; + edge_map_weights.push_back(p1); + + edge_map.push_back(std::make_pair(v2, v1)); + EdgeP p2; + p2.edge_capacity = w2; + edge_map_weights.push_back(p2); + } + + +}; + +// tags +struct Alpha_expansion_boost_adjacency_list_tag { }; +struct Alpha_expansion_boost_compressed_sparse_row_tag { }; +struct Alpha_expansion_MaxFlow_tag { }; + +// forward declaration +class Alpha_expansion_MaxFlow_impl; + +/// \endcond + +// NOTE: latest performances check (2019-07-22) +// +// Using a random graph with 50000 vertices, 100000 edges and 30 labels: +// +// METHOD TIMING MEMORY +// Boost Adjacency list 49s 122MiB +// Boost CSR 187s 77MiB +// MaxFlow 12s 717MiB + +/** + \ingroup PkgBGLPartition + + regularizes a partition of a graph into `n` labels using the alpha + expansion algorithm \cgalCite{Boykov2001FastApproximate}. + + For a graph \f$(V,E)\f$, this function computes a partition `f` + that minimizes the following cost function: + + \f[ + \mathrm{C}(f) = \sum_{\{v0,v1\} \in E} C_E(v0,v1) + \sum_{v \in V} C_V(f_v) + \f] + + where \f$C_E(v0,v1)\f$ is the edge cost of assigning a different + label to \f$v0\f$ and \f$v1\f$, and \f$C_V(f_v)\f$ is the vertex + cost of assigning the label \f$f\f$ to the vertex \f$v\f$. + + \tparam InputGraph a model of `VertexAndEdgeListGraph` + + \tparam EdgeCostMap a model of `ReadablePropertyMap` with + `boost::graph_traits::%edge_descriptor` as key and `double` + as value + + \tparam VertexLabelCostMap a model of `ReadablePropertyMap` + with `boost::graph_traits::%vertex_descriptor` as key and + `std::vector` as value + + \tparam VertexLabelMap a model of `ReadWritePropertyMap` with + `boost::graph_traits::%vertex_descriptor` as key and + `std::size_t` as value + + \tparam NamedParameters a sequence of named parameters + + \param input_graph the input graph. + + \param edge_cost_map a property map providing the weight of each + edge. + + \param vertex_label_map a property map providing the label of each + vertex. This map will be updated by the algorithm with the + regularized version of the partition. + + \param vertex_label_cost_map a property map providing, for each + vertex, an `std::vector` containing the cost of this vertex to + belong to each label. Each `std::vector` should have the same size + `n` (which is the number of labels), each label being indexed from + `0` to `n-1`. For example, `get(vertex_label_cost_map, + vd)[label_idx]` returns the cost of vertex `vd` to belong to the + label `label_idx`. + + \param np optional sequence of named parameters among the ones listed below + + \cgalNamedParamsBegin + \cgalParamBegin{vertex_index_map} + a property map providing the index of each vertex + \cgalParamEnd + \cgalParamBegin{implementation_tag} + tag used to select + which implementation of the alpha expansion should be + used. Available implementation tags are: + - `CGAL::Alpha_expansion_boost_adjacency_list` (default) + - `CGAL::Alpha_expansion_boost_compressed_sparse_row_tag` + - `CGAL::Alpha_expansion_MaxFlow_tag` + \cgalParamEnd + \cgalNamedParamsEnd + + \note The `MaxFlow` implementation is provided by the \ref PkgSurfaceMeshSegmentationRef + under GPL license. The header + `` + must be included if users want to use this implementation. + +*/ +template +double alpha_expansion_graphcut (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 GT; + typedef typename GT::edge_descriptor input_edge_descriptor; + typedef typename GT::vertex_descriptor input_vertex_descriptor; + + typedef typename GetInitializedVertexIndexMap::type VertexIndexMap; + VertexIndexMap vertex_index_map = CGAL::get_initialized_vertex_index_map(input_graph, np); + + typedef typename GetImplementationTag::type Impl_tag; + + // select implementation + typedef typename std::conditional + ::value, + Alpha_expansion_boost_adjacency_list_impl, + typename std::conditional + ::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 alpha_expansion; + + // TODO: check this hardcoded parameter + const double tolerance = 1e-10; + + double min_cut = (std::numeric_limits::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 inserted_vertices; + inserted_vertices.resize(num_vertices (input_graph)); + + std::size_t number_of_labels = get(vertex_label_cost_map, *(vertices(input_graph).first)).size(); + + bool success; + do { + success = false; + + for (std::size_t alpha = 0; alpha < number_of_labels; ++ alpha) + { + alpha_expansion.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 = alpha_expansion.add_vertex(); + inserted_vertices[vertex_i] = new_vertex; + double source_weight = get(vertex_label_cost_map, vd)[alpha]; + // since it is expansion move, current alpha labeled vertices will be assigned to alpha again, + // making sink_weight 'infinity' guarantee this. + double sink_weight = (std::size_t(get(vertex_label_map, vd)) == alpha ? + (std::numeric_limits::max)() + : get(vertex_label_cost_map, vd)[get(vertex_label_map, vd)]); + + alpha_expansion.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]; + + std::size_t label_1 = get (vertex_label_map, vd1); + std::size_t label_2 = get (vertex_label_map, vd2); + if(label_1 == label_2) { + if(label_1 != alpha) { + alpha_expansion.add_edge(v1, v2, weight, weight); + } + } else { + Vertex_descriptor inbetween = alpha_expansion.add_vertex(); + + double w1 = (label_1 == alpha) ? 0 : weight; + double w2 = (label_2 == alpha) ? 0 : weight; + alpha_expansion.add_edge(inbetween, v1, w1, w1); + alpha_expansion.add_edge(inbetween, v2, w2, w2); + alpha_expansion.add_tweight(inbetween, 0., weight); + } + } +#ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT + edge_creation_time += timer.time(); +#endif + + alpha_expansion.init_vertices(); + +#ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT + timer.reset(); +#endif + + double flow = alpha_expansion.max_flow(); + +#ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT + cut_time += timer.time(); +#endif + + if(min_cut - flow <= flow * tolerance) { + continue; + } + min_cut = flow; + success = true; + //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); + } + } + } 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; +#endif + + return min_cut; +} + + +/// \cond SKIP_IN_MANUAL +// variant with default NP +template +double alpha_expansion_graphcut (const InputGraph& input_graph, + EdgeCostMap edge_cost_map, + VertexLabelCostMap vertex_label_cost_map, + VertexLabelMap vertex_label_map) +{ + return alpha_expansion_graphcut (input_graph, edge_cost_map, + vertex_label_cost_map, vertex_label_map, + CGAL::parameters::all_default()); +} + +// Old API +inline double alpha_expansion_graphcut (const std::vector >& edges, + const std::vector& edge_costs, + const std::vector >& cost_matrix, + std::vector& labels) +{ + internal::Alpha_expansion_old_API_wrapper_graph graph (edges, edge_costs, cost_matrix, labels); + return alpha_expansion_graphcut(graph, + graph.edge_cost_map(), + graph.vertex_label_cost_map(), + graph.vertex_label_map(), + CGAL::parameters::vertex_index_map (graph.vertex_index_map())); +} + +template +double alpha_expansion_graphcut (const std::vector >& edges, + const std::vector& edge_costs, + const std::vector >& cost_matrix, + std::vector& labels, + const AlphaExpansionImplementationTag&) +{ + internal::Alpha_expansion_old_API_wrapper_graph graph (edges, edge_costs, cost_matrix, labels); + + return alpha_expansion_graphcut(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 + +namespace boost +{ + +template <> +struct property_map +{ + typedef CGAL::internal::Alpha_expansion_old_API_wrapper_graph::Vertex_index_map type; + typedef CGAL::internal::Alpha_expansion_old_API_wrapper_graph::Vertex_index_map const_type; +}; +} + +#endif //CGAL_BOOST_GRAPH_ALPHA_EXPANSION_GRAPHCUT_H diff --git a/BGL/include/CGAL/boost/graph/internal/initialized_index_maps_helpers.h b/BGL/include/CGAL/boost/graph/internal/initialized_index_maps_helpers.h index 928c2c7bc76..c735823a0fa 100644 --- a/BGL/include/CGAL/boost/graph/internal/initialized_index_maps_helpers.h +++ b/BGL/include/CGAL/boost/graph/internal/initialized_index_maps_helpers.h @@ -35,9 +35,11 @@ bool is_index_map_valid(IndexMap idmap, Id_type max_id = static_cast(num_simplices); std::vector indices(max_id); - for(const auto& d : range) + + // According to concepts, the descriptor ranges such as 'vertices(g)' return a 'std::pair' + for(auto it = range.first; it != range.second; ++it) { - const Id_type id = get(idmap, d); + const Id_type id = get(idmap, *it); if(id >= 0 && id < max_id && !indices[id]) { indices[id] = true; diff --git a/BGL/include/CGAL/boost/graph/named_params_helper.h b/BGL/include/CGAL/boost/graph/named_params_helper.h index 6891abf0e27..6267dde6e41 100644 --- a/BGL/include/CGAL/boost/graph/named_params_helper.h +++ b/BGL/include/CGAL/boost/graph/named_params_helper.h @@ -40,6 +40,7 @@ namespace CGAL { class Default_diagonalize_traits; class Eigen_svd; class Lapack_svd; + struct Alpha_expansion_boost_adjacency_list_tag; // @@ -550,6 +551,16 @@ CGAL_DEF_GET_INITIALIZED_INDEX_MAP(face, typename boost::graph_traits::fa > ::type type; }; + template + class GetImplementationTag + { + public: + typedef typename internal_np::Lookup_named_param_def < + internal_np::implementation_tag_t, + NamedParameters, + Alpha_expansion_boost_adjacency_list_tag + >::type type; + }; } //namespace CGAL diff --git a/BGL/include/CGAL/boost/graph/parameters_interface.h b/BGL/include/CGAL/boost/graph/parameters_interface.h index 850c3ad3764..23c2cfc4a5c 100644 --- a/BGL/include/CGAL/boost/graph/parameters_interface.h +++ b/BGL/include/CGAL/boost/graph/parameters_interface.h @@ -36,6 +36,7 @@ CGAL_add_named_parameter(face_to_face_output_iterator_t, face_to_face_output_ite CGAL_add_named_parameter(vertex_to_vertex_map_t, vertex_to_vertex_map, vertex_to_vertex_map) CGAL_add_named_parameter(halfedge_to_halfedge_map_t, halfedge_to_halfedge_map, halfedge_to_halfedge_map) CGAL_add_named_parameter(face_to_face_map_t, face_to_face_map, face_to_face_map) +CGAL_add_named_parameter(implementation_tag_t, implementation_tag, implementation_tag) // List of named parameters that we use in the package 'Mesh_3' CGAL_add_named_parameter(vertex_feature_degree_t, vertex_feature_degree, vertex_feature_degree_map) @@ -135,6 +136,7 @@ CGAL_add_named_parameter(plane_index_t, plane_index_map, plane_index_map) CGAL_add_named_parameter(select_percentage_t, select_percentage, select_percentage) CGAL_add_named_parameter(require_uniform_sampling_t, require_uniform_sampling, require_uniform_sampling) CGAL_add_named_parameter(point_is_constrained_t, point_is_constrained, point_is_constrained_map) +CGAL_add_named_parameter(maximum_number_of_faces_t, maximum_number_of_faces, maximum_number_of_faces) CGAL_add_named_parameter(transformation_t, transformation, transformation) CGAL_add_named_parameter(point_set_filters_t, point_set_filters, point_set_filters) CGAL_add_named_parameter(matcher_t, matcher, matcher) diff --git a/BGL/include/CGAL/boost/graph/selection.h b/BGL/include/CGAL/boost/graph/selection.h index ae44f79c0fa..4d067f313d9 100644 --- a/BGL/include/CGAL/boost/graph/selection.h +++ b/BGL/include/CGAL/boost/graph/selection.h @@ -652,4 +652,3 @@ bool is_selection_a_topological_disk(const FaceRange& face_selection, } //end of namespace CGAL #endif //CGAL_BOOST_GRAPH_SELECTION_H - diff --git a/BGL/include/CGAL/boost/graph/split_graph_into_polylines.h b/BGL/include/CGAL/boost/graph/split_graph_into_polylines.h index f7c20ed7f27..b6f5ea58aa8 100644 --- a/BGL/include/CGAL/boost/graph/split_graph_into_polylines.h +++ b/BGL/include/CGAL/boost/graph/split_graph_into_polylines.h @@ -192,6 +192,7 @@ void duplicate_terminal_vertices(Graph& graph, } // namespace internal +#ifndef DOXYGEN_RUNNING template struct Surface_fixture_1 { Surface_fixture_1() { - assert(read_a_mesh(m, "data/fixture1.off")); + const bool is_reading_successful = read_a_mesh(m, "data/fixture1.off"); + assert(is_reading_successful); assert(CGAL::is_valid_polygon_mesh(m)); typename boost::property_map::const_type pm = get(CGAL::vertex_point, const_cast(m)); @@ -276,7 +277,8 @@ struct Surface_fixture_1 { template struct Surface_fixture_2 { Surface_fixture_2() { - assert(read_a_mesh(m, "data/fixture2.off")); + const bool is_reading_successful = read_a_mesh(m, "data/fixture2.off"); + assert(is_reading_successful); assert(CGAL::is_valid_polygon_mesh(m)); typename boost::property_map::const_type @@ -337,7 +339,8 @@ struct Surface_fixture_2 { template struct Surface_fixture_3 { Surface_fixture_3() { - assert(read_a_mesh(m, "data/fixture3.off")); + const bool is_reading_successful = read_a_mesh(m, "data/fixture3.off"); + assert(is_reading_successful); assert(CGAL::is_valid_polygon_mesh(m)); typename boost::property_map::const_type @@ -383,7 +386,8 @@ struct Surface_fixture_3 { template struct Surface_fixture_4 { Surface_fixture_4() { - assert(read_a_mesh(m, "data/fixture4.off")); + const bool is_reading_successful = read_a_mesh(m, "data/fixture4.off"); + assert(is_reading_successful); assert(CGAL::is_valid_polygon_mesh(m)); typename boost::property_map::const_type @@ -418,7 +422,8 @@ struct Surface_fixture_4 { template struct Surface_fixture_5 { Surface_fixture_5() { - assert(read_a_mesh(m, "data/add_face_to_border.off")); + const bool is_reading_successful = read_a_mesh(m, "data/add_face_to_border.off"); + assert(is_reading_successful); assert(CGAL::is_valid_polygon_mesh(m)); typename boost::property_map::const_type @@ -448,7 +453,8 @@ struct Surface_fixture_5 { template struct Surface_fixture_6 { Surface_fixture_6() { - assert(read_a_mesh(m, "data/quad.off")); + const bool is_reading_successful = read_a_mesh(m, "data/quad.off"); + assert(is_reading_successful); assert(CGAL::is_valid_polygon_mesh(m)); typename boost::graph_traits::halfedge_descriptor h; @@ -467,7 +473,8 @@ struct Surface_fixture_6 { template struct Surface_fixture_7 { Surface_fixture_7() { - assert(read_a_mesh(m, "data/cube.off")); + const bool is_reading_successful = read_a_mesh(m, "data/cube.off"); + assert(is_reading_successful); assert(CGAL::is_valid_polygon_mesh(m)); h = *(halfedges(m).first); @@ -480,7 +487,8 @@ struct Surface_fixture_7 { template struct Surface_fixture_8 { Surface_fixture_8() { - assert(read_a_mesh(m, "data/fixture5.off")); + const bool is_reading_successful = read_a_mesh(m, "data/fixture5.off"); + assert(is_reading_successful); assert(CGAL::is_valid_polygon_mesh(m)); typename boost::property_map::const_type diff --git a/BGL/test/BGL/test_cgal_bgl_named_params.cpp b/BGL/test/BGL/test_cgal_bgl_named_params.cpp index dd88f358857..e7c73e5d825 100644 --- a/BGL/test/BGL/test_cgal_bgl_named_params.cpp +++ b/BGL/test/BGL/test_cgal_bgl_named_params.cpp @@ -96,6 +96,7 @@ void test(const NamedParameters& np) assert(get_parameter(np, CGAL::internal_np::dry_run).v == 59); assert(get_parameter(np, CGAL::internal_np::do_lock_mesh).v == 60); assert(get_parameter(np, CGAL::internal_np::do_simplify_border).v == 61); + assert(get_parameter(np, CGAL::internal_np::maximum_number_of_faces).v == 78910); // Named parameters that we use in the package 'Surface Mesh Simplification' assert(get_parameter(np, CGAL::internal_np::get_cost_policy).v == 34); @@ -198,6 +199,7 @@ void test(const NamedParameters& np) check_same_type<59>(get_parameter(np, CGAL::internal_np::dry_run)); check_same_type<60>(get_parameter(np, CGAL::internal_np::do_lock_mesh)); check_same_type<61>(get_parameter(np, CGAL::internal_np::do_simplify_border)); + check_same_type<78910>(get_parameter(np, CGAL::internal_np::maximum_number_of_faces)); // Named parameters that we use in the package 'Surface Mesh Simplification' check_same_type<34>(get_parameter(np, CGAL::internal_np::get_cost_policy)); @@ -376,6 +378,7 @@ int main() .inspector(A<9032>(9032)) .logger(A<9033>(9033)) .maximum_normal_deviation(A<9034>(9034)) + .maximum_number_of_faces(A<78910>(78910)) ); return EXIT_SUCCESS; } diff --git a/Cartesian_kernel/include/CGAL/Cartesian/function_objects.h b/Cartesian_kernel/include/CGAL/Cartesian/function_objects.h index ed59fdd9351..943cc686804 100644 --- a/Cartesian_kernel/include/CGAL/Cartesian/function_objects.h +++ b/Cartesian_kernel/include/CGAL/Cartesian/function_objects.h @@ -526,6 +526,39 @@ namespace CartesianKernelFunctors { } }; + template + class Compare_signed_distance_to_line_2 + { + typedef typename K::Point_2 Point_2; + typedef typename K::Line_2 Line_2; + typedef typename K::Equal_2 Equal_2; + typedef typename K::Less_signed_distance_to_line_2 Less_signed_distance_to_line_2; + + public: + typedef typename K::Comparison_result result_type; + + result_type + operator()(const Point_2& a, const Point_2& b, + const Point_2& c, const Point_2& d) const + { + CGAL_kernel_precondition_code(Equal_2 equal;) + CGAL_kernel_precondition(! equal(a,b)); + return cmp_signed_dist_to_lineC2( a.x(), a.y(), + b.x(), b.y(), + c.x(), c.y(), + d.x(), d.y()); + } + + result_type + operator()(const Line_2& l, const Point_2& p, const Point_2& q) const + { + Less_signed_distance_to_line_2 less = K().less_signed_distance_to_line_2_object(); + if (less(l, p, q)) return SMALLER; + if (less(l, q, p)) return LARGER; + return EQUAL; + } + }; + template class Compare_squared_radius_3 { @@ -3884,7 +3917,7 @@ namespace CartesianKernelFunctors { } }; - // TODO ... + template class Less_signed_distance_to_line_2 { diff --git a/Classification/doc/Classification/Classification.txt b/Classification/doc/Classification/Classification.txt index d2fc890bafd..56bf40b6a77 100644 --- a/Classification/doc/Classification/Classification.txt +++ b/Classification/doc/Classification/Classification.txt @@ -21,7 +21,7 @@ This component implements the algorithm described in \cgalCite{cgal:lm-clscm-12} - a set of labels (for example: _ground_, _building_, _vegetation_) is defined by the user; - a classifier is defined and trained: from the set of values taken by the features at an input item, it measures the likelihood of this item to belong to one label or another; - classification is computed itemwise using the classifier; -- additional regularization can be used by smoothing either locally or globally through a _Graph Cut_ \cgalCite{Boykov2001FastApproximate} approach. +- additional regularization can be used by smoothing either locally or globally through a _graph cut_ \cgalCite{Boykov2001FastApproximate} approach. \cgalFigureBegin{Classification_organization_fig,organization.svg} Organization of the package. @@ -431,7 +431,7 @@ quantifies the strengh of the regularization, \f$i \sim j\f$ represents the pairs of neighboring items and \f$\mathbf{1}_{\{.\}}\f$ the characteristic function. -A _Graph Cut_ based algorithm (Alpha Expansion) is used to quickly reach +A _graph cut_ based algorithm (alpha expansion) is used to quickly reach an approximate solution close to the global optimum of this energy. This method allows to consistently segment the input data set in diff --git a/Classification/include/CGAL/Classification/classify.h b/Classification/include/CGAL/Classification/classify.h index 88ed32aa677..9928ca808a3 100644 --- a/Classification/include/CGAL/Classification/classify.h +++ b/Classification/include/CGAL/Classification/classify.h @@ -15,7 +15,7 @@ #include -#include +#include #include #include #include @@ -234,12 +234,6 @@ namespace internal { const std::vector >& m_input_to_indices; LabelIndexRange& m_out; -#ifdef CGAL_DO_NOT_USE_BOYKOV_KOLMOGOROV_MAXFLOW_SOFTWARE - typedef CGAL::internal::Alpha_expansion_graph_cut_boost Alpha_expansion; -#else - typedef CGAL::internal::Alpha_expansion_graph_cut_boykov_kolmogorov Alpha_expansion; -#endif - public: Classify_functor_graphcut (const ItemRange& input, @@ -310,8 +304,7 @@ namespace internal { assigned_label[j] = nb_class_best; } - Alpha_expansion graphcut; - graphcut(edges, edge_weights, probability_matrix, assigned_label); + CGAL::alpha_expansion_graphcut (edges, edge_weights, probability_matrix, assigned_label); for (std::size_t i = 0; i < assigned_label.size(); ++ i) m_out[m_indices[sub][i]] = static_cast(assigned_label[i]); diff --git a/Classification/package_info/Classification/dependencies b/Classification/package_info/Classification/dependencies index 0f3cc824448..363c1d6ce64 100644 --- a/Classification/package_info/Classification/dependencies +++ b/Classification/package_info/Classification/dependencies @@ -20,4 +20,3 @@ STL_Extension Solver_interface Spatial_searching Stream_support -Surface_mesh_segmentation diff --git a/Distance_2/include/CGAL/squared_distance_2_1.h b/Distance_2/include/CGAL/squared_distance_2_1.h index 12f31495c8f..f9b62838e91 100644 --- a/Distance_2/include/CGAL/squared_distance_2_1.h +++ b/Distance_2/include/CGAL/squared_distance_2_1.h @@ -188,7 +188,108 @@ namespace internal { typename K::FT squared_distance(const typename K::Segment_2 &seg1, const typename K::Segment_2 &seg2, - const K& k) + const K& k, + const Cartesian_tag&) + { + typedef typename K::RT RT; + typedef typename K::FT FT; + bool crossing1, crossing2; + RT c1s, c1e, c2s, c2e; + if (seg1.source() == seg1.target()) + return internal::squared_distance(seg1.source(), seg2, k); + if (seg2.source() == seg2.target()) + return internal::squared_distance(seg2.source(), seg1, k); + + Orientation o1s = orientation(seg2.source(), seg2.target(), seg1.source()); + Orientation o1e = orientation(seg2.source(), seg2.target(), seg1.target()); + if (o1s == RIGHT_TURN) { + crossing1 = (o1e != RIGHT_TURN); + } else { + if (o1e != LEFT_TURN) { + if (o1s == COLLINEAR && o1e == COLLINEAR) + return internal::squared_distance_parallel(seg1, seg2, k); + crossing1 = true; + } else { + crossing1 = (o1s == COLLINEAR); + } + } + + Orientation o2s = orientation(seg1.source(), seg1.target(), seg2.source()); + Orientation o2e = orientation(seg1.source(), seg1.target(), seg2.target()); + if (o2s == RIGHT_TURN) { + crossing2 = (o2e != RIGHT_TURN); + } else { + if (o2e != LEFT_TURN) { + if (o2s == COLLINEAR && o2e == COLLINEAR) + return internal::squared_distance_parallel(seg1, seg2, k); + crossing2 = true; + } else { + crossing2 = (o2s == COLLINEAR); + } + } + + if (crossing1) { + if (crossing2) + return (FT)0; + + c2s = CGAL::abs(wcross(seg1.source(), seg1.target(), seg2.source(), k)); + c2e = CGAL::abs(wcross(seg1.source(), seg1.target(), seg2.target(), k)); + Comparison_result dm = compare(c2s,c2e); + + if (dm == SMALLER) { + return internal::squared_distance(seg2.source(), seg1, k); + } else { + if (dm == LARGER) { + return internal::squared_distance(seg2.target(), seg1, k); + } else { + // parallel, should not happen (no crossing) + return internal::squared_distance_parallel(seg1, seg2, k); + } + } + } else { + c1s = CGAL::abs(wcross(seg2.source(), seg2.target(), seg1.source(), k)); + c1e = CGAL::abs(wcross(seg2.source(), seg2.target(), seg1.target(), k)); + Comparison_result dm = compare(c1s,c1e); + if (crossing2) { + if (dm == SMALLER) { + return internal::squared_distance(seg1.source(), seg2, k); + } else { + if (dm == LARGER) { + return internal::squared_distance(seg1.target(), seg2, k); + } else { + // parallel, should not happen (no crossing) + return internal::squared_distance_parallel(seg1, seg2, k); + } + } + } else { + FT min1, min2; + + if (dm == EQUAL) + return internal::squared_distance_parallel(seg1, seg2, k); + min1 = (dm == SMALLER) ? + internal::squared_distance(seg1.source(), seg2, k): + internal::squared_distance(seg1.target(), seg2, k); + + c2s = CGAL::abs(wcross(seg1.source(), seg1.target(), seg2.source(), k)); + c2e = CGAL::abs(wcross(seg1.source(), seg1.target(), seg2.target(), k)); + dm = compare(c2s,c2e); + + if (dm == EQUAL) // should not happen. + return internal::squared_distance_parallel(seg1, seg2, k); + min2 = (dm == SMALLER) ? + internal::squared_distance(seg2.source(), seg1, k): + internal::squared_distance(seg2.target(), seg1, k); + return (min1 < min2) ? min1 : min2; + } + } + } + + template + typename K::FT + squared_distance(const typename K::Segment_2 &seg1, + const typename K::Segment_2 &seg2, + const K& k, + const Homogeneous_tag&) { typedef typename K::RT RT; typedef typename K::FT FT; @@ -277,6 +378,7 @@ namespace internal { } } + template inline typename K::RT _distance_measure_sub(const typename K::RT &startwcross, @@ -690,7 +792,8 @@ template inline typename K::FT squared_distance(const Segment_2 &seg1, const Segment_2 &seg2) { - return internal::squared_distance(seg1, seg2, K()); + typedef typename K::Kernel_tag Tag; + return internal::squared_distance(seg1, seg2, K(), Tag()); } template diff --git a/Distance_2/test/Distance_2/issue_4189.cpp b/Distance_2/test/Distance_2/issue_4189.cpp new file mode 100644 index 00000000000..cc24b627e31 --- /dev/null +++ b/Distance_2/test/Distance_2/issue_4189.cpp @@ -0,0 +1,34 @@ +#include + +#include +#include +#include + +typedef CGAL::Simple_cartesian SC; +typedef CGAL::Simple_cartesian EC; + +template +double fct() { + + typedef typename Kernel::Segment_2 Segment_2; + const Segment_2 segi = { + { -4.0380854964382, -1.9947196614192 }, + { 10.43442091460618, -0.5886833953492263 } }; + const Segment_2 segj = { + { -11.5138934277993, -2.721011070186227 }, + { -8.822747585009402, -2.459560251317805 } }; + + const auto dist = CGAL::squared_distance(segi, segj); + std::cout << "#dist: " << dist << std::endl; + + return CGAL::to_double(dist); +} + +int main() +{ + auto approx_dist = fct(); + fct(); + auto exact_dist = fct(); + assert(CGAL::abs(approx_dist - exact_dist) < 0.05 * CGAL::abs(exact_dist)); + return 0; +} diff --git a/Documentation/doc/Documentation/Doxyfile.in b/Documentation/doc/Documentation/Doxyfile.in index c4efea1bc5e..e90d8b66149 100644 --- a/Documentation/doc/Documentation/Doxyfile.in +++ b/Documentation/doc/Documentation/Doxyfile.in @@ -11,7 +11,8 @@ HTML_HEADER = ${CGAL_DOC_HEADER} LAYOUT_FILE = ${CGAL_DOC_RESOURCE_DIR}/DoxygenLayout.xml GENERATE_TAGFILE = ${CGAL_DOC_TAG_GEN_DIR}/Manual.tag EXAMPLE_PATH = ${CGAL_Convex_hull_2_EXAMPLE_DIR} \ - ${CGAL_Kernel_23_EXAMPLE_DIR} + ${CGAL_Kernel_23_EXAMPLE_DIR} \ + ${CGAL_Poisson_surface_reconstruction_3_EXAMPLE_DIR} FILTER_PATTERNS = *.txt=${CMAKE_BINARY_DIR}/pkglist_filter HTML_EXTRA_FILES += ${CGAL_DOC_RESOURCE_DIR}/hacks.js \ diff --git a/Documentation/doc/Documentation/Tutorials/Tutorial_reconstruction.txt b/Documentation/doc/Documentation/Tutorials/Tutorial_reconstruction.txt new file mode 100644 index 00000000000..fb60465bbc0 --- /dev/null +++ b/Documentation/doc/Documentation/Tutorials/Tutorial_reconstruction.txt @@ -0,0 +1,295 @@ +namespace CGAL { +/*! +\example Poisson_surface_reconstruction_3/tutorial_example.cpp +*/ + +/*! + +\page tuto_reconstruction Surface Reconstruction from Point Clouds +\cgalAutoToc + +\author Simon Giraudot + +Surface reconstruction from point clouds is a core topic in geometry +processing \cgalCite{cgal:btsag-asosr-16}. It is an ill-posed problem: +there is an infinite number of surfaces that approximate a single +point cloud and a point cloud does not define a surface in +itself. Thus additional assumptions and constraints must be defined by +the user and reconstruction can be achieved in many different +ways. This tutorial provides guidance on how to use the different +algorithms of \cgal to effectively perform surface reconstruction. + +\section TutorialsReconstruction_algorithms Which algorithm should I use? + +\cgal offers three different algorithms for surface reconstruction: + +- \ref Chapter_Poisson_Surface_Reconstruction "Poisson Surface Reconstruction" +- \ref Chapter_Advancing_Front_Surface_Reconstruction "Advancing Front Surface Reconstruction" +- \ref Chapter_Scale_space_reconstruction "Scale Space Surface Reconstruction" + + +Because reconstruction is an ill-posed problem, it must be regularized +via prior knowledge. Differences in prior lead to different +algorithms, and choosing one or the other of these methods is +dependent on these priors. For example, Poisson always generates +closed shapes (bounding a volume) and requires normals but does not +interpolate input points (the output surface does not pass exactly +through the input points). The following table lists different +properties of the input and output to help the user choose the method +best suited to each problem: + +
+| | Poisson | Advancing front | Scale space | +|------------------------------------------|:-------:|:----------------:|:----------------:| +| Are normals required? | Yes | No | No | +| Is noise handled? | Yes | By preprocessing | Yes | +| Is variable sampling handled? | Yes | Yes | By preprocessing | +| Are input points exactly on the surface? | No | Yes | Yes | +| Is the output always closed? | Yes | No | No | +| Is the output always smooth? | Yes | No | No | +| Is the output always manifold? | Yes | Yes | Optional | +| Is the output always orientable? | Yes | Yes | Optional | +
+ +\cgalFigureBegin{TutorialsReconstructionFigComparisons, compare_reconstructions.png} +Comparison of reconstruction methods applied to the same input (full shape and close-up). From left to right: original point cloud; Poisson; advancing front; scale space. +\cgalFigureEnd + +More information on these different methods can be found on their +respective manual pages and in Section \ref TutorialsReconstruction_reconstruction. + +\section TutorialsReconstruction_overview Pipeline Overview + +This tutorial aims at providing a more comprehensive view of the +possibilities offered by \cgal for dealing with point clouds, for +surface reconstruction purposes. The following diagram shows an +overview (not exhaustive) of common reconstruction steps using \cgal +tools. + +\cgalFigureBegin{TutorialsReconstructionFigPipeline, reconstruction.svg} +Pipeline Overview +\cgalFigureEnd + +We now review some of these steps in more detail. + + +\section TutorialsReconstruction_input Reading Input + +The reconstruction algorithms in \cgal take a range of iterators on a +container as input and use property maps to access the points (and the +normals when they are required). Points are typically stored in plain +text format (denoted as 'XYZ' format) where each point is separated by +a newline character and each coordinate separated by a white +space. Other formats available are 'OFF', 'PLY' and 'LAS'. \cgal +provides functions to read such formats: + +- `read_xyz_points()` +- `read_off_points()` +- `read_ply_points()` +- `read_ply_points_with_properties()` to read additional PLY properties +- `read_las_points()` +- `read_las_points_with_properties()` to read additional LAS properties + +\cgal also provides a dedicated container `CGAL::Point_set_3` to +handle point sets with additional properties such as normal +vectors. In this case, property maps are easily handled as shown in +the following sections. This structure also handles the stream +operator to read point sets in any of the formats previously +described. Using this method yields substantially shorter code, as can +be seen on the following example: + +\snippet Poisson_surface_reconstruction_3/tutorial_example.cpp Reading input + +\section TutorialsReconstruction_preprocessing Preprocessing + +Because reconstruction algorithms have some specific requirements that +point clouds do not always meet, some preprocessing might be necessary +to yield the best results. + +Note that this _preprocessing_ step is optional: when the input point +cloud has no imperfections, reconstruction can be applied to it +without any preprocessing. + + +\cgalFigureBegin{TutorialsReconstructionFigPreprocessing, reconstruction_preproc.png} +Comparison of advancing front reconstruction output using different +preprocessing on the same input. The smooth point cloud was generated +using _jet smoothing_; the simplified point cloud was generated using +_grid simplification_. +\cgalFigureEnd + + +\subsection TutorialsReconstruction_preprocessing_outliers Outlier removal + +Some acquisition techniques generate points which are far away from +the surface. These points, commonly referred to as "outliers", have no +relevance for reconstruction. Using the \cgal reconstruction +algorithms on outlier-ridden point clouds produce overly distorted +output, it is therefore strongly advised to filter these outliers +_before_ performing reconstruction. + +\snippet Poisson_surface_reconstruction_3/tutorial_example.cpp Outlier removal + +\subsection TutorialsReconstruction_preprocessing_simplification Simplification + +Some laser scanners generate points with widely variable +sampling. Typically, lines of scan are very densely sampled but the +gap between two lines of scan is much larger, leading to an overly +massive point cloud with large variations of sampling density. This +type of input point cloud might generate imperfect output using +algorithms which, in general, only handle small variations of sampling +density. + +\cgal provides several simplification algorithms. In addition to +reducing the size of the input point cloud and therefore decreasing +computation time, some of them can help making the input more +uniform. This is the case of the function `grid_simplify_point_set()` +which defines a grid of a user-specified size and keeps one point per +occupied cell. + +\snippet Poisson_surface_reconstruction_3/tutorial_example.cpp Simplification + + +\subsection TutorialsReconstruction_preprocessing_smoothing Smoothing + +Although reconstructions via 'Poisson' or 'Scale space' handle noise +internally, one may want to get tighter control over the smoothing +step. For example, a slightly noisy point cloud can benefit from some +reliable smoothing algorithms and be reconstructed via 'Advancing +front' which provides relevant properties (oriented mesh with +boundaries). + +Two functions are provided to smooth a noisy point cloud with a good +approximation (i.e. without degrading curvature, for example): + +- `jet_smooth_point_set()` +- `bilateral_smooth_point_set()` + +These functions directly modify the container: + +\snippet Poisson_surface_reconstruction_3/tutorial_example.cpp Smoothing + + +\subsection TutorialsReconstruction_preprocessing_normal Normal Estimation and Orientation + +\ref Chapter_Poisson_Surface_Reconstruction "Poisson Surface Reconstruction" +requires points with oriented normal vectors. To apply the algorithm +to a raw point cloud, normals must be estimated first, for example +with one of these two functions: + +- `pca_estimate_normals()` +- `jet_estimate_normals()` + +PCA is faster but jet is more accurate in the presence of high +curvatures. These function only estimates the _direction_ of the +normals, not their orientation (the orientation of the vectors might +not be locally consistent). To properly orient the normals, the +function `mst_orient_normals()` can be used. Notice that it can also +be used directly on input normals if their orientation is not +consistent. + +\snippet Poisson_surface_reconstruction_3/tutorial_example.cpp Normal estimation + +\section TutorialsReconstruction_reconstruction Reconstruction + +\subsection TutorialsReconstruction_reconstruction_poisson Poisson + +Poisson reconstruction consists in computing an implicit function +whose gradient matches the input normal vector field: this indicator +function has opposite signs inside and outside of the inferred shape +(hence the need for closed shapes). This method thus requires normals +and produces smooth closed surfaces. It is not appropriate if the +surface is expected to interpolate the input points. On the contrary, +it performs well if the aim is to approximate a noisy point cloud with +a smooth surface. + +\snippet Poisson_surface_reconstruction_3/tutorial_example.cpp Poisson reconstruction + +\subsection TutorialsReconstruction_reconstruction_advancing Advancing Front + +Advancing front is a Delaunay-based approach which interpolates a +subset of the input points. It generates triples of point indices +which describe the triangular facets of the reconstruction: it uses a +priority queue to sequentially pick the Delaunay facet the most likely +to be part of the surface, based on a size criterion (to favor the +small facets) and an angle criterion (to favor smoothness). Its main +virtue is to generate oriented manifold surfaces with boundaries: +contrary to Poisson, it does not require normals and is not bound to +reconstruct closed shapes. However, it requires preprocessing if the +point cloud is noisy. + +The \ref Chapter_Advancing_Front_Surface_Reconstruction "Advancing Front" +package provides several ways of constructing the function. Here is +a simple example: + +\snippet Poisson_surface_reconstruction_3/tutorial_example.cpp Advancing front reconstruction + +\subsection TutorialsReconstruction_reconstruction_scale_space Scale Space + +Scale space reconstruction aims at producing a surface which +interpolates the input points (interpolant) while offering some +robustness to noise. More specifically, it first applies several times +a smoothing filter (such as Jet Smoothing) to the input point set to +produce a scale space; then, the smoothest scale is meshed (using for +example the Advancing Front mesher); finally, the resulting +connectivity between smoothed points is propagated to the original raw +input point set. This method is the right choice if the input point +cloud is noisy but the user still wants the surface to pass exactly +through the points. + +\snippet Poisson_surface_reconstruction_3/tutorial_example.cpp Scale space reconstruction + +\section TutorialsReconstruction_postprocessing Output and Postprocessing + +Each of these methods produce a triangle mesh stored in different +ways. If this output mesh is hampered by defects such as holes or +self-intersections, \cgal provide several algorithms to post-process +it (hole filling, remeshing, etc.) in the package \ref +Chapter_PolygonMeshProcessing "Polygon Mesh Processing". + +We do not discuss these functions here as there are many +postprocessing possibilities whose relevance strongly depends on the +user's expectations on the output mesh. + +The mesh (postprocessed or not) can easily be saved in the PLY format +(here, using the binary variant): + +\snippet Poisson_surface_reconstruction_3/tutorial_example.cpp Output poisson + +A polygon soup can also be saved in the OFF format by iterating on the +points and faces: + +\snippet Poisson_surface_reconstruction_3/tutorial_example.cpp Output scale space + +Finally, if the polygon soup can be converted into a polygon mesh, it +can also be saved directly in the OFF format using the stream +operator: + +\snippet Poisson_surface_reconstruction_3/tutorial_example.cpp Output advancing front + +\section TutorialsReconstruction_recap Full Code Example + +All the code snippets used in this tutorial can be assembled to create +a full algorithm pipeline (provided the correct _includes_ are +used). We give a full code example which achieves all the steps +described in this tutorial. The reconstruction method can be selected +by the user at runtime with the second argument. + +\include Poisson_surface_reconstruction_3/tutorial_example.cpp + +\section TutorialsReconstruction_pipeline Full Pipeline Images + +The following figure an example of a full reconstruction pipeline +applied to a bear statue (courtesy _EPFL Computer Graphics and +Geometry Laboratory_ \cgalCite{cgal:e-esmr}). Two mesh processing +algorithms (hole filling and isotropic remeshing) are also applied +(refer to the chapter \ref Chapter_PolygonMeshProcessing "Polygon Mesh Processing" +for more information). + +\cgalFigureBegin{TutorialsReconstructionFigFull, reconstruction_pipeline.png} +Full reconstruction pipeline (with close-ups). +\cgalFigureEnd + + +*/ +} diff --git a/Documentation/doc/Documentation/Tutorials/Tutorials.txt b/Documentation/doc/Documentation/Tutorials/Tutorials.txt index aab618e954f..76aa5daf111 100644 --- a/Documentation/doc/Documentation/Tutorials/Tutorials.txt +++ b/Documentation/doc/Documentation/Tutorials/Tutorials.txt @@ -16,6 +16,8 @@ User Manual. geometric primitives, the notion of traits classes which define what primitives are used by a geometric algorithm, the notions of \em concept and \em model. +- \subpage tuto_reconstruction + \section tuto_examples Package Examples diff --git a/Documentation/doc/Documentation/fig/compare_reconstructions.png b/Documentation/doc/Documentation/fig/compare_reconstructions.png new file mode 100644 index 00000000000..5c9a41f0169 Binary files /dev/null and b/Documentation/doc/Documentation/fig/compare_reconstructions.png differ diff --git a/Documentation/doc/Documentation/fig/reconstruction.svg b/Documentation/doc/Documentation/fig/reconstruction.svg new file mode 100644 index 00000000000..d32ecdb5c75 --- /dev/null +++ b/Documentation/doc/Documentation/fig/reconstruction.svg @@ -0,0 +1,1447 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Input + + + Preprocessing + + + Reconstruction + + + Postprocessing + + + Output + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Poisson reconstruction + + + + + + Advancing front reconstruction + + + + + + Scale space reconstruction + + + + + + Normal estimation + + + + + + Simplification + + + + + + Smoothing + + + + + + Points + + + + + + Points with normals + + + + + + Outlier removal + + + + + + Simplification + + + + + + Smoothing + + + + + + Outlier removal + + + + + + Normal orientation + + + + + + XYZ file + + + + + + OFF file + + + + + + read_xyz_points + + + + + + read_off_points + + + + + + PLY file + + + + + + read_ply_points + + + + + + LAS/LAZ file + + + + + + read_las_points + + + + + + Mesh + + + + + + Hole filling + + + + + + Remeshing + + + + + + OFF file + + + + + + write_off + + + + + + PLY file + + + + + + write_ply + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Documentation/doc/Documentation/fig/reconstruction_pipeline.png b/Documentation/doc/Documentation/fig/reconstruction_pipeline.png new file mode 100644 index 00000000000..ce240b9fad8 Binary files /dev/null and b/Documentation/doc/Documentation/fig/reconstruction_pipeline.png differ diff --git a/Documentation/doc/Documentation/fig/reconstruction_preproc.png b/Documentation/doc/Documentation/fig/reconstruction_preproc.png new file mode 100644 index 00000000000..ddcab5a9e0b Binary files /dev/null and b/Documentation/doc/Documentation/fig/reconstruction_preproc.png differ diff --git a/Documentation/doc/biblio/cgal_manual.bib b/Documentation/doc/biblio/cgal_manual.bib index 41fe234c9dc..785b25e2e36 100644 --- a/Documentation/doc/biblio/cgal_manual.bib +++ b/Documentation/doc/biblio/cgal_manual.bib @@ -708,6 +708,12 @@ Teillaud" , year = 1999 } +@Misc{ cgal:e-esmr, + title = {{EPFL} statue model repository}, + howpublished = {{EPFL} Computer Graphics and Geometry Laboratory}, + url = {http://lgg.epfl.ch/statues_dataset.php} +} + @inproceedings{ cgal:eddhls-maam-95 ,author = {Matthias Eck and Tony DeRose and Tom Duchamp and Hugues Hoppe and Michael Lounsbery and Werner Stuetzle} @@ -1771,6 +1777,21 @@ ABSTRACT = {We present the first complete, exact and efficient C++ implementatio pages="" } +@article{cgal:btsag-asosr-16, + TITLE = {{A Survey of Surface Reconstruction from Point Clouds}}, + AUTHOR = {Berger, Matthew and Tagliasacchi, Andrea and Seversky, Lee and Alliez, Pierre and Guennebaud, Gael and Levine, Joshua and Sharf, Andrei and Silva, Claudio}, + URL = {https://hal.inria.fr/hal-01348404}, + JOURNAL = {{Computer Graphics Forum}}, + PUBLISHER = {{Wiley}}, + PAGES = {27}, + YEAR = {2016}, + DOI = {10.1111/cgf.12802}, + PDF = {https://hal.inria.fr/hal-01348404/file/survey-author.pdf}, + HAL_ID = {hal-01348404}, + HAL_VERSION = {v2} +} + + @TechReport{ cgal:pabl-cco-07, author = {Poudret, M. and Arnould, A. and Bertrand, Y. and Lienhardt, P.}, title = {Cartes Combinatoires Ouvertes.}, diff --git a/Documentation/doc/resources/1.8.14/BaseDoxyfile.in b/Documentation/doc/resources/1.8.14/BaseDoxyfile.in index 4b823147ac5..c9944e3f85f 100644 --- a/Documentation/doc/resources/1.8.14/BaseDoxyfile.in +++ b/Documentation/doc/resources/1.8.14/BaseDoxyfile.in @@ -302,12 +302,6 @@ ALIASES = "sc{1}=\1\cite \1" \ "cgalPackageSection{2}=\htmlonly[block]
\endhtmlonly \section \1 \2 ^^ \htmlonly[block]
\endhtmlonly" -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. - -TCL_SUBST = - # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all @@ -2188,12 +2182,6 @@ EXTERNAL_GROUPS = NO EXTERNAL_PAGES = NO -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of 'which perl'). -# The default file (with absolute path) is: /usr/bin/perl. - -PERL_PATH = /usr/bin/perl - #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- @@ -2207,15 +2195,6 @@ PERL_PATH = /usr/bin/perl CLASS_DIAGRAMS = NO -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see: -# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. diff --git a/Filtered_kernel/include/CGAL/Lazy.h b/Filtered_kernel/include/CGAL/Lazy.h index 1dc7f2f12a2..067fbe57463 100644 --- a/Filtered_kernel/include/CGAL/Lazy.h +++ b/Filtered_kernel/include/CGAL/Lazy.h @@ -116,9 +116,9 @@ templateinline std::enable_if_t::value, unsigned> dept // For an iterator, exact/approx applies to the objects it points to template ::value>> -auto exact(T const& t) {return make_transforming_iterator(t,[](auto const&u){return CGAL::exact(u);});} +auto exact(T const& t) {return make_transforming_iterator(t,[](auto const&u)->decltype(auto){return CGAL::exact(u);});} template ::value>> -auto approx(T const& t) {return make_transforming_iterator(t,[](auto const&u){return CGAL::approx(u);});} +auto approx(T const& t) {return make_transforming_iterator(t,[](auto const&u)->decltype(auto){return CGAL::approx(u);});} template ::value>> unsigned depth(T const&) {return 1;} // FIXME: depth(*t) would be better when t is valid, but not for end iterators, and the true answer would iterate on the range, but we can't do that with only one iterator... We need to replace iterators with ranges to solve that. diff --git a/Generator/include/CGAL/point_generators_3.h b/Generator/include/CGAL/point_generators_3.h index dd1e624b65c..1dcbf4ce935 100644 --- a/Generator/include/CGAL/point_generators_3.h +++ b/Generator/include/CGAL/point_generators_3.h @@ -539,6 +539,26 @@ public: const Tr& tr; }; + +template +class Triangle_3_from_soup +{ + typedef typename boost::range_value::type Point_3; + typedef typename Kernel_traits::Kernel Kernel; +public: + typedef typename Kernel::Triangle_3 result_type; + + Triangle_3_from_soup(const PointRange& pts) : points(pts) { } + + result_type operator()(const Triangle& t) const + { + return result_type(points[t[0]], points[t[1]], points[t[2]]); + } + +private: + const PointRange& points; +}; + }//end namespace internal template , + class Creator = Creator_uniform_3< + typename Kernel_traits< typename PointRange::value_type >::Kernel::RT, + typename PointRange::value_type> > +struct Random_points_in_triangle_soup + : public Generic_random_point_generator, + Random_points_in_triangle_3, + typename PointRange::value_type> +{ + typedef Generic_random_point_generator, + Random_points_in_triangle_3, + typename PointRange::value_type> Base; + typedef typename PointRange::value_type Point_3; + typedef typename Kernel_traits::Kernel Kernel; + typedef Triangle Id; + typedef Point_3 result_type; + typedef Random_points_in_triangle_soup This; + + template + Random_points_in_triangle_soup(const TriangleRange& triangles, + const PointRange& points, + Random& rnd = get_default_random()) + : Base(triangles, + internal::Triangle_3_from_soup(points), + internal::Apply_approx_sqrt::Kernel::Compute_squared_area_3>(), + rnd) + { } + + This& operator++() + { + Base::generate_point(); + return *this; + } + + This operator++(int) + { + This tmp = *this; + ++(*this); + return tmp; + } +}; + } //namespace CGAL #include diff --git a/Homogeneous_kernel/include/CGAL/Homogeneous/function_objects.h b/Homogeneous_kernel/include/CGAL/Homogeneous/function_objects.h index 649d18c1751..97e7a68d225 100644 --- a/Homogeneous_kernel/include/CGAL/Homogeneous/function_objects.h +++ b/Homogeneous_kernel/include/CGAL/Homogeneous/function_objects.h @@ -761,6 +761,52 @@ namespace HomogeneousKernelFunctors { } }; + template + class Compare_signed_distance_to_line_2 + { + typedef typename K::Point_2 Point_2; + typedef typename K::Line_2 Line_2; + typedef typename K::Less_signed_distance_to_line_2 Less_signed_distance_to_line_2; + + public: + typedef Comparison_result result_type; + + result_type + operator()(const Point_2& p, const Point_2& q, + const Point_2& r, const Point_2& s) const + { + typedef typename K::RT RT; + + const RT & phx = p.hx(); + const RT & phy = p.hy(); + const RT & phw = p.hw(); + const RT & qhx = q.hx(); + const RT & qhy = q.hy(); + const RT & qhw = q.hw(); + const RT & rhx = r.hx(); + const RT & rhy = r.hy(); + const RT & rhw = r.hw(); + const RT & shx = s.hx(); + const RT & shy = s.hy(); + const RT & shw = s.hw(); + + RT scaled_dist_r_minus_scaled_dist_s = + ( rhx*shw - shx*rhw ) * (phy*qhw - qhy*phw) + - ( rhy*shw - shy*rhw ) * (phx*qhw - qhx*phw); + + return compare(scaled_dist_r_minus_scaled_dist_s, 0); + } + + result_type + operator()(const Line_2& l, const Point_2& p, const Point_2& q) const + { + Less_signed_distance_to_line_2 less = K().less_signed_distance_to_line_2_object(); + if (less(l, p, q)) return SMALLER; + if (less(l, q, p)) return LARGER; + return EQUAL; + } + }; + template class Compare_slope_2 { @@ -4109,26 +4155,7 @@ namespace HomogeneousKernelFunctors { operator()(const Point_2& p, const Point_2& q, const Point_2& r, const Point_2& s) const { - typedef typename K::RT RT; - - const RT & phx= p.hx(); - const RT & phy= p.hy(); - const RT & phw= p.hw(); - const RT & qhx= q.hx(); - const RT & qhy= q.hy(); - const RT & qhw= q.hw(); - const RT & rhx= r.hx(); - const RT & rhy= r.hy(); - const RT & rhw= r.hw(); - const RT & shx= s.hx(); - const RT & shy= s.hy(); - const RT & shw= s.hw(); - - RT scaled_dist_r_minus_scaled_dist_s = - ( rhx*shw - shx*rhw ) * (phy*qhw - qhy*phw) - - ( rhy*shw - shy*rhw ) * (phx*qhw - qhx*phw); - - return scaled_dist_r_minus_scaled_dist_s < 0; + return Compare_signed_distance_to_line_2().operator()(p, q, r, s) == SMALLER; } result_type diff --git a/Installation/CHANGES.md b/Installation/CHANGES.md index fcd93c9ac8d..8b4d67730cf 100644 --- a/Installation/CHANGES.md +++ b/Installation/CHANGES.md @@ -2,6 +2,20 @@ Release History =============== [Release 5.1] (https://github.com/CGAL/cgal/releases/tag/releases%2FCGAL-5.1) + +### 2D and 3D Linear Geometry Kernel + - Add `CompareSignedDistanceToLine_2` in the 2D/3D Kernel concept to compare + the signed distance of two points to a line, or the line passing through two given points. + Corresponding functors in the model (`Compare_signed_distance_to_line_2`) are also added. + +### 2D Triangulations + - Add function `split_subconstraint_graph_into_constraints()` to + `Constrained_triangulation_plus_2` to initialize the constraints + from a soup of disconnected segments that should first be split + into polylines. + + +Release 5.0 ----------- Release date: June 2020 @@ -24,6 +38,8 @@ Release date: June 2020 ### CGAL and the Boost Graph Library (BGL) - Introduced the function `set_triangulation_ids(Triangulation& tr)` which must be used to initialize vertex, edge, and face indices of a triangulation meant to be used with BGL algorithms. + - Added function `alpha_expansion_graphcut()` which regularizes a + multi-label partition over a user-defined graph. ### Polygon Mesh Processing @@ -38,6 +54,10 @@ Release date: June 2020 components that would be removed with the specified threshold, but without actually removing them. - The function `CGAL::Polygon_mesh_processing::stitch_borders()` now returns the number of halfedge pairs that were stitched. +- Introduced the new functions `CGAL::Polygon_mesh_processing::merge_reversible_connected_components()`, + `CGAL::Polygon_mesh_processing::duplicate_incompatible_edges_in_polygon_soup()`, + and `CGAL::Polygon_mesh_processing::orient_triangle_soup_with_reference_triangle_mesh()` that can be helpful + when repairing a polygon soup. - New function to split meshes along a mesh or a plane: `CGAL::Polygon_mesh_processing::split()` - New function to split a single mesh containing several connected components into several meshes containing one connected component: @@ -47,6 +67,7 @@ Release date: June 2020 - The function `CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh` now allows passing a point map (for the point range) and a vertex point map (for the polygon mesh) via named parameters. - Added the function `CGAL::Polygon_mesh_processing::polygon_mesh_to_polygon_soup()`. + - Added a new function `CGAL::Polygon_mesh_processing::sample_triangle_soup()` that generates points on a triangle soup surface. ### Point Set Processing - Added wrapper functions for registration: @@ -62,6 +83,7 @@ Release date: June 2020 sets using ICP algorithm implemented in the third party library libpointmatcher, and registers the points sets by transforming the data point set using the computed transformation. + ### 2D Triangulations - To fix an inconsistency between code and documentation and to clarify which types of intersections are truly allowed in constrained Delaunay triangulations, the tag `CGAL::No_intersection_tag` @@ -74,6 +96,16 @@ Release date: June 2020 does not allow any intersection, except for the configuration of two constraints having a single common endpoints, for convience. +### 3D Triangulations +- The free function `CGAL::file_input()` and the member function `CGAL::Triangulation_3::file_input()` + have been added. The first allows to load a `Triangulation_3` from an input stream, using functors to create vertices and cells. + The second is simply the member function version of the first one. + +### 3D Triangulation Data Structure +- The free function `CGAL::file_input()` and the member function `CGAL::TDS_3::file_input()` + have been added. The first allows to load a `TDS_3` from an input stream, using functors to create vertices and cells. + The second is simply the member function version of the first one. + ### dD Spatial Searching - Improved the performance of the kd-tree in some cases: @@ -115,8 +147,7 @@ Release date: June 2020 ### STL Extensions for CGAL - Added a new concurrency tag: `CGAL::Parallel_if_available_tag`. This tag is a convenience typedef to `CGAL::Parallel_tag` if the third party library TBB has been found and linked with, and to `CGAL::Sequential_tag` otherwise. - - + ### Convex_hull_3 - A new overload for `convex_hull_3()` that takes a model of `VertexListGraph` has been added. diff --git a/Kernel_23/doc/Kernel_23/CGAL/Vector_3.h b/Kernel_23/doc/Kernel_23/CGAL/Vector_3.h index cfb6e25544b..036dafdc8f2 100644 --- a/Kernel_23/doc/Kernel_23/CGAL/Vector_3.h +++ b/Kernel_23/doc/Kernel_23/CGAL/Vector_3.h @@ -68,12 +68,12 @@ introduces a vector `v` initialized to `(x, y, z)`. Vector_3(int x, int y, int z); /*! -introduces a vector `v` initialized to `(x, y, z). +introduces a vector `v` initialized to `(x, y, z)`. */ Vector_3(double x, double y, double z); /*! -introduces a vector `v` initialized to `(hx/hw, hy/hw, hz/hw). +introduces a vector `v` initialized to `(hx/hw, hy/hw, hz/hw)`. */ Vector_3(const Kernel::RT &hx, const Kernel::RT &hy, const Kernel::RT &hz, const Kernel::RT &hw = RT(1)); diff --git a/Kernel_23/doc/Kernel_23/Concepts/FunctionObjectConcepts.h b/Kernel_23/doc/Kernel_23/Concepts/FunctionObjectConcepts.h index b63475c7188..44e13cfbbe4 100644 --- a/Kernel_23/doc/Kernel_23/Concepts/FunctionObjectConcepts.h +++ b/Kernel_23/doc/Kernel_23/Concepts/FunctionObjectConcepts.h @@ -976,6 +976,40 @@ public: /// @} }; /* end Kernel::ComparePowerDistance_3 */ + + + +/*! + \ingroup PkgKernel23ConceptsFunctionObjects + \cgalConcept + + \cgalRefines `AdaptableFunctor` (with four arguments) +*/ +class CompareSignedDistanceToLine_2 { +public: + + /// \name Operations + /// A model of this concept must provide: + /// @{ + + /*! + compares the signed distance of `r` and `s` to the directed line through `p` and `q`. + */ + Comparison_result operator()(const Kernel::Point_2& p, + const Kernel::Point_2& q, + const Kernel::Point_2& r, + const Kernel::Point_2& s); + + /*! + compares the signed distance of `r` and `s` to the directed line `l`. + */ + Comparison_result operator()(const Kernel::Line_2& l, + const Kernel::Point_2& r, + const Kernel::Point_2& s); + /// @} +}; /* end Kernel::CompareSignedDistanceToLine_2 */ + + /*! \ingroup PkgKernel23ConceptsFunctionObjects \cgalConcept @@ -8757,7 +8791,7 @@ public: \ingroup PkgKernel23ConceptsFunctionObjects \cgalConcept - \cgalRefines `AdaptableFunctor`` (with four arguments) + \cgalRefines `AdaptableFunctor` (with four arguments) \sa `has_smaller_signed_distance_to_line_grp` diff --git a/Kernel_23/doc/Kernel_23/Concepts/Kernel.h b/Kernel_23/doc/Kernel_23/Concepts/Kernel.h index 83bd6f4c0bd..22efddcdf89 100644 --- a/Kernel_23/doc/Kernel_23/Concepts/Kernel.h +++ b/Kernel_23/doc/Kernel_23/Concepts/Kernel.h @@ -647,6 +647,11 @@ public: */ typedef unspecified_type Compare_angle_with_x_axis_2; + /*! + a model of `Kernel::CompareSignedDistanceToLine_2` + */ + typedef unspecified_type Compare_signed_distance_to_line_2; + /*! a model of `Kernel::CompareSlope_2` */ diff --git a/Kernel_23/doc/Kernel_23/PackageDescription.txt b/Kernel_23/doc/Kernel_23/PackageDescription.txt index 982c92fc692..54fa6580087 100644 --- a/Kernel_23/doc/Kernel_23/PackageDescription.txt +++ b/Kernel_23/doc/Kernel_23/PackageDescription.txt @@ -281,6 +281,7 @@ - `Kernel::CompareSlope_3` - `Kernel::ComparePowerDistance_2` - `Kernel::ComparePowerDistance_3` +- `Kernel::CompareSignedDistanceToLine_2` - `Kernel::CompareSquaredDistance_2` - `Kernel::CompareSquaredDistance_3` - `Kernel::CompareSquaredRadius_3` diff --git a/Kernel_23/include/CGAL/Kernel/global_functions_internal_2.h b/Kernel_23/include/CGAL/Kernel/global_functions_internal_2.h index 6e1289b044e..42c1ac639ca 100644 --- a/Kernel_23/include/CGAL/Kernel/global_functions_internal_2.h +++ b/Kernel_23/include/CGAL/Kernel/global_functions_internal_2.h @@ -341,9 +341,7 @@ compare_signed_distance_to_line(const typename K::Point_2& p, const typename K::Point_2& s, const K& k) { - if (k.less_signed_distance_to_line_2_object()(p, q, r, s)) return SMALLER; - if (k.less_signed_distance_to_line_2_object()(p, q, s, r)) return LARGER; - return EQUAL; + return k.compare_signed_distance_to_line_2_object()(p, q, r, s); } template @@ -354,9 +352,7 @@ compare_signed_distance_to_line(const typename K::Line_2& l, const typename K::Point_2& q, const K& k) { - if (k.less_signed_distance_to_line_2_object()(l, p, q)) return SMALLER; - if (k.less_signed_distance_to_line_2_object()(l, q, p)) return LARGER; - return EQUAL; + return k.compare_signed_distance_to_line_2_object()(l, p, q); } template < class K > diff --git a/Kernel_23/include/CGAL/Kernel/interface_macros.h b/Kernel_23/include/CGAL/Kernel/interface_macros.h index 9993c699f02..9495ca4e3a4 100644 --- a/Kernel_23/include/CGAL/Kernel/interface_macros.h +++ b/Kernel_23/include/CGAL/Kernel/interface_macros.h @@ -116,6 +116,8 @@ CGAL_Kernel_pred_RT(Compare_power_distance_2, compare_power_distance_2_object) CGAL_Kernel_pred_RT(Compare_power_distance_3, compare_power_distance_3_object) +CGAL_Kernel_pred(Compare_signed_distance_to_line_2, + compare_signed_distance_to_line_2_object) CGAL_Kernel_pred(Compare_slope_2, compare_slope_2_object) CGAL_Kernel_pred(Compare_slope_3, diff --git a/Optimal_transportation_reconstruction_2/demo/Optimal_transportation_reconstruction_2/scene.h b/Optimal_transportation_reconstruction_2/demo/Optimal_transportation_reconstruction_2/scene.h index 758fc216ef4..6e804dc87a3 100644 --- a/Optimal_transportation_reconstruction_2/demo/Optimal_transportation_reconstruction_2/scene.h +++ b/Optimal_transportation_reconstruction_2/demo/Optimal_transportation_reconstruction_2/scene.h @@ -407,7 +407,7 @@ public: Point_3_from_sample()), boost::make_transform_iterator (m_samples.end(), Point_3_from_sample())), - 3); + 3, CGAL::parameters::point_map (CGAL::Identity_property_map_no_lvalue())); std::cerr << "Average spacing = " << spacing << std::endl; } diff --git a/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/Callback_wrapper.h b/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/Callback_wrapper.h new file mode 100644 index 00000000000..c52826db44b --- /dev/null +++ b/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/Callback_wrapper.h @@ -0,0 +1,184 @@ +// Copyright (c) 2018 GeometryFactory (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 + +#ifndef CGAL_PSP_INTERNAL_CALLBACK_WRAPPER_H +#define CGAL_PSP_INTERNAL_CALLBACK_WRAPPER_H + +#include + +#include + +#include + +namespace CGAL { +namespace Point_set_processing_3 { +namespace internal { + +template +class Callback_wrapper +{ + std::size_t m_advancement; + bool m_interrupted; + +public: + Callback_wrapper (const std::function&, std::size_t, std::size_t, bool = false) { } + void reset (std::size_t, std::size_t, bool = false) { } + std::size_t& advancement() { return m_advancement; } + bool& interrupted() { return m_interrupted; } + void join() { } +}; + +template <> +class Callback_wrapper +{ + const std::function& m_callback; + std::size_t m_advancement; + bool m_interrupted; + std::size_t m_size; + +public: + + Callback_wrapper (const std::function& callback, + std::size_t size, + std::size_t advancement = 0, + bool interrupted = false) + : m_callback (callback) + , m_advancement (advancement) + , m_interrupted (interrupted) + , m_size (size) + { } + + Callback_wrapper (const Callback_wrapper& other) + : m_callback (other.m_callback) + , m_advancement (other.m_advancement) + , m_interrupted (other.m_interrupted) + , m_size (other.m_size) + { } + + ~Callback_wrapper () { } + + void reset (std::size_t size, std::size_t advancement, bool interrupted = false) + { + m_size = size; + m_advancement = advancement; + m_interrupted = interrupted; + } + std::size_t& advancement() + { + return m_advancement; + } + + bool& interrupted() + { + if (m_callback) + m_interrupted = !(m_callback(m_advancement / double(m_size))); + return m_interrupted; + } + + void join() { } +}; + +#ifdef CGAL_LINKED_WITH_TBB +template <> +class Callback_wrapper +{ + const std::function& m_callback; + cpp11::atomic* m_advancement; + cpp11::atomic* m_interrupted; + std::size_t m_size; + bool m_creator; + cpp11::thread* m_thread; + + // assignment operator shouldn't be used (m_callback is const ref) + Callback_wrapper& operator= (const Callback_wrapper&) + { + return *this; + } + +public: + Callback_wrapper (const std::function& callback, + std::size_t size, + std::size_t advancement = 0, + bool interrupted = false) + : m_callback (callback) + , m_advancement (new cpp11::atomic()) + , m_interrupted (new cpp11::atomic()) + , m_size (size) + , m_creator (true) + , m_thread (nullptr) + { + // cpp11::atomic only has default constructor, initialization done in two steps + *m_advancement = advancement; + *m_interrupted = interrupted; + if (m_callback) + m_thread = new cpp11::thread (*this); + } + + Callback_wrapper (const Callback_wrapper& other) + : m_callback (other.m_callback) + , m_advancement (other.m_advancement) + , m_interrupted (other.m_interrupted) + , m_size (other.m_size) + , m_creator (false) + , m_thread (nullptr) + { + + } + + ~Callback_wrapper () + { + if (m_creator) + { + delete m_advancement; + delete m_interrupted; + } + if (m_thread != nullptr) + delete m_thread; + } + + void reset (std::size_t size, std::size_t advancement, bool interrupted = false) + { + m_size = size; + *m_advancement = advancement; + *m_interrupted = interrupted; + if (m_callback) + m_thread = new cpp11::thread (*this); + } + + cpp11::atomic& advancement() { return *m_advancement; } + cpp11::atomic& interrupted() { return *m_interrupted; } + void join() + { + if (m_thread != nullptr) + m_thread->join(); + } + + void operator()() + { + while (*m_advancement != m_size) + { + if (m_callback && !m_callback (*m_advancement / double(m_size))) + *m_interrupted = true; + if (*m_interrupted) + return; + cpp11::sleep_for (0.00001); + } + if (m_callback) + m_callback (1.); + } +}; +#endif + +} // namespace internal +} // namespace Point_set_processing_3 +} // namespace CGAL + +#endif // CGAL_PSP_INTERNAL_CALLBACK_WRAPPER_H diff --git a/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/Neighbor_query.h b/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/Neighbor_query.h new file mode 100644 index 00000000000..5e8996db035 --- /dev/null +++ b/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/Neighbor_query.h @@ -0,0 +1,180 @@ +// Copyright (c) 2019 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 + +#ifndef CGAL_PSP_INTERNAL_NEIGHBOR_QUERY_H +#define CGAL_PSP_INTERNAL_NEIGHBOR_QUERY_H + +#include + +#include +#include +#include +#include + +#include + +#include + +namespace CGAL { +namespace Point_set_processing_3 { +namespace internal { + +struct Maximum_points_reached_exception : public std::exception { }; + +template +class Neighbor_query +{ +public: + + typedef Kernel_ Kernel; + typedef PointRangeRef Point_range; + typedef PointMap Point_map; + + typedef typename Kernel::FT FT; + typedef typename Kernel::Point_3 Point_3; + + typedef typename Range_iterator_type::type input_iterator; + typedef typename input_iterator::value_type value_type; + + typedef CGAL::Prevent_deref iterator; + + struct Deref_point_map + { + typedef input_iterator key_type; + typedef typename boost::property_traits::reference reference; + typedef typename boost::property_traits::value_type value_type; + typedef typename boost::property_traits::category category; + + PointMap point_map; + + Deref_point_map () { } + Deref_point_map (PointMap point_map) : point_map(point_map) { } + + friend reference get (const Deref_point_map& map, key_type it) + { + return get(map.point_map, *it); + } + }; + + typedef CGAL::Search_traits_3 Tree_traits_base; + typedef CGAL::Search_traits_adapter Tree_traits; + typedef CGAL::Sliding_midpoint Splitter; + typedef CGAL::Distance_adapter > Distance; + typedef CGAL::Kd_tree Tree; + typedef CGAL::Fuzzy_sphere Sphere; + + typedef CGAL::Orthogonal_k_neighbor_search Neighbor_search; + typedef typename Neighbor_search::iterator Search_iterator; + +private: + + PointRangeRef m_points; + PointMap m_point_map; + Deref_point_map m_deref_map; + Tree_traits m_traits; + Tree m_tree; + Distance m_distance; + + // Forbid copy + Neighbor_query (const Neighbor_query&) { } + +public: + + Neighbor_query (PointRangeRef points, PointMap point_map) + : m_points (points) + , m_point_map (point_map) + , m_deref_map (point_map) + , m_traits (m_deref_map) + , m_tree (iterator(m_points.begin()), iterator(m_points.end()), Splitter(), m_traits) + , m_distance (m_deref_map) + { + m_tree.build(); + } + + PointMap point_map() const { return m_point_map; } + + template + void get_iterators (const Point_3& query, unsigned int k, FT neighbor_radius, + OutputIterator output) const + { + if (neighbor_radius != FT(0)) + { + Sphere fs (query, neighbor_radius, 0, m_traits); + + // if k=0, no limit on the number of neighbors returned + if (k == 0) + k = (std::numeric_limits::max)(); + + unsigned int nb = 0; + + try + { + std::function output_iterator_with_limit + = [&](const input_iterator& it) + { + *(output ++) = it; + if (++ nb == k) + throw Maximum_points_reached_exception(); + }; + + auto function_output_iterator + = boost::make_function_output_iterator (output_iterator_with_limit); + + m_tree.search (function_output_iterator, fs); + } + catch (const Maximum_points_reached_exception&) + { } + + // Fallback, if less than 3 points are return, search for the 3 + // first points + if (nb < 3) + k = 3; + // Else, no need to search for K nearest neighbors + else + k = 0; + } + + if (k != 0) + { + // Gather set of (k+1) neighboring points. + // Perform k+1 queries (as in point set, the query point is + // output first). Search may be aborted if k is greater + // than number of input points. + + Neighbor_search search (m_tree, query, k+1, 0, true, m_distance); + Search_iterator search_iterator = search.begin(); + unsigned int i; + for (i = 0; i < (k+1); ++ i) + { + if(search_iterator == search.end()) + break; // premature ending + *(output ++) = search_iterator->first; + search_iterator++; + } + } + } + + template + void get_points (const Point_3& query, unsigned int k, FT neighbor_radius, + OutputIterator output) const + { + return get_iterators(query, k, neighbor_radius, + boost::make_function_output_iterator + ([&](const input_iterator& it) + { + *(output ++) = get (m_point_map, *it); + })); + } +}; + +} } } // namespace CGAL::Point_set_processing_3::internal + +#endif // CGAL_PSP_INTERNAL_NEIGHBOR_QUERY_H diff --git a/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/Parallel_callback.h b/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/Parallel_callback.h deleted file mode 100644 index 87925ce7f87..00000000000 --- a/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/Parallel_callback.h +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) 2018 GeometryFactory (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 - -#ifndef CGAL_PSP_INTERNAL_PARALLEL_CALLBACK_H -#define CGAL_PSP_INTERNAL_PARALLEL_CALLBACK_H - -#include - -#include - -#include - -namespace CGAL { -namespace Point_set_processing_3 { -namespace internal { - -class Parallel_callback -{ - const std::function& m_callback; - cpp11::atomic* m_advancement; - cpp11::atomic* m_interrupted; - std::size_t m_size; - bool m_creator; - cpp11::thread* m_thread; - - // assignment operator shouldn't be used (m_callback is const ref) - Parallel_callback& operator= (const Parallel_callback&) - { - return *this; - } - -public: - Parallel_callback (const std::function& callback, - std::size_t size, - std::size_t advancement = 0, - bool interrupted = false) - : m_callback (callback) - , m_advancement (new cpp11::atomic()) - , m_interrupted (new cpp11::atomic()) - , m_size (size) - , m_creator (true) - , m_thread (nullptr) - { - // cpp11::atomic only has default constructor, initialization done in two steps - *m_advancement = advancement; - *m_interrupted = interrupted; - if (m_callback) - m_thread = new cpp11::thread (*this); - } - - Parallel_callback (const Parallel_callback& other) - : m_callback (other.m_callback) - , m_advancement (other.m_advancement) - , m_interrupted (other.m_interrupted) - , m_size (other.m_size) - , m_creator (false) - , m_thread (nullptr) - { - - } - - - ~Parallel_callback () - { - if (m_creator) - { - delete m_advancement; - delete m_interrupted; - } - if (m_thread != nullptr) - delete m_thread; - } - - cpp11::atomic& advancement() { return *m_advancement; } - cpp11::atomic& interrupted() { return *m_interrupted; } - void join() - { - if (m_thread != nullptr) - m_thread->join(); - } - - void operator()() - { - while (*m_advancement != m_size) - { - if (!m_callback (*m_advancement / double(m_size))) - *m_interrupted = true; - if (*m_interrupted) - return; - cpp11::sleep_for (0.00001); - } - m_callback (1.); - } -}; - -} // namespace internal -} // namespace Point_set_processing_3 -} // namespace CGAL - -#endif // CGAL_PSP_INTERNAL_PARALLEL_CALLBACK_H diff --git a/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/neighbor_query.h b/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/neighbor_query.h deleted file mode 100644 index 1905a0dcd3d..00000000000 --- a/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/neighbor_query.h +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) 2019 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 - -#ifndef CGAL_PSP_INTERNAL_NEIGHBOR_QUERY_H -#define CGAL_PSP_INTERNAL_NEIGHBOR_QUERY_H - -#include - -#include -#include -#include -#include - -#include - -namespace CGAL { -namespace Point_set_processing_3 { -namespace internal { - -struct Maximum_points_reached_exception : public std::exception { }; - -template -void neighbor_query (const Point& query, - const CGAL::Kd_tree& tree, - unsigned int k, - FT neighbor_radius, - PointContainer& points) -{ - typedef typename CGAL::Orthogonal_k_neighbor_search Neighbor_search; - typedef typename Neighbor_search::iterator Search_iterator; - typedef CGAL::Fuzzy_sphere Sphere; - - if (neighbor_radius != FT(0)) - { - Sphere fs (query, neighbor_radius, 0, tree.traits()); - - // if k=0, no limit on the number of neighbors returned - if (k == 0) - k = (std::numeric_limits::max)(); - - try - { - std::function back_insert_with_limit - = [&](const Point& point) -> void - { - points.push_back (point); - if (points.size() == k) - throw Maximum_points_reached_exception(); - }; - - auto function_output_iterator - = boost::make_function_output_iterator (back_insert_with_limit); - - tree.search (function_output_iterator, fs); - } - catch (const Maximum_points_reached_exception&) - { } - - // Fallback, if less than 3 points are return, search for the 3 - // first points - if (points.size() < 3) - k = 3; - // Else, no need to search for K nearest neighbors - else - k = 0; - } - - if (k != 0) - { - // Gather set of (k+1) neighboring points. - // Perform k+1 queries (as in point set, the query point is - // output first). Search may be aborted if k is greater - // than number of input points. - points.reserve(k+1); - Neighbor_search search(tree,query,k+1); - Search_iterator search_iterator = search.begin(); - unsigned int i; - for(i=0;i<(k+1);i++) - { - if(search_iterator == search.end()) - break; // premature ending - points.push_back(search_iterator->first); - search_iterator++; - } - CGAL_point_set_processing_precondition(points.size() >= 1); - } -} - -} } } // namespace CGAL::Point_set_processing_3::internal - -#endif // CGAL_PSP_INTERNAL_NEIGHBOR_QUERY_H diff --git a/Point_set_processing_3/include/CGAL/bilateral_smooth_point_set.h b/Point_set_processing_3/include/CGAL/bilateral_smooth_point_set.h index 35fce6f1992..cd208ce0b41 100644 --- a/Point_set_processing_3/include/CGAL/bilateral_smooth_point_set.h +++ b/Point_set_processing_3/include/CGAL/bilateral_smooth_point_set.h @@ -17,18 +17,19 @@ #include #include -#include -#include -#include +#include +#include +#include #include #include -#include #include #include #include #include +#include + #include #include #include @@ -38,23 +39,6 @@ #include #include -#ifdef CGAL_LINKED_WITH_TBB - -#include -#include -#include -#include -#include -#endif // CGAL_LINKED_WITH_TBB - -// Default allocator: use TBB allocators if available -#ifdef CGAL_LINKED_WITH_TBB -# define CGAL_PSP3_DEFAULT_ALLOCATOR tbb::scalable_allocator -#else // CGAL_LINKED_WITH_TBB -# define CGAL_PSP3_DEFAULT_ALLOCATOR std::allocator -#endif // CGAL_LINKED_WITH_TBB - - //#define CGAL_PSP3_VERBOSE namespace CGAL { @@ -66,47 +50,6 @@ namespace CGAL { namespace bilateral_smooth_point_set_internal{ -// Item in the Kd-tree: position (Point_3) + index -template -class Kd_tree_element : public Point_with_normal_3 -{ -public: - unsigned int index; - - // basic geometric types - typedef typename CGAL::Origin Origin; - typedef CGAL::Point_with_normal_3 Base; - - Kd_tree_element(const Origin& o = ORIGIN, unsigned int id=0) - : Base(o), index(id) - {} - Kd_tree_element(const Base& p, unsigned int id=0) - : Base(p), index(id) - {} - Kd_tree_element(const Kd_tree_element& other) - : Base(other), index(other.index) - {} - - Kd_tree_element& operator=(const Kd_tree_element&)=default; -}; - - -// Helper class for the Kd-tree -template -class Kd_tree_gt : public Kernel -{ -public: - typedef Kd_tree_element Point_3; -}; - -template -class Kd_tree_traits : public CGAL::Search_traits_3 > -{ -public: - typedef typename Kernel::Point_3 PointType; -}; - - /// Compute bilateral projection for each point /// according to their KNN neighborhood points /// @@ -117,12 +60,14 @@ public: /// /// @return -template -CGAL::Point_with_normal_3 +template +std::pair compute_denoise_projection( - const CGAL::Point_with_normal_3& query, ///< 3D point to project - const std::vector, - CGAL_PSP3_DEFAULT_ALLOCATOR > >& neighbor_pwns, // + const typename PointRange::iterator::value_type& vt, + PointMap point_map, + VectorMap normal_map, + const std::vector& neighbor_pwns, typename Kernel::FT radius, ///< accept neighborhood radius typename Kernel::FT sharpness_angle ///< control sharpness(0-90) ) @@ -133,7 +78,6 @@ compute_denoise_projection( // basic geometric types typedef typename Kernel::FT FT; - typedef CGAL::Point_with_normal_3 Pwn; typedef typename Kernel::Vector_3 Vector; typedef typename Kernel::Point_3 Point; @@ -148,23 +92,21 @@ compute_denoise_projection( FT cos_sigma = cos(sharpness_angle * CGAL_PI / 180.0); FT sharpness_bandwidth = std::pow((CGAL::max)(1e-8, 1 - cos_sigma), 2); - typename std::vector >::const_iterator - pwn_iter = neighbor_pwns.begin(); - for (; pwn_iter != neighbor_pwns.end(); ++pwn_iter) + for (typename PointRange::iterator it : neighbor_pwns) { - const Point& np = pwn_iter->position(); - const Vector& nn = pwn_iter->normal(); + const Point& np = get(point_map, *it); + const Vector& nn = get(normal_map, *it); - FT dist2 = CGAL::squared_distance(query.position(), np); + FT dist2 = CGAL::squared_distance(get(point_map, vt), np); if (dist2 < radius2) { FT theta = std::exp(dist2 * iradius16); - FT psi = std::exp(-std::pow(1 - query.normal() * nn, 2) + FT psi = std::exp(-std::pow(1 - get(normal_map, vt) * nn, 2) / sharpness_bandwidth); weight = theta * psi; - project_dist_sum += ((query.position() - np) * nn) * weight; + project_dist_sum += ((get(point_map, vt) - np) * nn) * weight; project_weight_sum += weight; normal_sum = normal_sum + nn * weight; } @@ -173,10 +115,10 @@ compute_denoise_projection( Vector update_normal = normal_sum / project_weight_sum; update_normal = update_normal / sqrt(update_normal.squared_length()); - Point update_point = query.position() - update_normal * + Point update_point = get(point_map, vt) - update_normal * (project_dist_sum / project_weight_sum); - return Pwn(update_point, update_normal); + return std::make_pair (update_point, update_normal); } /// Computes max-spacing of one query point from K nearest neighbors. @@ -187,41 +129,30 @@ compute_denoise_projection( /// @tparam Tree KD-tree. /// /// @return max spacing. -template < typename Kernel, - typename Tree > -typename Kernel::FT +template +typename NeighborQuery::Kernel::FT compute_max_spacing( - const CGAL::Point_with_normal_3& query, ///< 3D point - Tree& tree, ///< KD-tree + const typename NeighborQuery::value_type& vt, + typename NeighborQuery::Point_map point_map, + NeighborQuery& neighbor_query, ///< KD-tree unsigned int k) ///< number of neighbors { // basic geometric types + typedef typename NeighborQuery::Kernel Kernel; typedef typename Kernel::FT FT; - typedef CGAL::Point_with_normal_3 Pwn; - - // types for K nearest neighbors search - typedef bilateral_smooth_point_set_internal::Kd_tree_traits Tree_traits; - typedef CGAL::Orthogonal_k_neighbor_search Neighbor_search; - typedef typename Neighbor_search::iterator Search_iterator; // performs k + 1 queries (if unique the query point is // output first). search may be aborted when k is greater // than number of input points - Neighbor_search search(tree,query,k+1); - Search_iterator search_iterator = search.begin(); - ++search_iterator; FT max_distance = (FT)0.0; - unsigned int i; - for(i = 0; i < (k+1) ; ++i) - { - if(search_iterator == search.end()) - break; // premature ending - - Pwn pwn = search_iterator->first; - double dist2 = CGAL::squared_distance(query.position(), pwn.position()); - max_distance = (CGAL::max)(dist2, max_distance); - ++search_iterator; - } + neighbor_query.get_iterators + (get(point_map, vt), k, (FT)(0.0), + boost::make_function_output_iterator + ([&](const typename NeighborQuery::input_iterator& it) + { + double dist2 = CGAL::squared_distance (get(point_map, vt), get(point_map, *it)); + max_distance = (CGAL::max)(dist2, max_distance); + })); // output max spacing return std::sqrt(max_distance); @@ -231,102 +162,6 @@ compute_max_spacing( /// \endcond -#ifdef CGAL_LINKED_WITH_TBB -/// \cond SKIP_IN_MANUAL -/// This is for parallelization of function: bilateral_smooth_point_set() -template -class Compute_pwns_neighbors -{ - typedef typename CGAL::Point_with_normal_3 Pwn; - typedef typename std::vector > Pwns; - typedef typename std::vector > - Pwns_neighbors; - typedef typename Kernel::FT FT; - - unsigned int m_k; - FT m_neighbor_radius; - const Tree & m_tree; - const Pwns & m_pwns; - Pwns_neighbors & m_pwns_neighbors; - cpp11::atomic& advancement; - cpp11::atomic& interrupted; - -public: - Compute_pwns_neighbors(unsigned int k, FT neighbor_radius, const Tree &tree, - const Pwns &pwns, Pwns_neighbors &neighbors, - cpp11::atomic& advancement, - cpp11::atomic& interrupted) - : m_k(k), m_neighbor_radius (neighbor_radius), m_tree(tree) - , m_pwns(pwns), m_pwns_neighbors(neighbors) - , advancement (advancement), interrupted (interrupted) {} - - void operator() ( const tbb::blocked_range& r ) const - { - for (size_t i = r.begin(); i!=r.end(); i++) - { - if (interrupted) - break; - - CGAL::Point_set_processing_3::internal::neighbor_query - (m_pwns[i], m_tree, m_k, m_neighbor_radius, m_pwns_neighbors[i]); - - ++ advancement; - } - } -}; -/// \endcond - -/// \cond SKIP_IN_MANUAL -/// This is for parallelization of function: compute_denoise_projection() -template -class Pwn_updater -{ - typedef typename CGAL::Point_with_normal_3 Pwn; - typedef typename std::vector > Pwns; - typedef typename Kernel::FT FT; - - FT sharpness_angle; - FT radius; - Pwns* pwns; - Pwns* update_pwns; - std::vector >* pwns_neighbors; - cpp11::atomic& advancement; - cpp11::atomic& interrupted; - -public: - Pwn_updater(FT sharpness, - FT r, - Pwns *in, - Pwns *out, - std::vector >* neighbors, - cpp11::atomic& advancement, - cpp11::atomic& interrupted): - sharpness_angle(sharpness), - radius(r), - pwns(in), - update_pwns(out), - pwns_neighbors(neighbors), - advancement (advancement), - interrupted (interrupted) {} - - void operator() ( const tbb::blocked_range& r ) const - { - for (size_t i = r.begin(); i != r.end(); ++i) - { - if (interrupted) - break; - (*update_pwns)[i] = bilateral_smooth_point_set_internal:: - compute_denoise_projection((*pwns)[i], - (*pwns_neighbors)[i], - radius, - sharpness_angle); - ++ advancement; - } - } -}; -/// \endcond -#endif // CGAL_LINKED_WITH_TBB - // ---------------------------------------------------------------------------- // Public section @@ -400,16 +235,18 @@ bilateral_smooth_point_set( using parameters::get_parameter; // basic geometric types + typedef typename PointRange::iterator iterator; + typedef typename iterator::value_type value_type; typedef typename CGAL::GetPointMap::type PointMap; typedef typename Point_set_processing_3::GetNormalMap::type NormalMap; typedef typename Point_set_processing_3::GetK::Kernel Kernel; + typedef typename Kernel::Point_3 Point_3; + typedef typename Kernel::Vector_3 Vector_3; CGAL_static_assertion_msg(!(boost::is_same::NoMap>::value), "Error: no normal map"); - typedef typename CGAL::Point_with_normal_3 Pwn; - typedef typename std::vector > Pwns; typedef typename Kernel::FT FT; double sharpness_angle = choose_parameter(get_parameter(np, internal_np::sharpness_angle), 30.); @@ -420,43 +257,20 @@ bilateral_smooth_point_set( CGAL_point_set_processing_precondition(k > 1); // types for K nearest neighbors search structure - typedef bilateral_smooth_point_set_internal:: - Kd_tree_element Kd_tree_element; - typedef bilateral_smooth_point_set_internal::Kd_tree_traits Tree_traits; - typedef CGAL::Orthogonal_k_neighbor_search Neighbor_search; - typedef typename Neighbor_search::Tree Tree; + typedef Point_set_processing_3::internal::Neighbor_query Neighbor_query; PointMap point_map = choose_parameter(get_parameter(np, internal_np::point_map)); NormalMap normal_map = choose_parameter(get_parameter(np, internal_np::normal_map)); FT neighbor_radius = choose_parameter(get_parameter(np, internal_np::neighbor_radius), FT(0)); - // copy points and normals - Pwns pwns; - for(typename PointRange::iterator it = points.begin(); it != points.end(); ++it) - { - typename boost::property_traits::reference p = get(point_map, *it); - typename boost::property_traits::reference n = get(normal_map, *it); - CGAL_point_set_processing_precondition(n.squared_length() > 1e-10); - - pwns.push_back(Pwn(p, n)); - } - - std::size_t nb_points = pwns.size(); + std::size_t nb_points = points.size(); #ifdef CGAL_PSP3_VERBOSE std::cout << "Initialization and compute max spacing: " << std::endl; #endif // initiate a KD-tree search for points - std::vector > treeElements; - treeElements.reserve(pwns.size()); - typename std::vector >::iterator - pwn_iter = pwns.begin(); - for (unsigned int i = 0; pwn_iter != pwns.end(); ++pwn_iter) - { - treeElements.push_back(Kd_tree_element(*pwn_iter, i)); - } - Tree tree(treeElements.begin(), treeElements.end()); + Neighbor_query neighbor_query (points, point_map); + // Guess spacing #ifdef CGAL_PSP3_VERBOSE CGAL::Real_timer task_timer; @@ -464,10 +278,10 @@ bilateral_smooth_point_set( #endif FT guess_neighbor_radius = 0.0; - for(pwn_iter = pwns.begin(); pwn_iter != pwns.end(); ++pwn_iter) + for (const value_type& vt : points) { FT max_spacing = bilateral_smooth_point_set_internal:: - compute_max_spacing(*pwn_iter, tree, k); + compute_max_spacing (vt, point_map, neighbor_query, k); guess_neighbor_radius = (CGAL::max)(max_spacing, guess_neighbor_radius); } @@ -486,49 +300,40 @@ bilateral_smooth_point_set( task_timer.start(); #endif // compute all neighbors - std::vector > pwns_neighbors; + typedef std::vector iterators; + std::vector pwns_neighbors; pwns_neighbors.resize(nb_points); -#ifndef CGAL_LINKED_WITH_TBB - CGAL_static_assertion_msg (!(boost::is_convertible::value), - "Parallel_tag is enabled but TBB is unavailable."); -#else - if (boost::is_convertible::value) - { - Point_set_processing_3::internal::Parallel_callback - parallel_callback (callback, 2 * nb_points); + Point_set_processing_3::internal::Callback_wrapper + callback_wrapper (callback, 2 * nb_points); - Compute_pwns_neighbors f(k, neighbor_radius, tree, pwns, pwns_neighbors, - parallel_callback.advancement(), - parallel_callback.interrupted()); - tbb::parallel_for(tbb::blocked_range(0, nb_points), f); + typedef boost::zip_iterator::iterator> > Zip_iterator; - bool interrupted = parallel_callback.interrupted(); + CGAL::for_each + (CGAL::make_range (boost::make_zip_iterator (boost::make_tuple (points.begin(), pwns_neighbors.begin())), + boost::make_zip_iterator (boost::make_tuple (points.end(), pwns_neighbors.end()))), + [&](const typename Zip_iterator::reference& t) + { + if (callback_wrapper.interrupted()) + return false; - // We interrupt by hand as counter only goes halfway and won't terminate by itself - parallel_callback.interrupted() = true; - parallel_callback.join(); + neighbor_query.get_iterators (get(point_map, get<0>(t)), k, neighbor_radius, + std::back_inserter (get<1>(t))); - // If interrupted during this step, nothing is computed, we return NaN - if (interrupted) - return std::numeric_limits::quiet_NaN(); - } - else -#endif - { - typename std::vector >::iterator - pwns_iter = pwns_neighbors.begin(); + ++ callback_wrapper.advancement(); - std::size_t nb = 0; - for(pwn_iter = pwns.begin(); pwn_iter != pwns.end(); ++pwn_iter, ++pwns_iter, ++ nb) - { - CGAL::Point_set_processing_3::internal::neighbor_query - (*pwn_iter, tree, k, neighbor_radius, *pwns_iter); + return true; + }); - if (callback && !callback ((nb+1) / double(2. * nb_points))) - return std::numeric_limits::quiet_NaN(); - } - } + bool interrupted = callback_wrapper.interrupted(); + + // We interrupt by hand as counter only goes halfway and won't terminate by itself + callback_wrapper.interrupted() = true; + callback_wrapper.join(); + + // If interrupted during this step, nothing is computed, we return NaN + if (interrupted) + return std::numeric_limits::quiet_NaN(); #ifdef CGAL_PSP3_VERBOSE task_timer.stop(); @@ -541,53 +346,45 @@ bilateral_smooth_point_set( task_timer.start(); #endif // update points and normals - Pwns update_pwns(nb_points); + std::vector > update_pwns(nb_points); -#ifdef CGAL_LINKED_WITH_TBB - if(boost::is_convertible::value) - { - Point_set_processing_3::internal::Parallel_callback - parallel_callback (callback, 2 * nb_points, nb_points); + callback_wrapper.reset (2 * nb_points, nb_points); - //tbb::task_scheduler_init init(4); - tbb::blocked_range block(0, nb_points); - Pwn_updater pwn_updater(sharpness_angle, - guess_neighbor_radius, - &pwns, - &update_pwns, - &pwns_neighbors, - parallel_callback.advancement(), - parallel_callback.interrupted()); - tbb::parallel_for(block, pwn_updater); + typedef boost::zip_iterator + ::iterator, + typename std::vector >::iterator> > Zip_iterator_2; - parallel_callback.join(); - // If interrupted during this step, nothing is computed, we return NaN - if (parallel_callback.interrupted()) - return std::numeric_limits::quiet_NaN(); - } - else -#endif // CGAL_LINKED_WITH_TBB - { - std::size_t nb = nb_points; + CGAL::for_each + (CGAL::make_range (boost::make_zip_iterator (boost::make_tuple + (points.begin(), pwns_neighbors.begin(), update_pwns.begin())), + boost::make_zip_iterator (boost::make_tuple + (points.end(), pwns_neighbors.end(), update_pwns.end()))), + [&](const typename Zip_iterator_2::reference& t) + { + if (callback_wrapper.interrupted()) + return false; + + get<2>(t) = bilateral_smooth_point_set_internal:: + compute_denoise_projection + (get<0>(t), + point_map, normal_map, + get<1>(t), + guess_neighbor_radius, + sharpness_angle); + + ++ callback_wrapper.advancement(); + + return true; + }); + + callback_wrapper.join(); + + // If interrupted during this step, nothing is computed, we return NaN + if (callback_wrapper.interrupted()) + return std::numeric_limits::quiet_NaN(); - typename std::vector >::iterator - update_iter = update_pwns.begin(); - typename std::vector >::iterator - neighbor_iter = pwns_neighbors.begin(); - for(pwn_iter = pwns.begin(); pwn_iter != pwns.end(); - ++pwn_iter, ++update_iter, ++neighbor_iter, ++ nb) - { - *update_iter = bilateral_smooth_point_set_internal:: - compute_denoise_projection - (*pwn_iter, - *neighbor_iter, - guess_neighbor_radius, - sharpness_angle); - if (callback && !callback ((nb+1) / double(2. * nb_points))) - return std::numeric_limits::quiet_NaN(); - } - } #ifdef CGAL_PSP3_VERBOSE task_timer.stop(); memory = CGAL::Memory_sizer().virtual_size(); @@ -596,13 +393,13 @@ bilateral_smooth_point_set( #endif // save results FT sum_move_error = 0; - typename PointRange::iterator it = points.begin(); - for(unsigned int i = 0 ; it != points.end(); ++it, ++i) + std::size_t nb = 0; + for (value_type& vt : points) { - typename boost::property_traits::reference p = get(point_map, *it); - sum_move_error += CGAL::squared_distance(p, update_pwns[i].position()); - put (point_map, *it, update_pwns[i].position()); - put (normal_map, *it, update_pwns[i].normal()); + sum_move_error += CGAL::squared_distance(get(point_map, vt), update_pwns[nb].first); + put (point_map, vt, update_pwns[nb].first); + put (normal_map, vt, update_pwns[nb].second); + ++ nb; } return sum_move_error / nb_points; diff --git a/Point_set_processing_3/include/CGAL/compute_average_spacing.h b/Point_set_processing_3/include/CGAL/compute_average_spacing.h index c9f86dee613..968c60f1242 100644 --- a/Point_set_processing_3/include/CGAL/compute_average_spacing.h +++ b/Point_set_processing_3/include/CGAL/compute_average_spacing.h @@ -18,7 +18,9 @@ #include #include -#include +#include +#include +#include #include #include #include @@ -27,15 +29,12 @@ #include #include +#include + #include #include -#ifdef CGAL_LINKED_WITH_TBB -#include -#include -#include -#include -#endif // CGAL_LINKED_WITH_TBB + #ifdef DOXYGEN_RUNNING #define CGAL_BGL_NP_TEMPLATE_PARAMETERS NamedParameters @@ -60,81 +59,37 @@ namespace internal { /// @tparam Tree KD-tree. /// /// @return average spacing (scalar). -template < typename Kernel, - typename Tree > -typename Kernel::FT -compute_average_spacing(const typename Kernel::Point_3& query, ///< 3D point whose spacing we want to compute - const Tree& tree, ///< KD-tree +template +typename NeighborQuery::Kernel::FT +compute_average_spacing(const typename NeighborQuery::Kernel::Point_3& query, ///< 3D point whose spacing we want to compute + const NeighborQuery& neighbor_query, ///< KD-tree unsigned int k) ///< number of neighbors { // basic geometric types + typedef typename NeighborQuery::Kernel Kernel; typedef typename Kernel::FT FT; typedef typename Kernel::Point_3 Point; - // types for K nearest neighbors search - typedef Search_traits_3 Tree_traits; - typedef Orthogonal_k_neighbor_search Neighbor_search; - typedef typename Neighbor_search::iterator Search_iterator; // performs k + 1 queries (if unique the query point is // output first). search may be aborted when k is greater // than number of input points - Neighbor_search search(tree,query,k+1); - Search_iterator search_iterator = search.begin(); FT sum_distances = (FT)0.0; - unsigned int i; - for(i=0;i<(k+1);i++) - { - if(search_iterator == search.end()) - break; // premature ending - - Point p = search_iterator->first; - sum_distances += std::sqrt(CGAL::squared_distance(query,p)); - search_iterator++; - } + unsigned int i = 0; + neighbor_query.get_points + (query, k, 0, + boost::make_function_output_iterator + ([&](const Point& p) + { + sum_distances += std::sqrt(CGAL::squared_distance (query,p)); + ++ i; + })); // output average spacing return sum_distances / (FT)i; } -#ifdef CGAL_LINKED_WITH_TBB - template - class Compute_average_spacings { - typedef typename Kernel::Point_3 Point; - typedef typename Kernel::FT FT; - const Tree& tree; - const unsigned int k; - const std::vector& input; - std::vector& output; - cpp11::atomic& advancement; - cpp11::atomic& interrupted; - - public: - Compute_average_spacings(Tree& tree, unsigned int k, std::vector& points, - std::vector& output, - cpp11::atomic& advancement, - cpp11::atomic& interrupted) - : tree(tree), k (k), input (points), output (output) - , advancement (advancement) - , interrupted (interrupted) - { } - - void operator()(const tbb::blocked_range& r) const - { - for( std::size_t i = r.begin(); i != r.end(); ++i) - { - if (interrupted) - break; - - output[i] = CGAL::internal::compute_average_spacing(input[i], tree, k); - ++ advancement; - } - } - - }; -#endif // CGAL_LINKED_WITH_TBB - } /* namespace internal */ /// \endcond @@ -195,20 +150,17 @@ compute_average_spacing( using parameters::get_parameter; // basic geometric types + typedef typename PointRange::const_iterator iterator; typedef typename CGAL::GetPointMap::const_type PointMap; typedef typename Point_set_processing_3::GetK::Kernel Kernel; - typedef typename Kernel::Point_3 Point; - - PointMap point_map = choose_parameter(get_parameter(np, internal_np::point_map)); + PointMap point_map = choose_parameter(get_parameter(np, internal_np::point_map), PointMap()); const std::function& callback = choose_parameter(get_parameter(np, internal_np::callback), std::function()); // types for K nearest neighbors search structure typedef typename Kernel::FT FT; - typedef Search_traits_3 Tree_traits; - typedef Orthogonal_k_neighbor_search Neighbor_search; - typedef typename Neighbor_search::Tree Tree; + typedef Point_set_processing_3::internal::Neighbor_query Neighbor_query; // precondition: at least one element in the container. // to fix: should have at least three distinct points @@ -219,60 +171,46 @@ compute_average_spacing( CGAL_point_set_processing_precondition(k >= 2); // Instanciate a KD-tree search. - // Note: We have to convert each input iterator to Point_3. - std::vector kd_tree_points; - for(typename PointRange::const_iterator it = points.begin(); it != points.end(); it++) - kd_tree_points.push_back(get(point_map, *it)); - Tree tree(kd_tree_points.begin(), kd_tree_points.end()); + Neighbor_query neighbor_query (points, point_map); // iterate over input points, compute and output normal // vectors (already normalized) FT sum_spacings = (FT)0.0; std::size_t nb = 0; + std::size_t nb_points = std::distance(points.begin(), points.end()); -#ifndef CGAL_LINKED_WITH_TBB - CGAL_static_assertion_msg (!(boost::is_convertible::value), - "Parallel_tag is enabled but TBB is unavailable."); -#else - if (boost::is_convertible::value) - { - Point_set_processing_3::internal::Parallel_callback - parallel_callback (callback, kd_tree_points.size()); + Point_set_processing_3::internal::Callback_wrapper + callback_wrapper (callback, nb_points); - std::vector spacings (kd_tree_points.size (), -1); - CGAL::internal::Compute_average_spacings - f (tree, k, kd_tree_points, spacings, - parallel_callback.advancement(), - parallel_callback.interrupted()); - tbb::parallel_for(tbb::blocked_range(0, kd_tree_points.size ()), f); + std::vector spacings (nb_points, -1); - for (unsigned int i = 0; i < spacings.size (); ++ i) - if (spacings[i] >= 0.) - { - sum_spacings += spacings[i]; - ++ nb; - } + typedef boost::zip_iterator::iterator> > Zip_iterator; - parallel_callback.join(); - } - else -#endif + CGAL::for_each + (CGAL::make_range (boost::make_zip_iterator (boost::make_tuple (points.begin(), spacings.begin())), + boost::make_zip_iterator (boost::make_tuple (points.end(), spacings.end()))), + [&](const typename Zip_iterator::reference& t) { - for(typename PointRange::const_iterator it = points.begin(); it != points.end(); it++, nb++) - { - sum_spacings += internal::compute_average_spacing( - get(point_map,*it), - tree,k); - if (callback && !callback ((nb+1) / double(kd_tree_points.size()))) - { - ++ nb; - break; - } - } - } + if (callback_wrapper.interrupted()) + return false; + + get<1>(t) = CGAL::internal::compute_average_spacing + (get(point_map, get<0>(t)), neighbor_query, k); + ++ callback_wrapper.advancement(); + + return true; + }); + + for (unsigned int i = 0; i < spacings.size (); ++ i) + if (spacings[i] >= 0.) + { + sum_spacings += spacings[i]; + ++ nb; + } + callback_wrapper.join(); // return average spacing - return sum_spacings / (FT)(nb); + return sum_spacings / (FT)(nb); } /// \cond SKIP_IN_MANUAL diff --git a/Point_set_processing_3/include/CGAL/jet_estimate_normals.h b/Point_set_processing_3/include/CGAL/jet_estimate_normals.h index 16e3e131d29..b70bb6a7dda 100644 --- a/Point_set_processing_3/include/CGAL/jet_estimate_normals.h +++ b/Point_set_processing_3/include/CGAL/jet_estimate_normals.h @@ -17,9 +17,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include #include #include @@ -32,13 +32,6 @@ #include #include -#ifdef CGAL_LINKED_WITH_TBB -#include -#include -#include -#include -#endif // CGAL_LINKED_WITH_TBB - namespace CGAL { @@ -58,18 +51,16 @@ namespace internal { /// @tparam Tree KD-tree. /// /// @return Computed normal. Orientation is random. -template < typename Kernel, - typename SvdTraits, - typename Tree -> -typename Kernel::Vector_3 -jet_estimate_normal(const typename Kernel::Point_3& query, ///< point to compute the normal at - Tree& tree, ///< KD-tree +template +typename NeighborQuery::Kernel::Vector_3 +jet_estimate_normal(const typename NeighborQuery::Point_3& query, ///< point to compute the normal at + const NeighborQuery& neighbor_query, ///< KD-tree unsigned int k, ///< number of neighbors - typename Kernel::FT neighbor_radius, + typename NeighborQuery::FT neighbor_radius, unsigned int degree_fitting) { // basic geometric types + typedef typename NeighborQuery::Kernel Kernel; typedef typename Kernel::Point_3 Point; // types for jet fitting @@ -79,8 +70,7 @@ jet_estimate_normal(const typename Kernel::Point_3& query, ///< point to compute typedef typename Monge_jet_fitting::Monge_form Monge_form; std::vector points; - CGAL::Point_set_processing_3::internal::neighbor_query - (query, tree, k, neighbor_radius, points); + neighbor_query.get_points (query, k, neighbor_radius, std::back_inserter(points)); // performs jet fitting Monge_jet_fitting monge_fit; @@ -92,49 +82,6 @@ jet_estimate_normal(const typename Kernel::Point_3& query, ///< point to compute return monge_form.normal_direction(); } -#ifdef CGAL_LINKED_WITH_TBB - template - class Jet_estimate_normals { - typedef typename Kernel::FT FT; - typedef typename Kernel::Point_3 Point; - typedef typename Kernel::Vector_3 Vector; - const Tree& tree; - const unsigned int k; - const FT neighbor_radius; - const unsigned int degree_fitting; - const std::vector& input; - std::vector& output; - cpp11::atomic& advancement; - cpp11::atomic& interrupted; - - public: - Jet_estimate_normals(Tree& tree, unsigned int k, FT neighbor_radius, - std::vector& points, - unsigned int degree_fitting, std::vector& output, - cpp11::atomic& advancement, - cpp11::atomic& interrupted) - : tree(tree), k (k), neighbor_radius (neighbor_radius) - , degree_fitting (degree_fitting), input (points), output (output) - , advancement (advancement) - , interrupted (interrupted) - { } - - void operator()(const tbb::blocked_range& r) const - { - for( std::size_t i = r.begin(); i != r.end(); ++i) - { - if (interrupted) - break; - output[i] = CGAL::internal::jet_estimate_normal(input[i], tree, k, neighbor_radius, degree_fitting); - ++ advancement; - } - } - - }; -#endif // CGAL_LINKED_WITH_TBB - - - } /* namespace internal */ /// \endcond @@ -202,6 +149,8 @@ jet_estimate_normals( CGAL_TRACE("Calls jet_estimate_normals()\n"); // basic geometric types + typedef typename PointRange::iterator iterator; + typedef typename iterator::value_type value_type; typedef typename CGAL::GetPointMap::type PointMap; typedef typename Point_set_processing_3::GetNormalMap::type NormalMap; typedef typename Point_set_processing_3::GetK::Kernel Kernel; @@ -223,15 +172,8 @@ jet_estimate_normals( const std::function& callback = choose_parameter(get_parameter(np, internal_np::callback), std::function()); - typedef typename Kernel::Point_3 Point; - - // Input points types - typedef typename boost::property_traits::value_type Vector; - // types for K nearest neighbors search structure - typedef typename CGAL::Search_traits_3 Tree_traits; - typedef typename CGAL::Orthogonal_k_neighbor_search Neighbor_search; - typedef typename Neighbor_search::Tree Tree; + typedef Point_set_processing_3::internal::Neighbor_query Neighbor_query; // precondition: at least one element in the container. // to fix: should have at least three distinct points @@ -244,60 +186,32 @@ jet_estimate_normals( std::size_t memory = CGAL::Memory_sizer().virtual_size(); CGAL_TRACE(" %ld Mb allocated\n", memory>>20); CGAL_TRACE(" Creates KD-tree\n"); - typename PointRange::iterator it; - - // Instanciate a KD-tree search. - // Note: We have to convert each input iterator to Point_3. - std::vector kd_tree_points; - for(it = points.begin(); it != points.end(); it++) - kd_tree_points.push_back(get(point_map, *it)); - Tree tree(kd_tree_points.begin(), kd_tree_points.end()); + Neighbor_query neighbor_query (points, point_map); memory = CGAL::Memory_sizer().virtual_size(); CGAL_TRACE(" %ld Mb allocated\n", memory>>20); CGAL_TRACE(" Computes normals\n"); - // iterate over input points, compute and output normal - // vectors (already normalized) -#ifndef CGAL_LINKED_WITH_TBB - CGAL_static_assertion_msg (!(boost::is_convertible::value), - "Parallel_tag is enabled but TBB is unavailable."); -#else - if (boost::is_convertible::value) - { - Point_set_processing_3::internal::Parallel_callback - parallel_callback (callback, kd_tree_points.size()); + std::size_t nb_points = points.size(); - std::vector normals (kd_tree_points.size (), - CGAL::NULL_VECTOR); - CGAL::internal::Jet_estimate_normals - f (tree, k, neighbor_radius, - kd_tree_points, degree_fitting, normals, - parallel_callback.advancement(), - parallel_callback.interrupted()); - tbb::parallel_for(tbb::blocked_range(0, kd_tree_points.size ()), f); - std::size_t i = 0; - for(it = points.begin(); it != points.end(); ++ it, ++ i) - if (normals[i] != CGAL::NULL_VECTOR) - put (normal_map, *it, normals[i]); + Point_set_processing_3::internal::Callback_wrapper + callback_wrapper (callback, nb_points); - parallel_callback.join(); - } - else -#endif + CGAL::for_each + (points, + [&](value_type& vt) { - std::size_t nb = 0; - for(it = points.begin(); it != points.end(); it++, ++ nb) - { - Vector normal = internal::jet_estimate_normal( - get(point_map,*it), - tree, k, neighbor_radius, degree_fitting); + if (callback_wrapper.interrupted()) + return false; - put(normal_map, *it, normal); // normal_map[it] = normal - if (callback && !callback ((nb+1) / double(kd_tree_points.size()))) - break; - } - } + put (normal_map, vt, + CGAL::internal::jet_estimate_normal + (get(point_map, vt), neighbor_query, k, neighbor_radius, degree_fitting)); + ++ callback_wrapper.advancement(); + return true; + }); + + callback_wrapper.join(); memory = CGAL::Memory_sizer().virtual_size(); CGAL_TRACE(" %ld Mb allocated\n", memory>>20); CGAL_TRACE("End of jet_estimate_normals()\n"); diff --git a/Point_set_processing_3/include/CGAL/jet_smooth_point_set.h b/Point_set_processing_3/include/CGAL/jet_smooth_point_set.h index 1d089fb5c80..e787b48d18c 100644 --- a/Point_set_processing_3/include/CGAL/jet_smooth_point_set.h +++ b/Point_set_processing_3/include/CGAL/jet_smooth_point_set.h @@ -17,9 +17,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include #include #include @@ -28,16 +28,11 @@ #include #include +#include + #include #include -#ifdef CGAL_LINKED_WITH_TBB -#include -#include -#include -#include -#endif // CGAL_LINKED_WITH_TBB - namespace CGAL { @@ -57,20 +52,20 @@ namespace internal { /// @tparam Tree KD-tree. /// /// @return computed point -template -typename Kernel::Point_3 +typename NeighborQuery::Kernel::Point_3 jet_smooth_point( - const typename Kernel::Point_3& query, ///< 3D point to project - Tree& tree, ///< KD-tree + const typename NeighborQuery::Kernel::Point_3& query, ///< 3D point to project + NeighborQuery& neighbor_query, ///< KD-tree const unsigned int k, ///< number of neighbors. - typename Kernel::FT neighbor_radius, + typename NeighborQuery::Kernel::FT neighbor_radius, const unsigned int degree_fitting, const unsigned int degree_monge) { // basic geometric types + typedef typename NeighborQuery::Kernel Kernel; typedef typename Kernel::Point_3 Point; // types for jet fitting @@ -80,8 +75,7 @@ jet_smooth_point( typedef typename Monge_jet_fitting::Monge_form Monge_form; std::vector points; - CGAL::Point_set_processing_3::internal::neighbor_query - (query, tree, k, neighbor_radius, points); + neighbor_query.get_points (query, k, neighbor_radius, std::back_inserter(points)); // performs jet fitting Monge_jet_fitting monge_fit; @@ -92,50 +86,6 @@ jet_smooth_point( return monge_form.origin(); } -#ifdef CGAL_LINKED_WITH_TBB - template - class Jet_smooth_pwns { - typedef typename Kernel::Point_3 Point; - const Tree& tree; - const unsigned int k; - const typename Kernel::FT neighbor_radius; - unsigned int degree_fitting; - unsigned int degree_monge; - const std::vector& input; - std::vector& output; - cpp11::atomic& advancement; - cpp11::atomic& interrupted; - - public: - Jet_smooth_pwns (Tree& tree, unsigned int k, typename Kernel::FT neighbor_radius, - std::vector& points, - unsigned int degree_fitting, unsigned int degree_monge, std::vector& output, - cpp11::atomic& advancement, - cpp11::atomic& interrupted) - : tree(tree), k (k), neighbor_radius(neighbor_radius) - , degree_fitting (degree_fitting) - , degree_monge (degree_monge), input (points), output (output) - , advancement (advancement) - , interrupted (interrupted) - { } - - void operator()(const tbb::blocked_range& r) const - { - for( std::size_t i = r.begin(); i != r.end(); ++i) - { - if (interrupted) - break; - output[i] = CGAL::internal::jet_smooth_point(input[i], tree, k, - neighbor_radius, - degree_fitting, - degree_monge); - ++ advancement; - } - } - - }; -#endif // CGAL_LINKED_WITH_TBB - } /* namespace internal */ @@ -204,6 +154,7 @@ jet_smooth_point_set( using parameters::get_parameter; // basic geometric types + typedef typename PointRange::iterator iterator; typedef typename CGAL::GetPointMap::type PointMap; typedef typename Point_set_processing_3::GetK::Kernel Kernel; typedef typename GetSvdTraits::type SvdTraits; @@ -220,12 +171,8 @@ jet_smooth_point_set( const std::function& callback = choose_parameter(get_parameter(np, internal_np::callback), std::function()); - typedef typename Kernel::Point_3 Point; - // types for K nearest neighbors search structure - typedef typename CGAL::Search_traits_3 Tree_traits; - typedef typename CGAL::Orthogonal_k_neighbor_search Neighbor_search; - typedef typename Neighbor_search::Tree Tree; + typedef Point_set_processing_3::internal::Neighbor_query Neighbor_query; // precondition: at least one element in the container. // to fix: should have at least three distinct points @@ -235,56 +182,53 @@ jet_smooth_point_set( // precondition: at least 2 nearest neighbors CGAL_point_set_processing_precondition(k >= 2); - typename PointRange::iterator it; - // Instanciate a KD-tree search. - // Note: We have to convert each input iterator to Point_3. - std::vector kd_tree_points; - for(it = points.begin(); it != points.end(); it++) - kd_tree_points.push_back(get(point_map, *it)); - Tree tree(kd_tree_points.begin(), kd_tree_points.end()); + Neighbor_query neighbor_query (points, point_map); // Iterates over input points and mutates them. // Implementation note: the cast to Point& allows to modify only the point's position. -#ifndef CGAL_LINKED_WITH_TBB - CGAL_static_assertion_msg (!(boost::is_convertible::value), - "Parallel_tag is enabled but TBB is unavailable."); -#else - if (boost::is_convertible::value) - { - Point_set_processing_3::internal::Parallel_callback - parallel_callback (callback, kd_tree_points.size()); + std::size_t nb_points = points.size(); - std::vector mutated_points (kd_tree_points.size (), CGAL::ORIGIN); - CGAL::internal::Jet_smooth_pwns - f (tree, k, neighbor_radius, kd_tree_points, degree_fitting, degree_monge, - mutated_points, - parallel_callback.advancement(), - parallel_callback.interrupted()); - tbb::parallel_for(tbb::blocked_range(0, kd_tree_points.size ()), f); - unsigned int i = 0; - for(it = points.begin(); it != points.end(); ++ it, ++ i) - if (mutated_points[i] != CGAL::ORIGIN) - put(point_map, *it, mutated_points[i]); + Point_set_processing_3::internal::Callback_wrapper + callback_wrapper (callback, nb_points); - parallel_callback.join(); + std::vector smoothed (points.size()); - } - else -#endif + typedef boost::zip_iterator + ::iterator> > Zip_iterator; + + CGAL::for_each + (CGAL::make_range (boost::make_zip_iterator (boost::make_tuple (points.begin(), smoothed.begin())), + boost::make_zip_iterator (boost::make_tuple (points.end(), smoothed.end()))), + [&](const typename Zip_iterator::reference& t) { - std::size_t nb = 0; - for(it = points.begin(); it != points.end(); it++, ++ nb) - { - const typename boost::property_traits::reference p = get(point_map, *it); - put(point_map, *it , - internal::jet_smooth_point( - p,tree,k,neighbor_radius,degree_fitting,degree_monge) ); - if (callback && !callback ((nb+1) / double(kd_tree_points.size()))) - break; - } - } + if (callback_wrapper.interrupted()) + return false; + + get<1>(t) = CGAL::internal::jet_smooth_point + (get (point_map, get<0>(t)), neighbor_query, + k, + neighbor_radius, + degree_fitting, + degree_monge); + ++ callback_wrapper.advancement(); + + return true; + }); + + callback_wrapper.join(); + + // Finally, update points + CGAL::for_each + (CGAL::make_range (boost::make_zip_iterator (boost::make_tuple (points.begin(), smoothed.begin())), + boost::make_zip_iterator (boost::make_tuple (points.end(), smoothed.end()))), + [&](const typename Zip_iterator::reference& t) + { + put (point_map, get<0>(t), get<1>(t)); + return true; + }); } diff --git a/Point_set_processing_3/include/CGAL/mst_orient_normals.h b/Point_set_processing_3/include/CGAL/mst_orient_normals.h index 0415170bfa5..59cd06298a1 100644 --- a/Point_set_processing_3/include/CGAL/mst_orient_normals.h +++ b/Point_set_processing_3/include/CGAL/mst_orient_normals.h @@ -17,10 +17,7 @@ #include #include -#include -#include -#include -#include +#include #include #include #include @@ -296,17 +293,16 @@ mst_find_source( /// @tparam Kernel Geometric traits class. /// /// @return the Riemannian graph -template -Riemannian_graph +Riemannian_graph create_riemannian_graph( - ForwardIterator first, ///< iterator over the first input point. - ForwardIterator beyond, ///< past-the-end iterator over the input points. + PointRange& points, ///< input points PointMap point_map, ///< property map: value_type of ForwardIterator -> Point_3 NormalMap normal_map, ///< property map: value_type of ForwardIterator -> Vector_3 IndexMap index_map, ///< property map ForwardIterator -> index @@ -320,43 +316,23 @@ create_riemannian_graph( typedef typename boost::property_traits::reference Vector_ref; // Types for K nearest neighbors search structure - typedef Point_vertex_handle_3 Point_vertex_handle_3; - typedef internal::Search_traits_vertex_handle_3 Traits; - typedef Euclidean_distance_vertex_handle_3 KDistance; - typedef Orthogonal_k_neighbor_search Neighbor_search; - typedef typename Neighbor_search::Tree Tree; + typedef typename PointRange::iterator ForwardIterator; + typedef Point_set_processing_3::internal::Neighbor_query Neighbor_query; // Riemannian_graph types typedef internal::Riemannian_graph Riemannian_graph; typedef typename boost::property_map::type Riemannian_graph_weight_map; - // Precondition: at least one element in the container. - CGAL_point_set_processing_precondition(first != beyond); - // Precondition: at least 2 nearest neighbors CGAL_point_set_processing_precondition(k >= 2); // Number of input points - const std::size_t num_input_points = distance(first, beyond); + const std::size_t num_input_points = points.size(); std::size_t memory = CGAL::Memory_sizer().virtual_size(); CGAL_TRACE(" %ld Mb allocated\n", memory>>20); CGAL_TRACE(" Creates KD-tree\n"); - // Instanciate a KD-tree search. - // Notes: We have to wrap each input point by a Point_vertex_handle_3. - // The KD-tree is allocated dynamically to recover RAM as soon as possible. - std::vector kd_tree_points; kd_tree_points.reserve(num_input_points); - for (ForwardIterator it = first; it != beyond; it++) - { - - Point_ref point = get(point_map, *it); - Point_vertex_handle_3 point_wrapper(point.x(), point.y(), point.z(), it); - kd_tree_points.push_back(point_wrapper); - } - boost::shared_ptr tree( new Tree(kd_tree_points.begin(), kd_tree_points.end()) ); - - // Recover RAM - kd_tree_points.clear(); + Neighbor_query neighbor_query (points, point_map); memory = CGAL::Memory_sizer().virtual_size(); CGAL_TRACE(" %ld Mb allocated\n", memory>>20); CGAL_TRACE(" Creates Riemannian Graph\n"); @@ -369,7 +345,7 @@ create_riemannian_graph( Riemannian_graph riemannian_graph; // // add vertices - for (ForwardIterator it = first; it != beyond; it++) + for (ForwardIterator it = points.begin(); it != points.end(); it++) { typename Riemannian_graph::vertex_descriptor v = add_vertex(riemannian_graph); CGAL_point_set_processing_assertion(v == get(index_map,it)); @@ -383,16 +359,14 @@ create_riemannian_graph( // // add edges Riemannian_graph_weight_map riemannian_graph_weight_map = get(boost::edge_weight, riemannian_graph); - for (ForwardIterator it = first; it != beyond; it++) + for (ForwardIterator it = points.begin(); it != points.end(); it++) { std::size_t it_index = get(index_map,it); Vector_ref it_normal_vector = get(normal_map,*it); Point_ref point = get(point_map, *it); - Point_vertex_handle_3 point_wrapper(point.x(), point.y(), point.z(), it); - std::vector neighbor_points; - CGAL::Point_set_processing_3::internal::neighbor_query - (point_wrapper, *tree, k, neighbor_radius, neighbor_points); + std::vector neighbor_points; + neighbor_query.get_iterators (point, k, neighbor_radius, std::back_inserter(neighbor_points)); for (std::size_t i = 0; i < neighbor_points.size(); ++ i) { @@ -665,7 +639,7 @@ mst_orient_normals( if (boost::is_same::NoMap>::value) - riemannian_graph = create_riemannian_graph(points.begin(), points.end(), + riemannian_graph = create_riemannian_graph(points, point_map, normal_map, index_map, Default_constrained_map (mst_find_source(points.begin(), points.end(), @@ -675,7 +649,7 @@ mst_orient_normals( neighbor_radius, kernel); else - riemannian_graph = create_riemannian_graph(points.begin(), points.end(), + riemannian_graph = create_riemannian_graph(points, point_map, normal_map, index_map, constrained_map, k, diff --git a/Point_set_processing_3/include/CGAL/pca_estimate_normals.h b/Point_set_processing_3/include/CGAL/pca_estimate_normals.h index 4225b9081b7..db146e1d94a 100644 --- a/Point_set_processing_3/include/CGAL/pca_estimate_normals.h +++ b/Point_set_processing_3/include/CGAL/pca_estimate_normals.h @@ -18,9 +18,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include #include #include @@ -33,13 +33,6 @@ #include #include -#ifdef CGAL_LINKED_WITH_TBB -#include -#include -#include -#include -#endif // CGAL_LINKED_WITH_TBB - namespace CGAL { @@ -58,22 +51,20 @@ namespace internal { /// @tparam Tree KD-tree. /// /// @return Computed normal. Orientation is random. -template < typename Kernel, - typename Tree -> -typename Kernel::Vector_3 -pca_estimate_normal(const typename Kernel::Point_3& query, ///< point to compute the normal at - const Tree& tree, ///< KD-tree +template +typename NeighborQuery::Kernel::Vector_3 +pca_estimate_normal(const typename NeighborQuery::Kernel::Point_3& query, ///< point to compute the normal at + const NeighborQuery& neighbor_query, ///< KD-tree unsigned int k, ///< number of neighbors - typename Kernel::FT neighbor_radius) + typename NeighborQuery::Kernel::FT neighbor_radius) { // basic geometric types + typedef typename NeighborQuery::Kernel Kernel; typedef typename Kernel::Point_3 Point; typedef typename Kernel::Plane_3 Plane; std::vector points; - CGAL::Point_set_processing_3::internal::neighbor_query - (query, tree, k, neighbor_radius, points); + neighbor_query.get_points (query, k, neighbor_radius, std::back_inserter(points)); // performs plane fitting by point-based PCA Plane plane; @@ -83,48 +74,6 @@ pca_estimate_normal(const typename Kernel::Point_3& query, ///< point to compute return plane.orthogonal_vector(); } - -#ifdef CGAL_LINKED_WITH_TBB - template - class PCA_estimate_normals { - typedef typename Kernel::FT FT; - typedef typename Kernel::Point_3 Point; - typedef typename Kernel::Vector_3 Vector; - const Tree& tree; - const unsigned int k; - const FT neighbor_radius; - const std::vector& input; - std::vector& output; - cpp11::atomic& advancement; - cpp11::atomic& interrupted; - - public: - PCA_estimate_normals(Tree& tree, unsigned int k, FT neighbor_radius, - std::vector& points, - std::vector& output, - cpp11::atomic& advancement, - cpp11::atomic& interrupted) - : tree(tree), k (k), neighbor_radius (neighbor_radius) - , input (points), output (output) - , advancement (advancement) - , interrupted (interrupted) - { } - - void operator()(const tbb::blocked_range& r) const - { - for( std::size_t i = r.begin(); i != r.end(); ++i) - { - if (interrupted) - break; - output[i] = CGAL::internal::pca_estimate_normal(input[i], tree, k, neighbor_radius); - ++ advancement; - } - } - - }; -#endif // CGAL_LINKED_WITH_TBB - - } /* namespace internal */ /// \endcond @@ -206,15 +155,12 @@ pca_estimate_normals( const std::function& callback = choose_parameter(get_parameter(np, internal_np::callback), std::function()); - typedef typename Kernel::Point_3 Point; - // Input points types - typedef typename boost::property_traits::value_type Vector; + typedef typename PointRange::iterator iterator; + typedef typename iterator::value_type value_type; // types for K nearest neighbors search structure - typedef typename CGAL::Search_traits_3 Tree_traits; - typedef typename CGAL::Orthogonal_k_neighbor_search Neighbor_search; - typedef typename Neighbor_search::Tree Tree; + typedef Point_set_processing_3::internal::Neighbor_query Neighbor_query; // precondition: at least one element in the container. // to fix: should have at least three distinct points @@ -227,59 +173,33 @@ pca_estimate_normals( std::size_t memory = CGAL::Memory_sizer().virtual_size(); CGAL_TRACE(" %ld Mb allocated\n", memory>>20); CGAL_TRACE(" Creates KD-tree\n"); - typename PointRange::iterator it; - - // Instanciate a KD-tree search. - // Note: We have to convert each input iterator to Point_3. - std::vector kd_tree_points; - for(it = points.begin(); it != points.end(); it++) - kd_tree_points.push_back(get(point_map, *it)); - Tree tree(kd_tree_points.begin(), kd_tree_points.end()); + Neighbor_query neighbor_query (points, point_map); memory = CGAL::Memory_sizer().virtual_size(); CGAL_TRACE(" %ld Mb allocated\n", memory>>20); CGAL_TRACE(" Computes normals\n"); - // iterate over input points, compute and output normal - // vectors (already normalized) -#ifndef CGAL_LINKED_WITH_TBB - CGAL_static_assertion_msg (!(boost::is_convertible::value), - "Parallel_tag is enabled but TBB is unavailable."); -#else - if (boost::is_convertible::value) - { - Point_set_processing_3::internal::Parallel_callback - parallel_callback (callback, kd_tree_points.size()); + std::size_t nb_points = points.size(); - std::vector normals (kd_tree_points.size (), - CGAL::NULL_VECTOR); - CGAL::internal::PCA_estimate_normals - f (tree, k, neighbor_radius, kd_tree_points, normals, - parallel_callback.advancement(), - parallel_callback.interrupted()); - tbb::parallel_for(tbb::blocked_range(0, kd_tree_points.size ()), f); - unsigned int i = 0; - for(it = points.begin(); it != points.end(); ++ it, ++ i) - if (normals[i] != CGAL::NULL_VECTOR) - put (normal_map, *it, normals[i]); + Point_set_processing_3::internal::Callback_wrapper + callback_wrapper (callback, nb_points); - parallel_callback.join(); - } - else -#endif - { - std::size_t nb = 0; - for(it = points.begin(); it != points.end(); it++, ++ nb) - { - Vector normal = internal::pca_estimate_normal( - get(point_map,*it), - tree, - k, neighbor_radius); + CGAL::for_each + (points, + [&](value_type& vt) + { + if (callback_wrapper.interrupted()) + return false; - put(normal_map, *it, normal); // normal_map[it] = normal - if (callback && !callback ((nb+1) / double(kd_tree_points.size()))) - break; - } - } + put (normal_map, vt, + CGAL::internal::pca_estimate_normal + (get(point_map, vt), neighbor_query, k, neighbor_radius)); + + ++ callback_wrapper.advancement(); + + return true; + }); + + callback_wrapper.join(); memory = CGAL::Memory_sizer().virtual_size(); CGAL_TRACE(" %ld Mb allocated\n", memory>>20); CGAL_TRACE("End of pca_estimate_normals()\n"); diff --git a/Point_set_processing_3/include/CGAL/remove_outliers.h b/Point_set_processing_3/include/CGAL/remove_outliers.h index 008ef8f5c0d..611269da396 100644 --- a/Point_set_processing_3/include/CGAL/remove_outliers.h +++ b/Point_set_processing_3/include/CGAL/remove_outliers.h @@ -16,9 +16,7 @@ #include -#include -#include -#include +#include #include #include #include @@ -49,22 +47,21 @@ namespace internal { /// @tparam Tree KD-tree. /// /// @return computed distance. -template < typename Kernel, - typename Tree > -typename Kernel::FT +template +typename NeighborQuery::Kernel::FT compute_avg_knn_sq_distance_3( - const typename Kernel::Point_3& query, ///< 3D point to project - Tree& tree, ///< KD-tree + const typename NeighborQuery::Kernel::Point_3& query, ///< 3D point to project + NeighborQuery& neighbor_query, ///< KD-tree unsigned int k, ///< number of neighbors - typename Kernel::FT neighbor_radius) + typename NeighborQuery::Kernel::FT neighbor_radius) { // geometric types + typedef typename NeighborQuery::Kernel Kernel; typedef typename Kernel::FT FT; typedef typename Kernel::Point_3 Point; std::vector points; - CGAL::Point_set_processing_3::internal::neighbor_query - (query, tree, k, neighbor_radius, points); + neighbor_query.get_points (query, k, neighbor_radius, std::back_inserter(points)); // compute average squared distance typename Kernel::Compute_squared_distance_3 sqd; @@ -162,15 +159,14 @@ remove_outliers( typedef typename Kernel::FT FT; // basic geometric types - typedef typename Kernel::Point_3 Point; + typedef typename PointRange::iterator iterator; + typedef typename iterator::value_type value_type; // actual type of input points typedef typename std::iterator_traits::value_type Enriched_point; // types for K nearest neighbors search structure - typedef typename CGAL::Search_traits_3 Tree_traits; - typedef typename CGAL::Orthogonal_k_neighbor_search Neighbor_search; - typedef typename Neighbor_search::Tree Tree; + typedef Point_set_processing_3::internal::Neighbor_query Neighbor_query; // precondition: at least one element in the container. // to fix: should have at least three distinct points @@ -182,26 +178,22 @@ remove_outliers( CGAL_point_set_processing_precondition(threshold_percent >= 0 && threshold_percent <= 100); - typename PointRange::iterator it; + Neighbor_query neighbor_query (points, point_map); - // Instanciate a KD-tree search. - // Note: We have to convert each input iterator to Point_3. - std::vector kd_tree_points; - for(it = points.begin(); it != points.end(); it++) - kd_tree_points.push_back( get(point_map, *it) ); - Tree tree(kd_tree_points.begin(), kd_tree_points.end()); + std::size_t nb_points = points.size(); // iterate over input points and add them to multimap sorted by distance to k std::multimap sorted_points; std::size_t nb = 0; - for(it = points.begin(); it != points.end(); it++, ++ nb) + for(const value_type& vt : points) { - FT sq_distance = internal::compute_avg_knn_sq_distance_3( - get(point_map,*it), - tree, k, neighbor_radius); - sorted_points.insert( std::make_pair(sq_distance, *it) ); - if (callback && !callback ((nb+1) / double(kd_tree_points.size()))) + FT sq_distance = internal::compute_avg_knn_sq_distance_3( + get(point_map, vt), + neighbor_query, k, neighbor_radius); + sorted_points.insert( std::make_pair(sq_distance, vt) ); + if (callback && !callback ((nb+1) / double(nb_points))) return points.end(); + ++ nb; } // Replaces [points.begin(), points.end()) range by the multimap content. diff --git a/Point_set_processing_3/include/CGAL/wlop_simplify_and_regularize_point_set.h b/Point_set_processing_3/include/CGAL/wlop_simplify_and_regularize_point_set.h index e7743317485..b586a59a08a 100644 --- a/Point_set_processing_3/include/CGAL/wlop_simplify_and_regularize_point_set.h +++ b/Point_set_processing_3/include/CGAL/wlop_simplify_and_regularize_point_set.h @@ -32,12 +32,8 @@ #include #include -#ifdef CGAL_LINKED_WITH_TBB -#include -#include -#include -#include -#endif // CGAL_LINKED_WITH_TBB +#include +#include #include #include @@ -329,67 +325,6 @@ compute_density_weight_for_sample_point( /// \endcond -#ifdef CGAL_LINKED_WITH_TBB -/// \cond SKIP_IN_MANUAL -/// This is for parallelization of function: compute_denoise_projection() -template -class Sample_point_updater -{ - typedef typename Kernel::Point_3 Point; - typedef typename Kernel::FT FT; - - std::vector &update_sample_points; - std::vector &sample_points; - const Tree &original_kd_tree; - const Tree &sample_kd_tree; - const typename Kernel::FT radius; - const std::vector &original_densities; - const std::vector &sample_densities; - cpp11::atomic& advancement; - cpp11::atomic& interrupted; - -public: - Sample_point_updater( - std::vector &out, - std::vector &in, - const Tree &_original_kd_tree, - const Tree &_sample_kd_tree, - const typename Kernel::FT _radius, - const std::vector &_original_densities, - const std::vector &_sample_densities, - cpp11::atomic& advancement, - cpp11::atomic& interrupted): - update_sample_points(out), - sample_points(in), - original_kd_tree(_original_kd_tree), - sample_kd_tree(_sample_kd_tree), - radius(_radius), - original_densities(_original_densities), - sample_densities(_sample_densities), - advancement (advancement), - interrupted (interrupted) {} - - void operator() ( const tbb::blocked_range& r ) const - { - for (size_t i = r.begin(); i != r.end(); ++i) - { - if (interrupted) - break; - update_sample_points[i] = simplify_and_regularize_internal:: - compute_update_sample_point( - sample_points[i], - original_kd_tree, - sample_kd_tree, - radius, - original_densities, - sample_densities); - ++ advancement; - } - } -}; -/// \endcond -#endif // CGAL_LINKED_WITH_TBB - // ---------------------------------------------------------------------------- // Public section @@ -529,7 +464,6 @@ wlop_simplify_and_regularize_point_set( #endif } - FT radius2 = radius * radius; CGAL_point_set_processing_precondition(radius > 0); // Initiate a KD-tree search for original points @@ -588,79 +522,50 @@ wlop_simplify_and_regularize_point_set( sample_density_weights.push_back(density); } + typedef boost::zip_iterator::iterator, + typename std::vector::iterator> > Zip_iterator; - typename std::vector::iterator update_iter = update_sample_points.begin(); -#ifndef CGAL_LINKED_WITH_TBB - CGAL_static_assertion_msg (!(boost::is_convertible::value), - "Parallel_tag is enabled but TBB is unavailable."); -#else - //parallel - if (boost::is_convertible::value) - { - Point_set_processing_3::internal::Parallel_callback - parallel_callback (callback, iter_number * number_of_sample, iter_n * number_of_sample); + Point_set_processing_3::internal::Callback_wrapper + callback_wrapper (callback, iter_number * number_of_sample, iter_n * number_of_sample); - tbb::blocked_range block(0, number_of_sample); - Sample_point_updater sample_updater( - update_sample_points, - sample_points, - original_kd_tree, - sample_kd_tree, - radius2, - original_density_weights, - sample_density_weights, - parallel_callback.advancement(), - parallel_callback.interrupted()); + CGAL::for_each + (CGAL::make_range (boost::make_zip_iterator (boost::make_tuple (sample_points.begin(), update_sample_points.begin())), + boost::make_zip_iterator (boost::make_tuple (sample_points.end(), update_sample_points.end()))), + [&](const typename Zip_iterator::reference& t) + { + if (callback_wrapper.interrupted()) + return false; - tbb::parallel_for(block, sample_updater); + get<1>(t) = simplify_and_regularize_internal:: + compute_update_sample_point( + get<0>(t), + original_kd_tree, + sample_kd_tree, + radius, + original_density_weights, + sample_density_weights); + ++ callback_wrapper.advancement(); - bool interrupted = parallel_callback.interrupted(); + return true; + }); - // We interrupt by hand as counter only goes halfway and won't terminate by itself - parallel_callback.interrupted() = true; - parallel_callback.join(); + bool interrupted = callback_wrapper.interrupted(); - // If interrupted during this step, nothing is computed, we return NaN - if (interrupted) - return output; - }else -#endif - { - //sequential - std::size_t nb = iter_n * number_of_sample; - for (sample_iter = sample_points.begin(); - sample_iter != sample_points.end(); ++sample_iter, ++update_iter, ++ nb) - { - *update_iter = simplify_and_regularize_internal:: - compute_update_sample_point - (*sample_iter, - original_kd_tree, - sample_kd_tree, - radius2, - original_density_weights, - sample_density_weights); - if (callback && !callback ((nb+1) / double(iter_number * number_of_sample))) - return output; - } - } + // We interrupt by hand as counter only goes halfway and won't terminate by itself + callback_wrapper.interrupted() = true; + callback_wrapper.join(); + + // If interrupted during this step, nothing is computed, we return NaN + if (interrupted) + return output; sample_iter = sample_points.begin(); - for (update_iter = update_sample_points.begin(); - update_iter != update_sample_points.end(); - ++update_iter, ++sample_iter) - { - *sample_iter = *update_iter; - } + for (std::size_t i = 0; i < sample_points.size(); ++ i) + sample_points[i] = update_sample_points[i]; } // final output - for(sample_iter = sample_points.begin(); - sample_iter != sample_points.end(); ++sample_iter) - { - *output++ = *sample_iter; - } + std::copy (sample_points.begin(), sample_points.end(), output); return output; } diff --git a/Poisson_surface_reconstruction_3/doc/Poisson_surface_reconstruction_3/Poisson_surface_reconstruction_3.txt b/Poisson_surface_reconstruction_3/doc/Poisson_surface_reconstruction_3/Poisson_surface_reconstruction_3.txt index 137fa4194b1..f76f4ad34e8 100644 --- a/Poisson_surface_reconstruction_3/doc/Poisson_surface_reconstruction_3/Poisson_surface_reconstruction_3.txt +++ b/Poisson_surface_reconstruction_3/doc/Poisson_surface_reconstruction_3/Poisson_surface_reconstruction_3.txt @@ -28,6 +28,11 @@ function of the inferred solid (Poisson Surface Reconstruction - referred to as Poisson). Poisson is a two steps process: it requires solving for the implicit function before function evaluation. +\note A \ref tuto_reconstruction "detailed tutorial on surface reconstruction" +is provided with a guide to choose the most appropriate method along +with pre- and post-processing. + + \section Poisson_surface_reconstruction_3Common Common Reconstruction Pipeline Surface reconstruction from point sets is often a sequential process diff --git a/Poisson_surface_reconstruction_3/examples/Poisson_surface_reconstruction_3/CMakeLists.txt b/Poisson_surface_reconstruction_3/examples/Poisson_surface_reconstruction_3/CMakeLists.txt index 85259f8bd2f..a7eb729ec77 100644 --- a/Poisson_surface_reconstruction_3/examples/Poisson_surface_reconstruction_3/CMakeLists.txt +++ b/Poisson_surface_reconstruction_3/examples/Poisson_surface_reconstruction_3/CMakeLists.txt @@ -31,6 +31,8 @@ if ( CGAL_FOUND ) CGAL_target_use_Eigen(poisson_reconstruction) create_single_source_cgal_program( "poisson_reconstruction_function.cpp" ) CGAL_target_use_Eigen(poisson_reconstruction_function) + create_single_source_cgal_program( "tutorial_example.cpp" ) + CGAL_target_use_Eigen(tutorial_example) else() message(STATUS "NOTICE: The examples need Eigen 3.1 (or greater) will not be compiled.") endif() diff --git a/Poisson_surface_reconstruction_3/examples/Poisson_surface_reconstruction_3/tutorial_example.cmd b/Poisson_surface_reconstruction_3/examples/Poisson_surface_reconstruction_3/tutorial_example.cmd new file mode 100644 index 00000000000..12b759aa160 --- /dev/null +++ b/Poisson_surface_reconstruction_3/examples/Poisson_surface_reconstruction_3/tutorial_example.cmd @@ -0,0 +1,3 @@ +data/kitten.xyz +data/kitten.xyz 1 +data/kitten.xyz 2 diff --git a/Poisson_surface_reconstruction_3/examples/Poisson_surface_reconstruction_3/tutorial_example.cpp b/Poisson_surface_reconstruction_3/examples/Poisson_surface_reconstruction_3/tutorial_example.cpp new file mode 100644 index 00000000000..ff58e34ed31 --- /dev/null +++ b/Poisson_surface_reconstruction_3/examples/Poisson_surface_reconstruction_3/tutorial_example.cpp @@ -0,0 +1,223 @@ +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +// types +typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; +typedef Kernel::FT FT; +typedef Kernel::Point_3 Point_3; +typedef Kernel::Vector_3 Vector_3; +typedef Kernel::Sphere_3 Sphere_3; +typedef CGAL::Point_set_3 Point_set; + +int main(int argc, char*argv[]) +{ + /////////////////////////////////////////////////////////////////// + //! [Reading input] + + Point_set points; + + if (argc < 2) + { + std::cerr << "Usage: " << argv[0] << " [input.xyz/off/ply/las]" << std::endl; + return EXIT_FAILURE; + } + + const char* input_file = argv[1]; + std::ifstream stream (input_file, std::ios_base::binary); + if (!stream) + { + std::cerr << "Error: cannot read file " << input_file << std::endl; + return EXIT_FAILURE; + } + + stream >> points; + + std::cout << "Read " << points.size () << " point(s)" << std::endl; + if (points.empty()) + return EXIT_FAILURE; + + //! [Reading input] + /////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////// + //! [Outlier removal] + + CGAL::remove_outliers (points, + 24, // Number of neighbors considered for evaluation + points.parameters().threshold_percent (5.0)); // Percentage of points to remove + + std::cout << points.number_of_removed_points() + << " point(s) are outliers." << std::endl; + + // Applying point set processing algorithm to a CGAL::Point_set_3 + // object does not erase the points from memory but place them in + // the garbage of the object: memory can be freeed by the user. + points.collect_garbage(); + + //! [Outlier removal] + /////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////// + //! [Simplification] + + // Compute average spacing using neighborhood of 6 points + double spacing = CGAL::compute_average_spacing (points, 6); + + // Simplify using a grid of size 2 * average spacing + CGAL::grid_simplify_point_set (points, 2. * spacing); + + std::cout << points.number_of_removed_points() + << " point(s) removed after simplification." << std::endl; + + points.collect_garbage(); + + //! [Simplification] + /////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////// + //! [Smoothing] + + CGAL::jet_smooth_point_set (points, 24); + + //! [Smoothing] + /////////////////////////////////////////////////////////////////// + + unsigned int reconstruction_choice + = (argc < 3 ? 0 : atoi(argv[2])); + + if (reconstruction_choice == 0) // Poisson + { + /////////////////////////////////////////////////////////////////// + //! [Normal estimation] + + CGAL::jet_estimate_normals + (points, 24); // Use 24 neighbors + + // Orientation of normals, returns iterator to first unoriented point + typename Point_set::iterator unoriented_points_begin = + CGAL::mst_orient_normals(points, 24); // Use 24 neighbors + + points.remove (unoriented_points_begin, points.end()); + + //! [Normal estimation] + /////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////// + //! [Poisson reconstruction] + + CGAL::Surface_mesh output_mesh; + CGAL::poisson_surface_reconstruction_delaunay + (points.begin(), points.end(), + points.point_map(), points.normal_map(), + output_mesh, spacing); + + //! [Poisson reconstruction] + /////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////// + //! [Output poisson] + + std::ofstream f ("out.ply", std::ios_base::binary); + CGAL::set_binary_mode (f); + CGAL::write_ply (f, output_mesh); + f.close (); + + //! [Output poisson] + /////////////////////////////////////////////////////////////////// + } + else if (reconstruction_choice == 1) // Advancing front + { + /////////////////////////////////////////////////////////////////// + //! [Advancing front reconstruction] + + typedef std::array Facet; // Triple of indices + + std::vector facets; + + // The function is called using directly the points raw iterators + CGAL::advancing_front_surface_reconstruction(points.points().begin(), + points.points().end(), + std::back_inserter(facets)); + std::cout << facets.size () + << " facet(s) generated by reconstruction." << std::endl; + + //! [Advancing front reconstruction] + /////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////// + //! [Output advancing front] + + // copy points for random access + std::vector vertices; + vertices.reserve (points.size()); + std::copy (points.points().begin(), points.points().end(), std::back_inserter (vertices)); + + CGAL::Surface_mesh output_mesh; + CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh (vertices, facets, output_mesh); + std::ofstream f ("out.off"); + f << output_mesh; + f.close (); + + //! [Output advancing front] + /////////////////////////////////////////////////////////////////// + } + else if (reconstruction_choice == 2) // Scale space + { + /////////////////////////////////////////////////////////////////// + //! [Scale space reconstruction] + + CGAL::Scale_space_surface_reconstruction_3 reconstruct + (points.points().begin(), points.points().end()); + + // Smooth using 4 iterations of Jet Smoothing + reconstruct.increase_scale (4, CGAL::Scale_space_reconstruction_3::Jet_smoother()); + // Mesh with the Advancing Front mesher with a maximum facet length of 0.5 + reconstruct.reconstruct_surface (CGAL::Scale_space_reconstruction_3::Advancing_front_mesher(0.5)); + + //! [Scale space reconstruction] + /////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////// + //! [Output scale space] + + std::ofstream f ("out.off"); + f << "OFF" << std::endl << points.size () << " " + << reconstruct.number_of_facets() << " 0" << std::endl; + for (Point_set::Index idx : points) + f << points.point (idx) << std::endl; + for (const auto& facet : CGAL::make_range (reconstruct.facets_begin(), reconstruct.facets_end())) + f << "3 "<< facet << std::endl; + f.close (); + + //! [Output scale space] + /////////////////////////////////////////////////////////////////// + } + else // Handle error + { + std::cerr << "Error: invalid reconstruction id: " << reconstruction_choice << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/Poisson_surface_reconstruction_3/include/CGAL/Poisson_reconstruction_function.h b/Poisson_surface_reconstruction_3/include/CGAL/Poisson_reconstruction_function.h index 10db057f40b..aaace81b7ba 100644 --- a/Poisson_surface_reconstruction_3/include/CGAL/Poisson_reconstruction_function.h +++ b/Poisson_surface_reconstruction_3/include/CGAL/Poisson_reconstruction_function.h @@ -47,6 +47,7 @@ #include #include #include +#include /*! \file Poisson_reconstruction_function.h @@ -104,18 +105,6 @@ struct Poisson_visitor { {} }; -struct Poisson_skip_vertices { - double ratio; - Random& m_random; - Poisson_skip_vertices(const double ratio, Random& random) - : ratio(ratio), m_random(random) {} - - template - bool operator()(Iterator) const { - return m_random.get_double() < ratio; - } -}; - // Given f1 and f2, two sizing fields, that functor wrapper returns // max(f1, f2*f2) // The wrapper stores only pointers to the two functors. @@ -415,11 +404,15 @@ public: // then the cell is considered as small enough, and the first sizing // field, more costly, is not evaluated. - typedef Filter_iterator Some_points_iterator; //make it deterministic Random random(0); - Poisson_skip_vertices skip(1.-approximation_ratio,random); + double ratio = 1.-approximation_ratio; + + std::vector some_points; + for (typename Triangulation::Input_point_iterator + it = m_tr->input_points_begin(); it != m_tr->input_points_end(); ++ it) + if (random.get_double() >= ratio) + some_points.push_back (it); CGAL_TRACE_STREAM << "SPECIAL PASS that uses an approximation of the result (approximation ratio: " << approximation_ratio << ")" << std::endl; @@ -427,11 +420,8 @@ public: CGAL::Timer sizing_field_timer; sizing_field_timer.start(); Poisson_reconstruction_function - coarse_poisson_function(Some_points_iterator(m_tr->input_points_end(), - skip, - m_tr->input_points_begin()), - Some_points_iterator(m_tr->input_points_end(), - skip), + coarse_poisson_function(boost::make_indirect_iterator (some_points.begin()), + boost::make_indirect_iterator (some_points.end()), Normal_of_point_with_normal_map() ); coarse_poisson_function.compute_implicit_function(solver, Poisson_visitor(), 0.); diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt index 36f6844cdfb..5fe5a41c38f 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt @@ -596,6 +596,15 @@ It is for example used in the fucntion `keep_large_connected_components()`. Default: `false` \cgalNPEnd +\cgalNPBegin{maximum_number_of_faces} \anchor PMP_maximum_number_of_faces + +Parameter that specifies the maximum number of faces that a component +of a mesh can have to be considered reversible \n +If it is left default, then it is ignored and all components are considered reversible. \n +Type: `size_type` \n +Default: 0 +\cgalNPEnd + \cgalNPTableEnd */ diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt index c0faeaa18a4..2717405f1bc 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt @@ -141,6 +141,9 @@ and provides a list of the parameters that are used in this package. - `CGAL::Polygon_mesh_processing::volume_connected_components()` - `CGAL::Polygon_mesh_processing::is_outward_oriented()` - `CGAL::Polygon_mesh_processing::reverse_face_orientations()` +- `CGAL::Polygon_mesh_processing::duplicate_non_manifold_edges_in_polygon_soup()` +- `CGAL::Polygon_mesh_processing::orient_triangle_soup_with_reference_triangle_mesh()` +- `CGAL::Polygon_mesh_processing::merge_reversible_connected_components()` \cgalCRPSection{Combinatorial Repairing Functions} - `CGAL::Polygon_mesh_processing::merge_duplicate_points_in_polygon_soup()` @@ -200,6 +203,7 @@ and provides a list of the parameters that are used in this package. - `CGAL::Polygon_mesh_processing::approximate_max_distance_to_point_set()` - `CGAL::Polygon_mesh_processing::max_distance_to_triangle_mesh()` - `CGAL::Polygon_mesh_processing::sample_triangle_mesh()` +- `CGAL::Polygon_mesh_processing::sample_triangle_soup()` \cgalCRPSection{Feature Detection Functions} - `CGAL::Polygon_mesh_processing::sharp_edges_segmentation()` diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/Polygon_mesh_processing.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/Polygon_mesh_processing.txt index d356437bacc..f89940c1e37 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/Polygon_mesh_processing.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/Polygon_mesh_processing.txt @@ -601,9 +601,13 @@ As a consequence, the normal computed for each face (see Section - The function `CGAL::Polygon_mesh_processing::volume_connected_components()` provides information about the 3D arrangement of the surface connected components in a given triangle mesh. It comes with many named parameter options making it also a more general version of `is_outward_oriented()`. - - -\subsection PolygonSoupExample Orientation Example +- The function `CGAL::Polygon_mesh_processing::duplicate_non_manifold_edges_in_polygon_soup()` +duplicates points and edges to make a soup orientable, without changing the orientation of the faces. +- The function `CGAL::Polygon_mesh_processing::orient_triangle_soup_with_reference_triangle_mesh()` +takes an input mesh as a reference and orients the triangles of a soup according to it. +- The function `CGAL::Polygon_mesh_processing::merge_reversible_connected_components()` +merges the connected components of a polygon mesh if possible. +\subsection PolygonSoupExample Orientation Examples This example shows how to generate a mesh from a polygon soup. The first step is to get a soup of consistently oriented faces, before @@ -614,6 +618,11 @@ Section \ref PMPOrientation. \cgalExample{Polygon_mesh_processing/orient_polygon_soup_example.cpp} +This example shows how to correctly repair and orient a soup to get a mesh from a reference : + +\cgalExample{Polygon_mesh_processing/orientation_pipeline_example.cpp} + + **************************************** \section PMPRepairing Combinatorial Repairing diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt index f50f3512ede..66c4609d25c 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt @@ -28,4 +28,5 @@ \example Polygon_mesh_processing/mesh_smoothing_example.cpp \example Polygon_mesh_processing/shape_smoothing_example.cpp \example Polygon_mesh_processing/locate_example.cpp +\example Polygon_mesh_processing/orientation_pipeline_example.cpp */ diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt index 26646ab4ad1..151f4fe333c 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt @@ -88,6 +88,7 @@ create_single_source_cgal_program( "manifoldness_repair_example.cpp" ) create_single_source_cgal_program( "repair_polygon_soup_example.cpp" ) create_single_source_cgal_program( "mesh_smoothing_example.cpp") create_single_source_cgal_program( "locate_example.cpp") +create_single_source_cgal_program( "orientation_pipeline_example.cpp") #create_single_source_cgal_program( "self_snapping_example.cpp") #create_single_source_cgal_program( "snapping_example.cpp") @@ -109,8 +110,8 @@ target_link_libraries( point_inside_example_OM PRIVATE ${OPENMESH_LIBRARIES} ) create_single_source_cgal_program( "stitch_borders_example_OM.cpp" ) target_link_libraries( stitch_borders_example_OM PRIVATE ${OPENMESH_LIBRARIES} ) -#create_single_source_cgal_program( "remove_degeneracies_example_OM.cpp") -#target_link_libraries( remove_degeneracies_example_OM PRIVATE ${OPENMESH_LIBRARIES} ) +#create_single_source_cgal_program( "remove_degeneracies_example.cpp") +#target_link_libraries( remove_degeneracies_example PRIVATE ${OPENMESH_LIBRARIES} ) create_single_source_cgal_program( "triangulate_faces_example_OM.cpp") target_link_libraries( triangulate_faces_example_OM PRIVATE ${OPENMESH_LIBRARIES} ) @@ -118,8 +119,6 @@ endif(OpenMesh_FOUND) find_package( TBB ) if( TBB_FOUND ) -# CGAL_target_use_TBB(self_snapping_example) -# CGAL_target_use_TBB(snapping_example) CGAL_target_use_TBB(self_intersections_example) CGAL_target_use_TBB(hausdorff_distance_remeshing_example) else() @@ -131,4 +130,3 @@ if(TARGET ceres) target_compile_definitions( mesh_smoothing_example PRIVATE CGAL_PMP_USE_CERES_SOLVER ) target_link_libraries( mesh_smoothing_example PRIVATE ceres ) endif(TARGET ceres) - diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/data/blobby-shuffled.off b/Polygon_mesh_processing/examples/Polygon_mesh_processing/data/blobby-shuffled.off new file mode 100644 index 00000000000..6d3eba1d8ca --- /dev/null +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/data/blobby-shuffled.off @@ -0,0 +1,6104 @@ +# Output of a CGAL tool +#CBP +# polyhedral_surface 0 +# halfedges 0 +# triangulated 0 +# non_empty_facets 0 +# terrain 0 +# normalized_to_sphere 0 +# radius 0 +# rounded 0 +# rounded_bits 0 +# ENDCBP + +OFF +2027 4050 0 + +# 2027 vertices +# ------------------------------------------ + + +0.095849400000000001 0.00115985 -0.166522 +-0.0890262 -0.025355099999999998 -0.18482899999999999 +0.027536999999999999 -0.14418500000000001 0.12545899999999999 +-0.095787600000000001 -0.13067799999999999 0.14583599999999999 +-0.20519999999999999 0.091158199999999995 -0.071373599999999995 +0.23333999999999999 0.15764300000000001 -0.079066399999999995 +0.23333000000000001 0.18367900000000001 0.031482900000000001 +0.011625399999999999 0.0348884 0.195738 +0.061604699999999998 0.16266600000000001 0.15098 +0.13172500000000001 0.21107500000000001 -0.0224619 +-0.15770999999999999 -0.062169500000000003 -0.114255 +-0.32595299999999999 -0.116315 0.036301699999999999 +-0.25827699999999998 -9.9632799999999999e-05 -0.066961999999999994 +0.13411300000000001 -0.042284000000000002 0.121878 +0.30214299999999999 0.124693 0.029570300000000001 +0.133766 0.13006100000000001 -0.094515100000000005 +0.18373900000000001 0.12406499999999999 0.13048199999999999 +-0.039973399999999999 -0.094566600000000001 -0.18862499999999999 +-0.39484200000000003 0.062733800000000006 0.0053626899999999998 +-0.13799800000000001 0.173848 0.096777500000000002 +-0.038674800000000002 0.229049 0.036186200000000002 +-0.039295200000000002 -0.16598199999999999 -0.11582199999999999 +-0.171824 -0.17691200000000001 -0.0086522700000000001 +-0.15034600000000001 -0.0322864 0.16289999999999999 +-0.21992300000000001 -0.105035 -0.044157000000000002 +-0.172879 0.078466900000000006 0.134243 +0.016566999999999998 0.21765100000000001 -0.019554599999999998 +0.079410099999999997 -0.0895981 -0.047201300000000002 +-0.31369799999999998 0.038077399999999997 0.094854999999999995 +0.31854700000000002 0.125087 -0.019869399999999999 +0.23227 0.0225103 -0.021871499999999999 +0.28860200000000003 0.062350299999999997 -0.0028626099999999998 +0.232539 0.033764500000000003 0.088465600000000005 +-0.257774 -0.0579123 0.114719 +0.056384900000000002 0.22962199999999999 0.073868199999999995 +0.27843600000000002 0.16783200000000001 -0.0053950400000000003 +-0.31471300000000002 0.091573799999999997 -0.060780500000000001 +0.085349099999999997 0.066700200000000001 -0.13686200000000001 +0.0238762 -0.124001 -0.17752000000000001 +0.025990099999999999 -0.039926999999999997 -0.19495199999999999 +-0.105554 0.070428099999999993 -0.15769900000000001 +0.28937400000000002 0.136378 -0.071541099999999996 +0.235568 0.076571600000000004 -0.082325300000000004 +-0.245592 -0.0554978 -0.058124799999999997 +-0.39116899999999999 -0.063444299999999995 -0.086140999999999995 +-0.31947300000000001 -0.0445621 -0.070685100000000001 +-0.29419699999999999 -0.11161699999999999 -0.049417599999999999 +-0.37454900000000002 0.0271624 -0.088377200000000003 +-0.25372899999999998 -0.173014 -0.0128907 +-0.21636 -0.14896999999999999 0.082385399999999998 +-0.066871 0.112543 0.16330800000000001 +-0.134132 -0.13648199999999999 -0.075767200000000007 +-0.068210400000000004 -0.196549 -0.0186103 +0.028011600000000001 0.037949999999999998 -0.18273200000000001 +-0.21726500000000001 0.036772199999999998 -0.076175099999999996 +-0.16211500000000001 0.056705999999999999 -0.114965 +-0.25992100000000001 0.073993799999999998 -0.0650284 +-0.26382699999999998 0.132437 0.034598700000000003 +-0.244668 0.13427600000000001 -0.032760900000000003 +-0.14700299999999999 0.16924900000000001 -0.035537899999999997 +-0.20061100000000001 0.16917099999999999 0.0171526 +-0.204369 0.13125800000000001 0.085634699999999994 +0.161721 0.051829300000000002 -0.109485 +-0.30307200000000001 0.036158299999999997 -0.073154499999999997 +-0.179865 -0.086242899999999997 -0.072876099999999999 +0.226968 0.189503 -0.028229799999999999 +0.17320099999999999 0.18445600000000001 -0.067608000000000001 +0.18254300000000001 0.205347 0.0085282099999999996 +0.175562 0.18662799999999999 0.084190899999999999 +0.125387 0.21876300000000001 0.047042599999999997 +0.111015 0.194442 0.111774 +-0.17502999999999999 -0.129411 -0.045934200000000001 +0.17146900000000001 -0.016288500000000001 0.030969099999999999 +0.062003299999999997 -0.1101 0.043838599999999998 +0.00082855799999999996 -0.18148600000000001 0.043399399999999998 +-0.091978000000000004 -0.221835 0.085260799999999998 +-0.21368599999999999 -0.148588 -0.0242963 +-0.25551600000000002 -0.132659 -0.034891600000000002 +-0.29915799999999998 -0.15709400000000001 -0.028081499999999999 +-0.37190299999999998 -0.12923299999999999 -0.045323299999999997 +-0.28935100000000002 -0.15696099999999999 0.026597300000000001 +-0.27149099999999998 -0.114067 0.073301199999999997 +-0.319216 -0.063680299999999995 0.081839200000000001 +-0.41884900000000003 -0.0484997 0.0253466 +-0.24013000000000001 -0.18160399999999999 0.041943500000000002 +-0.168769 -0.21110100000000001 0.064175899999999994 +-0.031372900000000002 0.18620100000000001 -0.079775200000000004 +0.0310134 0.15207200000000001 -0.11733 +-0.047766799999999998 0.13189600000000001 -0.143761 +0.12242400000000001 0.139982 0.14562700000000001 +0.13770299999999999 0.045102799999999998 0.16714499999999999 +0.070826299999999995 0.087710399999999994 0.17714299999999999 +-0.25870100000000001 0.084023500000000001 0.087675400000000001 +-0.245925 0.020130700000000001 0.137908 +-0.31407000000000002 0.087551900000000002 0.047375199999999999 +-0.48033700000000001 -0.0353406 -0.045931 +-0.44279400000000002 0.010582299999999999 -0.100726 +0.075401599999999999 0.22422900000000001 0.0064139399999999999 +0.0053423500000000001 0.12164 0.17535200000000001 +-0.034947600000000002 0.200792 0.131192 +0.072180999999999995 -0.10968799999999999 -0.12035 +0.119341 -0.053477299999999998 0.056595899999999998 +0.086788500000000005 -0.090323000000000001 0.10555200000000001 +0.0632331 -0.043990000000000001 0.18492700000000001 +-0.039255600000000002 -0.053976200000000002 0.18488199999999999 +-0.36137399999999997 0.047788600000000001 0.055829200000000002 +-0.36119299999999999 -0.010701799999999999 0.0908968 +-0.416327 0.0142746 0.0438039 +-0.45555699999999999 0.027654600000000001 -0.017024299999999999 +-0.032399299999999999 -0.15038099999999999 -0.16214200000000001 +-0.097041600000000006 -0.12747 -0.13774800000000001 +0.015038899999999999 -0.155421 -0.13047600000000001 +0.039478199999999998 -0.13985 -0.0691441 +0.27858699999999997 0.072167999999999996 0.0614076 +0.24014099999999999 0.096043500000000004 0.10983 +0.196467 0.056071099999999999 0.13908300000000001 +0.18276400000000001 -0.00083176300000000001 0.111317 +-0.21378 -0.18512500000000001 -0.00072420799999999995 +-0.18268400000000001 -0.20657500000000001 0.022381399999999999 +-0.116864 -0.21174100000000001 0.0233662 +0.128798 -0.047684600000000001 -0.070965299999999995 +-0.151224 -0.159278 0.110503 +-0.18671699999999999 -0.0912332 0.126278 +-0.136046 0.197824 0.0292673 +-0.079970100000000002 0.20249500000000001 -0.021901899999999998 +-0.090814800000000001 0.21491099999999999 0.074684100000000003 +0.22185199999999999 0.15013699999999999 0.092841499999999993 +0.268648 0.127162 0.073768500000000001 +-0.100355 0.15726799999999999 -0.094683600000000007 +-0.151142 0.11729299999999999 -0.095301399999999994 +-0.059825099999999999 -0.21344299999999999 0.0369284 +-0.032155700000000002 -0.20306399999999999 0.092727100000000007 +-0.0758939 -0.18407699999999999 0.12844800000000001 +-0.0311162 -0.14380999999999999 0.15808700000000001 +0.017880699999999999 -0.096989699999999998 0.176592 +0.047416399999999997 -0.12537899999999999 -0.011883700000000001 +0.137437 -0.034659799999999998 0.0136343 +0.177953 -0.0103553 -0.035624000000000003 +0.13910900000000001 -0.034972900000000001 -0.034761199999999999 +0.158771 -0.024122000000000001 -0.065972600000000006 +0.141486 -0.020496 -0.127385 +0.19728200000000001 0.0147799 -0.083601300000000003 +0.098925899999999997 -0.061201499999999999 -0.152448 +0.14621600000000001 -0.033117599999999997 -0.096215800000000004 +0.11569500000000001 -0.062037799999999997 -0.111119 +0.168295 -0.0014477800000000001 -0.105241 +0.14328399999999999 0.020743399999999999 -0.12954299999999999 +0.123336 0.056542099999999998 -0.124097 +0.107295 0.097032499999999994 -0.111084 +0.083027799999999999 0.14480399999999999 -0.10519199999999999 +0.116715 0.18219199999999999 -0.077579300000000004 +0.064937800000000004 0.108018 -0.128027 +0.0088614899999999996 0.108014 -0.155996 +-0.0439072 0.076694399999999996 -0.178622 +0.043701299999999998 0.0767872 -0.155644 +0.10263899999999999 0.033935300000000002 -0.14744099999999999 +0.066484799999999997 0.0308188 -0.16812199999999999 +0.057418499999999997 -0.0086220099999999994 -0.18768599999999999 +0.0659222 -0.051221200000000001 -0.17997299999999999 +0.035288800000000002 -0.082328200000000004 -0.18673799999999999 +-0.0051496399999999996 -0.069933499999999996 -0.19578599999999999 +-0.029625100000000001 -0.013350900000000001 -0.19615099999999999 +-0.043444499999999997 -0.0528929 -0.192773 +-0.087276199999999998 -0.076467999999999994 -0.18007899999999999 +-0.12914700000000001 -0.050085699999999997 -0.15846499999999999 +-0.12928400000000001 0.0124896 -0.15528400000000001 +0.069558400000000006 -0.089867900000000001 -0.16298799999999999 +0.14744399999999999 0.090383400000000003 -0.099623100000000006 +0.18867400000000001 0.125502 -0.087038099999999993 +0.19051399999999999 0.081444600000000006 -0.092952800000000002 +0.054356500000000002 0.19081799999999999 -0.074467699999999998 +-0.067633600000000002 0.026418199999999999 -0.18554899999999999 +0.018353600000000001 0.000577163 -0.19738900000000001 +-0.016639299999999999 0.031641000000000002 -0.195381 +-0.0014350000000000001 0.069468299999999997 -0.18121200000000001 +0.248561 0.031737799999999997 0.032890500000000003 +-0.12876199999999999 -0.092239299999999996 -0.13675499999999999 +-0.14779999999999999 -0.10176 -0.098559599999999997 +-0.18376899999999999 0.00080942799999999995 -0.0981379 +-0.14241300000000001 -0.15939300000000001 -0.037900400000000001 +-0.093485299999999993 -0.17150799999999999 -0.061299899999999997 +-0.039723000000000001 -0.182589 -0.063459199999999993 +-0.011640599999999999 -0.185728 -0.0129543 +-0.13089400000000001 -0.187892 -0.0087295500000000009 +-0.00093736299999999998 -0.16902900000000001 -0.087658100000000003 +-0.36682399999999998 -0.072428199999999998 0.045307699999999999 +-0.39464199999999999 -0.095481200000000002 -0.0019439500000000001 +-0.44067400000000001 -0.069291599999999995 -0.0230396 +-0.458899 -0.026837099999999999 0.00052061700000000002 +-0.44978099999999999 -0.071578100000000006 -0.081636200000000006 +-0.41608699999999998 -0.102536 -0.055614200000000003 +-0.31006299999999998 0.12037299999999999 -0.0055827300000000002 +-0.35955700000000002 0.097333900000000001 -0.024699800000000001 +-0.40859800000000002 0.071229899999999999 -0.056654700000000002 +0.210226 0.0065472999999999998 0.0528917 +0.10443 -0.064571299999999998 -0.051228099999999999 +0.092888499999999999 -0.083854899999999996 -0.085273299999999996 +0.0647924 -0.11242099999999999 -0.073180499999999996 +-0.25816 -0.095233799999999993 -0.047642299999999999 +-0.29016999999999998 -0.071502599999999999 -0.059089900000000001 +-0.33406599999999997 -0.090019399999999999 -0.065301200000000004 +-0.088115399999999997 0.18359 0.121824 +-0.11695800000000001 0.131991 0.135245 +-0.11552800000000001 0.074086700000000005 0.15803300000000001 +-0.056259700000000003 0.046087400000000001 0.18268200000000001 +-0.15470700000000001 0.028122899999999999 0.16259199999999999 +-0.101345 0.00172185 0.18399799999999999 +-0.200295 -0.0055454800000000002 0.159778 +-0.055074999999999999 0.15928200000000001 0.15029400000000001 +-0.0051326100000000001 0.16801199999999999 0.15662999999999999 +0.025650900000000001 0.210455 0.123931 +-0.000484079 0.23336999999999999 0.078525600000000001 +0.024306299999999999 0.23203299999999999 0.0312676 +0.29746800000000001 0.085647699999999993 -0.051891600000000003 +0.264291 0.051681299999999999 -0.043179599999999999 +-0.00033375899999999999 -0.14362 -0.17393 +-0.0081177899999999997 -0.111725 -0.18914500000000001 +0.023316799999999999 -0.144538 -0.156498 +0.052056900000000003 -0.121215 -0.153498 +0.040349200000000002 -0.13908899999999999 -0.13219700000000001 +0.0307313 -0.148756 -0.10091 +0.052015199999999998 -0.12936600000000001 -0.093870599999999998 +-0.035846900000000001 -0.12528300000000001 -0.18140000000000001 +-0.071996299999999999 -0.112037 -0.17452500000000001 +-0.057900500000000001 -0.13610900000000001 -0.165268 +-0.053518999999999997 -0.152974 -0.13985900000000001 +-0.082604800000000006 -0.15557499999999999 -0.104309 +-0.102837 -0.101678 -0.161026 +-0.080197099999999993 -0.12789300000000001 -0.156609 +-0.076707200000000003 -0.142872 -0.13619899999999999 +0.0010389100000000001 -0.15568399999999999 -0.151643 +-0.0126392 -0.16380500000000001 -0.12728 +-0.15321299999999999 -0.020072900000000001 -0.132827 +-0.157003 0.019224499999999999 -0.127469 +-0.11382 -0.074437900000000001 -0.16651199999999999 +-0.10638400000000001 -0.047926999999999997 -0.17710799999999999 +-0.11755500000000001 -0.016950699999999999 -0.16802900000000001 +-0.099159399999999995 0.0131089 -0.174764 +-0.091370599999999996 0.044859200000000002 -0.17249600000000001 +-0.075640600000000002 0.073617600000000005 -0.170824 +-0.087346800000000002 0.11047800000000001 -0.147509 +-0.059453199999999998 0.101489 -0.163026 +-0.024470800000000001 0.103135 -0.16602800000000001 +-0.12742300000000001 0.098632200000000003 -0.129408 +-0.118816 0.041986900000000001 -0.157553 +-0.133878 0.066418900000000003 -0.13894000000000001 +-0.0153011 0.128439 -0.14722399999999999 +-0.0252286 0.16062100000000001 -0.11677800000000001 +0.0063503700000000001 0.1777 -0.095487299999999997 +0.0040088499999999996 0.20053299999999999 -0.058022200000000003 +-0.031746200000000002 0.211172 -0.0259917 +-0.081196099999999993 -0.050982800000000002 -0.185782 +-0.061615200000000002 -0.030768799999999999 -0.19125400000000001 +-0.070431400000000005 -0.0027779599999999999 -0.18848699999999999 +-0.062954099999999999 -0.069891300000000003 -0.18779499999999999 +-0.045516399999999999 0.0102212 -0.193522 +-0.043004300000000002 0.037348699999999999 -0.19054699999999999 +-0.0241987 0.0567533 -0.18857599999999999 +-0.063225000000000003 0.052565099999999997 -0.18177699999999999 +-0.065562599999999999 0.15537300000000001 -0.11569500000000001 +-0.068756800000000007 0.17671700000000001 -0.083513199999999996 +-0.104738 0.17966799999999999 -0.054078000000000001 +-0.056661599999999999 0.19581100000000001 -0.052082000000000003 +-0.094967399999999993 0.13598199999999999 -0.123239 +-0.123404 0.13445299999999999 -0.104745 +-0.14158899999999999 0.14890800000000001 -0.070838999999999999 +-0.18996499999999999 0.138072 -0.048089300000000001 +-0.139982 0.033953900000000002 -0.14260800000000001 +-0.069864399999999993 -0.090859499999999996 -0.18245500000000001 +-0.086995100000000006 -0.098003199999999999 -0.17360800000000001 +-0.088834700000000003 -0.113272 -0.16358300000000001 +-0.100216 -0.115422 -0.150258 +-0.116397 -0.113163 -0.131517 +-0.109588 -0.13589899999999999 -0.107769 +-0.113596 -0.103854 -0.14671100000000001 +-0.11530899999999999 -0.089547399999999999 -0.15719 +-0.129883 -0.073039800000000002 -0.149787 +-0.144897 -0.054548600000000003 -0.136877 +-0.14124300000000001 -0.075265499999999999 -0.131382 +-0.14052899999999999 -0.092973399999999998 -0.117774 +-0.12981899999999999 -0.11432199999999999 -0.11049200000000001 +-0.15495700000000001 -0.082057199999999997 -0.10525 +-0.17319000000000001 -0.066030400000000003 -0.091166200000000003 +-0.20571600000000001 -0.054378099999999999 -0.068628900000000007 +-0.17418700000000001 -0.032837499999999999 -0.103341 +-0.16337199999999999 -0.091157199999999994 -0.086861800000000003 +-0.15534100000000001 -0.116364 -0.074342400000000003 +-0.198099 -0.024419300000000001 -0.081596699999999994 +-0.20949599999999999 0.00564775 -0.0793296 +-0.104811 -0.15370700000000001 -0.083402500000000004 +-0.19336400000000001 0.0274989 -0.090507299999999999 +-0.19509799999999999 0.061398099999999997 -0.085237800000000002 +-0.17486599999999999 0.090639300000000006 -0.090710399999999997 +-0.18304200000000001 0.115353 -0.070731500000000003 +-0.209624 0.117075 -0.056809100000000001 +-0.23627999999999999 0.097237400000000002 -0.060618600000000002 +-0.27478000000000002 0.110265 -0.049717200000000003 +-0.22836699999999999 0.066876900000000003 -0.069848400000000005 +-0.251112 0.040523000000000003 -0.068888000000000005 +-0.21660799999999999 0.13717099999999999 -0.0376511 +-0.227411 0.154531 -0.0055548400000000001 +-0.19477700000000001 0.158609 -0.019337799999999999 +-0.164962 0.178815 -0.00017977800000000001 +-0.231456 0.15257499999999999 0.032368899999999999 +-0.199826 0.160548 0.057260499999999999 +-0.23696500000000001 0.12865599999999999 0.063383300000000004 +-0.17122999999999999 0.18273300000000001 0.040482600000000001 +-0.12856400000000001 0.19044800000000001 -0.0083741900000000001 +-0.099112900000000004 0.20863000000000001 0.0142043 +-0.16852400000000001 0.16987099999999999 0.075512599999999999 +-0.16358800000000001 0.13642799999999999 0.107596 +-0.14139199999999999 0.192471 0.065252599999999994 +-0.062453300000000003 0.21782499999999999 0.0084292499999999992 +-0.073927000000000007 0.22186600000000001 0.042957299999999997 +-0.049288400000000003 0.227715 0.078474699999999994 +-0.26221100000000003 0.14244299999999999 -0.0010980600000000001 +-0.26684400000000003 0.107888 0.061363800000000003 +-0.28881000000000001 0.082738199999999998 0.070589100000000002 +-0.28231400000000001 0.057168400000000001 0.096185699999999999 +-0.24498400000000001 0.055590199999999999 0.115396 +-0.28409000000000001 0.023000400000000001 0.120573 +-0.20538999999999999 0.04444 0.13869000000000001 +-0.21665699999999999 0.091373499999999996 0.10624599999999999 +-0.321301 -0.0037008000000000002 0.113436 +-0.26943400000000001 -0.018037999999999998 0.13242899999999999 +-0.227907 -0.034297800000000003 0.138796 +-0.31689800000000001 0.062854800000000002 0.071180499999999994 +-0.29585699999999998 -0.040717299999999998 0.108514 +-0.33918799999999999 0.0416768 0.077048000000000005 +-0.36685699999999999 0.020597299999999999 0.076817499999999997 +-0.39415299999999998 -0.0072429 0.068874900000000003 +-0.33737699999999998 0.017679199999999999 0.097116400000000005 +-0.37254799999999999 -0.040200699999999999 0.067309599999999997 +-0.38872499999999999 0.029425699999999999 0.053718700000000001 +-0.40494400000000003 0.040333500000000001 0.027247299999999999 +-0.42627300000000001 0.044979100000000001 -0.00258157 +-0.43641099999999999 0.052614599999999997 -0.038485699999999998 +-0.47139199999999998 0.0283371 -0.064143099999999995 +-0.43483100000000002 0.047724599999999999 -0.080985600000000005 +-0.44171700000000003 0.016121 0.016048699999999999 +-0.37862499999999999 0.056637100000000003 0.031346300000000001 +-0.34679900000000002 0.075044299999999994 0.0354924 +-0.367064 0.078526899999999997 0.0104993 +-0.33643499999999998 0.099260799999999996 0.0095090000000000001 +-0.43493999999999999 -0.016029499999999999 0.033262399999999998 +-0.38249 0.080793100000000007 -0.015004099999999999 +-0.38071899999999997 0.086307900000000007 -0.047486300000000002 +-0.41036 0.065654000000000004 -0.023395300000000001 +-0.377973 0.063850699999999996 -0.078274999999999997 +-0.34126400000000001 0.047466899999999999 -0.078586799999999998 +-0.33605200000000002 -0.000329239 -0.079713599999999996 +-0.34850100000000001 0.092618900000000004 -0.0565738 +-0.32839699999999999 0.11079799999999999 -0.034086699999999998 +-0.37736700000000001 -0.019203899999999999 -0.089504 +-0.421433 -0.030545300000000001 -0.098560499999999995 +-0.11468200000000001 0.20774200000000001 0.052704500000000001 +-0.116467 0.19922699999999999 0.082537600000000003 +-0.093815300000000004 0.20181099999999999 0.100205 +-0.063842700000000002 0.20569899999999999 0.114819 +-0.037225300000000003 0.22053200000000001 0.106709 +-0.0071348599999999998 0.21589 0.120472 +0.00412912 0.19402800000000001 0.140734 +0.028646100000000001 0.17255899999999999 0.15181600000000001 +0.036943499999999997 0.13911499999999999 0.16606299999999999 +0.075980900000000004 0.12629000000000001 0.16377700000000001 +0.11276600000000001 0.099520600000000001 0.164051 +0.051920099999999997 0.191606 0.135347 +0.0585701 0.215447 0.107917 +0.091660099999999994 0.21785099999999999 0.086749300000000001 +0.085837700000000003 0.18144099999999999 0.13312199999999999 +0.086650000000000005 0.22678200000000001 0.0485108 +0.037780099999999997 0.104182 0.17802599999999999 +0.0020230299999999999 0.078279199999999993 0.18759999999999999 +0.039565400000000001 0.065842899999999996 0.18756700000000001 +0.085516700000000001 0.034203600000000001 0.183976 +0.11866400000000001 -0.0089809 0.165937 +0.11601599999999999 0.16884399999999999 0.13156200000000001 +0.15592500000000001 0.155755 0.124628 +0.13918700000000001 0.18004400000000001 0.113112 +0.13562199999999999 0.205066 0.082698999999999995 +0.16142599999999999 0.20608199999999999 0.053754700000000002 +0.19921900000000001 0.191721 0.051740500000000002 +0.029606 0.22756000000000001 0.094773499999999997 +0.15004899999999999 0.10953400000000001 0.14941499999999999 +-0.0301278 0.10138900000000001 0.177374 +-0.027991700000000001 0.137845 0.16642799999999999 +0.18098900000000001 0.089541700000000002 0.14410999999999999 +0.20983399999999999 0.100628 0.127196 +0.15212899999999999 0.075748399999999994 0.15832099999999999 +0.22376599999999999 0.12330099999999999 0.109789 +0.200021 0.14199400000000001 0.11224199999999999 +0.19623099999999999 0.16669700000000001 0.094654299999999997 +0.21687300000000001 0.17302999999999999 0.070927400000000002 +0.247026 0.154665 0.068370799999999995 +0.27526200000000001 0.14904400000000001 0.042698199999999999 +0.24914900000000001 0.11998 0.094881999999999994 +0.26560800000000001 0.090727199999999994 0.087311899999999998 +0.24728900000000001 0.063513299999999995 0.099306500000000006 +0.223522 0.069351300000000005 0.123084 +0.21421599999999999 0.037139600000000002 0.116998 +0.187747 0.024219999999999998 0.13308900000000001 +0.15481900000000001 -0.0011934199999999999 0.14399899999999999 +0.168326 0.043484700000000001 0.15293599999999999 +0.170987 0.169879 0.10595300000000001 +-0.17428199999999999 0.0232202 -0.10807 +-0.28459699999999999 -0.073205500000000007 0.093172599999999994 +-0.25108000000000003 -0.092020000000000005 0.097697999999999993 +-0.222464 -0.075360899999999995 0.11957 +-0.21421299999999999 -0.11310099999999999 0.103683 +-0.30290299999999998 -0.094511600000000001 0.068640199999999998 +-0.19542799999999999 -0.055683900000000001 0.139432 +-0.156829 -0.070041800000000001 0.14629800000000001 +-0.097674800000000006 -0.065800800000000007 0.166048 +-0.14442099999999999 -0.115749 0.13261899999999999 +-0.29611999999999999 -0.12327299999999999 0.051463200000000001 +-0.266231 -0.14585600000000001 0.054020600000000002 +0.048883099999999999 0.029467400000000001 0.19211300000000001 +0.023935999999999999 -0.014548200000000001 0.19886300000000001 +0.071751300000000004 -0.0041070400000000002 0.187615 +-0.028724099999999999 -0.0015641400000000001 0.19486700000000001 +-0.15239800000000001 -0.135765 -0.057188999999999997 +-0.100872 -0.086491100000000001 -0.17091700000000001 +-0.059381799999999998 0.18434700000000001 0.134966 +-0.035312000000000003 0.178094 0.14674100000000001 +-0.077654299999999996 0.16547300000000001 0.13800000000000001 +-0.105632 0.15925500000000001 0.12751299999999999 +-0.076679600000000001 0.13824400000000001 0.15093899999999999 +-0.096847799999999998 0.113293 0.151863 +-0.085348199999999999 0.085329100000000005 0.16491500000000001 +-0.091735300000000006 0.049600900000000003 0.17249200000000001 +-0.12656600000000001 0.10163 0.143708 +-0.13344700000000001 0.15071200000000001 0.11687500000000001 +-0.14485700000000001 0.12063500000000001 0.126973 +-0.173737 0.107421 0.11894299999999999 +-0.16139999999999999 -0.14654200000000001 -0.039030500000000003 +-0.18660299999999999 -0.146865 -0.028232699999999999 +-0.201541 -0.12522800000000001 -0.037903100000000002 +-0.193134 -0.10312399999999999 -0.0539853 +-0.21104899999999999 -0.080392099999999994 -0.056783599999999997 +-0.159914 -0.163462 -0.0232774 +-0.22752800000000001 -0.128247 -0.034006099999999997 +-0.23887900000000001 -0.14904500000000001 -0.025366300000000001 +-0.22639699999999999 -0.167794 -0.014013599999999999 +-0.20136899999999999 -0.16749700000000001 -0.0134536 +-0.26285700000000001 -0.15434 -0.025979700000000001 +-0.28056799999999998 -0.17109099999999999 -0.014478599999999999 +-0.28174399999999999 -0.13850299999999999 -0.036859900000000001 +-0.309473 -0.13516600000000001 -0.043453199999999997 +-0.34013399999999999 -0.14111399999999999 -0.0045747799999999996 +-0.269395 -0.17985400000000001 0.0148325 +-0.30532599999999999 -0.16272600000000001 0.0012573899999999999 +-0.31532900000000003 -0.14105400000000001 0.020993600000000001 +-0.328044 -0.15237100000000001 -0.028746500000000001 +-0.34048800000000001 -0.131656 -0.0493684 +-0.321044 -0.11310000000000001 -0.0550007 +-0.35669299999999998 -0.110151 -0.063365699999999997 +-0.36424000000000001 -0.079950300000000002 -0.075380900000000001 +-0.387596 -0.11068699999999999 -0.0644618 +-0.36255900000000002 -0.047636100000000001 -0.081756999999999996 +-0.39623700000000001 -0.11378099999999999 -0.035386099999999997 +-0.37159599999999998 -0.120753 -0.010980200000000001 +-0.36457600000000001 -0.10081 0.020393499999999998 +-0.41761900000000002 -0.091820899999999997 -0.025175300000000001 +-0.418352 -0.071948700000000004 0.00161618 +-0.39399699999999999 -0.071492600000000003 0.0251311 +-0.39477200000000001 -0.0475977 0.045948799999999998 +-0.40268599999999999 -0.089535299999999998 -0.078489799999999998 +-0.42083999999999999 -0.068101700000000001 -0.089703199999999997 +-0.44040000000000001 -0.050982699999999999 0.0012606200000000001 +-0.45965499999999998 -0.047228399999999997 -0.020399899999999999 +-0.460671 -0.063892299999999999 -0.050193000000000002 +-0.47519600000000001 -0.0484471 -0.077546199999999996 +-0.48947099999999999 -0.0080625899999999997 -0.077667100000000003 +-0.475138 -0.024776099999999999 -0.021313200000000001 +-0.48642400000000002 -0.0015352499999999999 -0.044028100000000001 +-0.46840300000000001 0.00156711 -0.0067831100000000002 +-0.24207400000000001 -0.191329 0.0083080800000000007 +-0.42978300000000003 -0.087785299999999997 -0.073571899999999996 +-0.43628 -0.085082699999999997 -0.046137299999999999 +-0.19173100000000001 -0.18348100000000001 -0.00157165 +-0.201183 -0.19839000000000001 0.012441499999999999 +-0.20744899999999999 -0.198571 0.049506700000000001 +-0.17322299999999999 -0.19345499999999999 0.0066396600000000004 +-0.14982100000000001 -0.209649 0.0233901 +-0.13411500000000001 -0.22322900000000001 0.055024000000000003 +-0.13610800000000001 -0.19972400000000001 0.094550800000000004 +-0.15280099999999999 -0.18782299999999999 -0.00205719 +-0.18837999999999999 -0.17875099999999999 0.079835900000000001 +-0.099533499999999997 -0.22301099999999999 0.051333400000000001 +-0.114673 -0.17524799999999999 0.119217 +-0.44047700000000001 -0.049986900000000001 -0.095649999999999999 +-0.451957 -0.020404599999999998 -0.101857 +-0.073532500000000001 0.21576699999999999 0.093454300000000004 +-0.23469300000000001 0.0151621 -0.0705707 +-0.23231299999999999 -0.0142483 -0.068253999999999995 +-0.256967 -0.0290178 -0.062962299999999999 +-0.29299599999999998 -0.016672800000000002 -0.068659999999999999 +-0.20158100000000001 -0.20730599999999999 0.031393400000000002 +-0.22372500000000001 -0.19963600000000001 0.022959199999999999 +-0.183119 -0.21274199999999999 0.045317000000000003 +-0.18884600000000001 -0.19935 0.063469300000000006 +-0.160028 -0.21804699999999999 0.043169300000000001 +-0.20458399999999999 -0.184112 0.065892099999999995 +-0.172989 -0.19443199999999999 0.077981300000000003 +-0.16651299999999999 -0.17455000000000001 0.094699199999999997 +-0.18471000000000001 -0.14161699999999999 0.103157 +-0.15323700000000001 -0.20608099999999999 0.080472000000000002 +-0.13192000000000001 -0.21789500000000001 0.078765000000000002 +-0.113273 -0.211482 0.095236799999999996 +-0.087693900000000005 -0.20511699999999999 0.110143 +-0.061790900000000003 -0.21538099999999999 0.094153600000000004 +-0.046707100000000001 -0.213033 0.066072099999999995 +-0.027199999999999998 -0.201713 0.039605500000000002 +-0.0428995 -0.20211200000000001 0.0068477499999999997 +-0.050040500000000002 -0.19896 0.11833100000000001 +-0.0092382799999999998 -0.17464099999999999 0.123557 +-0.044816399999999999 -0.17225599999999999 0.14143500000000001 +0.016263400000000001 -0.16553399999999999 0.084978799999999993 +0.048965500000000002 -0.13069500000000001 0.084936899999999996 +0.032825699999999999 -0.14700299999999999 0.043867400000000001 +-0.064879099999999995 -0.14555499999999999 0.150587 +-0.0537054 -0.100021 0.16964099999999999 +-0.014756500000000001 -0.19434799999999999 0.067762799999999998 +-0.0079664100000000002 -0.18554999999999999 0.095633899999999994 +0.016580999999999999 -0.16354399999999999 0.0088471799999999996 +0.017707899999999999 -0.16073399999999999 -0.035924200000000003 +0.058777999999999997 -0.116217 0.12311800000000001 +0.077807600000000005 -0.081498000000000001 0.15444099999999999 +0.035232399999999997 -0.119032 0.15195600000000001 +0.0011408799999999999 -0.126919 0.16329099999999999 +-0.0183444 -0.100065 0.17824599999999999 +-0.0023533899999999999 -0.066350199999999998 0.19079199999999999 +0.034104099999999998 -0.066021999999999997 0.18746199999999999 +0.049249500000000002 -0.089232000000000006 0.16932700000000001 +-0.012811400000000001 -0.032974400000000001 0.194855 +-0.115415 -0.22503100000000001 0.066271099999999999 +-0.073077299999999998 -0.220943 0.060525799999999998 +-0.34335700000000002 -0.039519199999999997 0.085378099999999998 +-0.0113899 -0.18948999999999999 0.016989400000000002 +-0.118843 -0.19353100000000001 0.107193 +-0.13802300000000001 -0.17901800000000001 0.10664899999999999 +-0.100643 -0.19026000000000001 0.11654100000000001 +-0.094766100000000006 -0.17258599999999999 0.12842799999999999 +-0.111739 -0.149926 0.13206000000000001 +-0.076093300000000003 -0.16314200000000001 0.13944500000000001 +-0.234903 -0.085372000000000003 -0.049788699999999998 +-0.029894 0.2223 0.0050602800000000003 +-0.0077000000000000002 0.23047400000000001 0.027081999999999998 +-0.016640499999999999 0.23341400000000001 0.054480000000000001 +0.0101539 0.23432500000000001 0.053928900000000002 +0.037209399999999997 0.233262 0.0550673 +0.055545499999999998 0.23034199999999999 0.029847700000000001 +0.04283 0.22407199999999999 -0.00021755799999999999 +0.067147999999999999 0.21096999999999999 -0.036040700000000002 +0.011472 0.226074 0.0068730299999999996 +-0.055859100000000002 -0.10463 -0.18293400000000001 +-0.056593600000000001 -0.12168900000000001 -0.17641999999999999 +0.12175900000000001 -0.045740799999999998 -0.017806700000000002 +-0.053073700000000001 0.134352 0.160908 +0.123973 0.071887099999999995 0.168299 +0.0221523 0.23358599999999999 0.073706800000000003 +-0.0035341700000000001 -0.15252099999999999 0.14538100000000001 +-0.157503 -0.040724900000000001 -0.123137 +-0.40274399999999999 0.044257400000000002 -0.087763499999999994 +-0.40564099999999997 0.0079917900000000004 -0.096693600000000005 +-0.121506 -0.157661 -0.0590498 +-0.111905 -0.177728 -0.034197499999999999 +-0.077898800000000004 0.022964200000000001 0.18259 +-0.065333500000000003 -0.014212499999999999 0.187109 +-0.049683999999999999 0.018220799999999999 0.189225 +-0.0227026 0.040543200000000001 0.19126199999999999 +-0.10136000000000001 -0.0715673 -0.17546600000000001 +-0.30970999999999999 0.108636 0.023971200000000002 +0.28432800000000003 0.10201300000000001 0.065753500000000006 +0.30034 0.087615600000000002 0.035027700000000002 +0.31309300000000001 0.101645 0.0055769499999999998 +0.28173399999999998 0.059859000000000002 0.0301131 +0.31369000000000002 0.093917200000000006 -0.025519400000000001 +0.313301 0.115146 -0.051408700000000002 +0.30536099999999999 0.14809800000000001 -0.0427014 +0.26683099999999998 0.16988500000000001 -0.051427899999999999 +0.28652 0.12620000000000001 0.0529506 +0.30213800000000002 0.14977799999999999 -0.0123269 +0.29091 0.14998 0.016253500000000001 +0.26822000000000001 0.16663800000000001 0.021285399999999999 +0.247534 0.182808 -0.00123423 +0.21554499999999999 0.19503899999999999 0.0040064499999999999 +0.25356600000000001 0.16545299999999999 0.0442228 +0.28728500000000001 0.162995 -0.030038700000000002 +0.26483299999999999 0.17541200000000001 -0.0261528 +0.24618300000000001 0.18101 -0.0421722 +0.216699 0.18173800000000001 -0.060451600000000001 +0.191444 0.19706299999999999 -0.037413799999999997 +0.19761799999999999 0.159773 -0.081418099999999993 +0.16262599999999999 0.15104400000000001 -0.086919499999999997 +0.24641299999999999 0.171038 -0.064018500000000006 +0.262152 0.14856 -0.076847100000000002 +0.23690600000000001 0.117298 -0.083770399999999995 +0.26650800000000002 0.119186 -0.080167500000000003 +0.29137299999999999 0.107261 -0.070558999999999997 +0.26872699999999999 0.080423800000000004 -0.071397199999999994 +0.15784400000000001 0.20099 -0.041992700000000001 +0.127221 0.199769 -0.052480800000000001 +0.092187099999999994 0.19914399999999999 -0.058280699999999998 +0.16055800000000001 0.208675 -0.012496500000000001 +0.13623399999999999 0.216589 0.0128056 +0.106226 0.20925299999999999 -0.0354265 +0.10578899999999999 0.21884999999999999 -0.00422888 +0.098713899999999993 0.22445399999999999 0.022799799999999999 +0.31013200000000002 0.13089999999999999 0.0048277299999999997 +0.26433499999999999 0.048680800000000003 0.050985999999999997 +0.242897 0.032061600000000003 0.061379900000000001 +0.25531500000000001 0.050923099999999999 0.076559199999999994 +0.223445 0.0189006 0.070345400000000002 +0.19844600000000001 0.00226779 0.082946900000000004 +0.15964200000000001 -0.026474299999999999 0.077045500000000003 +0.18465899999999999 -0.0099512100000000003 0.058162800000000001 +0.29580800000000002 0.072896100000000005 -0.027399799999999998 +0.30413299999999999 0.081461199999999998 -0.0056807699999999999 +0.29666599999999999 0.074601799999999996 0.015656699999999999 +0.27832200000000001 0.056460799999999998 -0.024005499999999999 +0.262042 0.040640099999999998 0.0022073399999999999 +0.25966899999999998 0.041196400000000001 -0.0218671 +0.24385200000000001 0.034963000000000001 -0.042100100000000001 +0.21526600000000001 0.0162635 -0.0527158 +0.24581500000000001 0.051806400000000002 -0.067567100000000005 +0.21494199999999999 0.046667 -0.086470699999999998 +0.28263199999999999 0.0654666 -0.0447211 +0.26852300000000001 0.063384200000000002 -0.059689699999999998 +0.28129599999999999 0.076098200000000005 -0.059218699999999999 +0.28325400000000001 0.091085200000000005 -0.069259600000000004 +0.26701999999999998 0.099399399999999999 -0.0774233 +0.25233899999999998 0.086640400000000006 -0.080310999999999994 +0.235073 0.096938499999999997 -0.084024699999999994 +0.21082600000000001 0.109417 -0.086493700000000007 +0.214308 0.085839799999999994 -0.088021299999999997 +0.21404699999999999 0.13722599999999999 -0.084492700000000004 +0.25352000000000002 0.068597500000000006 -0.073375599999999999 +0.25236900000000001 0.10843899999999999 -0.082266300000000001 +0.205928 0.065937200000000001 -0.091677099999999997 +0.18831300000000001 0.043192899999999999 -0.099293300000000001 +0.17059099999999999 0.025554299999999999 -0.111447 +0.19422700000000001 -0.0034471300000000001 0.034471599999999998 +0.186031 -0.0064174100000000001 -0.000610057 +0.22162100000000001 0.013203299999999999 0.020514500000000001 +0.158466 -0.0217212 -0.011189899999999999 +0.203821 0.0053486200000000001 -0.024697799999999999 +-0.087367299999999995 -0.213787 0.025887500000000001 +-0.098808800000000002 -0.19930899999999999 -0.0031412100000000002 +-0.025040699999999999 0.23188400000000001 0.077572000000000002 +-0.013288700000000001 0.22770199999999999 0.099276100000000006 +0.0090716900000000003 0.22456699999999999 0.10582 +-0.038122299999999998 0.23139299999999999 0.0596562 +-0.059791299999999999 0.22719600000000001 0.059059899999999999 +-0.068061999999999998 0.222998 0.076201599999999994 +-0.076820399999999997 0.22189500000000001 0.060625499999999999 +-0.094470600000000002 0.215866 0.048674000000000002 +-0.107247 0.20984700000000001 0.033351199999999998 +-0.119042 0.202294 0.0174804 +-0.110264 0.199934 -0.0020657100000000001 +-0.10877199999999999 0.189998 -0.028283200000000001 +-0.0885764 0.21595300000000001 0.029554899999999999 +-0.13533700000000001 0.19406300000000001 0.0098582900000000005 +-0.15611700000000001 0.18779299999999999 0.020395300000000002 +-0.17702100000000001 0.179366 0.018911299999999999 +-0.29922399999999999 0.119756 -0.034104299999999997 +-0.28539500000000001 0.131717 -0.0127806 +-0.281551 0.13092799999999999 0.0140859 +-0.28594700000000001 0.115513 0.0369868 +-0.15040500000000001 -0.21848200000000001 0.065269999999999995 +0.015202800000000001 -0.043048200000000002 0.19713900000000001 +0.040283399999999997 -0.0349374 0.19456100000000001 +-0.085107299999999997 -0.18548999999999999 -0.038290499999999998 +-0.067330799999999996 -0.18115400000000001 -0.059406100000000003 +-0.057076200000000001 -0.172068 -0.089080699999999999 +-0.028735199999999999 -0.17444999999999999 -0.090752700000000006 +-0.015901700000000001 -0.17755799999999999 -0.069441000000000003 +-0.020461799999999999 -0.18479499999999999 -0.041148900000000002 +0.0087381100000000003 -0.16711100000000001 -0.061560900000000002 +-0.038143299999999998 -0.19418299999999999 -0.020434899999999999 +-0.051034000000000003 -0.189416 -0.041780200000000003 +-0.35121999999999998 -0.059801199999999999 0.066219700000000006 +-0.33698 -0.088017700000000004 0.052541499999999998 +-0.41443799999999997 -0.029614000000000001 0.043753599999999997 +-0.39668399999999998 -0.0282297 0.0588966 +-0.41073399999999999 -0.012068499999999999 0.055849500000000003 +-0.40436 0.0070651100000000003 0.058606800000000001 +-0.38616099999999998 0.013236899999999999 0.069292699999999999 +-0.37776100000000001 -0.0020853899999999999 0.081099299999999999 +-0.37323600000000001 -0.022365300000000001 0.078308199999999994 +-0.35734100000000002 -0.027750899999999998 0.085550200000000007 +-0.337893 -0.019052599999999999 0.10051599999999999 +-0.32018999999999997 -0.038641000000000002 0.098219699999999993 +-0.31034699999999998 -0.0235748 0.112107 +-0.29525499999999999 -0.0032073000000000002 0.12631500000000001 +-0.290437 -0.023630100000000001 0.120756 +-0.27395000000000003 -0.040667200000000001 0.11799 +-0.25102000000000002 -0.037702899999999998 0.128663 +-0.22742100000000001 -0.066667400000000002 -0.056902500000000002 +-0.226022 -0.044700299999999998 -0.063681199999999993 +-0.210588 -0.035672700000000002 -0.071603100000000003 +-0.21408099999999999 -0.016863099999999999 -0.0739229 +-0.19480700000000001 -0.043006700000000002 -0.079277600000000004 +-0.188472 -0.062925999999999996 -0.077075199999999996 +-0.22180900000000001 -0.0025995699999999998 -0.072599800000000006 +-0.22142000000000001 0.0115402 -0.074242799999999998 +-0.20996600000000001 0.0222645 -0.079748100000000002 +-0.20374200000000001 0.035738699999999998 -0.083051 +-0.208844 0.052203399999999997 -0.078747399999999995 +-0.19082199999999999 0.045095099999999999 -0.090910699999999997 +-0.177425 0.061166100000000001 -0.099123900000000001 +-0.20915400000000001 0.071997000000000005 -0.075677300000000003 +-0.16361000000000001 0.078118900000000005 -0.106493 +-0.19264300000000001 0.080421699999999999 -0.081731100000000001 +-0.188553 0.098312899999999995 -0.076963100000000007 +-0.15820699999999999 0.098198599999999997 -0.101697 +-0.14588999999999999 0.087709400000000007 -0.11913600000000001 +-0.14155999999999999 0.107293 -0.111634 +-0.12382700000000001 0.117911 -0.119323 +-0.108028 0.110926 -0.135713 +-0.112326 0.089653399999999994 -0.14544399999999999 +-0.091987899999999997 0.0895894 -0.15718299999999999 +-0.35440700000000003 -0.140129 -0.027650899999999999 +0.0608099 -0.102747 0.14763699999999999 +0.079597399999999999 -0.093919199999999994 0.13094600000000001 +0.10627300000000001 -0.066251099999999993 0.13344900000000001 +0.114315 -0.063354800000000003 0.0946988 +0.091175599999999996 -0.080818000000000001 0.066456399999999999 +0.094593399999999994 -0.069621299999999997 0.0242046 +0.094465999999999994 -0.049551900000000003 0.16490099999999999 +0.103223 -0.059938999999999999 -0.015014100000000001 +0.107659 -0.055741100000000002 0.0087524100000000004 +0.112343 -0.054881199999999998 0.032969499999999999 +0.13300999999999999 -0.039976699999999997 0.038853800000000001 +0.14019400000000001 -0.039123900000000003 0.064310999999999993 +0.14047399999999999 -0.0411727 0.093419500000000003 +0.15845000000000001 -0.022454399999999999 0.11312999999999999 +0.064370499999999997 -0.103524 -0.016815699999999999 +0.058664300000000003 -0.110442 0.0129414 +0.075187900000000002 -0.090970300000000004 0.026571000000000001 +0.079995499999999997 -0.083989900000000006 -0.018291100000000001 +0.080597699999999994 -0.082260700000000006 0.0052742099999999997 +-0.199652 -0.070645200000000005 -0.065935400000000005 +-0.19524 -0.087056499999999995 -0.061056899999999997 +-0.36365199999999998 0.0067447899999999996 0.087702000000000002 +-0.34859200000000001 0.0017769800000000001 0.099163799999999996 +-0.352099 0.020551799999999999 0.087143799999999993 +-0.33396100000000001 0.0044392199999999998 0.106489 +-0.31508599999999998 0.018744199999999999 0.10863100000000001 +-0.00316359 0.048916899999999999 -0.19022700000000001 +0.0082884800000000008 0.0264747 -0.19409199999999999 +-0.0089128200000000001 0.0052444600000000003 -0.19856599999999999 +-0.00083717899999999996 -0.0247146 -0.19836899999999999 +-0.024708000000000001 -0.19103500000000001 0.113136 +-0.028143999999999999 -0.180474 0.13120999999999999 +-0.0241438 -0.16289600000000001 0.14561299999999999 +-0.045124499999999998 -0.186443 0.130327 +-0.060770499999999998 -0.19034000000000001 0.12726899999999999 +-0.068835800000000003 -0.203401 0.115117 +-0.062611 -0.17513000000000001 0.13708400000000001 +-0.0574099 -0.160773 0.145842 +-0.047680199999999999 -0.14828 0.15374499999999999 +-0.04727 -0.124358 0.16361999999999999 +-0.074239899999999998 -0.11637599999999999 0.158165 +-0.103323 -0.097917000000000004 0.15429200000000001 +-0.022877999999999999 -0.122805 0.16883200000000001 +0.027446000000000002 0.018355300000000001 -0.190554 +0.047287900000000001 0.024984099999999999 -0.18012 +0.047031000000000003 0.048085200000000002 -0.16816800000000001 +0.0245826 0.063830300000000006 -0.173037 +0.068977899999999995 0.0532592 -0.15346899999999999 +0.039009000000000002 0.00069482799999999998 -0.19258600000000001 +0.039916899999999998 -0.022020700000000001 -0.193298 +0.059217499999999999 -0.030858199999999999 -0.18629899999999999 +0.088181899999999994 -0.0293513 -0.172655 +0.11654 -0.023742200000000001 -0.15226000000000001 +0.045732099999999998 -0.0484051 -0.189 +0.028347299999999999 -0.061920599999999999 -0.19234399999999999 +0.0088470900000000002 -0.0532585 -0.19677700000000001 +-0.0167742 -0.045966300000000002 -0.19631599999999999 +0.014673500000000001 -0.0790357 -0.193471 +0.0207706 -0.101966 -0.18762000000000001 +0.044992699999999997 -0.105242 -0.174844 +0.065162300000000006 0.0109671 -0.17912700000000001 +0.078185400000000002 -0.0078636299999999999 -0.178011 +0.081928000000000001 0.0167058 -0.16800699999999999 +0.083948599999999998 0.0345127 -0.157031 +0.012745299999999999 0.0857324 -0.16769300000000001 +0.029395899999999999 0.0958981 -0.15381300000000001 +0.030408000000000001 0.12443 -0.136266 +-0.00876124 0.088804599999999997 -0.17280999999999999 +-0.020671499999999999 0.075122700000000001 -0.18135399999999999 +0.053330799999999998 0.13374900000000001 -0.12095 +0.0571218 0.15846299999999999 -0.10569199999999999 +0.082139900000000002 0.17514399999999999 -0.088263599999999998 +0.033958700000000001 0.17282600000000001 -0.098476099999999997 +0.025033799999999998 0.190467 -0.076702599999999996 +0.036119800000000001 0.205898 -0.048656199999999997 +0.13370000000000001 0.159717 -0.088580800000000001 +0.15319199999999999 0.17372499999999999 -0.079540899999999998 +0.15035699999999999 0.19109300000000001 -0.062839400000000004 +0.000183964 -0.174592 -0.0415267 +0.0062644399999999996 -0.172179 -0.019166900000000001 +0.022800399999999998 -0.15598799999999999 -0.0130102 +0.035125099999999999 -0.14147299999999999 0.0086342199999999997 +0.036023699999999999 -0.140713 -0.031627599999999999 +0.0020287399999999998 -0.17727200000000001 0.0012006499999999999 +0.0054414499999999996 -0.176177 0.022733900000000001 +0.017425 -0.16474800000000001 0.039874399999999997 +0.023538799999999999 -0.15918599999999999 0.062702800000000003 +0.053802200000000001 -0.119801 -0.039014300000000002 +0.0025606000000000001 -0.095045400000000002 -0.19255700000000001 +-0.018823800000000002 -0.093355199999999999 -0.19203700000000001 +-0.030994399999999998 -0.073046200000000006 -0.192797 +-0.028106800000000001 -0.109252 -0.187754 +-0.075042999999999999 -0.21301 0.103434 +-0.089424799999999999 -0.215145 0.098752900000000005 +-0.101426 -0.21724599999999999 0.093419299999999997 +-0.112609 -0.221304 0.082051200000000005 +-0.102007 -0.206618 0.104586 +-0.33370899999999998 0.073468800000000001 -0.070933899999999994 +-0.30549999999999999 0.065284300000000003 -0.070127700000000001 +-0.35792200000000002 0.075518000000000002 -0.071040400000000004 +-0.35893599999999998 0.057208000000000002 -0.079511700000000005 +0.0591707 0.23129 0.051818200000000002 +0.20324800000000001 0.015473300000000001 0.10573299999999999 +-0.47254600000000002 0.0131591 -0.028855599999999999 +-0.46147100000000002 0.032282100000000001 -0.0411949 +-0.45131100000000002 0.044170399999999999 -0.061588700000000003 +-0.45650800000000002 0.033673599999999998 -0.084952299999999994 +-0.47535300000000003 0.013651999999999999 -0.087154400000000007 +-0.48302 -0.028537799999999999 -0.091103900000000002 +-0.48918099999999998 -0.030111300000000001 -0.067796700000000001 +-0.46326200000000001 -0.040469699999999997 -0.096372600000000003 +-0.43277100000000002 0.057318099999999997 -0.060452199999999998 +-0.41554999999999997 0.060145200000000003 -0.076112100000000002 +-0.39707500000000001 0.067692199999999994 -0.072884299999999999 +-0.38215399999999999 0.078306299999999995 -0.065485000000000002 +-0.366919 0.087188100000000004 -0.059812900000000002 +-0.364506 0.094590199999999999 -0.042663399999999997 +-0.34636699999999998 0.102863 -0.039051700000000002 +-0.34191100000000002 0.10630000000000001 -0.019330099999999999 +-0.35290700000000003 0.095371700000000004 -0.0041110699999999997 +-0.33055499999999999 0.100814 -0.051665099999999999 +-0.31083 0.10825799999999999 -0.047939799999999998 +-0.29252899999999998 0.097151699999999994 -0.056889200000000001 +-0.376027 0.088421 -0.0301143 +-0.39543099999999998 0.078534000000000007 -0.033118799999999997 +-0.27186199999999999 0.091324900000000001 -0.059845500000000003 +-0.28328300000000001 0.071777800000000003 -0.066117499999999996 +-0.42215200000000003 0.063294699999999995 -0.045624499999999998 +-0.33332299999999998 0.088285299999999997 -0.062727900000000003 +-0.437052 0.030601900000000001 -0.094291100000000003 +-0.42083199999999998 0.0396817 -0.090828099999999995 +-0.42020800000000003 0.0209628 -0.096290500000000001 +-0.42460300000000001 0.00147628 -0.100178 +-0.40923799999999999 -0.01306 -0.097180500000000003 +-0.397117 -0.034928899999999999 -0.092550599999999997 +-0.40230700000000003 0.0263118 -0.0936004 +-0.38781900000000002 0.0136986 -0.092105500000000007 +-0.36457600000000001 0.0032477199999999999 -0.087299699999999994 +-0.39181700000000003 -0.0052851299999999999 -0.093420699999999995 +-0.38889899999999999 0.0353587 -0.089889999999999998 +-0.373861 0.0459462 -0.084154599999999996 +-0.35658099999999998 0.035406100000000003 -0.084031900000000007 +-0.33561600000000003 0.023919099999999999 -0.079937999999999995 +-0.31268600000000002 0.0114246 -0.074903499999999998 +-0.28382099999999999 0.014154 -0.069988700000000001 +-0.38866299999999998 0.050131700000000001 -0.085424600000000003 +-0.40062500000000001 0.057077099999999999 -0.081787200000000004 +-0.277312 0.039557700000000001 -0.069580100000000006 +-0.26359500000000002 0.0224806 -0.068115200000000001 +-0.41149400000000003 0.052111699999999997 -0.084395800000000007 +-0.423315 0.0520061 -0.0810506 +-0.42659999999999998 0.058336699999999998 -0.071036699999999994 +-0.42052800000000001 0.064336500000000005 -0.059779400000000003 +-0.43721500000000002 0.052997900000000001 -0.070598900000000006 +-0.44746599999999997 0.044273100000000003 -0.075328199999999998 +-0.45922400000000002 0.037086899999999999 -0.071263999999999994 +-0.46836100000000003 0.028244200000000001 -0.078411300000000003 +-0.47800900000000002 0.0179968 -0.072937600000000005 +-0.47995100000000002 0.0140616 -0.053883899999999998 +-0.46308300000000002 0.021495199999999999 -0.090712399999999999 +-0.46345700000000001 0.0029889000000000001 -0.101217 +-0.48569099999999998 0.0064728099999999998 -0.078871399999999994 +-0.49019400000000002 0.0012374300000000001 -0.061941700000000002 +-0.48902699999999999 -0.016199399999999999 -0.055318100000000002 +-0.48362300000000003 -0.018897500000000001 -0.037591100000000002 +-0.47831899999999999 -0.0052931799999999998 -0.024031199999999999 +-0.48393199999999997 -0.0027069199999999998 -0.089892299999999994 +-0.48110700000000001 -0.015395499999999999 -0.093549599999999997 +-0.46911000000000003 -0.025215899999999999 -0.099654099999999995 +-0.48830099999999999 -0.019441 -0.083633799999999994 +-0.490506 -0.018627399999999999 -0.071670600000000001 +-0.48746800000000001 -0.0311436 -0.079934599999999995 +-0.47920400000000002 -0.039190599999999999 -0.085295200000000002 +-0.48227300000000001 -0.039566999999999998 -0.072251599999999999 +-0.47358800000000001 -0.0502137 -0.057610000000000001 +-0.48236200000000001 -0.037578300000000002 -0.058993499999999997 +-0.46798899999999999 -0.049158199999999999 -0.038457999999999999 +-0.46488099999999999 -0.063439499999999996 -0.069595699999999996 +-0.45116400000000001 -0.075200100000000006 -0.062683000000000003 +-0.461144 -0.0570308 -0.087161699999999995 +-0.47021000000000002 -0.046769900000000003 -0.088560600000000003 +-0.45486799999999999 -0.061269999999999998 -0.032468400000000001 +-0.44309900000000002 0.039289999999999999 -0.086606500000000003 +-0.47388599999999997 -0.034972200000000002 -0.094978999999999994 +-0.487452 -0.028162 -0.052937600000000001 +-0.46882000000000001 -0.0106219 -0.099822900000000006 +-0.44827400000000001 0.030902499999999999 -0.092328599999999997 +-0.443498 0.021339299999999999 -0.096643099999999996 +-0.45456000000000002 0.014838799999999999 -0.097697800000000001 +-0.43189899999999998 0.016569400000000001 -0.098347500000000004 +-0.47469600000000001 0.00041085399999999999 -0.097229200000000002 +-0.46948499999999999 0.0096302100000000002 -0.095245999999999997 +0.29695500000000002 0.106656 0.047249800000000002 +0.291078 0.088316699999999998 0.052780599999999997 +0.28881299999999999 0.0737593 0.040867199999999999 +0.27787699999999999 0.061405300000000003 0.047343200000000002 +0.27035100000000001 0.059129899999999999 0.060632100000000001 +0.26618700000000001 0.069788799999999998 0.0793687 +0.305647 0.105588 0.029909700000000001 +0.31212600000000001 0.116415 0.0165124 +0.31736900000000001 0.11674900000000001 0.0010400100000000001 +0.31616300000000003 0.104461 -0.011578 +0.31821100000000002 0.110014 -0.027318200000000001 +0.313357 0.10102700000000001 -0.0413356 +0.31855899999999998 0.12438 -0.037421099999999999 +0.31109599999999998 0.133216 -0.0514791 +0.303008 0.123002 -0.066372100000000003 +0.31335200000000002 0.13942299999999999 -0.0292404 +0.30516799999999999 0.10059800000000001 -0.056823199999999997 +0.30038300000000001 0.145261 -0.060021699999999997 +0.28053099999999997 0.15626000000000001 -0.063975099999999993 +0.29135299999999997 0.15679599999999999 -0.049708000000000002 +0.30542599999999998 0.087295499999999998 -0.037831700000000003 +0.311948 0.13780899999999999 -0.0138391 +0.307419 0.14267299999999999 -0.00177921 +0.29540899999999998 0.151976 0.0015104700000000001 +0.30266399999999999 0.141484 0.0124035 +0.29664000000000001 0.13967299999999999 0.027252499999999999 +0.30335299999999998 0.15110000000000001 -0.0276759 +-0.035541799999999998 -0.15595000000000001 0.15140100000000001 +0.24587600000000001 0.184257 -0.021774000000000002 +0.23197400000000001 0.18943399999999999 -0.0098991200000000008 +0.21463399999999999 0.19536999999999999 -0.0149903 +0.197855 0.20113600000000001 -0.0045619299999999996 +0.150815 0.022534999999999999 0.157779 +0.12464600000000001 0.019225099999999998 0.17016899999999999 +0.098797800000000005 0.0082900600000000001 0.17925199999999999 +0.093536900000000006 -0.0207146 0.176347 +0.13756699999999999 0.00340953 0.15896099999999999 +0.13530900000000001 -0.0228522 0.14630199999999999 +0.12446500000000001 -0.043863399999999997 -0.13242399999999999 +-0.33631800000000001 -0.12790599999999999 0.016509599999999999 +-0.33239600000000002 -0.0089667299999999991 0.107708 +-0.32433400000000001 -0.021719499999999999 0.10666200000000001 +-0.34302700000000003 -0.0083719499999999995 0.10223 +-0.34997899999999998 -0.017221899999999998 0.094727500000000006 +-0.35156300000000001 -0.0075691999999999999 0.097001199999999996 +-0.35756700000000002 0.00049845499999999997 0.093700699999999998 +-0.352358 0.010696199999999999 0.093009700000000001 +-0.36506100000000002 -0.0024184499999999999 0.0889268 +-0.37137799999999999 -0.011685299999999999 0.084558999999999995 +-0.38150099999999998 -0.013303799999999999 0.077807600000000005 +-0.38503999999999999 -0.025347499999999999 0.068713800000000005 +-0.26522099999999998 0.13580400000000001 -0.022798200000000001 +-0.24834700000000001 0.143818 -0.015516200000000001 +-0.22962399999999999 0.14505699999999999 -0.023862000000000001 +0.083714700000000003 0.203074 0.114313 +0.191547 0.00061397600000000002 -0.058867000000000003 +0.17957200000000001 -0.0045589100000000002 -0.081695000000000004 +-0.0179285 0.19112499999999999 0.14182500000000001 +0.074671000000000001 0.125449 -0.115605 +0.100378 0.12325700000000001 -0.105614 +0.109501 0.14819099999999999 -0.096700099999999997 +0.0861875 0.10497099999999999 -0.118148 +0.120268 0.113011 -0.101312 +0.23039999999999999 0.033054 -0.062778700000000007 +-0.017502299999999998 0.205236 0.13064000000000001 +-0.024694500000000001 0.215588 0.118203 +-0.040251299999999997 0.210836 0.119478 +-0.052051699999999999 0.19969200000000001 0.12662899999999999 +-0.052046700000000001 0.21516299999999999 0.109544 +-0.049136399999999997 0.22328100000000001 0.094889500000000002 +-0.033057400000000001 0.227876 0.091985300000000006 +-0.00155887 0.20564099999999999 0.13136999999999999 +0.0094258099999999997 0.214006 0.121922 +0.0140483 0.20339299999999999 0.132712 +0.023025400000000001 0.188966 0.14258000000000001 +0.028378299999999999 0.19961100000000001 0.13371 +0.044337500000000002 0.205985 0.12504499999999999 +0.064579300000000006 0.202014 0.122581 +0.038270699999999998 0.218421 0.11045199999999999 +0.037754500000000003 0.18965699999999999 0.13985900000000001 +0.049509699999999997 0.17627999999999999 0.14607300000000001 +0.067353200000000002 0.18032699999999999 0.13928299999999999 +0.080270800000000003 0.16595799999999999 0.14452999999999999 +0.076589400000000002 0.14677899999999999 0.15516099999999999 +0.0990867 0.13400400000000001 0.155061 +0.043261599999999997 0.16105800000000001 0.15548500000000001 +0.055619300000000003 0.14540500000000001 0.160331 +0.053991999999999998 0.124489 0.168818 +0.024575300000000001 0.15474499999999999 0.161164 +0.047873499999999999 0.224578 0.095836599999999994 +0.068413100000000004 0.22211800000000001 0.0900175 +0.078492999999999993 0.22573699999999999 0.069893899999999995 +0.10087599999999999 0.22176299999999999 0.065964999999999996 +0.113688 0.21227399999999999 0.082663399999999998 +0.10401100000000001 0.15382999999999999 0.14432800000000001 +0.126494 0.21418899999999999 0.066895499999999997 +-0.066551700000000005 0.19398099999999999 0.12488200000000001 +-0.080472199999999994 0.198127 0.113743 +-0.074614 0.18105199999999999 0.13045499999999999 +0.020276300000000001 0.21959100000000001 0.113828 +0.12228799999999999 0.20153699999999999 0.097016000000000005 +0.14039099999999999 0.19308400000000001 0.097989800000000002 +0.15592800000000001 0.19652 0.082752199999999998 +0.103478 0.20722199999999999 0.098770399999999994 +0.158633 0.184228 0.098665600000000006 +0.14837 0.206869 0.068135500000000002 +0.14377200000000001 0.21349399999999999 0.050460699999999997 +0.157944 0.211701 0.027766800000000001 +0.13782800000000001 0.216728 0.032207300000000001 +0.119203 0.220974 0.023940199999999998 +0.181201 0.20346600000000001 0.036704899999999999 +0.20399400000000001 0.19661400000000001 0.026939399999999999 +0.10811900000000001 0.22308800000000001 0.039594200000000003 +0.16981599999999999 0.19757 0.069134899999999999 +0.18832699999999999 0.18931200000000001 0.068669999999999995 +0.18108199999999999 0.19875499999999999 0.054555899999999997 +0.088028200000000001 0.211392 0.101128 +-0.046627300000000003 -0.13242300000000001 -0.17415700000000001 +-0.031881399999999997 -0.139512 -0.17436099999999999 +-0.0157739 -0.12979399999999999 -0.182809 +-0.0169605 -0.14768400000000001 -0.170043 +-0.017156500000000002 -0.156499 -0.15394099999999999 +-0.0342955 -0.157638 -0.143482 +0.0039245299999999999 -0.12660199999999999 -0.18346299999999999 +-0.042501200000000003 -0.14293400000000001 -0.16689999999999999 +-0.045460199999999999 -0.15029500000000001 -0.15321000000000001 +-0.060321199999999998 -0.144284 -0.15159700000000001 +-0.056104800000000003 -0.20834900000000001 0.10713399999999999 +-0.046628299999999998 -0.20996300000000001 0.094216999999999995 +-0.039513600000000003 -0.20919499999999999 0.079873399999999997 +-0.030061899999999999 -0.20460500000000001 0.064361600000000005 +-0.0542184 -0.21560799999999999 0.080012100000000003 +-0.072394700000000006 -0.220944 0.079464699999999999 +-0.039844499999999998 -0.202712 0.10667699999999999 +-0.026282 -0.20185600000000001 0.078793100000000005 +-0.019110599999999998 -0.19503599999999999 0.090665099999999998 +-0.060259300000000002 -0.21837000000000001 0.067015400000000003 +-0.054789499999999998 -0.214918 0.052209499999999999 +-0.043168100000000001 -0.20902499999999999 0.040407400000000003 +-0.049205400000000003 -0.207652 0.023148999999999999 +-0.068837400000000007 -0.205485 0.0059794999999999996 +-0.031862300000000003 -0.20133799999999999 0.021691999999999999 +-0.025042700000000001 -0.195297 0.0047607400000000003 +-0.078398899999999994 -0.21936600000000001 0.091723100000000002 +-0.036840999999999999 -0.19533400000000001 0.117821 +-0.085594299999999998 -0.22392500000000001 0.070458499999999993 +-0.101036 -0.22503400000000001 0.072294600000000001 +0.0079754800000000001 -0.111749 -0.18823799999999999 +0.19828899999999999 0.201344 0.012056900000000001 +0.12970699999999999 -0.000104893 -0.14339099999999999 +0.122803 0.022641999999999999 -0.141595 +0.13120399999999999 0.037935299999999998 -0.129884 +0.065233100000000002 -0.068316100000000005 0.174012 +0.169741 0.022357700000000001 0.14605299999999999 +0.174405 0.0061380999999999996 0.13108 +0.16862199999999999 -0.0086767500000000004 0.12098 +0.15368699999999999 -0.016482900000000002 0.13142699999999999 +0.171817 -0.013763900000000001 0.103162 +0.160136 -0.025257700000000001 0.095212199999999997 +0.17286899999999999 -0.016820499999999999 0.085170300000000004 +0.18521199999999999 -0.0056057600000000004 0.093273599999999998 +-0.26668500000000001 -0.165547 0.036670899999999999 +0.27587899999999999 0.083884600000000004 0.074508199999999997 +-0.093713199999999997 0.19223000000000001 0.111342 +-0.10699699999999999 0.17665900000000001 0.115898 +-0.11094999999999999 0.19046299999999999 0.10029 +-0.126606 0.18709300000000001 0.091594300000000003 +-0.14208499999999999 0.184033 0.0817885 +-0.154062 0.170706 0.087510400000000002 +-0.16913900000000001 0.15326999999999999 0.092154299999999995 +-0.149253 0.15639500000000001 0.103116 +-0.18696199999999999 0.15484800000000001 0.076457800000000006 +-0.122296 0.176425 0.105839 +-0.1187 0.16406100000000001 0.117324 +-0.20508599999999999 0.146206 0.071282300000000007 +-0.22044800000000001 0.14922199999999999 0.0532732 +-0.21107899999999999 0.163023 0.037024300000000003 +-0.21949399999999999 0.16155800000000001 0.018494 +-0.237511 0.15316099999999999 0.0125252 +-0.249528 0.14479800000000001 0.026799300000000002 +-0.24638699999999999 0.136378 0.046015599999999997 +-0.26455699999999999 0.13919799999999999 0.017173600000000001 +-0.24715899999999999 0.148788 3.9831299999999999e-05 +-0.25006899999999999 0.147707 0.013403099999999999 +0.0553132 0.17621000000000001 -0.091744300000000001 +0.17332400000000001 0.167907 -0.080498399999999998 +0.18978200000000001 0.175871 -0.073497000000000007 +0.190862 0.188891 -0.056890299999999998 +0.20769699999999999 0.17263800000000001 -0.073397299999999999 +0.224967 0.17091999999999999 -0.071398100000000006 +0.21594099999999999 0.158803 -0.080172900000000005 +-0.48482399999999998 -0.0268311 -0.043449399999999999 +-0.47713800000000001 -0.0315994 -0.033087499999999999 +-0.46851500000000001 -0.036879000000000002 -0.023458199999999998 +-0.46089599999999997 -0.036047099999999999 -0.0108418 +-0.45048300000000002 -0.039582300000000001 -0.00021644899999999999 +-0.43702200000000002 -0.035512500000000002 0.018455900000000001 +-0.0068489400000000004 0.10378999999999999 -0.16331899999999999 +-0.18601799999999999 0.12388100000000001 0.10274800000000001 +0.013715700000000001 -0.136103 -0.17514299999999999 +0.0264414 -0.13470799999999999 -0.16736200000000001 +0.038856099999999998 -0.13453499999999999 -0.153306 +0.039530799999999998 -0.121548 -0.16784199999999999 +0.011156900000000001 -0.14665300000000001 -0.16489500000000001 +-0.00075087799999999996 -0.15146000000000001 -0.16369700000000001 +0.0111868 0.056744000000000003 -0.182724 +0.23311499999999999 0.173929 0.053353900000000003 +-0.14163899999999999 -0.218195 0.038290400000000002 +-0.122474 -0.21968199999999999 0.040040899999999997 +-0.105452 -0.21801200000000001 0.035391199999999998 +0.240064 0.061858799999999999 -0.078420500000000004 +0.230353 0.046217399999999999 -0.076155399999999998 +0.22317500000000001 0.063331999999999999 -0.085683999999999996 +0.21889800000000001 0.032636900000000003 -0.076313699999999998 +0.204732 0.031191300000000002 -0.088331699999999999 +0.18990399999999999 0.024669900000000002 -0.097609100000000004 +0.18399699999999999 0.0080698200000000001 -0.093779899999999999 +0.20787700000000001 0.0225787 -0.076558200000000007 +0.20093800000000001 0.0102395 -0.068265199999999998 +-0.299431 0.082018400000000005 -0.065027000000000001 +-0.31395099999999998 0.077696899999999999 -0.067183800000000002 +-0.32093899999999997 0.064638000000000001 -0.071951100000000004 +-0.31232399999999999 0.0502342 -0.073403499999999997 +-0.32094899999999998 0.0344246 -0.076760999999999996 +-0.29531200000000002 0.051381799999999998 -0.070795899999999995 +-0.173537 -0.106557 -0.064935000000000007 +-0.454017 -0.0053947800000000001 0.012489800000000001 +0.21116099999999999 0.0175973 -0.064992900000000006 +0.0065589400000000001 -0.169937 0.10631699999999999 +0.024311300000000001 -0.15434700000000001 0.10427699999999999 +0.041125000000000002 -0.137241 0.106784 +0.033020399999999998 -0.14849799999999999 0.082684900000000006 +0.0077330599999999999 -0.159576 0.126719 +0.013880699999999999 -0.14596300000000001 0.139351 +0.0049515999999999996 -0.139462 0.152362 +-0.012690699999999999 -0.13936000000000001 0.15942000000000001 +0.018480199999999999 -0.12678500000000001 0.15585599999999999 +0.0241502 -0.11124199999999999 0.16547000000000001 +0.028689300000000001 -0.13331000000000001 0.14110500000000001 +0.042905699999999998 -0.130217 0.12626299999999999 +0.049125099999999998 -0.118004 0.13923099999999999 +0.0120257 0.069555900000000004 -0.17655799999999999 +0.083469299999999996 0.21720100000000001 -0.017059600000000001 +0.0421476 0.21581 -0.025116699999999999 +0.17894099999999999 0.017489999999999999 -0.10319299999999999 +0.16916300000000001 0.011325699999999999 -0.111613 +0.15301100000000001 -0.0010493200000000001 -0.122574 +0.15734200000000001 0.019799400000000002 -0.120215 +0.14863599999999999 0.037525200000000002 -0.12060700000000001 +0.15490599999999999 -0.0180664 -0.109226 +0.16200300000000001 -0.019309099999999999 -0.087811100000000003 +0.17632100000000001 0.0062856300000000004 -0.10222000000000001 +0.174375 -0.00362072 -0.0937611 +0.16558 -0.012255 -0.0982933 +0.16989799999999999 -0.0118878 -0.088347599999999998 +0.16742699999999999 -0.016197699999999999 -0.074634000000000006 +0.172483 -0.013695300000000001 -0.055010400000000001 +0.15857299999999999 -0.0228562 -0.038754999999999998 +0.17826800000000001 -0.0090176000000000006 -0.067720000000000002 +0.14363100000000001 -0.0308124 -0.113389 +0.13102900000000001 -0.047100900000000001 -0.104742 +0.11914 -0.0584212 -0.088755799999999996 +-0.47744300000000001 0.0072796800000000002 -0.0926757 +0.200075 0.18190200000000001 -0.065452300000000005 +0.205429 0.18839800000000001 -0.052903499999999999 +0.220716 0.18753900000000001 -0.044447 +0.233543 0.18065800000000001 -0.054730099999999997 +-0.039324600000000001 -0.026668299999999999 0.189718 +-0.274121 0.0055636699999999997 0.133906 +-0.25280999999999998 -0.00309231 0.14310800000000001 +-0.22636600000000001 0.0043005700000000001 0.15212400000000001 +-0.204148 0.0203161 0.15160899999999999 +-0.18010599999999999 0.036868999999999999 0.15116299999999999 +-0.15584899999999999 0.057039300000000001 0.149975 +-0.12928400000000001 0.046357500000000003 0.16258500000000001 +-0.12731200000000001 0.016773099999999999 0.17379800000000001 +-0.126864 -0.0148933 0.17518800000000001 +-0.096267699999999998 -0.0321701 0.176177 +-0.177678 0.0113344 0.16351099999999999 +-0.15202099999999999 0.00089938799999999999 0.173289 +-0.17436699999999999 -0.016959200000000001 0.16284599999999999 +-0.24421999999999999 -0.021609900000000001 0.13974400000000001 +-0.22625999999999999 -0.016026800000000001 0.148336 +-0.20318600000000001 -0.0300506 0.14880299999999999 +0.31216899999999997 0.091062299999999999 -0.011913200000000001 +0.30472199999999999 0.082997600000000005 -0.0216259 +0.31066199999999999 0.090270500000000004 0.00061645500000000002 +0.306807 0.090399400000000005 0.018326100000000001 +0.30343300000000001 0.081803000000000001 0.0072619700000000004 +0.296626 0.072420499999999999 0.00064681099999999996 +0.29473100000000002 0.069975200000000001 -0.013378599999999999 +0.28840100000000002 0.063922099999999996 -0.022481999999999999 +0.25839899999999999 0.076055300000000006 0.093871999999999997 +0.24593200000000001 0.079880300000000001 0.10576000000000001 +0.25372800000000001 0.093903600000000004 0.099414500000000003 +0.26049800000000001 0.107331 0.090236899999999995 +0.27343099999999998 0.103919 0.079148999999999997 +0.236537 0.067061300000000004 0.11217199999999999 +0.234206 0.047732200000000002 0.10414 +0.23352700000000001 0.082538399999999998 0.115799 +0.22527900000000001 0.097760299999999994 0.119174 +0.21699199999999999 0.084853100000000001 0.127225 +0.199159 0.0862903 0.13627900000000001 +0.20776500000000001 0.069711200000000001 0.13312399999999999 +0.19091900000000001 0.10520599999999999 0.13497500000000001 +0.17057 0.11035499999999999 0.141543 +0.15637300000000001 0.13250799999999999 0.137569 +0.211033 0.051971400000000001 0.12801499999999999 +0.22650999999999999 0.055134200000000001 0.116173 +0.18876799999999999 0.072531300000000007 0.14355100000000001 +0.17066600000000001 0.074287099999999995 0.15144299999999999 +0.16181799999999999 0.092777799999999994 0.15065200000000001 +0.18045600000000001 0.056701599999999998 0.14835999999999999 +0.18363399999999999 0.039098899999999999 0.143398 +0.19848499999999999 0.039036899999999999 0.13270000000000001 +0.161023 0.059768300000000003 0.157114 +0.217859 0.11188099999999999 0.119231 +0.201322 0.116662 0.12529799999999999 +0.23116 0.109891 0.11167000000000001 +0.27771800000000002 0.11536 0.069743600000000003 +0.28670200000000001 0.11400299999999999 0.059374299999999998 +0.29292299999999999 0.11704000000000001 0.050170899999999997 +0.29391699999999998 0.12780900000000001 0.040831300000000001 +0.29987399999999997 0.11642 0.039266799999999998 +0.28494199999999997 0.138435 0.045188100000000002 +0.27150600000000003 0.13945099999999999 0.059086899999999998 +0.26220300000000002 0.15337600000000001 0.053674399999999997 +0.26556200000000002 0.15896299999999999 0.041355000000000003 +0.27518199999999998 0.15706999999999999 0.030226699999999999 +-0.074569800000000006 0.208428 0.10571800000000001 +-0.28553499999999998 0.085776099999999994 -0.062733899999999995 +-0.27472999999999997 0.080274899999999996 -0.0641795 +-0.27114100000000002 0.068362699999999998 -0.066259799999999994 +-0.258023 0.056701500000000002 -0.067850599999999997 +-0.28099200000000002 0.056736700000000001 -0.0686977 +-0.244837 0.066967399999999996 -0.066857899999999998 +-0.23703299999999999 0.051332299999999997 -0.070258399999999999 +-0.234541 0.033337499999999999 -0.070957300000000001 +-0.245976 0.083558900000000005 -0.063375399999999998 +-0.25484099999999998 0.099248400000000001 -0.057681900000000001 +-0.24187700000000001 0.11691500000000001 -0.048652000000000001 +-0.18667 0.140069 0.090438900000000003 +0.29503600000000002 0.093583600000000003 -0.062768400000000002 +-0.0092626700000000006 0.0174249 0.19642599999999999 +-0.0032777499999999998 -0.0084101699999999998 0.19849700000000001 +0.0150689 0.0094961799999999999 0.19802900000000001 +0.0405031 0.0051091499999999998 0.19528999999999999 +0.059443299999999998 0.011896500000000001 0.19127 +-0.48195300000000002 -0.0263701 -0.036237999999999999 +-0.48020200000000002 -0.020542299999999999 -0.0285568 +-0.47638200000000003 -0.014776600000000001 -0.021227900000000001 +-0.46876699999999999 -0.0190157 -0.0084552399999999993 +-0.47247600000000001 -0.00650871 -0.014068000000000001 +-0.47228300000000001 0.0056392100000000004 -0.018385100000000001 +-0.462142 0.014903899999999999 -0.0113607 +-0.44938099999999997 0.022748399999999998 0.00012384000000000001 +-0.45730500000000002 0.0086536900000000003 0.0030441000000000001 +-0.48158699999999999 -0.0110502 -0.031619000000000001 +-0.48247499999999999 0.000430245 -0.033265500000000003 +-0.48034700000000002 0.0094886499999999995 -0.041986000000000002 +-0.47247299999999998 0.0222522 -0.043652499999999997 +-0.46677600000000002 0.024509099999999999 -0.032044799999999998 +-0.485126 0.0062711599999999996 -0.0502161 +-0.037030300000000002 -0.109778 0.171268 +-0.0368659 -0.089202400000000001 0.177455 +-0.060640100000000002 -0.071844900000000003 0.17511599999999999 +-0.018829200000000001 -0.079185199999999997 0.18451799999999999 +-0.066590999999999997 -0.0431507 0.180288 +0.0012136499999999999 -0.086199499999999998 0.18413199999999999 +0.015836900000000001 -0.073501899999999995 0.18798500000000001 +0.000163277 -0.10355200000000001 0.176731 +0.094084799999999996 -0.083839999999999998 -0.12686700000000001 +0.091261200000000001 -0.079095399999999996 0.139096 +0.097552700000000006 -0.078761600000000001 0.119196 +0.091510900000000006 -0.066797599999999999 0.154999 +0.106504 -0.054628299999999998 0.14974000000000001 +0.11228100000000001 -0.032540800000000002 0.16014100000000001 +0.120223 -0.050942500000000002 0.13462299999999999 +0.120697 -0.057035000000000002 0.114118 +0.13031400000000001 -0.037256400000000002 0.13803499999999999 +0.118329 -0.041934600000000002 0.14816699999999999 +0.078691999999999998 -0.068969299999999997 0.16483500000000001 +0.076521800000000001 -0.053140100000000003 0.17500599999999999 +0.079637600000000003 -0.034270799999999997 0.17985599999999999 +0.064064200000000002 -0.023470600000000001 0.18856000000000001 +0.053909699999999998 -0.0080092700000000006 0.19264300000000001 +0.068506200000000003 -0.079074800000000001 0.16509699999999999 +0.066006800000000004 -0.091062900000000002 0.15597800000000001 +0.075012200000000001 -0.091818999999999998 0.14458499999999999 +0.053982000000000002 -0.097950800000000005 0.15914500000000001 +0.038619000000000001 -0.102088 0.16547100000000001 +0.048001599999999998 -0.10957500000000001 0.15166299999999999 +0.033427699999999998 -0.089589699999999994 0.176126 +0.043980699999999998 -0.077008499999999994 0.17952699999999999 +0.051638099999999999 -0.060694999999999999 0.18419099999999999 +0.078784499999999993 -0.0183292 0.183534 +0.087645600000000004 -0.0040645300000000002 0.181976 +0.080052600000000002 0.0145518 0.18593399999999999 +-0.030808100000000001 0.088540499999999994 -0.17465700000000001 +0.155665 0.212315 0.00724246 +0.141239 0.21351000000000001 -0.0057057799999999997 +0.29269299999999998 0.10929999999999999 0.054666300000000001 +0.29271999999999998 0.098807699999999998 0.053018999999999997 +0.297462 0.095888799999999996 0.042752800000000001 +0.053781000000000002 -0.085918900000000006 -0.17676500000000001 +0.068457400000000002 -0.071068800000000001 -0.17282700000000001 +0.083951899999999996 -0.055962199999999997 -0.16795299999999999 +0.051669600000000003 -0.066894400000000007 -0.18332100000000001 +0.098579899999999998 -0.043157899999999999 -0.161885 +0.111766 -0.046847600000000003 -0.147448 +0.109403 -0.0636708 -0.13267399999999999 +-0.29431600000000002 0.11065700000000001 -0.047223899999999999 +-0.28312799999999999 0.119926 -0.038833600000000003 +-0.26274799999999998 0.12402000000000001 -0.039768199999999997 +-0.28836200000000001 0.12687000000000001 -0.0266383 +-0.30218299999999998 0.12418700000000001 -0.0189976 +-0.31845800000000002 0.117372 -0.020931399999999999 +-0.32725500000000002 0.11022999999999999 -0.00445948 +-0.31620599999999999 0.112645 0.0085312900000000004 +-0.29883799999999999 0.121181 0.013046500000000001 +-0.31412699999999999 0.115203 -0.0350952 +-0.29103099999999998 0.12880800000000001 0.00166 +-0.277171 0.135902 -0.000114267 +-0.27324799999999999 0.136459 -0.0125396 +0.097695000000000004 -0.0737844 -0.14055100000000001 +0.082734699999999994 -0.089580199999999999 -0.145093 +0.086838499999999999 -0.074848799999999993 -0.15546599999999999 +0.067970299999999997 -0.106364 -0.147753 +0.060353200000000003 -0.120556 -0.134077 +0.058830399999999998 -0.104422 -0.16323299999999999 +-0.32267400000000002 0.0086848299999999993 0.11067200000000001 +-0.30726300000000001 0.0067620500000000004 0.118571 +-0.30800499999999997 -0.0086171800000000003 0.119522 +-0.29358099999999998 0.011466499999999999 0.12300999999999999 +-0.29932900000000001 0.0239726 0.11282300000000001 +-0.28615400000000002 0.039842299999999997 0.107825 +-0.26321 0.037897399999999998 0.11920799999999999 +-0.13457 0.121943 -0.106906 +-0.139297 0.13466500000000001 -0.089991199999999993 +-0.160382 0.13081200000000001 -0.074639399999999995 +-0.121714 0.150283 -0.087212799999999993 +-0.16375600000000001 0.148893 -0.052594799999999997 +-0.109282 0.16651299999999999 -0.074029499999999998 +-0.12864800000000001 0.16718 -0.055228800000000002 +-0.16725499999999999 0.11383600000000001 -0.083424700000000004 +-0.39439299999999999 -0.018681099999999999 0.065612699999999996 +-0.22717399999999999 0.111419 0.086208300000000002 +-0.29622700000000002 0.12784599999999999 -0.0091753600000000005 +-0.29336699999999999 0.128193 -0.0184368 +-0.238147 -0.0055974099999999997 0.14847399999999999 +-0.239063 0.0091714599999999993 0.14564099999999999 +-0.22414600000000001 0.026270399999999999 0.141961 +-0.250836 0.0091494499999999999 0.141379 +-0.26247900000000002 0.018606899999999999 0.132247 +-0.26232299999999997 0.0042172599999999996 0.13866600000000001 +-0.26321899999999998 -0.0084791099999999998 0.13811300000000001 +-0.256828 -0.019899099999999999 0.13605300000000001 +-0.26305400000000001 -0.0300816 0.12826699999999999 +-0.27682800000000002 -0.027548 0.12421699999999999 +-0.27500799999999997 -0.0069395400000000001 0.13413700000000001 +-0.28145900000000001 -0.0163763 0.12839100000000001 +-0.259073 0.11239399999999999 -0.049898900000000003 +-0.32078200000000001 0.10963100000000001 -0.042513099999999998 +-0.33068799999999998 0.10691000000000001 -0.043354200000000002 +-0.33934300000000001 0.10236199999999999 -0.046549699999999999 +-0.348883 0.098677799999999996 -0.048183900000000002 +-0.35806900000000003 0.093758999999999995 -0.051441000000000001 +-0.13687299999999999 -0.0120468 -0.151363 +-0.14122100000000001 -0.034050700000000003 -0.14651600000000001 +-0.128382 -0.030968599999999999 -0.16051299999999999 +-0.11523700000000001 -0.033887199999999999 -0.17164699999999999 +-0.10415000000000001 -0.022127299999999999 -0.177401 +-0.092526399999999995 -0.0054494499999999998 -0.18079400000000001 +-0.106711 -0.0068729999999999998 -0.173788 +-0.119908 0.00027730400000000003 -0.164218 +-0.114456 0.018165899999999999 -0.165215 +-0.082517300000000002 0.0101783 -0.18265999999999999 +-0.118672 -0.048041 -0.16878099999999999 +-0.12155299999999999 -0.061881899999999997 -0.163771 +-0.11089400000000001 -0.0624358 -0.17219899999999999 +-0.39573000000000003 0.079365500000000005 -0.049794100000000001 +-0.39736399999999999 0.075222999999999998 -0.062139699999999999 +-0.38926899999999998 0.080610100000000004 -0.058042499999999997 +-0.37967600000000001 0.084154499999999993 -0.057447199999999997 +-0.37281300000000001 0.088494799999999998 -0.0523992 +-0.373143 0.090201600000000007 -0.0428662 +-0.40528700000000001 0.069833199999999998 -0.066590399999999994 +-0.050975199999999998 -0.010730200000000001 -0.19368299999999999 +0.068437899999999996 0.058758199999999997 0.18426799999999999 +0.095081700000000005 0.070039299999999999 0.17625399999999999 +0.11186599999999999 0.048432900000000001 0.175538 +0.0135438 0.23103000000000001 0.088735400000000006 +-0.148114 -0.22111700000000001 0.051070999999999998 +-0.159772 -0.21833 0.056201500000000001 +-0.17114099999999999 -0.21582699999999999 0.049958000000000002 +-0.172124 -0.214009 0.034900899999999999 +-0.183868 -0.21162500000000001 0.033302100000000001 +-0.48066999999999999 0.0014756299999999999 -0.094153100000000003 +-0.48325000000000001 0.0051100800000000004 -0.087128800000000006 +-0.47888599999999998 -0.0067006399999999999 -0.095381300000000002 +-0.48850900000000003 -0.00024797699999999997 -0.083196999999999993 +-0.49010599999999999 0.00017326200000000001 -0.074509199999999998 +-0.48638399999999998 0.0077295999999999997 -0.068793800000000002 +-0.49083900000000003 -0.0072984699999999996 -0.067552500000000001 +-0.48757299999999998 -0.0076937899999999998 -0.085185899999999995 +0.30541299999999999 0.115443 0.0310865 +0.30306699999999998 0.109776 0.036199099999999998 +0.30152800000000002 0.100881 0.0364219 +0.30454199999999998 0.095028199999999993 0.027837799999999999 +0.308585 0.101812 0.019347799999999998 +0.30202000000000001 0.085854200000000006 0.025549800000000001 +0.294651 0.077623899999999996 0.030824799999999999 +-0.13972999999999999 -0.120127 -0.089437100000000005 +0.077412099999999998 -0.101688 -0.13441 +0.084279999999999994 -0.0964758 -0.11860999999999999 +0.075763499999999998 -0.104018 -0.095217800000000005 +0.0953267 -0.083999299999999999 -0.106017 +0.0029394500000000001 0.19001299999999999 -0.077441399999999994 +-0.0158096 0.19506699999999999 -0.066558199999999998 +-0.0158703 0.20566599999999999 -0.044179299999999998 +0.091303899999999993 -0.074953099999999995 -0.036518099999999998 +-0.24309700000000001 0.15007799999999999 0.0206614 +-0.23361000000000001 0.15435099999999999 0.0222242 +-0.071663699999999997 -0.19434399999999999 0.123031 +-0.086263300000000001 -0.19167899999999999 0.120669 +-0.079351000000000005 -0.19966900000000001 0.116734 +0.090543700000000005 -0.072083099999999997 -0.0082290999999999996 +-0.44237500000000002 -0.080560199999999998 -0.072939100000000007 +-0.43468400000000001 -0.074604000000000004 -0.085476499999999997 +-0.437054 -0.086967500000000003 -0.060754700000000002 +-0.42770999999999998 -0.094516799999999998 -0.052825700000000003 +-0.418352 -0.098305400000000001 -0.040073499999999998 +-0.42327900000000002 -0.096600099999999994 -0.065513100000000005 +-0.40865899999999999 -0.10148699999999999 -0.069723800000000002 +-0.416431 -0.090925400000000003 -0.0763075 +-0.41177900000000001 -0.078854099999999996 -0.0841388 +-0.40598000000000001 -0.065549200000000002 -0.088420399999999996 +-0.41605500000000001 -0.049127400000000002 -0.093640699999999993 +-0.400038 -0.0506823 -0.090530299999999994 +-0.38142100000000001 -0.045395499999999998 -0.086927599999999994 +-0.40224300000000002 -0.110281 -0.057950799999999997 +-0.39021600000000001 -0.11874 -0.050110799999999997 +-0.383494 -0.123101 -0.0364651 +-0.386125 -0.115131 -0.020342800000000001 +-0.36962800000000001 -0.13037399999999999 -0.0274223 +-0.40145599999999998 -0.102315 -0.019867900000000001 +-0.39417099999999999 -0.100274 -0.072764499999999996 +-0.37404100000000001 -0.097433599999999995 -0.071859199999999998 +-0.38566499999999998 -0.086013500000000007 -0.077930700000000006 +-0.35971199999999998 -0.131969 -0.0141493 +-0.35573500000000002 -0.12106699999999999 0.0058952099999999997 +-0.40775499999999998 -0.10759100000000001 -0.044512500000000003 +-0.44210500000000003 -0.063409699999999999 -0.090683299999999994 +-0.42282500000000001 -0.082393900000000006 -0.081556000000000003 +-0.45077 -0.055459799999999997 -0.092926400000000006 +-0.45119500000000001 -0.043705000000000001 -0.097222299999999998 +-0.437782 -0.034036499999999997 -0.100162 +-0.43303599999999998 -0.0155961 -0.100753 +-0.45492199999999999 -0.032423100000000003 -0.100841 +-0.458814 -0.0492048 -0.0932669 +-0.44563000000000003 -0.0053807300000000002 -0.10256700000000001 +-0.44582500000000003 -0.028368000000000001 -0.102242 +0.18756700000000001 0.011524100000000001 0.12186900000000001 +0.19992299999999999 0.0237015 0.120686 +0.20960799999999999 0.0257357 0.11161 +0.21748300000000001 0.0222293 0.093870899999999993 +0.22097 0.032477300000000001 0.10435700000000001 +0.15055099999999999 0.010878199999999999 -0.12618799999999999 +0.141789 0.00011003299999999999 -0.133433 +0.15970799999999999 0.0081676700000000001 -0.119042 +0.16130900000000001 -0.0024486199999999999 -0.114514 +0.154667 -0.0107676 -0.11754299999999999 +0.147178 -0.010362400000000001 -0.12647 +0.148617 -0.020408800000000001 -0.118466 +0.13852400000000001 -0.011450999999999999 -0.134851 +0.12890699999999999 -0.026258699999999999 -0.13917599999999999 +0.12589600000000001 -0.0139325 -0.146588 +0.113147 -0.00053748299999999995 -0.155555 +0.103133 0.016400100000000001 -0.15639400000000001 +0.104298 -0.0149856 -0.16309000000000001 +0.12017 -0.035598499999999998 -0.145343 +0.110453 -0.035544100000000002 -0.154722 +-0.044026500000000003 0.056760699999999997 -0.18568799999999999 +0.27999200000000002 0.16592899999999999 -0.043703899999999997 +-0.21535899999999999 -0.201377 0.036184000000000001 +-0.223693 -0.188967 0.047823400000000002 +-0.22968 -0.16561699999999999 0.063274399999999995 +-0.24318400000000001 -0.14052799999999999 0.072279800000000005 +-0.22803499999999999 -0.19495199999999999 0.035675900000000003 +-0.23849600000000001 -0.19363 0.023692399999999999 +-0.25412400000000002 -0.18660599999999999 0.0200977 +-0.257716 -0.18540699999999999 0.0029471599999999999 +-0.27287099999999997 -0.17936299999999999 -0.00179042 +-0.28758299999999998 -0.170182 0.0102401 +-0.24412800000000001 -0.18221699999999999 -0.0044050299999999999 +-0.22953100000000001 -0.18651499999999999 -5.5947399999999999e-05 +-0.220475 -0.194692 0.0089781199999999992 +-0.26251099999999999 -0.176872 0.0277986 +-0.25326300000000002 -0.17311000000000001 0.039993899999999999 +-0.24254600000000001 -0.168318 0.052385899999999999 +-0.25711499999999998 -0.15942600000000001 0.049328299999999999 +-0.27587699999999998 -0.16930799999999999 0.024501599999999998 +-0.24587100000000001 -0.15378 0.061338299999999998 +-0.25172699999999998 -0.18192800000000001 0.031364700000000002 +-0.21268400000000001 -0.20027700000000001 0.0170569 +-0.20352300000000001 -0.203845 0.0214033 +-0.192659 -0.203121 0.018234400000000001 +-0.188052 -0.194159 0.0076752000000000001 +-0.21226800000000001 -0.20365 0.026703299999999999 +-0.231299 -0.195988 0.014245799999999999 +-0.19641400000000001 -0.20655399999999999 0.024866200000000001 +-0.19286 -0.20988799999999999 0.0326474 +-0.198432 -0.20674699999999999 0.044257499999999998 +-0.28556100000000001 -0.174648 -0.0023933600000000002 +-0.29736299999999999 -0.16808200000000001 -0.012934899999999999 +-0.31544100000000003 -0.16035199999999999 -0.0156542 +-0.32242599999999999 -0.15056600000000001 0.0015451600000000001 +-0.27945999999999999 -0.17655899999999999 0.0065269300000000002 +-0.32907799999999998 -0.152562 -0.012822800000000001 +-0.34145199999999998 -0.14733099999999999 -0.021559600000000002 +-0.33934700000000001 -0.14391100000000001 -0.037897800000000002 +-0.32578200000000002 -0.13728899999999999 -0.044510099999999997 +-0.31670300000000001 -0.146788 -0.037035899999999997 +-0.31557600000000002 -0.155996 -0.027913199999999999 +-0.35231899999999999 -0.13774 -0.042353000000000002 +-0.35627700000000001 -0.12551100000000001 -0.054545700000000003 +-0.37187599999999998 -0.118612 -0.059590600000000001 +-0.36245300000000003 -0.135739 -0.035773300000000001 +-0.30760100000000001 -0.16083500000000001 -0.022816699999999999 +-0.29867300000000002 -0.163768 -0.021315199999999999 +-0.28379500000000002 -0.159885 -0.025242400000000002 +-0.28890100000000002 -0.16657 -0.018883 +-0.27143099999999998 -0.163684 -0.021205700000000001 +-0.26730599999999999 -0.17346300000000001 -0.0127247 +-0.27183400000000002 -0.18046200000000001 0.00664249 +-0.26405000000000001 -0.184059 0.0090717000000000002 +-0.25620300000000001 -0.18756700000000001 0.011472599999999999 +-0.248391 -0.190551 0.0145387 +-0.19015299999999999 -0.20661399999999999 0.0534964 +-0.44255499999999998 0.00026552299999999999 0.026730199999999999 +-0.43035200000000001 0.014090999999999999 0.0310689 +-0.42472100000000002 0.0323378 0.017327499999999999 +-0.42703600000000003 -0.00112736 0.0417282 +-0.42339199999999999 -0.016438299999999999 0.044238899999999998 +-0.41653699999999999 0.00176833 0.051877 +0.28040399999999999 0.13509299999999999 0.054525999999999998 +0.27876099999999998 0.125803 0.062829300000000005 +-0.3705 -0.13137799999999999 -0.036506900000000002 +-0.0820636 -0.222416 0.082619399999999996 +0.0171345 0.16531999999999999 -0.108665 +0.0025037499999999999 0.14652799999999999 -0.128943 +-0.0048198399999999997 0.165963 -0.110419 +-0.0228077 0.17507700000000001 -0.098515900000000003 +-0.046374699999999998 0.16789599999999999 -0.10471999999999999 +0.0078590199999999995 0.12789700000000001 -0.142484 +0.018770800000000001 0.138019 -0.13144900000000001 +-0.028861299999999999 0.144009 -0.13425300000000001 +-0.044995199999999999 0.15353600000000001 -0.122945 +0.29028300000000001 0.121714 -0.074014399999999994 +0.27744999999999997 0.12898000000000001 -0.076940400000000006 +0.27683200000000002 0.144257 -0.075574500000000003 +0.26365499999999997 0.13398499999999999 -0.080384700000000003 +0.24396200000000001 0.140125 -0.081940600000000002 +0.25181799999999999 0.124002 -0.082061400000000007 +0.27904099999999998 0.112623 -0.076369599999999996 +0.27115800000000001 0.153665 -0.070900599999999994 +0.25792100000000001 0.162187 -0.070153199999999999 +0.26986399999999999 0.163795 -0.062653600000000004 +0.24820800000000001 0.15371099999999999 -0.0775922 +0.25947399999999998 0.16913900000000001 -0.060685200000000002 +0.25480599999999998 0.17483000000000001 -0.049851899999999998 +0.26311499999999999 0.17435200000000001 -0.039377700000000002 +0.24249799999999999 0.16399900000000001 -0.073516499999999999 +0.286047 0.14730399999999999 -0.069066299999999997 +0.29011700000000001 0.152194 -0.0598459 +0.29456100000000002 0.14326 -0.067360299999999998 +0.30188999999999999 0.13453899999999999 -0.064544699999999997 +0.30783100000000002 0.12835099999999999 -0.059514299999999999 +0.30943500000000002 0.118425 -0.060111699999999997 +0.300896 0.11136699999999999 -0.064607399999999995 +0.31357400000000002 0.12434199999999999 -0.053265699999999999 +0.31838899999999998 0.120863 -0.046066000000000003 +0.31739299999999998 0.11330800000000001 -0.0379339 +0.320268 0.118282 -0.030761899999999998 +0.320218 0.116784 -0.022126400000000001 +0.31948100000000001 0.11809 -0.010456500000000001 +0.31556000000000001 0.12664800000000001 -0.00415804 +0.31660199999999999 0.113926 -0.045093500000000002 +0.31177700000000003 0.106867 -0.048073299999999999 +0.30827500000000002 0.10918600000000001 -0.056207 +0.30850499999999997 0.098341200000000004 -0.048610199999999999 +0.30314400000000002 0.092595200000000003 -0.053105100000000002 +0.30960100000000002 0.093476400000000001 -0.041793799999999999 +0.31315599999999999 0.095964599999999997 -0.0340974 +0.296983 0.11741500000000001 -0.0699211 +0.320685 0.122446 -0.025589600000000001 +0.316942 0.13097800000000001 -0.0270583 +0.31485400000000002 0.133023 -0.0360942 +0.31084699999999998 0.14150299999999999 -0.038310900000000002 +0.30931799999999998 0.14124100000000001 -0.047368899999999999 +0.304008 0.14735100000000001 -0.0517981 +0.30704500000000001 0.147922 -0.034493999999999997 +0.29826799999999998 0.15381300000000001 -0.036844500000000002 +0.30964900000000001 0.14619399999999999 -0.027128099999999999 +0.30556299999999997 0.14827399999999999 -0.019965 +0.29749399999999998 0.154475 -0.020069300000000002 +0.292877 0.15678 -0.0092131699999999997 +0.28607100000000002 0.16011 0.00062224299999999997 +0.27559499999999998 0.166073 0.0087612000000000002 +0.26022400000000001 0.17458899999999999 0.0077726000000000002 +0.25062200000000001 0.175429 0.025409500000000002 +0.31161299999999997 0.14075599999999999 -0.021410100000000001 +0.30851800000000001 0.14502599999999999 -0.013265300000000001 +0.296427 0.15654799999999999 -0.0286188 +0.29088799999999998 0.15989800000000001 -0.0225221 +0.28007199999999999 0.16805500000000001 -0.0192366 +0.00514285 -0.026233699999999999 0.19953000000000001 +0.000257161 -0.040387600000000003 0.19702800000000001 +-0.014043 -0.051568999999999997 0.191857 +0.019852100000000001 -0.028945800000000001 0.19900799999999999 +-0.44991500000000001 0.0066942399999999997 0.013299699999999999 +0.189521 0.18335399999999999 -0.065903900000000001 +0.18108299999999999 0.18820200000000001 -0.061319100000000001 +0.16613900000000001 0.19374 -0.055086700000000002 +0.18040100000000001 0.19547999999999999 -0.047881100000000003 +0.173792 0.202184 -0.030743599999999999 +0.014203 -0.120681 -0.18353 +0.023841399999999999 -0.113012 -0.18310100000000001 +0.033454299999999999 -0.103091 -0.18218599999999999 +0.033035500000000002 -0.116504 -0.176425 +0.031873400000000003 -0.12510099999999999 -0.17236699999999999 +0.035683899999999998 -0.130691 -0.16428899999999999 +0.043684300000000002 -0.12582399999999999 -0.158725 +0.048837400000000003 -0.116976 -0.16342999999999999 +-0.073536900000000002 0.130882 -0.137882 +-0.0601936 0.14341200000000001 -0.130466 +-0.0624708 0.11858399999999999 -0.151005 +-0.041211999999999999 0.115328 -0.15739800000000001 +-0.042079699999999998 0.10029399999999999 -0.16700599999999999 +0.18629799999999999 0.15686 0.109529 +-0.0106882 -0.179034 -0.053539000000000003 +-0.0257532 -0.183285 -0.056009999999999997 +-0.446546 -0.020147200000000001 0.019944799999999999 +-0.455322 -0.018398899999999999 0.0101318 +-0.46273900000000001 -0.0092777199999999997 0.0013349499999999999 +-0.44792399999999999 -0.0304864 0.010348100000000001 +-0.32777299999999998 -0.145984 -0.0378562 +-0.48141099999999998 0.0150496 -0.063860799999999995 +0.32042100000000001 0.119931 -0.016534400000000001 +0.31704199999999999 0.125637 -0.0119931 +0.31845400000000001 0.112192 -0.015792899999999999 +0.092939900000000006 0.15981200000000001 -0.095894300000000002 +0.110625 0.16691700000000001 -0.0892709 +0.124473 0.17113600000000001 -0.084585300000000002 +0.136186 0.183251 -0.074210600000000002 +0.13866000000000001 0.17169200000000001 -0.082379800000000003 +0.148566 0.160826 -0.085964700000000005 +0.142872 0.14471200000000001 -0.090559700000000007 +0.16079499999999999 0.122901 -0.090634900000000004 +0.12608800000000001 0.14530199999999999 -0.093695299999999995 +0.115879 0.13087099999999999 -0.099057199999999998 +0.12432699999999999 0.191494 -0.066376900000000003 +0.106391 0.192491 -0.067065600000000003 +0.088387199999999999 0.18826200000000001 -0.074576600000000007 +0.071950700000000006 0.19680600000000001 -0.064225500000000005 +0.108844 0.20166500000000001 -0.051652999999999998 +0.098499900000000001 0.17773700000000001 -0.083660499999999999 +-0.12026000000000001 -0.22390199999999999 0.053194999999999999 +-0.109446 -0.22493099999999999 0.056583099999999997 +-0.111541 -0.22161900000000001 0.045167800000000001 +-0.24204600000000001 -0.18762300000000001 0.032577599999999998 +-0.32623400000000002 0.0938195 0.029149899999999999 +-0.010242100000000001 -0.165045 0.13696 +-0.37358799999999998 0.010098899999999999 0.080166899999999999 +0.00996937 -0.012989799999999999 0.200095 +-0.46387699999999998 -0.032463699999999998 -0.100365 +-0.46022400000000002 -0.024182800000000001 -0.10134 +-0.45973900000000001 -0.0142811 -0.10137699999999999 +0.13320799999999999 -0.041244900000000001 -0.119383 +0.123352 -0.052679700000000003 -0.120194 +0.132637 -0.034374099999999998 -0.13009399999999999 +-0.164742 -0.12217799999999999 -0.059505200000000001 +0.044514999999999999 -0.137297 -0.111748 +-0.061279100000000003 -0.16256599999999999 -0.10968799999999999 +-0.071012900000000004 -0.16483400000000001 -0.095825300000000002 +-0.073553300000000002 -0.17218600000000001 -0.076810600000000007 +-0.087645200000000006 -0.16161 -0.086792900000000006 +-0.476659 -0.027153799999999999 -0.096453300000000006 +-0.47442899999999999 -0.0190092 -0.097352800000000003 +0.073971800000000004 -0.0242315 -0.180703 +0.162304 -0.0056067299999999999 0.13305800000000001 +-0.479103 -0.032098500000000002 -0.094002699999999995 +-0.48141699999999998 -0.034085799999999999 -0.088405700000000004 +-0.48612699999999998 -0.030643500000000001 -0.086058300000000004 +-0.48601800000000001 -0.024288299999999999 -0.087257699999999994 +-0.48210399999999998 -0.021943899999999999 -0.092236399999999996 +-0.48388599999999998 -0.035583200000000002 -0.082741899999999993 +-0.480962 -0.040286799999999998 -0.078852199999999997 +-0.48836800000000002 -0.026736300000000001 -0.083000900000000002 +-0.48974400000000001 -0.025216499999999999 -0.0755797 +-0.49086000000000002 -0.024424600000000001 -0.069549799999999995 +-0.489653 -0.0247292 -0.060789900000000001 +-0.49031599999999997 -0.019490500000000001 -0.064984799999999995 +-0.49036400000000002 -0.028426799999999999 -0.072102700000000006 +-0.48628100000000002 -0.033962199999999998 -0.072745199999999996 +-0.484931 -0.035798099999999999 -0.065703200000000003 +-0.48041699999999998 -0.042245199999999997 -0.065058900000000003 +-0.48662300000000003 -0.0314942 -0.059831200000000001 +-0.47195999999999999 -0.030398999999999999 -0.098425499999999999 +-0.066902500000000004 0.0064793100000000003 0.18904799999999999 +-0.0025919599999999999 0.054607299999999998 0.192242 +-0.028547699999999999 0.070800699999999994 0.18473400000000001 +-0.0057345 0.037484400000000001 0.19470999999999999 +-0.0268013 0.15953400000000001 0.15797900000000001 +-0.0096282999999999994 0.14879200000000001 0.16497200000000001 +-0.078126299999999996 -0.119784 -0.16633500000000001 +-0.069360599999999994 -0.12950500000000001 -0.164109 +-0.068875199999999998 -0.121624 -0.171204 +-0.30148399999999997 0.103619 -0.053148000000000001 +-0.29309600000000002 0.10467 -0.053065500000000002 +-0.28153499999999998 0.10184799999999999 -0.055146500000000001 +-0.28510200000000002 0.10936999999999999 -0.050098200000000002 +-0.27017799999999997 0.10181900000000001 -0.055790199999999998 +0.14641299999999999 0.21560099999999999 0.021743200000000001 +-0.055876799999999997 -0.20058100000000001 -0.0058347 +-0.121903 -0.22239800000000001 0.075382699999999997 +-0.13148399999999999 -0.222972 0.067294300000000001 +-0.366143 0.0142557 0.083265599999999995 +-0.011362199999999999 -0.139899 -0.17758599999999999 +-0.46108399999999999 -0.018208200000000001 0.00293327 +-0.464563 -0.022432400000000002 -0.00273456 +-0.46595599999999998 -0.0279422 -0.010426899999999999 +-0.079657800000000001 0.18559999999999999 -0.060871700000000001 +-0.40898200000000001 -0.00209341 0.057713 +-0.40249400000000002 -0.0094119900000000003 0.062336500000000003 +-0.40335300000000002 -0.019611199999999999 0.058583000000000003 +-0.40576200000000001 -0.028626700000000001 0.051533799999999998 +-0.40561799999999998 -0.039816699999999997 0.044003100000000003 +-0.40473599999999998 -0.052921799999999998 0.033436300000000002 +-0.409831 -0.063620099999999999 0.018912499999999999 +-0.40256599999999998 -0.078547400000000003 0.0097687 +-0.38431100000000001 -0.088619199999999995 0.015902400000000001 +-0.39992299999999997 0.00020637100000000001 0.064814800000000006 +-0.39525900000000003 0.0101254 0.063961900000000002 +-0.39940900000000001 0.0198184 0.054190200000000001 +-0.40101500000000001 0.031749899999999998 0.041754800000000002 +-0.386013 0.042619600000000001 0.040975900000000003 +-0.39055499999999999 0.0202045 0.061387499999999998 +-0.37609100000000001 0.028226000000000001 0.064886899999999997 +-0.35996899999999998 0.035405800000000001 0.068977700000000003 +-0.39047799999999999 0.0030778699999999999 0.071252399999999994 +-0.41499200000000003 0.027358899999999998 0.032616800000000001 +-0.082224900000000004 -0.219415 0.043609299999999997 +0.320548 0.121146 -0.020907599999999998 +-0.030711800000000001 0.20911199999999999 0.12504399999999999 +0.24469399999999999 0.071696200000000002 -0.078943899999999997 +-0.32278600000000002 -0.150478 -0.034060199999999999 +-0.14291799999999999 -0.22189800000000001 0.059583299999999999 +-0.14105500000000001 -0.21972700000000001 0.069530499999999995 +-0.14838399999999999 -0.21382300000000001 0.074768699999999993 +-0.140352 -0.208727 0.084856600000000004 +-0.15936900000000001 -0.210758 0.071433800000000006 +-0.12697 -0.21054800000000001 0.090340000000000004 +-0.123362 -0.21745700000000001 0.084004499999999996 +0.29131899999999999 0.148647 -0.065299999999999997 +0.28592299999999998 0.152334 -0.064874000000000001 +0.28020600000000001 0.15112999999999999 -0.068714300000000006 +-0.011495 -0.128161 0.165796 +-0.010448900000000001 -0.11426799999999999 0.172787 +-0.45632800000000001 0.0023828899999999999 0.0091199899999999997 +-0.46271600000000002 0.00053364999999999999 0.0018853699999999999 +-0.45271899999999998 0.00080143200000000003 0.0144475 +-0.44771100000000003 -0.0056644800000000004 0.020379000000000001 +-0.44707200000000002 0.0029647300000000001 0.0202268 +-0.43997700000000001 0.0094005700000000005 0.0233511 +-0.44131500000000001 -0.010126599999999999 0.027390600000000001 +-0.434861 -0.0036457 0.033997899999999998 +-0.43562299999999998 0.0054161599999999997 0.030922499999999999 +-0.39701399999999998 -0.107359 -0.0661138 +-0.39450099999999999 -0.112676 -0.060127800000000002 +-0.39739000000000002 -0.114494 -0.052232000000000001 +-0.38694499999999998 -0.116865 -0.0581355 +-0.37967299999999998 -0.123432 -0.051806699999999997 +-0.37934899999999999 -0.114175 -0.062503500000000004 +-0.36884499999999998 -0.10815 -0.065851699999999999 +-0.35989900000000002 -0.097353599999999998 -0.069139000000000006 +-0.37942399999999998 -0.105627 -0.068412500000000001 +-0.34553299999999998 -0.099972900000000003 -0.064961400000000002 +-0.34044799999999997 -0.115776 -0.057915000000000001 +-0.34899400000000003 -0.084583800000000001 -0.0704153 +-0.33274199999999998 -0.065673099999999998 -0.070522600000000005 +-0.35099399999999997 -0.066454700000000005 -0.075408699999999995 +-0.34181800000000001 -0.047047100000000001 -0.076195700000000005 +-0.351962 -0.0231594 -0.082381800000000005 +-0.39551999999999998 -0.11584700000000001 -0.043825299999999998 +-0.37087399999999998 -0.124971 -0.053235600000000001 +-0.36336400000000002 -0.12976199999999999 -0.049667999999999997 +-0.33008999999999999 -0.123253 -0.052732000000000001 +-0.316778 -0.125001 -0.049363499999999998 +-0.30263899999999999 -0.12310699999999999 -0.047369599999999998 +-0.28703899999999999 -0.12488200000000001 -0.043681200000000003 +-0.27375899999999997 -0.109 -0.046197000000000002 +-0.27046900000000001 -0.12602099999999999 -0.039752500000000003 +-0.25473800000000002 -0.113942 -0.041688099999999999 +-0.27834500000000001 -0.088689400000000002 -0.052707200000000003 +-0.26637699999999997 -0.067844299999999996 -0.056034500000000001 +-0.28153499999999998 -0.044186099999999999 -0.062687599999999996 +-0.30022599999999999 -0.091271699999999997 -0.056407499999999999 +-0.31246699999999999 -0.073381799999999997 -0.063487699999999994 +-0.26353799999999999 -0.049345800000000002 -0.059380200000000001 +-0.38483400000000001 -0.12300899999999999 -0.044951100000000001 +-0.37869599999999998 -0.12701100000000001 -0.041614499999999999 +-0.37681999999999999 -0.12697600000000001 -0.033598299999999998 +-0.38166 -0.12148 -0.026721700000000001 +-0.45038 -0.074929800000000005 -0.072447300000000006 +-0.45795999999999998 -0.068416900000000003 -0.075791499999999998 +-0.46366800000000002 -0.061313199999999998 -0.078911400000000007 +-0.470167 -0.0560323 -0.073572799999999994 +-0.45690799999999998 -0.064758099999999999 -0.084254200000000001 +-0.46812300000000001 -0.054153600000000003 -0.082180500000000004 +-0.35536499999999999 0.085403400000000004 -0.063983100000000001 +-0.345495 0.078401799999999994 -0.068668099999999996 +-0.34556599999999998 0.064270499999999994 -0.075424400000000003 +-0.345752 0.086915099999999995 -0.063962599999999994 +-0.34040900000000002 0.092115600000000006 -0.059582099999999999 +-0.320658 -0.15615499999999999 -0.0073590299999999999 +0.031028699999999999 -0.049854700000000002 0.19364100000000001 +0.019889400000000002 -0.14193700000000001 -0.16545899999999999 +-0.228047 -0.0061159700000000001 0.15153900000000001 +-0.21379000000000001 -0.010193900000000001 0.15467800000000001 +-0.21124000000000001 0.0052466500000000003 0.15638099999999999 +-0.196797 0.0093086999999999996 0.159224 +-0.188163 0.0229398 0.155281 +-0.186748 -0.00071344699999999997 0.16439999999999999 +-0.17146900000000001 -0.0024138699999999998 0.16889299999999999 +-0.15892999999999999 -0.013439400000000001 0.16819300000000001 +-0.21956100000000001 -0.0022946400000000001 0.154558 +-0.211896 -0.0027186900000000002 0.15682499999999999 +0.24188599999999999 0.14030899999999999 0.086030200000000001 +-0.25437799999999999 -0.18060799999999999 -0.0057263100000000001 +-0.025636200000000001 -0.19279499999999999 -0.0108483 +-0.0213145 -0.18801300000000001 -0.025495899999999998 +-0.00679366 -0.18057300000000001 -0.0278423 +0.26587899999999998 0.079333600000000004 0.085775799999999999 +-0.178895 -0.21406 0.038848300000000002 +-0.17230000000000001 -0.21589800000000001 0.042354700000000002 +-0.244753 0.00128651 0.14643700000000001 +-0.237347 0.0022347999999999999 0.14898500000000001 +0.25157000000000002 0.045726700000000002 -0.052765399999999997 +-0.47526099999999999 -0.0012175899999999999 -0.018558100000000001 +-0.47783799999999998 0.0041643299999999999 -0.025906100000000001 +-0.49047400000000002 0.0025801399999999999 -0.0683864 +0.19606899999999999 0.034771700000000003 -0.095783099999999996 +0.20161499999999999 0.0491382 -0.093995200000000001 +0.18803 0.062183099999999998 -0.0972113 +0.16736400000000001 0.075642699999999993 -0.099624299999999999 +0.17239699999999999 0.099855899999999997 -0.092121900000000007 +0.187663 0.033633000000000003 -0.100368 +0.174757 0.0400994 -0.106834 +0.19705300000000001 0.026279299999999998 -0.091385900000000006 +0.202678 0.022654899999999999 -0.084570599999999996 +0.210031 0.055648599999999999 -0.090792899999999996 +0.19250700000000001 0.052704500000000001 -0.097813999999999998 +0.18021599999999999 0.052982799999999997 -0.102021 +0.175563 0.065276399999999998 -0.100425 +0.16292599999999999 0.064042299999999996 -0.104369 +0.142127 0.062495299999999997 -0.113056 +0.15193899999999999 0.075628399999999998 -0.10379099999999999 +0.13278899999999999 0.079786499999999996 -0.10854 +-0.348441 -0.14252699999999999 -0.034886899999999998 +-0.34230100000000002 -0.14637 -0.030130400000000002 +0.14871100000000001 0.18328900000000001 -0.072625099999999998 +0.16165599999999999 0.18123600000000001 -0.072920200000000004 +0.163216 0.188442 -0.064501000000000003 +-0.23855599999999999 -0.031050600000000001 -0.0635487 +-0.0029130699999999998 0.035265100000000001 -0.19427 +0.010807499999999999 0.041325300000000002 -0.188804 +-0.0055335699999999998 0.020327600000000001 -0.19745599999999999 +-0.0214647 0.015901599999999998 -0.196991 +0.0071276999999999998 0.0113207 -0.197326 +0.029401299999999998 -0.148697 -0.11991599999999999 +0.0138806 -0.159193 -0.10931200000000001 +0.0172829 -0.15901699999999999 -0.088773500000000005 +0.0277653 -0.14779900000000001 -0.13544999999999999 +0.0027332699999999999 -0.1618 -0.121337 +-0.011110999999999999 -0.16848399999999999 -0.106465 +-0.023505100000000001 -0.16736400000000001 -0.11629299999999999 +0.024216399999999999 -0.15465899999999999 -0.069148500000000002 +-0.014351900000000001 -0.17225199999999999 -0.092658000000000004 +-0.010652 0.060463599999999999 -0.18701599999999999 +0.100536 0.224554 0.051798499999999997 +0.114136 0.219745 0.057887399999999999 +0.091313000000000005 0.22528699999999999 0.059526299999999997 +0.079702599999999998 0.227767 0.0581816 +0.0723327 0.22902700000000001 0.0453777 +0.082926899999999998 0.227441 0.030913 +0.066844700000000007 0.229241 0.063203899999999993 +0.114338 0.21690599999999999 0.070553500000000005 +0.103966 0.21777099999999999 0.077286099999999996 +0.27313999999999999 0.090428900000000006 -0.074272900000000003 +0.27865000000000001 0.100969 -0.074016399999999996 +-0.031820099999999997 -0.172097 0.140491 +-0.15099499999999999 0.19156899999999999 0.044903899999999997 +-0.16286100000000001 0.18260999999999999 0.059771600000000001 +-0.181673 0.17237 0.056407400000000003 +-0.19064600000000001 0.17325099999999999 0.036338599999999999 +-0.13341900000000001 0.19962199999999999 0.048712999999999999 +0.30869000000000002 0.14619199999999999 -0.039147000000000001 +0.310112 0.144647 -0.035131500000000003 +0.31212800000000002 0.140484 -0.0337784 +0.31284600000000001 0.137243 -0.037271100000000001 +0.31362600000000002 0.13300000000000001 -0.043947 +0.31150600000000001 0.13802500000000001 -0.0426565 +0.314828 0.12872900000000001 -0.048667599999999998 +-0.39890999999999999 0.035649800000000002 -0.091534000000000004 +-0.41048299999999999 0.034610099999999998 -0.092735100000000001 +-0.39403899999999997 0.042540000000000001 -0.088651099999999997 +-0.087818800000000002 0.21124599999999999 0.089453599999999994 +-0.10222199999999999 0.20704 0.083188100000000001 +-0.105324 0.210562 0.066740999999999995 +-0.119615 0.203259 0.067314700000000005 +-0.0114042 -0.15342700000000001 -0.16265399999999999 +-0.021692400000000001 -0.15275900000000001 -0.162414 +-0.027649699999999999 -0.15523300000000001 -0.15404100000000001 +-0.022919100000000001 -0.159385 -0.14436399999999999 +-0.028222000000000001 -0.162884 -0.13029399999999999 +-0.0091154500000000006 -0.15975500000000001 -0.143127 +0.0038177300000000001 -0.15817400000000001 -0.138235 +0.015862899999999999 -0.15197099999999999 -0.145374 +-0.043041999999999997 -0.15981100000000001 -0.13128300000000001 +-0.0578498 -0.157583 -0.12483 +-0.073061100000000004 -0.15382100000000001 -0.118257 +-0.0918654 -0.14227100000000001 -0.119321 +-0.086545800000000006 0.15179899999999999 -0.110721 +-0.0747559 0.16490299999999999 -0.099600300000000003 +-0.087713100000000002 0.16961100000000001 -0.084725800000000004 +-0.104298 0.14448 -0.108615 +-0.111502 0.130777 -0.117599 +-0.29059800000000002 0.122665 -0.0344017 +-0.28304800000000002 0.12528600000000001 -0.032653799999999997 +-0.27710000000000001 0.13156100000000001 -0.023489699999999999 +-0.273262 0.12407799999999999 -0.037146600000000002 +-0.27542 0.117855 -0.0440148 +-0.18649299999999999 0.17033999999999999 -0.0011688200000000001 +-0.17177000000000001 0.166823 -0.020371400000000001 +-0.18068400000000001 0.153502 -0.035443700000000002 +-0.20599200000000001 0.16261600000000001 -0.0020504799999999999 +-0.198324 0.14709 -0.033816600000000002 +-0.0059202899999999999 0.100685 0.18144099999999999 +-0.0167332 0.118964 0.17482900000000001 +0.015946700000000001 0.103079 0.18060999999999999 +0.028412400000000001 0.084941699999999995 0.18443200000000001 +-0.44251800000000002 -0.070944300000000002 -0.086369100000000004 +-0.44885599999999998 -0.066220100000000004 -0.087481199999999995 +-0.13466700000000001 0.00094315399999999998 0.177011 +-0.00310122 0.23491300000000001 0.062836199999999995 +-0.46231 -0.058624900000000001 -0.040429600000000003 +0.22753799999999999 0.0369226 -0.072126899999999994 +0.236821 0.042365600000000003 -0.066803500000000002 +0.24060500000000001 0.037123200000000002 -0.054989900000000001 +0.22984099999999999 0.0265624 -0.0470128 +-0.46360699999999999 0.028646100000000001 -0.085726200000000002 +-0.45672600000000002 0.027279000000000001 -0.090690900000000005 +-0.469667 0.022226300000000001 -0.084804500000000005 +-0.45082699999999998 0.0237483 -0.094695799999999997 +0.21632100000000001 0.13897799999999999 0.105626 +0.21636 0.17601800000000001 -0.068996000000000002 +0.22472400000000001 0.177449 -0.064092700000000002 +0.23515 0.17280300000000001 -0.066168099999999994 +0.26077800000000001 0.097572000000000006 0.093073299999999998 +0.25369700000000001 0.10379099999999999 0.096948800000000002 +0.00042625600000000002 0.0061583499999999999 0.19870499999999999 +-0.0143538 0.0030840799999999999 0.19675899999999999 +-0.025944800000000001 0.0152079 0.19378100000000001 +0.0049670399999999998 0.019903899999999999 0.19777 +-0.013013200000000001 0.23198099999999999 0.085514800000000002 +-0.00048819800000000002 0.23011499999999999 0.092733999999999997 +-0.486599 0.0044273799999999999 -0.083403900000000003 +-0.48317399999999999 0.0083421499999999996 -0.083037799999999995 +-0.48153099999999999 0.012534999999999999 -0.0769011 +-0.47923100000000002 0.0093157599999999993 -0.087203699999999995 +-0.47913600000000001 0.0130796 -0.082447800000000002 +-0.47456700000000002 0.018227799999999999 -0.082057099999999994 +-0.48859900000000001 0.0026895600000000001 -0.079400200000000004 +-0.48931200000000002 -0.0020909800000000001 -0.078754299999999999 +-0.48787900000000001 0.0045522799999999997 -0.074895199999999995 +-0.48457299999999998 0.0089092100000000007 -0.074025499999999994 +-0.48162100000000002 0.013351099999999999 -0.070635600000000007 +-0.48615700000000001 0.0013552 -0.087000099999999997 +0.074113999999999999 -0.0959285 -0.152444 +0.0788079 -0.084809499999999996 -0.156079 +0.077666100000000002 -0.075125800000000006 -0.16463800000000001 +0.30785699999999999 0.12345 -0.062920500000000004 +0.30563200000000001 0.118432 -0.064303899999999997 +0.306506 0.11278000000000001 -0.061284600000000002 +0.303699 0.10655199999999999 -0.060595099999999999 +0.29678599999999999 0.103863 -0.064865800000000001 +0.29050900000000002 0.098705399999999999 -0.068410899999999997 +0.30475000000000002 0.12803400000000001 -0.064310099999999995 +0.29785099999999998 0.128749 -0.068135699999999993 +-0.33938800000000002 0.107776 -0.030821100000000001 +-0.33142199999999999 0.111016 -0.0225385 +0.1951 0.19827800000000001 0.038898000000000002 +0.20821000000000001 0.19181000000000001 0.040146500000000002 +0.21546899999999999 0.18287600000000001 0.054990900000000002 +0.218888 0.19023799999999999 0.0280165 +0.23067299999999999 0.18829799999999999 0.0132706 +-0.46731400000000001 -0.059894999999999997 -0.0588218 +-0.45948499999999998 -0.067780400000000005 -0.060704399999999999 +0.30712899999999999 0.14621899999999999 -0.0474896 +-0.42225099999999999 0.057628600000000002 -0.076709100000000002 +-0.42860999999999999 0.054164200000000003 -0.076605000000000006 + +# 4050 facets +# ------------------------------------------ + +3 671 417 1634 +3 945 65 944 +3 874 2025 835 +3 1009 997 369 +3 1598 927 1597 +3 1355 128 1357 +3 294 1255 299 +3 266 299 294 +3 1482 1162 146 +3 611 612 32 +3 313 20 312 +3 67 1314 1022 +3 1147 1148 561 +3 119 483 1122 +3 1715 1714 1718 +3 1718 832 1715 +3 782 1117 1648 +3 991 992 367 +3 554 547 546 +3 31 619 576 +3 359 985 650 +3 650 980 359 +3 969 367 992 +3 674 1700 673 +3 181 673 674 +3 660 59 307 +3 854 855 912 +3 1630 1498 589 +3 246 88 1571 +3 1138 1137 349 +3 349 1137 1136 +3 1426 917 576 +3 151 154 37 +3 72 642 643 +3 1285 530 1783 +3 495 494 12 +3 496 12 495 +3 427 202 426 +3 380 1021 1020 +3 192 846 345 +3 1526 118 1411 +3 1810 79 1811 +3 1540 1811 1810 +3 427 202 430 +3 1275 337 827 +3 1518 84 1685 +3 544 759 132 +3 1545 1547 444 +3 1762 1758 687 +3 7 373 416 +3 665 844 1326 +3 1953 665 1326 +3 376 70 378 +3 1325 1339 142 +3 468 187 469 +3 332 681 537 +3 1232 389 1231 +3 240 1651 241 +3 1463 456 44 +3 265 1358 1356 +3 2003 1412 889 +3 628 637 625 +3 184 1900 1896 +3 501 1860 1410 +3 1362 1336 666 +3 1256 1089 1086 +3 1086 1256 1084 +3 495 1821 1824 +3 382 651 1406 +3 406 81 405 +3 1412 1413 1177 +3 70 376 369 +3 239 241 153 +3 602 800 601 +3 1638 800 601 +3 336 827 108 +3 1947 110 273 +3 1188 1189 203 +3 1188 1189 205 +3 870 867 871 +3 1028 380 1020 +3 1020 1028 1017 +3 976 973 151 +3 35 589 1624 +3 1624 589 585 +3 904 187 478 +3 462 187 478 +3 794 1678 170 +3 794 1099 170 +3 958 690 537 +3 151 789 788 +3 802 182 806 +3 1021 1023 1022 +3 802 524 806 +3 558 426 50 +3 1976 1978 879 +3 882 1978 1976 +3 752 751 161 +3 1726 570 1725 +3 1458 460 186 +3 1458 186 1460 +3 207 1844 1852 +3 1852 207 1845 +3 490 1469 1467 +3 1720 1719 896 +3 667 57 668 +3 1870 1871 169 +3 1022 1314 1738 +3 98 1964 1963 +3 780 160 778 +3 57 1096 667 +3 57 1096 1094 +3 377 378 376 +3 1321 158 774 +3 703 64 282 +3 703 64 742 +3 854 1930 858 +3 1767 536 1060 +3 181 676 1656 +3 857 856 354 +3 1018 1008 1015 +3 632 637 1770 +3 632 42 1770 +3 1672 1674 15 +3 422 1013 1011 +3 572 94 668 +3 824 550 34 +3 1244 1012 358 +3 273 51 289 +3 56 1250 1253 +3 1253 1250 297 +3 1636 1178 1101 +3 1102 1178 1636 +3 1093 1098 1097 +3 404 233 178 +3 1137 63 1139 +3 767 53 766 +3 1729 1964 385 +3 479 1522 480 +3 480 479 117 +3 1302 1301 527 +3 527 1302 1303 +3 688 744 962 +3 688 744 1688 +3 613 32 611 +3 740 27 737 +3 1435 27 740 +3 1078 1080 357 +3 1502 406 81 +3 82 327 692 +3 1258 1988 570 +3 1727 1258 570 +3 873 338 853 +3 928 1614 1927 +3 928 1926 1927 +3 543 489 121 +3 1226 1220 386 +3 504 487 505 +3 1218 115 1222 +3 358 1244 983 +3 983 1244 492 +3 408 505 49 +3 879 880 337 +3 181 674 675 +3 1285 530 1283 +3 1846 1186 1847 +3 1292 725 1293 +3 1292 725 1290 +3 147 155 37 +3 158 776 773 +3 150 1668 1669 +3 1086 309 1919 +3 1198 1195 410 +3 161 162 252 +3 980 1769 981 +3 229 1947 1946 +3 214 623 1863 +3 1863 623 1974 +3 1293 726 1288 +3 1288 1293 725 +3 546 312 250 +3 1016 1017 379 +3 619 1204 1203 +3 1217 1224 1218 +3 360 980 650 +3 105 340 1761 +3 307 662 659 +3 658 662 659 +3 1452 490 1471 +3 408 406 1502 +3 1494 0 1492 +3 1955 1956 966 +3 1955 1956 1954 +3 267 245 55 +3 554 26 546 +3 1676 1677 1680 +3 446 1817 77 +3 660 307 659 +3 2007 1595 1593 +3 1208 114 1209 +3 1214 114 1208 +3 91 364 365 +3 1629 588 1630 +3 376 377 89 +3 482 22 486 +3 137 970 646 +3 546 250 26 +3 2005 2004 1340 +3 277 276 164 +3 693 323 956 +3 108 1269 1270 +3 1676 150 1677 +3 177 286 1427 +3 120 138 139 +3 1353 129 1352 +3 285 282 64 +3 1332 1333 343 +3 595 1581 1587 +3 53 768 769 +3 1499 1503 498 +3 435 434 71 +3 822 348 837 +3 823 348 822 +3 637 632 600 +3 628 600 637 +3 486 439 183 +3 1672 798 1674 +3 1624 585 1625 +3 634 597 636 +3 1394 235 1392 +3 171 1391 238 +3 1987 419 1988 +3 754 1916 1687 +3 515 754 1687 +3 1963 1965 372 +3 1054 1053 130 +3 486 22 439 +3 414 80 451 +3 414 11 451 +3 276 277 278 +3 1030 380 1028 +3 332 684 465 +3 1582 580 1498 +3 881 337 1662 +3 1728 208 558 +3 385 1728 558 +3 1897 184 1896 +3 1384 1383 1382 +3 167 15 977 +3 432 430 25 +3 432 430 202 +3 2004 1342 1344 +3 1897 220 1902 +3 1032 556 224 +3 1143 1144 517 +3 1375 324 1374 +3 1330 191 1362 +3 1343 1342 1428 +3 100 1428 1343 +3 737 740 741 +3 1975 1974 623 +3 638 1578 598 +3 1672 594 1673 +3 1673 15 1672 +3 937 938 939 +3 939 609 937 +3 315 966 967 +3 58 966 967 +3 84 1500 1503 +3 1503 84 1685 +3 507 1740 1778 +3 907 1722 898 +3 695 696 327 +3 434 420 71 +3 1330 1362 1363 +3 170 797 796 +3 1098 315 1096 +3 1098 315 1097 +3 821 849 1139 +3 1139 849 1249 +3 563 868 869 +3 636 5 1105 +3 1105 5 1104 +3 1393 234 1394 +3 386 1219 1220 +3 1207 1208 397 +3 397 1208 1212 +3 675 21 674 +3 1628 941 1620 +3 402 1227 1228 +3 1214 1215 114 +3 1214 1215 1216 +3 1034 814 216 +3 1650 88 1649 +3 127 1234 1211 +3 765 133 1150 +3 765 1150 1782 +3 91 1002 364 +3 655 656 313 +3 313 655 653 +3 984 359 985 +3 1454 458 44 +3 2 1153 1154 +3 1154 1153 1155 +3 1525 1526 497 +3 497 1526 1527 +3 524 807 808 +3 1001 8 998 +3 557 195 730 +3 1028 1017 68 +3 409 81 405 +3 1733 36 845 +3 845 36 1134 +3 685 1558 1559 +3 1726 570 204 +3 505 121 413 +3 1894 750 766 +3 44 1454 1453 +3 377 403 378 +3 1654 403 377 +3 527 1301 1296 +3 1067 1301 1296 +3 322 25 321 +3 468 463 83 +3 83 463 1754 +3 1120 1156 174 +3 656 125 1934 +3 1353 1354 129 +3 1544 78 1543 +3 1946 1947 226 +3 1372 1373 696 +3 231 1941 1942 +3 450 1529 1528 +3 1528 1508 450 +3 1404 91 1403 +3 1409 85 1408 +3 1409 85 1553 +3 744 961 1742 +3 555 223 556 +3 973 974 976 +3 173 257 749 +3 748 1345 331 +3 129 715 1359 +3 1088 425 1079 +3 1441 1435 740 +3 1007 368 1006 +3 1010 1906 69 +3 1038 1743 1034 +3 382 993 1014 +3 109 1039 1033 +3 430 203 1188 +3 1973 1974 625 +3 1332 841 842 +3 1332 343 842 +3 505 413 122 +3 358 1011 1012 +3 436 76 435 +3 260 1950 1949 +3 1917 311 1918 +3 306 1917 1918 +3 299 266 1962 +3 1086 1919 304 +3 110 228 229 +3 185 681 682 +3 459 1828 1457 +3 459 1828 1458 +3 373 372 1966 +3 1334 191 1333 +3 1475 883 1692 +3 75 1563 1058 +3 1220 383 1221 +3 994 366 991 +3 999 364 365 +3 367 1005 1031 +3 969 367 1031 +3 1404 1403 374 +3 424 208 426 +3 269 227 270 +3 1986 1987 1258 +3 1695 953 1693 +3 588 1498 934 +3 1806 1805 1807 +3 138 120 195 +3 1180 591 1179 +3 646 643 644 +3 64 743 742 +3 742 743 438 +3 1094 1436 303 +3 4 713 711 +3 291 713 710 +3 82 405 409 +3 1155 526 1154 +3 695 327 693 +3 93 1367 1365 +3 730 1435 1441 +3 3 543 413 +3 1963 98 1965 +3 407 697 325 +3 1349 320 1348 +3 1936 1937 1035 +3 1036 1937 1936 +3 523 1143 517 +3 453 1536 1812 +3 1000 363 1003 +3 240 720 721 +3 240 720 719 +3 390 1654 16 +3 159 780 781 +3 52 672 680 +3 611 610 612 +3 834 336 828 +3 2018 381 2019 +3 1002 1001 363 +3 995 8 1000 +3 669 1776 1774 +3 371 363 98 +3 1562 79 1826 +3 1562 79 1542 +3 357 1078 1012 +3 174 791 790 +3 628 625 1863 +3 63 866 1138 +3 354 857 1452 +3 775 1496 1495 +3 1538 452 1771 +3 421 275 234 +3 19 1082 1083 +3 313 312 661 +3 1034 814 222 +3 970 1171 1173 +3 111 1942 1899 +3 757 759 132 +3 407 122 410 +3 1816 1817 1815 +3 565 179 566 +3 1132 1142 1128 +3 1952 264 718 +3 1240 393 127 +3 271 228 270 +3 1430 100 1429 +3 228 1730 1731 +3 1272 1264 1265 +3 110 273 272 +3 43 698 699 +3 283 698 699 +3 779 162 161 +3 438 545 24 +3 627 213 617 +3 617 213 935 +3 1208 1214 1212 +3 1212 1214 398 +3 1221 1220 16 +3 899 897 1971 +3 1220 16 1219 +3 1507 1854 1548 +3 1218 1216 398 +3 20 652 653 +3 86 1567 1568 +3 570 204 569 +3 443 442 117 +3 81 415 414 +3 1218 1217 1216 +3 788 151 154 +3 1904 257 791 +3 1166 145 1160 +3 1167 145 1166 +3 274 227 271 +3 368 1018 1913 +3 543 542 544 +3 846 345 847 +3 550 212 551 +3 275 227 421 +3 370 1027 1910 +3 494 493 704 +3 232 284 562 +3 548 652 20 +3 999 998 364 +3 1087 1088 431 +3 63 866 867 +3 256 171 255 +3 990 994 989 +3 571 251 163 +3 1273 888 1865 +3 1865 888 1864 +3 1904 791 174 +3 715 717 716 +3 307 59 302 +3 467 1451 1450 +3 262 250 124 +3 248 795 796 +3 1356 59 1358 +3 1614 2024 1615 +3 729 950 1291 +3 729 1290 1291 +3 1950 260 1747 +3 362 1003 1000 +3 996 995 366 +3 565 179 420 +3 51 420 565 +3 1317 915 1316 +3 1017 1016 1019 +3 1017 1019 68 +3 482 479 22 +3 942 133 755 +3 755 133 1150 +3 1944 225 1945 +3 1012 201 1011 +3 1067 1309 1308 +3 729 1298 950 +3 729 1298 1297 +3 1093 1098 1436 +3 1094 1098 1436 +3 1295 952 1294 +3 1102 1636 1637 +3 89 383 1221 +3 91 1966 373 +3 738 739 741 +3 738 741 737 +3 292 715 712 +3 1767 1060 488 +3 644 646 30 +3 1232 1219 16 +3 360 986 987 +3 1431 1430 1429 +3 195 27 196 +3 557 645 136 +3 557 138 645 +3 1169 1165 1170 +3 1393 276 164 +3 636 634 168 +3 509 815 1440 +3 87 1570 789 +3 554 212 547 +3 562 277 10 +3 676 1903 184 +3 1297 1067 1296 +3 546 312 20 +3 799 1671 1100 +3 1670 1671 799 +3 1033 1034 1743 +3 1343 218 1342 +3 1344 218 1342 +3 1042 1043 510 +3 250 1434 262 +3 1854 1506 1507 +3 95 1263 1106 +3 887 1263 1106 +3 402 90 947 +3 1433 1432 249 +3 1225 1224 386 +3 943 585 944 +3 1743 1038 215 +3 1772 1773 1741 +3 484 1772 1741 +3 1175 1176 120 +3 120 1175 143 +3 1358 660 261 +3 1884 1539 1535 +3 1535 1539 453 +3 1052 536 1051 +3 147 37 148 +3 637 625 1125 +3 1125 625 1126 +3 638 633 597 +3 627 214 620 +3 456 458 44 +3 1872 1130 1867 +3 1568 259 1949 +3 716 243 717 +3 61 1113 1256 +3 1091 1092 60 +3 1961 1092 60 +3 245 716 243 +3 1811 1542 1539 +3 79 1542 1811 +3 285 286 177 +3 1870 1882 1880 +3 1316 1235 1236 +3 718 243 717 +3 1905 1907 370 +3 634 168 1871 +3 310 1256 1084 +3 1937 1938 109 +3 269 163 421 +3 743 437 64 +3 386 1224 1217 +3 752 751 172 +3 227 270 271 +3 269 163 268 +3 1 252 251 +3 252 251 162 +3 1949 128 1948 +3 345 192 842 +3 345 342 842 +3 1868 1129 1867 +3 1868 1129 626 +3 168 1871 1673 +3 770 37 154 +3 1142 978 624 +3 1128 978 1142 +3 120 143 139 +3 1321 774 1323 +3 462 1446 478 +3 751 1894 1892 +3 1929 858 862 +3 244 40 245 +3 228 1730 270 +3 1701 289 180 +3 717 1352 129 +3 673 181 680 +3 1576 1578 598 +3 1797 1541 1798 +3 1798 1797 1796 +3 160 812 813 +3 1701 180 1700 +3 356 1935 311 +3 48 442 441 +3 441 444 48 +3 1729 1003 209 +3 2001 1994 884 +3 652 314 649 +3 957 691 955 +3 836 348 869 +3 702 283 700 +3 118 1522 1521 +3 1959 301 1958 +3 54 705 1252 +3 489 541 542 +3 220 1895 1697 +3 284 282 10 +3 304 1090 1091 +3 1357 128 1950 +3 1820 198 1819 +3 1552 476 1551 +3 1024 608 607 +3 172 771 772 +3 690 537 332 +3 606 1157 553 +3 464 185 465 +3 86 262 260 +3 1716 907 886 +3 1722 907 1716 +3 1013 424 422 +3 187 462 463 +3 1502 1501 1517 +3 1476 1470 1473 +3 1260 417 1689 +3 343 842 342 +3 1834 903 902 +3 1931 563 868 +3 815 1058 816 +3 509 815 816 +3 751 1893 161 +3 741 728 1441 +3 741 728 739 +3 75 818 817 +3 1043 131 1048 +3 886 1106 907 +3 489 542 543 +3 730 731 557 +3 532 1841 1309 +3 235 251 571 +3 425 424 426 +3 429 428 204 +3 151 789 792 +3 451 954 11 +3 461 954 11 +3 39 172 772 +3 776 773 772 +3 162 254 251 +3 251 163 254 +3 1478 1229 400 +3 531 1281 1633 +3 854 912 852 +3 111 1895 1898 +3 548 547 20 +3 406 33 407 +3 1809 1825 1457 +3 1826 1825 1457 +3 258 153 239 +3 604 9 601 +3 1279 530 1278 +3 1431 144 1286 +3 1119 1118 230 +3 219 218 1116 +3 1343 218 219 +3 375 1291 952 +3 837 348 836 +3 1612 1924 1925 +3 238 244 40 +3 331 961 745 +3 173 256 257 +3 542 1439 541 +3 783 156 785 +3 390 126 391 +3 432 431 202 +3 1385 236 1386 +3 460 461 1465 +3 768 156 767 +3 1172 138 139 +3 770 768 154 +3 44 1451 1453 +3 714 4 294 +3 294 714 293 +3 475 1785 1659 +3 1894 172 751 +3 515 1143 1147 +3 1047 1046 510 +3 792 87 789 +3 792 87 793 +3 406 408 407 +3 1518 84 1513 +3 455 1800 1802 +3 1808 458 353 +3 281 282 285 +3 172 766 1894 +3 1530 450 1840 +3 1895 219 1697 +3 763 764 3 +3 1847 321 1187 +3 1186 321 1847 +3 1316 1235 573 +3 1316 573 1317 +3 51 420 286 +3 398 1212 1223 +3 997 998 8 +3 568 206 1192 +3 1192 568 1282 +3 1163 62 1881 +3 1881 1066 1163 +3 423 99 422 +3 626 1126 1127 +3 1845 1185 1366 +3 1186 1845 1366 +3 326 94 317 +3 1969 1190 206 +3 170 1679 797 +3 796 249 797 +3 547 546 20 +3 288 178 290 +3 254 17 813 +3 1120 749 1891 +3 482 483 118 +3 362 1000 995 +3 779 752 161 +3 624 1975 30 +3 624 30 646 +3 1716 886 1717 +3 1745 1746 188 +3 1266 1746 1745 +3 1331 191 1330 +3 1901 231 1900 +3 879 1997 1978 +3 795 87 793 +3 1475 1472 855 +3 494 701 704 +3 121 504 505 +3 288 701 704 +3 1199 924 577 +3 1924 1618 930 +3 1924 1618 1923 +3 1849 1193 1194 +3 1194 1849 1850 +3 1885 722 1534 +3 435 439 434 +3 1156 787 769 +3 280 177 1427 +3 521 1280 1279 +3 1850 1195 1849 +3 47 863 862 +3 47 863 864 +3 896 1720 1721 +3 38 1114 1115 +3 1641 1114 38 +3 1056 512 1053 +3 1720 832 1722 +3 1147 1144 1143 +3 1144 1145 2 +3 141 1132 1875 +3 1826 1562 1827 +3 606 1680 602 +3 45 1805 1807 +3 21 1945 1698 +3 1043 131 1044 +3 217 1118 1943 +3 67 1063 946 +3 1318 1317 915 +3 1046 511 1051 +3 1315 9 604 +3 1474 1469 902 +3 253 171 255 +3 136 731 732 +3 99 423 972 +3 197 196 1430 +3 446 444 77 +3 958 691 957 +3 1814 454 46 +3 783 785 784 +3 0 785 784 +3 673 680 672 +3 115 1227 1228 +3 1623 1624 584 +3 21 1944 1940 +3 567 569 204 +3 1007 368 1913 +3 986 988 987 +3 668 57 316 +3 5 1587 1583 +3 1577 1583 5 +3 1568 86 260 +3 1567 248 1432 +3 828 336 827 +3 1733 36 844 +3 200 1823 1822 +3 914 1177 913 +3 112 221 197 +3 786 37 770 +3 1786 1784 1635 +3 1635 1788 1786 +3 1430 100 221 +3 766 767 771 +3 771 767 783 +3 531 1281 1283 +3 1038 215 1114 +3 1939 1036 1938 +3 186 460 461 +3 181 676 675 +3 1035 1937 109 +3 672 673 180 +3 226 1947 273 +3 226 289 273 +3 229 1041 225 +3 811 216 812 +3 750 1890 1892 +3 158 1704 774 +3 1952 718 719 +3 1699 226 1698 +3 807 806 524 +3 1040 225 1037 +3 511 1045 1044 +3 164 277 1383 +3 848 1735 1245 +3 848 1735 1737 +3 1854 1509 48 +3 270 223 1730 +3 277 278 10 +3 415 81 1502 +3 216 1038 1034 +3 510 815 1042 +3 1058 815 510 +3 462 1460 1446 +3 1806 458 1807 +3 237 1389 1390 +3 1538 78 1543 +3 1538 1530 1543 +3 847 846 346 +3 847 1395 346 +3 854 564 858 +3 1357 261 1950 +3 1540 1803 453 +3 276 278 176 +3 1393 276 234 +3 70 369 969 +3 992 369 969 +3 1385 235 1386 +3 342 18 340 +3 74 517 522 +3 679 680 52 +3 797 26 249 +3 21 1940 1901 +3 792 793 149 +3 792 149 973 +3 142 1324 1323 +3 1960 1959 59 +3 1960 1356 59 +3 1895 1896 111 +3 522 1045 1049 +3 419 1182 568 +3 621 620 31 +3 642 644 643 +3 1259 1987 1986 +3 1170 1173 1171 +3 747 955 957 +3 1319 1322 1320 +3 1578 1577 597 +3 269 421 227 +3 875 874 834 +3 176 272 274 +3 1521 480 1520 +3 1746 188 1109 +3 271 272 274 +3 1945 225 229 +3 126 391 392 +3 297 711 4 +3 230 1118 1943 +3 675 1901 21 +3 2022 900 2023 +3 1897 184 678 +3 438 437 743 +3 24 437 438 +3 46 1816 1819 +3 381 1029 1030 +3 201 1013 1011 +3 1748 1559 686 +3 107 1559 686 +3 1087 1079 1080 +3 1587 1581 1583 +3 268 163 254 +3 1325 1286 1339 +3 152 246 1112 +3 1569 246 152 +3 1397 1395 346 +3 1392 1384 1385 +3 1130 141 1874 +3 326 328 28 +3 1403 91 373 +3 223 555 268 +3 662 302 307 +3 720 719 243 +3 554 26 552 +3 1714 892 893 +3 491 1476 1473 +3 788 152 787 +3 1748 685 1559 +3 40 721 239 +3 1681 1677 150 +3 292 713 710 +3 712 710 292 +3 120 195 1176 +3 1638 1637 66 +3 1639 1637 1638 +3 1572 1568 247 +3 1347 694 1346 +3 323 1346 1347 +3 1484 1161 1482 +3 270 223 269 +3 359 980 981 +3 981 359 983 +3 171 258 256 +3 1 253 252 +3 1184 1371 1196 +3 1184 1196 1364 +3 754 753 515 +3 1059 753 754 +3 1356 266 1960 +3 1962 266 1960 +3 1695 1693 1174 +3 1174 1695 140 +3 933 934 1498 +3 1880 1882 1881 +3 16 1654 377 +3 258 238 171 +3 1385 1392 235 +3 1530 1840 1533 +3 1163 1873 62 +3 1878 1873 62 +3 866 350 496 +3 1246 849 1247 +3 1949 1948 259 +3 653 314 654 +3 296 1736 1957 +3 296 1957 1376 +3 1354 293 266 +3 294 293 266 +3 708 291 711 +3 711 708 297 +3 1890 749 173 +3 165 244 267 +3 102 526 724 +3 1288 102 724 +3 281 278 279 +3 271 228 110 +3 405 696 327 +3 1392 1393 1394 +3 186 462 463 +3 186 462 1460 +3 271 272 110 +3 1107 95 899 +3 275 274 227 +3 275 176 274 +3 278 176 279 +3 273 272 280 +3 317 94 668 +3 668 317 316 +3 276 275 176 +3 687 1763 329 +3 329 687 1688 +3 1742 1688 329 +3 1772 1773 669 +3 275 276 234 +3 1801 1462 1461 +3 1807 1808 458 +3 463 1755 1754 +3 280 176 272 +3 279 176 280 +3 565 180 566 +3 534 1259 1631 +3 1689 1259 1631 +3 549 550 212 +3 447 1536 1537 +3 703 283 702 +3 702 703 282 +3 280 279 177 +3 1818 1816 198 +3 1916 516 756 +3 532 1308 1307 +3 267 244 245 +3 189 1833 1968 +3 178 284 287 +3 222 1032 1033 +3 1033 1032 1039 +3 1340 1286 1428 +3 441 442 76 +3 1544 1546 1545 +3 1545 78 1544 +3 1230 388 1225 +3 314 985 649 +3 652 649 548 +3 232 178 284 +3 1766 1555 1556 +3 1507 445 1548 +3 1507 445 1528 +3 714 713 292 +3 319 318 92 +3 92 319 322 +3 870 298 871 +3 781 1642 1062 +3 1447 1444 1445 +3 405 33 696 +3 193 850 875 +3 1450 44 1451 +3 318 326 317 +3 516 942 1916 +3 55 712 710 +3 1568 1567 247 +3 299 58 968 +3 764 411 413 +3 265 1355 1353 +3 346 846 1400 +3 1451 1452 467 +3 301 1961 300 +3 301 1961 1958 +3 665 844 1335 +3 1377 844 1335 +3 716 245 55 +3 11 409 414 +3 920 919 612 +3 612 919 610 +3 872 869 563 +3 436 435 71 +3 295 4 294 +3 1893 255 161 +3 866 496 867 +3 291 709 708 +3 714 4 713 +3 429 1190 1189 +3 659 124 308 +3 988 990 210 +3 685 1750 1751 +3 417 1300 1261 +3 864 863 823 +3 312 124 250 +3 318 28 326 +3 124 312 308 +3 654 653 655 +3 872 853 873 +3 1948 1951 128 +3 356 1081 1082 +3 1275 1274 826 +3 826 1275 1276 +3 436 437 24 +3 24 436 440 +3 337 878 879 +3 1327 1736 1957 +3 552 551 97 +3 212 551 552 +3 304 1089 1090 +3 916 917 113 +3 916 917 574 +3 1197 1843 1364 +3 1197 1843 1844 +3 1087 1081 1080 +3 19 1081 1087 +3 407 410 325 +3 93 1368 1367 +3 105 341 326 +3 326 105 328 +3 208 423 422 +3 4 295 297 +3 1647 1117 1648 +3 979 360 980 +3 695 1347 693 +3 696 33 697 +3 1893 256 173 +3 1588 1779 1590 +3 1588 41 1590 +3 438 698 545 +3 1092 1437 303 +3 1095 305 316 +3 493 494 12 +3 483 486 183 +3 482 486 483 +3 119 183 483 +3 1572 88 1650 +3 489 539 541 +3 1069 1068 400 +3 1069 400 1477 +3 1042 1043 1048 +3 1042 514 1048 +3 531 1283 1284 +3 3 763 520 +3 1276 827 108 +3 94 341 326 +3 334 1556 335 +3 571 421 234 +3 1665 925 924 +3 1785 1659 1141 +3 1931 862 868 +3 1418 1866 885 +3 1389 165 1390 +3 859 858 862 +3 347 336 335 +3 229 110 1947 +3 1139 1249 870 +3 1447 477 1444 +3 1444 477 1442 +3 1931 862 1929 +3 1931 563 1929 +3 247 1571 1572 +3 1531 1533 448 +3 1840 1533 1531 +3 1500 481 1499 +3 1503 1499 1500 +3 859 564 861 +3 346 1400 1399 +3 839 1400 1399 +3 1453 1454 857 +3 1571 88 1572 +3 1860 1859 1410 +3 1248 870 298 +3 1174 1175 1693 +3 143 1175 1174 +3 824 551 550 +3 1134 1135 821 +3 1751 1750 684 +3 191 1333 1332 +3 1121 6 2019 +3 1890 1892 173 +3 1348 1346 694 +3 1183 1348 694 +3 1761 1760 334 +3 18 340 334 +3 857 353 861 +3 861 353 860 +3 790 242 1112 +3 861 856 857 +3 571 421 163 +3 1270 335 108 +3 652 314 653 +3 678 1897 1902 +3 1276 826 1269 +3 108 1269 1276 +3 856 861 564 +3 683 83 1111 +3 291 711 713 +3 709 710 404 +3 979 360 986 +3 450 1508 80 +3 1207 920 1858 +3 396 1207 1858 +3 1024 69 1027 +3 222 556 1032 +3 1684 1683 488 +3 1229 115 1228 +3 411 410 23 +3 991 990 210 +3 362 995 994 +3 989 988 361 +3 159 1319 1643 +3 1225 1226 386 +3 388 1226 1225 +3 1598 1599 925 +3 1965 1966 371 +3 1133 1142 1132 +3 141 1133 1132 +3 471 1834 903 +3 1647 1116 218 +3 1141 1657 1658 +3 1660 1657 1658 +3 1027 1906 69 +3 1312 949 1311 +3 1904 257 749 +3 990 994 991 +3 824 551 1909 +3 428 384 1726 +3 1213 1212 397 +3 1213 1212 1223 +3 395 1853 127 +3 1224 1218 115 +3 1227 1224 115 +3 877 829 878 +3 203 428 429 +3 1189 203 429 +3 392 393 126 +3 1739 679 513 +3 1853 393 126 +3 1219 1232 387 +3 1801 1461 457 +3 457 1798 1801 +3 1982 1104 1983 +3 1982 1181 1983 +3 1232 390 16 +3 194 644 175 +3 194 611 175 +3 55 267 233 +3 710 55 404 +3 889 1414 1412 +3 1564 87 795 +3 915 1238 1421 +3 1289 1296 729 +3 1297 1296 729 +3 1421 1238 1420 +3 1877 1869 1878 +3 1877 1878 640 +3 1233 389 395 +3 1109 1108 469 +3 388 559 90 +3 90 559 1405 +3 1222 1223 398 +3 1217 387 1219 +3 1217 386 1219 +3 612 397 1213 +3 1213 32 612 +3 375 949 948 +3 375 949 950 +3 45 1808 1807 +3 33 405 406 +3 451 1531 450 +3 454 46 1822 +3 179 439 183 +3 414 80 415 +3 504 506 485 +3 408 505 122 +3 408 122 407 +3 1550 1551 1505 +3 507 1774 1775 +3 1777 507 1775 +3 1368 1369 1183 +3 132 542 544 +3 704 288 705 +3 1115 1842 217 +3 993 367 1004 +3 1153 1151 528 +3 1148 1151 1153 +3 504 506 503 +3 411 413 122 +3 122 411 410 +3 1820 199 1821 +3 448 1533 1534 +3 1534 448 1464 +3 1546 445 1545 +3 332 185 465 +3 1518 1512 1513 +3 747 955 323 +3 81 414 409 +3 781 1062 811 +3 566 180 672 +3 908 913 1414 +3 209 1729 1728 +3 429 567 204 +3 1197 1196 1364 +3 424 422 208 +3 356 1081 1080 +3 1372 696 697 +3 322 25 433 +3 1079 1078 201 +3 1082 19 1081 +3 203 427 430 +3 202 425 426 +3 1191 206 1192 +3 321 25 1187 +3 1187 1188 205 +3 205 1193 1194 +3 1190 1194 205 +3 310 433 432 +3 432 431 310 +3 433 25 432 +3 202 431 425 +3 1365 1862 1861 +3 1861 1365 1367 +3 319 321 322 +3 545 698 43 +3 233 404 55 +3 420 179 434 +3 1160 1485 1484 +3 1873 640 1872 +3 563 872 853 +3 707 709 708 +3 1355 265 1358 +3 968 58 967 +3 1903 184 1900 +3 1341 142 1339 +3 1689 1986 1259 +3 268 17 555 +3 1108 899 1107 +3 77 441 440 +3 1107 473 1264 +3 1264 1107 1263 +3 1273 474 1272 +3 1266 473 1265 +3 1265 473 1264 +3 76 440 436 +3 1828 1827 1457 +3 1828 1827 1459 +3 76 442 443 +3 343 1686 1333 +3 1522 482 118 +3 444 77 441 +3 1524 1510 476 +3 1524 1510 1511 +3 439 179 434 +3 76 435 443 +3 22 439 435 +3 435 22 443 +3 443 22 479 +3 464 1753 1754 +3 1529 445 1546 +3 1529 445 1528 +3 10 282 281 +3 1816 1817 1818 +3 1524 476 1504 +3 185 461 1756 +3 464 1756 185 +3 1794 1793 457 +3 1552 476 1504 +3 80 450 451 +3 1469 1470 490 +3 2 1154 1145 +3 501 1408 1407 +3 1804 456 1806 +3 1529 1546 1544 +3 1817 77 1818 +3 1815 446 1817 +3 49 487 505 +3 199 1822 1823 +3 515 523 753 +3 1143 523 515 +3 1450 466 44 +3 461 1756 186 +3 1559 1558 1557 +3 1559 1557 107 +3 1515 1514 1517 +3 83 1753 1754 +3 876 834 828 +3 1696 1140 71 +3 747 331 745 +3 1808 350 45 +3 780 781 811 +3 1382 1389 165 +3 1805 1823 200 +3 454 1803 200 +3 200 1803 1802 +3 1826 1797 79 +3 1826 1797 1825 +3 460 1465 1464 +3 1464 1465 448 +3 1446 1466 190 +3 690 106 958 +3 245 243 720 +3 1755 1756 186 +3 1537 78 1538 +3 1472 491 1475 +3 463 187 468 +3 190 1445 1447 +3 1558 344 1557 +3 1748 1757 1749 +3 686 1757 1748 +3 909 905 852 +3 683 1111 344 +3 1930 853 563 +3 909 829 905 +3 988 990 989 +3 1724 567 206 +3 1520 1521 1525 +3 1158 1157 552 +3 890 1419 889 +3 898 907 95 +3 933 1780 1589 +3 1396 837 1397 +3 1396 1395 1397 +3 1463 1461 466 +3 1536 1812 1813 +3 1505 1504 1552 +3 1551 1552 1505 +3 499 1553 1409 +3 487 503 500 +3 480 1522 1521 +3 469 904 899 +3 899 904 1971 +3 1041 225 1040 +3 952 401 951 +3 1120 53 1891 +3 1502 49 1501 +3 119 183 648 +3 1822 1819 46 +3 162 813 779 +3 3 544 520 +3 354 1452 1471 +3 390 391 1654 +3 1978 830 882 +3 1419 892 890 +3 709 290 707 +3 1386 235 1 +3 1432 249 796 +3 1933 357 1080 +3 211 1991 1406 +3 1990 1991 211 +3 288 706 290 +3 863 348 823 +3 868 348 863 +3 178 404 290 +3 295 294 1255 +3 71 436 437 +3 1465 461 954 +3 1795 1809 1466 +3 1501 502 49 +3 487 502 49 +3 825 1478 1477 +3 209 423 1728 +3 487 503 504 +3 1365 1185 1366 +3 487 500 502 +3 1502 408 49 +3 1129 1128 1132 +3 1875 1132 1129 +3 806 807 538 +3 513 1056 1057 +3 449 1516 1532 +3 1508 1516 1532 +3 500 502 481 +3 1059 754 756 +3 540 504 485 +3 1372 1371 697 +3 1781 933 1580 +3 1776 506 1774 +3 267 165 233 +3 121 504 540 +3 1148 2 1153 +3 1803 455 1540 +3 1679 1678 603 +3 1677 1678 603 +3 780 160 811 +3 759 516 756 +3 757 759 756 +3 1438 1440 1439 +3 75 1061 818 +3 517 523 522 +3 489 539 540 +3 544 759 760 +3 760 516 759 +3 1903 675 1900 +3 772 157 771 +3 1763 329 1764 +3 522 74 512 +3 766 53 750 +3 1126 1128 626 +3 626 1128 1129 +3 1282 1182 104 +3 1188 25 430 +3 1187 25 1188 +3 521 764 763 +3 325 1197 1198 +3 325 410 1198 +3 804 738 135 +3 1675 15 1674 +3 1049 522 1050 +3 74 809 517 +3 1182 568 1282 +3 1431 144 1176 +3 1272 474 887 +3 1751 683 1752 +3 1279 1280 104 +3 526 518 102 +3 819 508 817 +3 2 1147 1148 +3 1308 1307 533 +3 1224 1225 1227 +3 1227 1225 1230 +3 1071 952 1294 +3 1071 13 1294 +3 121 540 489 +3 1124 1123 119 +3 508 1777 1778 +3 1644 1645 1117 +3 1644 1645 38 +3 540 485 539 +3 67 604 1314 +3 244 165 1390 +3 755 942 1916 +3 257 1497 256 +3 1393 1392 164 +3 239 238 258 +3 239 40 238 +3 268 223 269 +3 236 1386 1388 +3 1384 164 1392 +3 1390 244 238 +3 169 1871 634 +3 926 1597 925 +3 39 752 778 +3 1478 1229 399 +3 399 1479 1478 +3 1 235 251 +3 254 17 268 +3 131 1050 753 +3 254 813 162 +3 812 813 17 +3 1039 109 1040 +3 221 112 220 +3 229 1041 228 +3 1071 401 952 +3 1291 950 375 +3 1652 242 1653 +3 1350 28 318 +3 770 156 768 +3 556 223 1732 +3 708 54 1251 +3 297 1251 708 +3 960 961 745 +3 745 959 960 +3 1367 1369 1368 +3 559 1404 1405 +3 682 11 409 +3 682 11 461 +3 185 682 461 +3 681 682 82 +3 409 682 82 +3 413 121 543 +3 1026 2018 2020 +3 1255 58 299 +3 1328 58 1255 +3 745 957 747 +3 1843 1185 1862 +3 1851 1185 1843 +3 314 984 985 +3 549 212 547 +3 689 963 964 +3 964 963 688 +3 1150 1782 529 +3 529 1149 1150 +3 212 552 554 +3 1103 1101 1178 +3 1103 1178 1981 +3 553 1158 797 +3 1712 1719 896 +3 366 995 994 +3 1157 607 97 +3 752 172 39 +3 719 240 263 +3 674 1699 1698 +3 112 220 1902 +3 1841 1634 670 +3 1841 1634 671 +3 1067 533 1301 +3 767 53 768 +3 524 519 804 +3 776 39 777 +3 148 977 974 +3 1542 722 1884 +3 1539 1884 1542 +3 1565 1570 1569 +3 1865 1274 1273 +3 1904 174 1120 +3 749 1904 1120 +3 1600 1664 1601 +3 598 638 631 +3 1785 1271 1784 +3 1075 1074 1072 +3 1322 1319 159 +3 1414 913 1412 +3 1618 941 1619 +3 728 731 732 +3 1677 603 1680 +3 20 653 313 +3 906 1690 833 +3 1679 553 603 +3 1424 921 922 +3 583 938 939 +3 1935 1921 311 +3 1489 140 1487 +3 1487 1489 1483 +3 106 960 962 +3 963 106 962 +3 1431 1429 1286 +3 26 1434 249 +3 1433 1434 249 +3 1684 1682 1123 +3 1876 626 1127 +3 639 1127 1876 +3 1666 149 793 +3 794 793 1666 +3 974 977 1675 +3 1913 1008 1912 +3 1264 887 1263 +3 959 960 106 +3 1131 1130 1159 +3 1872 1130 1159 +3 139 1165 1170 +3 971 970 1133 +3 1173 970 971 +3 814 812 216 +3 969 1018 1031 +3 1830 189 1829 +3 115 1229 1222 +3 1404 374 1405 +3 1230 90 402 +3 1666 149 975 +3 1418 1866 1416 +3 632 42 633 +3 398 1218 1222 +3 550 560 549 +3 614 194 616 +3 830 1177 914 +3 644 194 642 +3 1287 1288 725 +3 725 1287 1289 +3 1515 1514 1513 +3 1513 1076 1515 +3 982 1011 422 +3 1443 1468 477 +3 1442 477 1443 +3 388 90 1230 +3 383 1220 1226 +3 141 1874 1875 +3 1231 389 1233 +3 1002 371 91 +3 1001 1002 364 +3 1028 68 1029 +3 9 601 602 +3 34 1004 1005 +3 1450 1449 1468 +3 621 644 175 +3 1697 219 1343 +3 68 1019 403 +3 584 1624 1625 +3 379 1015 1008 +3 1031 368 1018 +3 394 1243 940 +3 194 613 611 +3 996 997 369 +3 997 998 1009 +3 376 1009 369 +3 694 1374 1183 +3 1263 1107 95 +3 195 138 557 +3 195 27 1435 +3 1126 1125 1127 +3 41 1575 1574 +3 1172 1171 139 +3 1172 137 645 +3 1828 1459 460 +3 1464 1459 460 +3 401 947 1068 +3 401 947 951 +3 818 508 1778 +3 1780 933 1781 +3 1410 483 501 +3 483 501 1122 +3 1677 1681 1678 +3 97 608 1910 +3 1910 551 97 +3 1072 736 1073 +3 1070 1069 116 +3 1653 1313 153 +3 1688 1742 744 +3 520 762 761 +3 388 365 559 +3 1663 1664 29 +3 814 555 222 +3 556 555 222 +3 558 384 385 +3 641 1160 1162 +3 1098 1096 1094 +3 561 755 1150 +3 1150 561 1149 +3 1062 216 811 +3 345 347 847 +3 1164 143 1165 +3 1164 1168 1165 +3 1795 1456 1796 +3 544 760 520 +3 562 284 10 +3 1353 1354 265 +3 1444 901 478 +3 1444 478 1445 +3 1251 298 1252 +3 1251 1252 54 +3 236 1385 1384 +3 47 859 862 +3 850 834 875 +3 1075 825 116 +3 264 1352 1353 +3 206 567 429 +3 1374 324 1370 +3 1610 1598 1599 +3 1184 1364 1861 +3 183 179 566 +3 443 479 117 +3 565 51 289 +3 180 565 289 +3 470 901 478 +3 470 901 2023 +3 867 63 870 +3 1376 1737 1254 +3 853 854 1930 +3 821 1135 1136 +3 1335 1331 1330 +3 1330 1335 665 +3 6 2020 2021 +3 31 621 576 +3 1480 1481 32 +3 1832 1721 897 +3 897 1832 2022 +3 635 169 634 +3 1243 1623 583 +3 615 1073 735 +3 1288 724 1287 +3 930 1611 1626 +3 1612 1611 930 +3 636 5 1577 +3 1165 1169 1168 +3 31 1206 1205 +3 31 1206 620 +3 641 1872 1159 +3 785 786 156 +3 583 1623 1622 +3 175 610 576 +3 175 576 621 +3 1663 1665 1599 +3 1179 591 1178 +3 1159 1131 1166 +3 592 945 946 +3 1888 66 1887 +3 1886 1887 1888 +3 793 1099 794 +3 1646 1116 1115 +3 1115 1116 217 +3 1565 1564 1566 +3 616 642 194 +3 632 638 631 +3 157 783 784 +3 1209 396 1207 +3 1209 396 1984 +3 1693 1694 1175 +3 1066 146 1065 +3 633 635 634 +3 634 597 633 +3 1675 15 977 +3 1672 594 1671 +3 578 1593 1595 +3 942 516 760 +3 593 1105 1103 +3 1611 936 1626 +3 640 1867 1868 +3 87 1565 1570 +3 1668 1670 1669 +3 1714 894 1713 +3 1305 1307 533 +3 1537 78 447 +3 606 602 9 +3 1577 1578 1576 +3 596 1577 1576 +3 97 607 608 +3 1709 892 1710 +3 1710 890 892 +3 1069 401 1705 +3 103 1067 1297 +3 735 736 13 +3 735 736 1073 +3 1240 1241 394 +3 575 1424 1202 +3 918 919 610 +3 918 919 113 +3 1242 584 1243 +3 194 613 614 +3 1514 84 1513 +3 1500 84 1514 +3 601 1640 604 +3 947 90 948 +3 1695 1490 1495 +3 1695 1490 140 +3 72 136 733 +3 72 733 734 +3 101 733 734 +3 1591 928 932 +3 629 600 628 +3 629 600 630 +3 1205 1200 618 +3 1561 127 1234 +3 1561 127 1240 +3 1508 1528 1532 +3 1104 1105 1103 +3 1972 978 1973 +3 1974 978 1973 +3 585 943 589 +3 622 30 621 +3 1493 1065 1492 +3 1064 1483 1489 +3 1489 1064 1491 +3 1242 1241 394 +3 635 42 633 +3 644 30 621 +3 130 1055 647 +3 137 1171 1172 +3 642 616 72 +3 786 37 155 +3 627 629 628 +3 622 214 620 +3 724 1287 1303 +3 632 638 633 +3 628 627 214 +3 594 168 593 +3 593 168 636 +3 1130 1874 1867 +3 587 1242 584 +3 629 627 213 +3 1203 1202 1201 +3 1201 1202 575 +3 1610 927 1611 +3 1994 1993 884 +3 1883 977 148 +3 69 1021 1010 +3 65 943 944 +3 622 623 30 +3 1205 1200 617 +3 1579 598 1574 +3 1579 1574 1573 +3 1146 809 519 +3 879 1976 829 +3 62 1880 1878 +3 130 1054 1055 +3 807 538 74 +3 1168 1167 145 +3 72 643 645 +3 643 645 137 +3 1170 1171 139 +3 136 645 72 +3 197 27 810 +3 137 646 643 +3 1768 29 1610 +3 1610 1768 1599 +3 805 112 1902 +3 648 566 183 +3 946 604 67 +3 1415 1998 1999 +3 1415 1419 1999 +3 1415 1419 889 +3 650 360 651 +3 369 992 996 +3 362 994 989 +3 264 1952 1951 +3 125 655 654 +3 428 203 427 +3 242 1652 246 +3 1356 1354 265 +3 206 1190 429 +3 920 1077 113 +3 1082 311 1918 +3 660 659 124 +3 308 657 658 +3 308 312 661 +3 1572 1650 259 +3 1568 1572 259 +3 302 662 663 +3 657 661 308 +3 855 856 564 +3 797 1158 26 +3 1920 60 664 +3 306 664 1920 +3 311 1082 356 +3 579 2024 1922 +3 306 663 664 +3 579 2024 1615 +3 302 663 664 +3 273 280 1427 +3 1852 1844 1851 +3 1851 1852 1845 +3 1350 1349 28 +3 1574 41 1573 +3 2014 41 1573 +3 1660 188 1658 +3 1409 501 1408 +3 1059 753 1048 +3 935 617 1200 +3 1118 1842 1114 +3 648 672 52 +3 566 648 672 +3 38 1115 1645 +3 1515 415 1517 +3 1701 289 226 +3 538 74 512 +3 675 1903 676 +3 1731 1732 1730 +3 742 283 698 +3 742 283 703 +3 242 246 1112 +3 698 438 742 +3 496 1821 45 +3 82 537 681 +3 1898 219 1895 +3 353 1808 860 +3 185 681 332 +3 1076 1516 80 +3 323 1347 693 +3 683 83 1752 +3 332 690 689 +3 1558 685 683 +3 683 685 1751 +3 92 322 1361 +3 963 689 690 +3 327 405 82 +3 689 964 965 +3 1634 1631 1689 +3 695 694 1347 +3 959 958 957 +3 106 958 959 +3 692 537 691 +3 537 82 692 +3 959 957 745 +3 324 1372 1373 +3 1369 1370 1374 +3 1369 1374 1183 +3 1760 107 1759 +3 33 697 407 +3 705 288 706 +3 288 178 287 +3 288 287 701 +3 1309 671 103 +3 700 702 287 +3 287 702 284 +3 699 283 700 +3 284 282 702 +3 1456 1825 1809 +3 704 705 493 +3 1253 295 297 +3 709 710 291 +3 54 706 705 +3 712 716 55 +3 1252 298 871 +3 493 871 1252 +3 290 404 709 +3 107 686 1759 +3 1711 1707 1708 +3 1995 1177 830 +3 850 347 336 +3 684 465 1752 +3 1751 1752 684 +3 1254 295 1255 +3 1135 36 1134 +3 715 716 712 +3 838 1381 1399 +3 715 717 129 +3 40 720 721 +3 718 243 719 +3 40 720 245 +3 573 1234 1235 +3 1260 1261 416 +3 570 1988 569 +3 1290 1289 729 +3 1300 671 1299 +3 509 819 541 +3 680 1656 181 +3 197 112 810 +3 527 1289 1287 +3 375 952 951 +3 7 1725 1727 +3 1989 1727 7 +3 1944 1940 1037 +3 1052 1053 130 +3 1798 1796 457 +3 1900 1899 1896 +3 133 762 765 +3 1056 512 538 +3 224 1041 1040 +3 1922 1613 1614 +3 248 796 1432 +3 885 1277 474 +3 126 1980 390 +3 525 678 801 +3 1085 19 1083 +3 1939 1036 1941 +3 1945 1946 1698 +3 1289 1296 527 +3 1741 507 1773 +3 539 1777 485 +3 1140 1696 286 +3 1016 378 1019 +3 1696 420 71 +3 1284 1307 134 +3 1284 1307 532 +3 951 947 948 +3 948 951 375 +3 509 816 819 +3 1507 1506 1550 +3 1551 1506 1550 +3 209 361 972 +3 615 735 734 +3 1074 615 616 +3 518 727 102 +3 102 727 726 +3 730 195 1435 +3 1262 416 1261 +3 1261 417 1260 +3 615 616 72 +3 734 72 615 +3 727 732 101 +3 732 728 727 +3 557 136 731 +3 101 735 726 +3 733 136 732 +3 101 733 732 +3 101 726 727 +3 1061 535 818 +3 818 535 1740 +3 735 734 101 +3 1072 1075 116 +3 677 1656 680 +3 680 677 679 +3 1069 401 1068 +3 135 738 737 +3 727 728 739 +3 73 738 739 +3 727 739 73 +3 901 1444 1442 +3 1829 1442 901 +3 1433 1432 86 +3 1101 1103 593 +3 593 1101 1100 +3 1668 150 1667 +3 391 68 403 +3 873 2025 835 +3 510 1047 1058 +3 1854 48 1548 +3 1939 231 1940 +3 131 1049 1044 +3 1866 885 1417 +3 1799 1801 1798 +3 1798 1541 1799 +3 1388 1387 237 +3 1387 1388 1386 +3 1259 534 419 +3 833 906 903 +3 1773 507 1774 +3 746 1742 329 +3 2021 585 1625 +3 6 1625 2021 +3 1017 1020 379 +3 1600 1664 1663 +3 1262 1312 418 +3 230 1036 1941 +3 217 1943 1898 +3 231 1940 1901 +3 1499 497 1527 +3 1527 481 1499 +3 466 1449 1450 +3 1417 1662 885 +3 955 956 691 +3 1765 964 330 +3 1079 201 425 +3 331 746 328 +3 28 328 331 +3 962 744 960 +3 744 961 960 +3 1014 987 210 +3 331 748 28 +3 53 769 1120 +3 447 78 446 +3 750 53 1891 +3 1071 401 1705 +3 1791 1557 344 +3 344 1790 1791 +3 763 762 520 +3 1440 815 758 +3 752 778 779 +3 777 780 159 +3 1730 223 1732 +3 663 123 1917 +3 1046 510 1043 +3 132 1438 1439 +3 514 757 756 +3 758 757 514 +3 1441 731 730 +3 761 760 520 +3 371 98 1965 +3 1278 1279 521 +3 1175 144 1694 +3 761 762 133 +3 1193 205 1187 +3 764 411 412 +3 412 411 23 +3 764 413 3 +3 1642 781 1643 +3 1643 1642 1644 +3 771 172 766 +3 768 769 154 +3 1497 153 258 +3 773 772 157 +3 1322 776 158 +3 772 39 776 +3 1325 1324 953 +3 754 756 1916 +3 287 701 700 +3 778 160 779 +3 224 1039 1040 +3 1114 1118 215 +3 1996 1995 830 +3 783 771 157 +3 1583 1581 596 +3 1062 1641 1642 +3 1494 0 784 +3 1494 774 784 +3 783 156 767 +3 396 1211 1984 +3 869 348 868 +3 156 770 786 +3 154 787 769 +3 552 97 1157 +3 9 606 607 +3 1157 606 607 +3 1770 1125 42 +3 1127 1125 42 +3 788 152 789 +3 788 787 154 +3 907 1106 95 +3 1566 248 1564 +3 1564 248 795 +3 174 787 790 +3 858 564 859 +3 945 1180 592 +3 945 1180 65 +3 552 26 1158 +3 150 1676 1669 +3 1777 1778 507 +3 1565 1564 87 +3 886 887 1106 +3 1416 1999 472 +3 1868 1877 639 +3 639 1868 1876 +3 209 362 1003 +3 50 426 427 +3 1317 573 916 +3 423 209 972 +3 1423 1422 574 +3 1423 1425 574 +3 547 548 549 +3 45 1823 1805 +3 34 382 560 +3 560 382 1406 +3 1989 7 1260 +3 355 656 1934 +3 125 655 656 +3 50 384 428 +3 1175 144 1176 +3 656 313 661 +3 1126 1973 1972 +3 1935 355 1921 +3 1133 971 141 +3 1084 1083 309 +3 1926 1612 927 +3 662 658 123 +3 662 123 663 +3 1190 1189 205 +3 3 543 544 +3 1191 1850 1194 +3 1083 1082 309 +3 38 1641 1642 +3 292 1359 714 +3 782 1643 1644 +3 1117 1644 782 +3 1680 1676 602 +3 309 1084 1086 +3 1473 833 1470 +3 1236 1238 915 +3 1316 915 1236 +3 1312 1311 418 +3 302 59 1959 +3 92 318 317 +3 1350 1349 320 +3 677 1856 679 +3 723 1304 1306 +3 368 1006 1005 +3 1789 1635 339 +3 1768 29 1663 +3 1768 1663 1599 +3 1055 513 1054 +3 132 757 1438 +3 1635 1270 339 +3 1271 1270 1635 +3 765 530 1783 +3 1266 1746 473 +3 1501 1514 1517 +3 119 647 648 +3 1735 296 1737 +3 1736 296 1735 +3 104 1281 1279 +3 932 1589 1779 +3 676 1656 1655 +3 656 355 657 +3 140 1490 1489 +3 92 317 316 +3 1655 678 801 +3 1097 967 315 +3 1407 669 1772 +3 1368 93 1351 +3 678 184 676 +3 678 1655 676 +3 1407 484 1122 +3 1593 1604 2009 +3 692 693 327 +3 1704 784 774 +3 842 192 841 +3 14 1237 1238 +3 182 1856 1857 +3 1910 1909 370 +3 975 974 149 +3 170 1678 1679 +3 1484 1160 1162 +3 976 148 37 +3 182 538 1057 +3 1408 85 1776 +3 180 1700 673 +3 1946 1698 226 +3 802 801 1857 +3 1656 1655 677 +3 182 1857 802 +3 1310 418 1311 +3 1310 418 1299 +3 738 519 804 +3 73 519 738 +3 803 802 524 +3 73 518 519 +3 1327 1326 1736 +3 518 73 727 +3 805 810 112 +3 808 809 74 +3 1439 509 541 +3 1260 416 7 +3 1052 1053 511 +3 511 1053 1045 +3 1049 1050 131 +3 412 1282 1280 +3 412 1282 1192 +3 737 27 810 +3 519 518 1146 +3 517 1146 809 +3 104 1282 1280 +3 1989 1727 1258 +3 1946 1945 229 +3 340 105 341 +3 802 803 525 +3 876 874 834 +3 748 28 1349 +3 475 1269 1271 +3 475 1271 1785 +3 1846 1845 207 +3 1848 1846 207 +3 561 1687 1147 +3 519 809 808 +3 1272 1273 888 +3 525 801 802 +3 677 801 1857 +3 160 812 811 +3 1627 1626 936 +3 1627 1626 1619 +3 805 525 803 +3 803 804 524 +3 135 804 803 +3 135 803 805 +3 1155 723 526 +3 526 723 724 +3 1716 1715 1717 +3 74 808 807 +3 519 808 524 +3 135 810 805 +3 536 1051 1047 +3 345 347 18 +3 812 17 814 +3 756 1059 514 +3 484 1741 1682 +3 484 1122 1123 +3 1123 1124 1684 +3 1124 1684 488 +3 1910 1027 608 +3 1326 1953 1327 +3 1741 1740 507 +3 1740 1741 535 +3 1741 535 1682 +3 816 75 817 +3 816 817 819 +3 541 819 539 +3 560 550 34 +3 1249 849 1247 +3 1111 83 468 +3 892 472 893 +3 1471 1472 1476 +3 1476 1472 491 +3 848 56 1246 +3 714 293 1359 +3 715 292 1359 +3 839 1381 1399 +3 1214 1216 398 +3 1480 825 614 +3 614 825 1075 +3 1140 437 71 +3 867 496 12 +3 1734 1733 845 +3 38 1644 1642 +3 255 1893 256 +3 707 706 290 +3 1251 1250 1248 +3 1251 1250 297 +3 1481 32 1213 +3 1030 2017 381 +3 1025 2017 1030 +3 1418 885 886 +3 1969 1191 206 +3 886 887 474 +3 903 895 471 +3 338 2026 873 +3 873 2026 2025 +3 773 157 1704 +3 471 1832 1721 +3 1710 1702 831 +3 1275 337 881 +3 854 564 855 +3 2026 874 2025 +3 6 587 1121 +3 1534 452 1885 +3 721 240 241 +3 260 1568 1949 +3 239 721 241 +3 153 1653 241 +3 343 342 341 +3 1960 1959 301 +3 1580 1781 1575 +3 659 658 308 +3 1953 1329 665 +3 1875 1874 1129 +3 853 852 905 +3 467 1467 490 +3 1468 1443 467 +3 467 1468 1450 +3 1109 188 1110 +3 876 877 828 +3 1555 339 1556 +3 1270 339 1556 +3 1556 335 1270 +3 955 323 956 +3 1360 330 1749 +3 108 336 335 +3 338 905 877 +3 65 590 943 +3 1560 394 1239 +3 1239 581 1560 +3 1948 1649 1650 +3 1649 88 1651 +3 1934 1933 356 +3 128 1355 1951 +3 1138 63 1137 +3 1201 1199 924 +3 658 123 657 +3 302 1959 1958 +3 1739 513 1055 +3 876 877 338 +3 1380 839 1381 +3 827 337 828 +3 862 868 863 +3 949 374 1405 +3 949 374 1312 +3 840 1380 1379 +3 95 897 899 +3 2016 352 1331 +3 1331 352 1335 +3 921 1423 1422 +3 1735 1734 845 +3 1735 1734 1736 +3 1377 844 843 +3 843 1378 1377 +3 865 1138 866 +3 831 1710 1709 +3 1478 400 1477 +3 820 1135 851 +3 36 1135 851 +3 838 837 1398 +3 1397 1398 837 +3 56 1247 1246 +3 1245 849 1134 +3 1246 849 1245 +3 300 967 1097 +3 192 839 846 +3 843 851 36 +3 187 904 469 +3 2026 876 338 +3 834 336 850 +3 850 347 847 +3 850 193 847 +3 1395 193 1396 +3 859 861 860 +3 232 1383 562 +3 1778 1740 818 +3 1136 820 1135 +3 1575 1588 1781 +3 1121 392 2019 +3 253 1391 171 +3 2019 392 1029 +3 902 1834 1831 +3 1831 1833 902 +3 469 468 1110 +3 469 1110 1109 +3 1266 1267 1265 +3 913 883 914 +3 914 883 911 +3 1446 190 1445 +3 1836 822 1835 +3 1452 1451 1453 +3 1554 1790 1791 +3 352 1378 1377 +3 1335 1377 352 +3 47 860 864 +3 1706 1707 831 +3 1706 1702 831 +3 825 1478 1479 +3 852 853 854 +3 1993 884 1992 +3 1274 881 1277 +3 1274 474 1277 +3 865 866 350 +3 252 1402 161 +3 335 334 18 +3 964 1360 330 +3 1827 1562 1459 +3 859 860 47 +3 18 335 347 +3 252 253 1402 +3 1929 1930 563 +3 865 350 860 +3 864 865 860 +3 864 349 823 +3 865 349 864 +3 18 345 342 +3 233 232 178 +3 867 12 871 +3 871 12 493 +3 872 835 869 +3 286 1140 285 +3 285 64 1140 +3 1703 908 1414 +3 891 1691 1690 +3 872 873 835 +3 875 874 835 +3 1402 255 253 +3 878 337 828 +3 911 883 96 +3 877 828 878 +3 1108 1107 473 +3 1240 394 1560 +3 884 2001 2000 +3 905 829 877 +3 889 1414 890 +3 1994 1997 1996 +3 1996 1997 830 +3 1476 1470 1471 +3 913 883 908 +3 874 2026 876 +3 1704 157 784 +3 853 905 338 +3 2006 1321 1320 +3 833 1474 1470 +3 1020 1021 1010 +3 900 1830 2023 +3 1477 116 1069 +3 898 95 897 +3 855 856 1472 +3 491 1692 1475 +3 1976 1977 829 +3 898 1722 1720 +3 790 1112 787 +3 469 899 1108 +3 1472 1471 354 +3 1832 1834 1831 +3 1832 1831 900 +3 2023 1830 901 +3 2022 470 2023 +3 1462 1463 456 +3 722 1534 1464 +3 1793 1448 1455 +3 1794 1455 1793 +3 470 478 904 +3 895 906 903 +3 891 1702 1703 +3 1703 1710 890 +3 891 908 1703 +3 885 886 474 +3 912 96 855 +3 96 912 910 +3 1999 1419 472 +3 2001 1417 2002 +3 1417 1866 2000 +3 1416 1866 2000 +3 260 262 1747 +3 1418 1416 472 +3 1276 1275 827 +3 910 1979 909 +3 2002 1662 880 +3 1996 1993 1994 +3 1660 188 1110 +3 1993 1995 1996 +3 1977 909 829 +3 1954 1953 1329 +3 1954 1953 1327 +3 1272 1265 888 +3 1696 420 286 +3 879 878 829 +3 1412 1413 2003 +3 1177 1995 1413 +3 852 909 910 +3 96 910 911 +3 1997 880 879 +3 852 910 912 +3 882 830 914 +3 1104 1587 1983 +3 911 914 882 +3 1838 1836 851 +3 851 1839 1838 +3 835 836 869 +3 351 1838 1839 +3 351 1838 1835 +3 1331 2016 1332 +3 851 820 1836 +3 1920 1919 304 +3 697 1196 1371 +3 661 656 657 +3 1334 191 1336 +3 1334 1336 667 +3 1837 820 1136 +3 1136 349 1837 +3 1728 385 1729 +3 1799 1800 455 +3 1799 1800 1462 +3 1362 666 1363 +3 796 1099 170 +3 1088 1079 1087 +3 1210 395 1985 +3 1092 1961 300 +3 1131 1167 1166 +3 1944 225 1037 +3 1614 1922 2024 +3 500 85 1553 +3 1452 857 1453 +3 923 609 1601 +3 80 1516 1508 +3 1256 1089 61 +3 1077 1211 396 +3 1929 1930 858 +3 747 323 1345 +3 1452 490 467 +3 1449 466 1448 +3 1235 581 1236 +3 923 609 922 +3 923 575 922 +3 921 1421 1422 +3 257 1497 153 +3 791 153 257 +3 394 1243 1242 +3 1422 1318 574 +3 1497 258 256 +3 575 924 923 +3 14 609 939 +3 575 1201 924 +3 1241 1242 587 +3 928 1615 932 +3 1045 512 522 +3 599 1915 2012 +3 2012 599 2011 +3 1585 595 1181 +3 1585 590 1181 +3 1613 1924 1925 +3 410 23 1195 +3 925 1665 1599 +3 1239 1237 581 +3 1598 927 1610 +3 1611 1610 29 +3 1203 1204 618 +3 1425 1423 1202 +3 133 942 761 +3 1202 1424 1423 +3 100 1343 1697 +3 942 761 760 +3 922 609 14 +3 2011 1594 599 +3 599 1609 1594 +3 599 1609 1573 +3 1309 1067 103 +3 938 582 937 +3 940 583 939 +3 939 14 940 +3 1606 213 935 +3 1606 1607 935 +3 1928 1596 1595 +3 1595 1596 578 +3 1585 595 1584 +3 1619 1620 582 +3 941 1618 1616 +3 1713 1709 892 +3 1713 1714 892 +3 1287 1303 527 +3 943 590 589 +3 119 1124 647 +3 253 1387 1391 +3 945 586 946 +3 945 944 586 +3 1286 1339 1340 +3 1340 1339 1341 +3 1907 1007 1905 +3 1090 303 1091 +3 1092 303 1091 +3 796 795 1099 +3 428 1726 204 +3 372 1725 1726 +3 7 1725 373 +3 264 1355 1353 +3 263 1951 1952 +3 1966 371 91 +3 1920 1091 304 +3 1117 1645 1646 +3 1646 1645 1115 +3 1078 1079 1080 +3 1295 1290 1291 +3 1295 1292 1294 +3 1793 1448 1461 +3 442 117 1510 +3 447 1536 1813 +3 1547 445 1545 +3 2 1144 1147 +3 2005 166 2004 +3 1662 881 1277 +3 1662 1277 885 +3 1541 1810 1540 +3 1123 119 1122 +3 444 48 1547 +3 477 1449 1447 +3 1448 1449 1447 +3 1466 1455 1795 +3 190 1455 1466 +3 1685 1504 1503 +3 1255 1254 1376 +3 815 758 1042 +3 1207 920 397 +3 397 920 612 +3 814 555 17 +3 605 1314 1738 +3 1046 1044 511 +3 1531 1840 450 +3 553 1157 1158 +3 1041 1731 228 +3 224 1731 1732 +3 67 1025 1063 +3 1026 1025 1063 +3 941 1617 1628 +3 924 923 1600 +3 215 1118 1119 +3 67 1025 1022 +3 161 255 1402 +3 1319 1643 782 +3 224 1032 1039 +3 197 27 196 +3 1812 1803 453 +3 777 778 39 +3 778 780 777 +3 1321 158 1320 +3 1322 777 776 +3 151 792 973 +3 1127 42 635 +3 1322 777 159 +3 475 1268 1864 +3 475 1267 1864 +3 1389 1382 236 +3 43 545 1820 +3 46 1814 1815 +3 1461 1462 1463 +3 1990 650 985 +3 1832 2022 900 +3 1732 224 556 +3 1303 1302 723 +3 983 984 359 +3 1305 1152 134 +3 1673 168 594 +3 378 403 1019 +3 993 991 210 +3 1125 1770 637 +3 99 979 972 +3 1580 933 1582 +3 310 1113 1256 +3 1539 1540 1811 +3 1492 775 1491 +3 1941 230 1942 +3 1747 660 261 +3 1054 1056 1053 +3 1255 1328 1376 +3 1441 740 741 +3 1786 1787 1788 +3 160 813 779 +3 1898 1116 217 +3 805 1902 525 +3 1896 1897 220 +3 419 569 1724 +3 568 1724 419 +3 1344 2004 166 +3 1329 1330 665 +3 1061 1060 488 +3 806 182 538 +3 522 523 1050 +3 1061 75 1060 +3 818 508 817 +3 1041 224 1731 +3 1042 758 514 +3 1044 1043 1046 +3 1693 1694 953 +3 1797 79 1810 +3 691 958 537 +3 956 692 691 +3 1862 1365 1185 +3 693 956 692 +3 963 688 962 +3 93 1351 319 +3 961 746 331 +3 417 1689 1634 +3 316 1361 92 +3 332 965 684 +3 1760 107 1766 +3 106 690 963 +3 689 332 965 +3 328 1764 746 +3 746 329 1764 +3 315 1338 966 +3 315 1338 1337 +3 1354 266 1356 +3 967 968 300 +3 301 968 300 +3 333 1759 1760 +3 1762 1759 333 +3 686 1757 1758 +3 1758 1757 1765 +3 1757 1765 330 +3 1765 688 964 +3 965 1360 684 +3 1764 105 1763 +3 321 1366 1186 +3 1336 666 1337 +3 1350 320 1351 +3 319 1351 318 +3 1350 1351 318 +3 870 63 1139 +3 198 545 1820 +3 844 36 843 +3 1880 1879 1878 +3 1244 1012 357 +3 921 1420 1421 +3 822 837 838 +3 822 838 1835 +3 384 558 50 +3 1933 125 1934 +3 970 624 646 +3 641 1872 1873 +3 480 117 1511 +3 480 1511 1519 +3 629 213 1257 +3 1974 625 1863 +3 799 1100 1887 +3 973 149 974 +3 151 976 37 +3 974 148 976 +3 1979 1977 909 +3 1992 884 1998 +3 882 1976 1977 +3 1950 261 1747 +3 1134 821 849 +3 492 984 983 +3 99 422 982 +3 983 982 358 +3 983 982 981 +3 1134 1245 845 +3 1035 1743 215 +3 314 984 654 +3 988 361 986 +3 654 492 984 +3 986 979 972 +3 972 986 361 +3 651 987 360 +3 210 988 987 +3 1204 1205 618 +3 209 989 361 +3 989 362 209 +3 998 1001 364 +3 996 8 997 +3 993 367 991 +3 210 993 1014 +3 991 366 992 +3 1009 998 999 +3 8 996 995 +3 996 992 366 +3 1008 379 1010 +3 2012 1915 630 +3 371 363 1002 +3 383 89 365 +3 999 89 365 +3 1000 363 1001 +3 1000 1001 8 +3 1003 363 98 +3 558 208 426 +3 1004 382 34 +3 1006 1005 34 +3 1013 201 424 +3 424 201 425 +3 1016 378 1015 +3 1010 1008 1912 +3 982 1011 358 +3 999 1009 89 +3 1015 378 70 +3 89 1009 376 +3 1014 987 651 +3 1014 651 382 +3 382 993 1004 +3 379 1016 1015 +3 1029 391 392 +3 700 1889 701 +3 1018 1015 70 +3 969 1018 70 +3 368 1031 1005 +3 1029 391 68 +3 1913 1018 1008 +3 608 1027 1024 +3 2021 585 944 +3 1021 1022 380 +3 1958 664 302 +3 380 1022 1025 +3 69 1024 1023 +3 1006 1911 1908 +3 34 1911 1006 +3 1029 1028 1030 +3 392 393 1121 +3 1005 367 1004 +3 1243 583 940 +3 1207 1209 1208 +3 1037 1938 1939 +3 109 1033 1035 +3 222 1034 1033 +3 1284 670 1841 +3 2006 1341 2005 +3 387 1217 1216 +3 399 1223 1213 +3 231 1942 1899 +3 1211 127 1210 +3 1077 573 916 +3 916 113 1077 +3 920 113 919 +3 309 1082 1918 +3 309 1919 1918 +3 532 1284 1841 +3 542 1439 132 +3 1049 1045 1044 +3 1048 753 131 +3 509 1440 1439 +3 1440 758 1438 +3 728 731 1441 +3 130 536 1052 +3 1052 1051 511 +3 1047 1046 1051 +3 1616 1618 1923 +3 1924 1923 1613 +3 1045 512 1053 +3 1054 1056 513 +3 648 1055 52 +3 648 1055 647 +3 1855 1856 182 +3 1056 1057 538 +3 1058 75 816 +3 514 1059 1048 +3 536 1047 1060 +3 1062 1038 216 +3 539 819 508 +3 946 586 1063 +3 1063 586 1026 +3 1838 1836 1835 +3 1383 1382 232 +3 860 1808 350 +3 1345 747 331 +3 1458 1828 460 +3 1477 116 825 +3 1470 490 1471 +3 459 1809 1466 +3 459 1809 1457 +3 466 1463 44 +3 353 1454 857 +3 1793 1461 457 +3 1967 1968 189 +3 903 1474 833 +3 902 1469 1833 +3 1968 1469 1833 +3 908 883 1692 +3 1147 1687 515 +3 193 847 1395 +3 1114 1038 1641 +3 1123 1682 484 +3 1923 1922 1613 +3 1661 1535 1536 +3 1661 1537 1536 +3 1727 570 1725 +3 1858 920 1077 +3 1077 1858 396 +3 423 208 1728 +3 1643 781 159 +3 1062 1038 1641 +3 1880 1870 1879 +3 1536 1535 453 +3 1367 1861 1184 +3 1369 1367 1184 +3 561 755 1687 +3 1917 663 306 +3 953 1495 1695 +3 1064 1492 1065 +3 1064 1065 146 +3 147 155 1066 +3 1066 155 1065 +3 1563 1047 1058 +3 1298 103 1297 +3 1068 947 402 +3 1228 1068 400 +3 1229 400 1228 +3 1293 13 1292 +3 569 1988 419 +3 116 1070 1072 +3 1072 736 1070 +3 80 1076 415 +3 1071 736 13 +3 1292 1290 1295 +3 1073 1074 615 +3 1075 1074 616 +3 616 1075 614 +3 1073 1072 1074 +3 1515 415 1076 +3 1012 1078 201 +3 431 425 1088 +3 1245 845 1735 +3 1084 1085 310 +3 1119 215 1035 +3 1035 1119 1936 +3 579 1922 1616 +3 230 1036 1936 +3 334 340 1761 +3 306 1920 1919 +3 1089 1090 305 +3 431 1085 310 +3 1085 1084 1083 +3 431 19 1085 +3 1095 57 1094 +3 717 1352 718 +3 1918 306 1919 +3 60 1091 1920 +3 534 1182 419 +3 1765 1688 688 +3 1763 1761 333 +3 431 1087 19 +3 1086 1089 304 +3 1436 1437 1093 +3 1436 1437 303 +3 305 1095 1090 +3 1089 305 61 +3 1092 300 1093 +3 1437 1093 1092 +3 1321 142 1341 +3 1090 1095 303 +3 1099 793 795 +3 1100 593 594 +3 1093 300 1097 +3 798 1672 1671 +3 1095 57 316 +3 1095 1094 303 +3 1337 315 1096 +3 966 1328 1956 +3 1126 1128 1972 +3 1674 975 1675 +3 1181 1180 591 +3 590 1181 1180 +3 65 1180 590 +3 1587 5 1104 +3 1105 593 636 +3 1211 1210 1984 +3 1981 1104 1982 +3 1722 1716 832 +3 1112 152 787 +3 1609 929 2008 +3 2008 1594 1609 +3 1113 61 322 +3 433 1113 322 +3 1340 1341 2005 +3 310 1113 433 +3 1847 1187 1193 +3 218 1648 1344 +3 263 240 1649 +3 1121 587 393 +3 746 1742 961 +3 669 1408 1776 +3 1407 1408 669 +3 1775 506 485 +3 639 635 1127 +3 1167 971 1131 +3 141 971 1131 +3 630 2012 1257 +3 1162 641 1163 +3 1874 1129 1867 +3 1867 1872 640 +3 1168 1169 1167 +3 137 970 1171 +3 497 1520 1523 +3 1499 497 1523 +3 971 1169 1167 +3 141 1130 1131 +3 146 1483 1482 +3 1169 1170 971 +3 143 139 1165 +3 1882 167 1870 +3 970 1133 624 +3 1510 117 1511 +3 94 1686 572 +3 1499 1523 498 +3 1326 1733 844 +3 1331 1332 191 +3 349 865 1138 +3 1140 64 437 +3 821 1139 1137 +3 821 1137 1136 +3 1420 14 1238 +3 1248 56 1250 +3 56 1248 1247 +3 24 440 1818 +3 496 1821 495 +3 1747 124 262 +3 1964 384 1963 +3 1975 623 30 +3 1142 624 1133 +3 1145 518 526 +3 1145 1146 1144 +3 1144 1146 517 +3 769 1156 1120 +3 1783 765 1782 +3 1156 787 174 +3 1284 1283 134 +3 1151 1149 1148 +3 561 1149 1148 +3 1154 1145 526 +3 1151 529 1149 +3 1151 529 1152 +3 1153 528 1155 +3 1308 532 1309 +3 1308 1067 533 +3 1301 533 1302 +3 1719 1720 832 +3 1418 886 1717 +3 1660 1111 1657 +3 124 660 1747 +3 1719 894 1711 +3 906 1723 1702 +3 1706 1702 906 +3 1659 1658 1141 +3 1744 1658 1659 +3 261 1357 1358 +3 719 1952 263 +3 1160 1159 641 +3 1483 1161 1482 +3 146 1483 1064 +3 146 1163 1066 +3 1163 1162 146 +3 1164 143 1174 +3 1927 1925 1613 +3 1126 625 1973 +3 1486 1164 1485 +3 1160 1166 1159 +3 169 635 639 +3 1173 971 1170 +3 1172 645 138 +3 1694 953 1325 +3 196 1176 195 +3 1180 1179 592 +3 66 1101 1100 +3 1179 1102 1178 +3 592 1179 1102 +3 1406 211 560 +3 630 1257 629 +3 926 925 1608 +3 1647 1116 1646 +3 1647 1646 1117 +3 530 1283 1281 +3 1850 1191 23 +3 695 1373 1375 +3 296 1737 1376 +3 1345 323 1346 +3 763 521 762 +3 758 757 1438 +3 1281 530 1279 +3 674 21 1698 +3 1345 748 1346 +3 320 1183 1348 +3 1372 1371 324 +3 1351 1368 320 +3 1326 1733 1734 +3 1576 596 1575 +3 1580 596 1575 +3 23 1195 1850 +3 1846 1845 1186 +3 1591 1592 928 +3 1635 1271 1784 +3 1851 1185 1845 +3 697 1196 325 +3 1848 1846 1193 +3 1847 1846 1193 +3 1184 1370 1369 +3 1281 104 1633 +3 1843 1844 1851 +3 134 1285 1152 +3 1152 1285 529 +3 412 1192 23 +3 23 1192 1191 +3 50 428 427 +3 325 1197 1196 +3 1199 618 1201 +3 576 1426 619 +3 618 1199 1200 +3 1199 577 1200 +3 925 577 924 +3 66 1101 1636 +3 1637 66 1636 +3 1420 922 921 +3 618 1203 1201 +3 931 1604 1603 +3 495 1889 494 +3 1627 936 937 +3 31 1204 1205 +3 627 617 1206 +3 1206 620 627 +3 841 2015 2016 +3 1314 604 1315 +3 1055 1739 52 +3 1926 1927 1925 +3 1620 1619 941 +3 1422 1421 915 +3 2017 2018 381 +3 13 1293 735 +3 1480 1479 825 +3 1215 1216 387 +3 1228 402 1068 +3 1103 1104 1981 +3 1221 16 377 +3 377 1221 89 +3 399 1223 1222 +3 388 365 383 +3 1226 388 383 +3 1230 402 1227 +3 1229 1222 399 +3 387 1231 1215 +3 1215 1233 1231 +3 587 393 1241 +3 1232 1231 387 +3 1233 114 1215 +3 1241 393 1240 +3 113 918 917 +3 1234 573 1211 +3 1211 573 1077 +3 1565 1566 247 +3 1237 14 940 +3 940 394 1239 +3 940 1237 1239 +3 1626 930 1618 +3 576 918 610 +3 917 918 576 +3 619 1204 31 +3 1824 1820 43 +3 1935 1934 356 +3 1809 1456 1795 +3 1820 1821 1824 +3 1816 198 1819 +3 1819 199 1820 +3 1360 964 965 +3 1245 848 1246 +3 1252 705 493 +3 1247 1248 1249 +3 1249 1248 870 +3 298 1248 1251 +3 54 707 708 +3 707 706 54 +3 1253 56 1254 +3 848 56 1254 +3 1254 1253 295 +3 1338 1955 666 +3 666 1338 1337 +3 1600 1663 1665 +3 1151 1152 528 +3 1155 528 1306 +3 1305 134 1307 +3 1304 1305 533 +3 1283 134 1285 +3 765 530 1278 +3 83 1753 1752 +3 753 1050 523 +3 135 737 810 +3 724 723 1303 +3 1300 418 1262 +3 418 1300 1299 +3 1262 416 374 +3 153 1313 791 +3 1312 1262 374 +3 1108 1746 1109 +3 1267 1265 888 +3 1264 887 1272 +3 475 1269 1268 +3 1268 1269 826 +3 1271 1270 1269 +3 474 1274 1273 +3 1792 1557 1791 +3 1791 1554 1792 +3 1865 1274 826 +3 529 1783 1782 +3 1274 881 1275 +3 1999 1998 1416 +3 339 1555 1789 +3 762 1278 765 +3 521 1278 762 +3 764 1280 412 +3 764 1280 521 +3 531 670 1284 +3 529 1285 1783 +3 34 824 1911 +3 798 1667 1668 +3 798 1667 1674 +3 1906 1912 1010 +3 606 553 603 +3 1236 1237 581 +3 1679 797 553 +3 1639 601 1640 +3 1638 601 1639 +3 592 1639 1102 +3 1639 1102 1637 +3 1336 191 1362 +3 1668 1670 798 +3 262 86 1433 +3 1986 1689 1260 +3 1989 1260 1986 +3 1434 250 26 +3 1667 150 1681 +3 1682 1684 1683 +3 1750 684 1360 +3 1760 1761 333 +3 632 600 1914 +3 632 631 1914 +3 1177 1412 913 +3 263 1951 1948 +3 1344 1319 166 +3 197 221 1430 +3 725 1289 1290 +3 1288 726 102 +3 1070 1071 736 +3 13 1294 1292 +3 671 417 1300 +3 613 32 1480 +3 952 1295 1291 +3 1293 735 726 +3 1299 671 103 +3 1261 1300 1262 +3 403 1654 391 +3 723 1304 1302 +3 926 1605 1607 +3 935 577 1200 +3 1608 577 935 +3 1304 533 1302 +3 1155 1306 723 +3 1152 528 1305 +3 528 1305 1306 +3 1304 1305 1306 +3 1298 1299 103 +3 1298 1299 1310 +3 1298 1310 950 +3 1311 949 950 +3 1310 1311 950 +3 790 1313 791 +3 242 1313 790 +3 1902 678 525 +3 1314 1315 605 +3 607 1024 605 +3 605 1315 607 +3 981 982 99 +3 1237 1236 1238 +3 1935 1934 355 +3 985 1990 649 +3 211 1990 1970 +3 1986 1258 1989 +3 1781 1780 1588 +3 1325 1324 142 +3 574 1318 916 +3 916 1317 1318 +3 619 1203 1202 +3 1425 619 1202 +3 1319 166 1320 +3 2007 1595 1592 +3 158 1322 1320 +3 196 1431 1176 +3 1321 1323 142 +3 1429 1286 1428 +3 100 1428 1429 +3 69 1023 1021 +3 1326 1736 1734 +3 666 1329 1363 +3 666 1329 1955 +3 1254 848 1737 +3 1328 966 58 +3 1332 841 2016 +3 1328 1957 1956 +3 1738 1023 1022 +3 572 1333 1334 +3 1334 572 668 +3 667 1334 668 +3 1329 1330 1363 +3 1336 1337 667 +3 667 1337 1096 +3 1341 1321 2006 +3 1319 782 1344 +3 238 237 1391 +3 1861 1364 1862 +3 1862 1843 1364 +3 1349 1346 1348 +3 748 1346 1349 +3 1905 1007 1906 +3 264 1352 718 +3 1354 293 1359 +3 1358 1355 1357 +3 1354 1359 129 +3 686 1758 1759 +3 1759 1758 1762 +3 660 59 1358 +3 1380 1381 351 +3 305 316 1361 +3 1361 61 305 +3 322 61 1361 +3 1655 801 677 +3 1057 513 1855 +3 1366 319 321 +3 1366 319 93 +3 1856 679 1855 +3 93 1365 1366 +3 1373 324 1375 +3 320 1183 1368 +3 1371 324 1370 +3 1371 1184 1370 +3 696 695 1373 +3 694 1374 1375 +3 695 1375 694 +3 1378 1379 843 +3 840 839 1380 +3 2015 2016 352 +3 839 192 840 +3 1400 846 839 +3 233 232 1382 +3 1382 165 233 +3 236 1384 1382 +3 1384 1383 164 +3 562 277 1383 +3 1387 1386 1 +3 236 1389 1388 +3 1 1387 253 +3 237 1389 1388 +3 237 1390 238 +3 1391 237 1387 +3 1394 234 571 +3 571 235 1394 +3 836 835 1401 +3 836 1396 837 +3 1398 346 1397 +3 838 1398 1399 +3 1399 1398 346 +3 875 1401 193 +3 193 1401 1396 +3 836 1401 1396 +3 1401 875 835 +3 949 948 1405 +3 1403 373 416 +3 374 1403 416 +3 365 1404 559 +3 365 91 1404 +3 90 1405 948 +3 750 1892 1894 +3 264 1951 1355 +3 888 1267 1864 +3 501 1407 1122 +3 1409 499 1860 +3 1529 1543 1530 +3 1529 450 1530 +3 1804 1806 1805 +3 1777 1775 485 +3 1507 1549 1550 +3 483 1410 118 +3 1860 501 1409 +3 85 500 503 +3 1419 892 472 +3 1995 1413 1993 +3 1420 14 922 +3 1318 1422 915 +3 1424 1423 921 +3 922 1424 575 +3 1425 619 1426 +3 1426 1425 574 +3 917 1426 574 +3 51 273 1427 +3 51 1427 286 +3 144 1286 1325 +3 196 1431 1430 +3 1434 1433 262 +3 1108 473 1746 +3 1449 477 1468 +3 1446 478 1445 +3 1467 1443 467 +3 1448 190 1447 +3 855 1475 96 +3 1475 883 96 +3 458 1454 353 +3 1448 190 1455 +3 1474 1470 1469 +3 1530 1538 452 +3 1908 1909 370 +3 1458 459 1460 +3 1448 466 1461 +3 722 1464 1459 +3 1542 722 1459 +3 1539 1540 453 +3 459 1466 1460 +3 1466 1446 1460 +3 456 458 1806 +3 1797 1541 1810 +3 354 856 1472 +3 614 1480 613 +3 1707 1708 831 +3 1481 1479 1480 +3 1481 399 1479 +3 1481 399 1213 +3 586 944 2021 +3 1561 581 1560 +3 1235 1234 1561 +3 1603 1605 931 +3 701 1889 494 +3 1971 897 2022 +3 1971 470 2022 +3 1611 927 1612 +3 1026 586 2020 +3 6 1625 587 +3 1853 395 389 +3 981 99 1769 +3 589 1630 35 +3 1623 35 1622 +3 946 604 1640 +3 1640 592 946 +3 1627 937 582 +3 588 1498 1630 +3 1315 9 607 +3 589 1498 1586 +3 1493 155 786 +3 1491 1064 1492 +3 1493 155 1065 +3 1490 1495 775 +3 596 1581 1580 +3 1615 1589 934 +3 1853 393 127 +3 1795 1455 1794 +3 1621 1620 582 +3 1604 578 1603 +3 928 1926 1928 +3 1605 1606 1607 +3 1498 1582 933 +3 937 609 1601 +3 937 936 1601 +3 700 1889 699 +3 2010 1594 2011 +3 931 1606 1605 +3 590 1586 1585 +3 789 1569 152 +3 1589 934 933 +3 1500 1514 1501 +3 1495 1324 1496 +3 1160 145 1485 +3 1026 2018 2017 +3 1581 1582 1580 +3 1378 1379 2015 +3 1482 1484 1162 +3 1488 1164 1486 +3 1488 1487 1486 +3 1487 1488 140 +3 1485 1161 1486 +3 1623 35 1624 +3 2020 2019 2018 +3 6 2019 2020 +3 1596 578 1602 +3 582 1627 1619 +3 395 127 1210 +3 926 1603 1602 +3 1597 926 1602 +3 1030 1025 380 +3 221 1697 220 +3 935 1608 1607 +3 610 175 611 +3 925 577 1608 +3 214 623 622 +3 620 622 621 +3 1889 43 495 +3 1625 584 587 +3 1205 1206 617 +3 1209 1985 114 +3 1984 1985 1209 +3 1519 498 1511 +3 1561 1560 1240 +3 1739 679 52 +3 1906 1027 1905 +3 1488 1174 1164 +3 1915 630 1914 +3 1486 1487 1161 +3 1914 631 1915 +3 929 2007 2013 +3 1915 599 1579 +3 1484 1485 1161 +3 1168 1485 145 +3 1168 1485 1164 +3 1491 1489 1490 +3 1694 1325 144 +3 775 1496 1494 +3 1487 1483 1161 +3 1490 775 1491 +3 953 1324 1495 +3 1493 785 786 +3 774 1323 1494 +3 785 1493 0 +3 1493 1492 0 +3 1494 1492 775 +3 1323 1496 1324 +3 1494 1496 1323 +3 1504 498 1503 +3 502 1500 481 +3 1500 1501 502 +3 94 341 1686 +3 1729 1003 98 +3 442 48 1509 +3 1076 1512 1513 +3 1506 476 1509 +3 1506 476 1551 +3 1909 551 1910 +3 446 1545 444 +3 1532 1528 1507 +3 1507 1549 1532 +3 481 500 1553 +3 481 1527 1553 +3 1512 1518 1505 +3 1512 1505 449 +3 1520 480 1519 +3 1509 476 1510 +3 118 1521 1525 +3 1517 1502 415 +3 1516 1512 1076 +3 449 1512 1516 +3 76 440 441 +3 442 1509 1510 +3 1519 1520 1523 +3 498 1519 1523 +3 1525 118 1526 +3 1410 1411 118 +3 1522 482 479 +3 498 1511 1524 +3 498 1524 1504 +3 1525 1520 497 +3 1860 499 1859 +3 1526 1527 499 +3 499 1411 1526 +3 1686 572 1333 +3 1533 452 1534 +3 1533 452 1530 +3 451 1531 954 +3 954 1531 448 +3 1465 448 954 +3 1662 337 880 +3 1885 722 1884 +3 1543 1544 1529 +3 1146 518 1145 +3 1804 200 1802 +3 484 1772 1407 +3 75 1060 1563 +3 1801 1462 1799 +3 1047 1563 1060 +3 449 1549 1532 +3 446 1545 78 +3 1547 48 1548 +3 1547 1548 445 +3 449 1550 1505 +3 1550 1549 449 +3 1527 1553 499 +3 683 1558 344 +3 1749 330 1757 +3 1557 107 1555 +3 1561 1235 581 +3 1794 1796 1795 +3 1488 1174 140 +3 1459 1542 1562 +3 1442 189 1829 +3 1474 902 903 +3 1593 2009 2008 +3 1832 1834 471 +3 1567 1432 86 +3 1100 1671 594 +3 1566 248 1567 +3 1854 1509 1506 +3 932 1589 1615 +3 1615 1614 928 +3 1923 1922 1616 +3 1567 247 1566 +3 1569 1565 246 +3 800 1888 1638 +3 1603 578 1602 +3 1615 579 934 +3 1597 927 1596 +3 1612 1925 1926 +3 45 199 1823 +3 1571 247 1565 +3 1571 1565 246 +3 928 1592 1595 +3 1593 578 1604 +3 1574 1576 1575 +3 1586 580 1585 +3 1574 598 1576 +3 1584 595 1581 +3 597 1578 638 +3 1577 636 597 +3 631 598 1579 +3 1769 979 980 +3 596 1583 1577 +3 1498 580 1586 +3 1584 580 1582 +3 1582 1584 1581 +3 590 589 1586 +3 1769 99 979 +3 1585 1584 580 +3 1590 932 1591 +3 1590 41 1591 +3 1589 1779 1780 +3 1657 1790 344 +3 1575 1588 41 +3 1588 1779 1780 +3 931 1604 2010 +3 1614 1927 1613 +3 192 2015 841 +3 840 2015 192 +3 1573 1579 599 +3 1597 1598 925 +3 1606 213 1257 +3 582 1621 938 +3 1603 926 1605 +3 931 1606 1257 +3 1600 924 1665 +3 1601 923 1600 +3 1597 1596 1602 +3 931 2011 2010 +3 931 2011 1257 +3 2008 1594 2009 +3 1608 1607 926 +3 1664 936 1601 +3 1928 927 1596 +3 1611 29 936 +3 1928 927 1926 +3 1619 1626 1618 +3 352 1378 2015 +3 1379 840 2015 +3 934 1617 588 +3 1620 1629 1628 +3 1628 588 1617 +3 1628 588 1629 +3 1617 1616 941 +3 1617 1616 579 +3 1617 579 934 +3 1621 1629 1630 +3 1621 35 1622 +3 938 1621 1622 +3 1621 1630 35 +3 1622 938 583 +3 1621 1629 1620 +3 1623 584 1243 +3 534 1632 1631 +3 1631 1634 670 +3 670 1632 1631 +3 531 1632 670 +3 1118 1842 217 +3 1632 531 1633 +3 1633 534 1632 +3 1182 1633 534 +3 1633 104 1182 +3 1908 1909 1911 +3 1657 1787 1790 +3 1309 671 1841 +3 1638 66 1888 +3 1887 66 1100 +3 1888 1886 800 +3 592 1640 1639 +3 344 1111 1657 +3 1537 1771 1661 +3 452 1661 1771 +3 1648 1647 218 +3 1344 782 1648 +3 1652 1651 88 +3 241 1651 1652 +3 1651 1649 240 +3 1652 246 88 +3 1653 242 1313 +3 241 1652 1653 +3 595 1983 1587 +3 342 340 341 +3 1411 499 1859 +3 1676 602 800 +3 1410 1411 1859 +3 679 513 1855 +3 1659 1267 1266 +3 475 1267 1659 +3 1745 188 1744 +3 1744 188 1658 +3 468 1111 1660 +3 1660 468 1110 +3 1767 488 1124 +3 1664 936 29 +3 1667 1666 975 +3 1682 1683 535 +3 1569 1570 789 +3 1671 798 1670 +3 1674 1667 975 +3 167 15 1673 +3 1661 1535 452 +3 1676 800 1669 +3 1675 975 974 +3 606 603 1680 +3 1681 1667 1666 +3 1681 794 1666 +3 1678 794 1681 +3 488 1061 1683 +3 535 1061 1683 +3 1505 1685 1518 +3 1505 1504 1685 +3 1686 341 343 +3 1786 1787 1141 +3 1687 755 1916 +3 569 567 1724 +3 1473 1690 833 +3 1691 491 1473 +3 1473 1691 1690 +3 893 1714 1715 +3 491 1692 1691 +3 891 1692 1691 +3 1692 908 891 +3 1704 773 158 +3 1456 1796 1797 +3 452 1535 1885 +3 1884 1885 1535 +3 1006 1907 1007 +3 1906 1007 1912 +3 100 1697 221 +3 1699 1700 674 +3 1723 891 1690 +3 1699 1701 1700 +3 1699 1701 226 +3 1414 1703 890 +3 895 1707 1711 +3 1690 1723 906 +3 1070 1069 1705 +3 1070 1071 1705 +3 1707 906 1706 +3 1708 831 1709 +3 896 1712 471 +3 1710 1703 1702 +3 906 1707 895 +3 1712 1711 895 +3 894 1711 1708 +3 1718 1719 894 +3 1702 1723 891 +3 894 1713 1708 +3 471 895 1712 +3 1713 1709 1708 +3 1724 206 568 +3 832 1715 1716 +3 1718 894 1714 +3 1721 896 471 +3 893 1418 472 +3 893 1418 1717 +3 1715 1717 893 +3 1831 1833 1830 +3 1720 898 1721 +3 1712 1719 1711 +3 1718 1719 832 +3 898 1721 897 +3 1020 1010 379 +3 1725 372 373 +3 385 1964 384 +3 1970 649 548 +3 1024 605 1023 +3 1023 605 1738 +3 177 279 281 +3 285 177 281 +3 281 278 10 +3 1763 1761 105 +3 328 105 1764 +3 1033 1035 1743 +3 1924 930 1612 +3 1659 1266 1745 +3 1744 1745 1659 +3 1749 1748 685 +3 685 1749 1750 +3 1750 1360 1749 +3 1752 1753 465 +3 186 1755 463 +3 464 465 1753 +3 1555 107 1766 +3 464 1755 1754 +3 1756 1755 464 +3 1763 1762 687 +3 130 1767 536 +3 1688 687 1765 +3 334 1766 1760 +3 1556 1766 334 +3 1763 1762 333 +3 1765 1758 687 +3 1767 1124 647 +3 1767 130 647 +3 1982 591 1181 +3 1538 1771 1537 +3 1948 263 1649 +3 1775 506 1774 +3 1776 506 503 +3 85 1776 503 +3 669 1773 1774 +3 1777 508 539 +3 932 1779 1590 +3 1787 1141 1657 +3 1789 1635 1788 +3 1784 1786 1141 +3 1785 1784 1141 +3 1787 1788 1554 +3 1554 1788 1789 +3 1790 1554 1787 +3 1792 1557 1555 +3 1789 1555 1792 +3 1789 1792 1554 +3 1541 455 1799 +3 1540 455 1541 +3 457 1794 1796 +3 1800 456 1462 +3 1802 455 1803 +3 1813 1812 454 +3 1812 1803 454 +3 1804 1800 1802 +3 456 1804 1800 +3 200 1805 1804 +3 45 350 496 +3 1813 1814 447 +3 1816 1815 46 +3 1814 454 1813 +3 199 45 1821 +3 446 1815 447 +3 447 1814 1815 +3 1818 545 198 +3 1818 545 24 +3 1818 440 77 +3 200 1822 454 +3 1822 1819 199 +3 495 1824 43 +3 1825 1456 1797 +3 1457 1827 1826 +3 1467 1968 1967 +3 1830 1829 901 +3 1467 1968 1469 +3 1830 1831 900 +3 1833 189 1830 +3 1836 1837 822 +3 1836 1837 820 +3 823 1837 822 +3 349 823 1837 +3 1835 1381 838 +3 1835 351 1381 +3 1379 1839 843 +3 351 1839 1380 +3 1380 1839 1379 +3 843 851 1839 +3 1114 1842 1115 +3 1848 207 1195 +3 1849 1195 1848 +3 1193 1848 1849 +3 1863 628 214 +3 1197 1844 1198 +3 1198 207 1844 +3 1198 1195 207 +3 1970 549 560 +3 560 211 1970 +3 548 549 1970 +3 1987 1258 1988 +3 2010 1604 2009 +3 2009 1594 2010 +3 1857 677 1856 +3 1855 182 1057 +3 1975 1974 978 +3 978 624 1975 +3 1268 1865 826 +3 1864 1865 1268 +3 1673 1871 167 +3 639 169 1869 +3 1877 1869 639 +3 1870 167 1871 +3 1879 1870 169 +3 1876 626 1868 +3 1163 641 1873 +3 640 1868 1877 +3 1878 640 1873 +3 169 1879 1869 +3 1879 1869 1878 +3 1883 977 167 +3 1066 1881 147 +3 1882 1883 1881 +3 1881 1883 147 +3 62 1881 1880 +3 148 147 1883 +3 167 1882 1883 +3 370 1027 1905 +3 1670 799 1886 +3 1886 1670 1669 +3 1669 1886 800 +3 799 1887 1886 +3 1889 43 699 +3 1891 749 1890 +3 750 1891 1890 +3 1893 173 1892 +3 751 1893 1892 +3 1943 1942 111 +3 220 1896 1895 +3 219 1898 1116 +3 1899 231 1900 +3 1896 1899 111 +3 675 1900 1901 +3 370 1908 1907 +3 1908 1006 1907 +3 1911 824 1909 +3 1958 60 664 +3 1007 1913 1912 +3 630 600 1914 +3 1257 2011 2012 +3 631 1915 1579 +3 230 1936 1119 +3 657 355 1921 +3 657 1921 123 +3 1921 123 1917 +3 1921 1917 311 +3 1595 1928 928 +3 1932 492 654 +3 654 1932 125 +3 1244 1932 357 +3 1244 1932 492 +3 356 1933 1080 +3 1932 1933 357 +3 1932 1933 125 +3 1036 1938 1937 +3 1938 1037 1040 +3 1037 1939 1940 +3 109 1040 1938 +3 1941 1939 231 +3 111 1943 1898 +3 1942 230 1943 +3 21 1944 1945 +3 259 1948 1650 +3 1950 128 1949 +3 1956 1957 1327 +3 1338 966 1955 +3 1328 1957 1376 +3 1329 1955 1954 +3 1954 1327 1956 +3 1958 1961 60 +3 301 1960 1962 +3 301 1962 968 +3 968 299 1962 +3 1963 1726 372 +3 384 1726 1963 +3 1442 1443 1967 +3 98 1964 1729 +3 1442 1967 189 +3 372 1966 1965 +3 1969 1191 1194 +3 1190 1194 1969 +3 1443 1967 1467 +3 470 904 1971 +3 1128 978 1972 +3 882 911 1979 +3 1977 882 1979 +3 1979 910 911 +3 1232 389 1980 +3 1980 1232 390 +3 1853 126 1980 +3 1853 389 1980 +3 1981 1178 591 +3 595 1983 1181 +3 591 1982 1981 +3 1984 1210 1985 +3 395 1233 1985 +3 114 1233 1985 +3 1970 649 1990 +3 1987 1259 419 +3 1406 1991 651 +3 650 651 1991 +3 1990 650 1991 +3 1997 830 1978 +3 1413 1992 1993 +3 1416 1998 2000 +3 2002 880 1994 +3 2002 1994 2001 +3 1997 880 1994 +3 2002 1417 1662 +3 889 2003 1415 +3 2000 884 1998 +3 1415 1992 1998 +3 2001 1417 2000 +3 2003 1413 1992 +3 2003 1992 1415 +3 1428 1340 2004 +3 1428 2004 1342 +3 2005 2006 166 +3 166 1320 2006 +3 2008 1593 2007 +3 2007 2008 929 +3 1591 2013 1592 +3 1592 2007 2013 +3 41 1591 2014 +3 2014 1591 2013 +3 929 2014 2013 +3 1609 929 2014 +3 2014 1609 1573 +3 1025 1026 2017 +3 2019 1029 381 +3 586 2021 2020 + + +# End of OFF # diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/orientation_pipeline_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/orientation_pipeline_example.cpp new file mode 100644 index 00000000000..aab205af422 --- /dev/null +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/orientation_pipeline_example.cpp @@ -0,0 +1,114 @@ +#include + +#include + +#include +#include +#include + + +#include +#include + +#include +#include +#include + +int main(int argc, char** argv) +{ + typedef CGAL::Exact_predicates_inexact_constructions_kernel K; + typedef K::Point_3 Point_3; + typedef CGAL::Surface_mesh Mesh; + + std::vector points; + std::vector< std::vector > polygons; + Mesh ref1; + const char* input_filename = argc<2 ? "data/blobby-shuffled.off" : argv[1]; + std::ifstream input(input_filename); + if ( !input){ + std::cerr << "Error: can not read input file.\n"; + return 1; + } + typedef K::Point_3 Point_3; + CGAL::File_scanner_OFF scanner(input); + points.resize(scanner.size_of_vertices()); + polygons.resize(scanner.size_of_facets()); + //read points + for (std::size_t i = 0; i < scanner.size_of_vertices(); ++i) + { + double x, y, z, w; + scanner.scan_vertex( x, y, z, w); + points[i] = Point_3(x, y, z, w); + scanner.skip_to_next_vertex( i); + } + //read triangles + for (std::size_t i = 0; i < scanner.size_of_facets(); ++i) + { + std::size_t no; + scanner.scan_facet( no, i); + polygons[i].resize(no); + + for(std::size_t j = 0; j < no; ++j) { + std::size_t id; + scanner.scan_facet_vertex_index(id, i); + if(id < scanner.size_of_vertices()) + { + polygons[i][j] = id; + } + else + { + std::cerr << "Error: input file not valid.\n"; + return 1; + } + } + } + input.close(); + + if(points.size() == 0 || polygons.size()==0) + { + std::cerr << "Error: input file not valid.\n"; + return 1; + } + + const char* reference_filename = argc<2 ? "data/blobby.off" : argv[2]; + input.open(reference_filename); + + if ( !input || !(input >> ref1)){ + std::cerr << "Error: can not read reference file.\n"; + return 1; + } + input.close(); + + std::cout << "Is the soup a polygon mesh ? : " + << CGAL::Polygon_mesh_processing::is_polygon_soup_a_polygon_mesh(polygons) + << std::endl; + + CGAL::Polygon_mesh_processing::orient_triangle_soup_with_reference_triangle_mesh(ref1, points, polygons); + + std::cout << "And now ? : " + << CGAL::Polygon_mesh_processing::is_polygon_soup_a_polygon_mesh(polygons) + << std::endl; + + CGAL::Polygon_mesh_processing::duplicate_non_manifold_edges_in_polygon_soup(points, polygons); + + std::cout << "And now ? : " + << CGAL::Polygon_mesh_processing::is_polygon_soup_a_polygon_mesh(polygons) + << std::endl; + + Mesh poly; + CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh( + points, polygons, poly); + + typedef boost::property_map >::type Fccmap; + Fccmap fccmap = get(CGAL::dynamic_face_property_t(), poly); + + std::cout< -#include - -#include -#include - -#include -#include - -typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; - -typedef Kernel::FT FT; -typedef Kernel::Point_3 Point_3; -typedef Kernel::Vector_3 Vector_3; -typedef CGAL::Surface_mesh Surface_mesh; - -typedef boost::graph_traits::vertex_descriptor vertex_descriptor; - -namespace PMP = CGAL::Polygon_mesh_processing; - -int main(int argc, char** argv) -{ - std::cout.precision(17); - - if(argc != 3) - { - std::cerr << "Usage: " << argv[0] << " input_mesh tolerance" << std::endl; - return EXIT_FAILURE; - } - - Surface_mesh sm; - std::ifstream in(argv[1]); - if(!in || in >> sm) - { - std::cerr << "Problem loading the input data" << std::endl; - return EXIT_FAILURE; - } - - Surface_mesh::Property_map tolerance_map; - tolerance_map = sm.add_property_map("v:t").first; - for(vertex_descriptor v : vertices(sm)) - put(tolerance_map, v, std::atof(argv[3])); - - std::chrono::steady_clock::time_point start_time = std::chrono::steady_clock::now(); - - // Snap - std::size_t nb_snapped = PMP::experimental::snap_borders(sm, tolerance_map); - std::cout << "#snapped: " << nb_snapped << std::endl; - - std::chrono::steady_clock::time_point snap_time = std::chrono::steady_clock::now(); - std::cout << "Time elapsed (snap): " - << std::chrono::duration_cast(snap_time - start_time).count() - << "ms" << std::endl; - - // Stitch - std::cout << "Stitch, #ne: " << edges(sm).size() << std::endl; - PMP::stitch_borders(sm); - - std::chrono::steady_clock::time_point stitch_time = std::chrono::steady_clock::now(); - std::cout << "Time elapsed (stitch): " - << std::chrono::duration_cast(stitch_time - snap_time).count() - << "ms" << std::endl; - - std::chrono::steady_clock::time_point end_time = std::chrono::steady_clock::now(); - std::cout << "Total time elapsed: " - << std::chrono::duration_cast(end_time - start_time).count() - << "ms" << std::endl; - - std::cout << "#border: " << PMP::number_of_borders(sm) << std::endl; - std::cout << "Done!" << std::endl; - - std::ofstream("snapped.off") << std::setprecision(17) << sm; - - return EXIT_SUCCESS; -} diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/snapping_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/snapping_example.cpp deleted file mode 100644 index b5bb4d913ed..00000000000 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/snapping_example.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include -#include - -#include - -#include -#include - -typedef CGAL::Simple_cartesian Kernel; -typedef Kernel::Point_3 Point_3; -typedef CGAL::Surface_mesh Surface_mesh; - -typedef boost::graph_traits::vertex_descriptor vertex_descriptor; - -namespace PMP = CGAL::Polygon_mesh_processing; - -int main(int argc, char** argv) -{ - if(argc != 4) - { - std::cerr << "Usage: " << argv[0] << " movable_mesh fixed_mesh tolerance" << std::endl; - return EXIT_FAILURE; - } - - Surface_mesh movable_mesh, fixed_mesh; - - std::ifstream in_m(argv[1]); - if(!in_m || in_m >> movable_mesh) - { - std::cerr << "Problem loading the input data" << std::endl; - return EXIT_FAILURE; - } - - std::ifstream in_f(argv[2]); - if(!in_f || in_f >> fixed_mesh) - { - std::cerr << "Problem loading the input data" << std::endl; - return EXIT_FAILURE; - } - - std::cout << "Movable mesh: " << num_vertices(movable_mesh) << std::endl; - std::cout << "Fixed mesh: " << num_vertices(fixed_mesh) << std::endl; - - const double tolerance = std::atof(argv[3]); - - Surface_mesh::Property_map movable_tolerance_map; - movable_tolerance_map = movable_mesh.add_property_map("v:t").first; - for(vertex_descriptor v : vertices(movable_mesh)) - put(movable_tolerance_map, v, tolerance); - - Surface_mesh::Property_map fixed_tolerance_map; - fixed_tolerance_map = fixed_mesh.add_property_map("v:t").first; - for(vertex_descriptor v : vertices(fixed_mesh)) - put(fixed_tolerance_map, v, tolerance); - - std::chrono::steady_clock::time_point start_time = std::chrono::steady_clock::now(); - - // Choice of named parameters indicates that: - // - We want to simplify the boundary of the first mesh - // - The geometry of the second mesh cannot change - std::size_t nb_snapped = PMP::experimental::snap_borders(movable_mesh, movable_tolerance_map, - fixed_mesh, fixed_tolerance_map, - CGAL::parameters::do_simplify_border(true), - CGAL::parameters::do_lock_mesh(true)); - - std::chrono::steady_clock::time_point end_time = std::chrono::steady_clock::now(); - - std::cout << "Time elapsed: " - << std::chrono::duration_cast(end_time - start_time).count() - << "ms" << std::endl; - - std::cout << "#Snapped: " << nb_snapped << std::endl; - - std::ofstream("snapped_movable.off") << std::setprecision(17) << movable_mesh; - std::ofstream("snapped_fixed.off") << std::setprecision(17) << fixed_mesh; - - return EXIT_SUCCESS; -} diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h index 05ca9388ebb..d059c3eb33d 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/clip.h @@ -547,6 +547,65 @@ bool clip(TriangleMesh& tm, return clip(tm, clipper, np, parameters::all_default()); } +/** + * \ingroup PMP_corefinement_grp + * clips `tm` by keeping the part that is inside `iso_cuboid`. + * If `tm` is closed, the clipped part can be closed too if the named parameter `clip_volume` is set to `true`. + * See Subsection \ref coref_clip for more details. + * + * \note In the current implementation it is not possible to set the vertex point map and the default will be used. `Iso_cuboid_3` must be + * from the same %Kernel as the point of the vertex point map. + * \pre \link CGAL::Polygon_mesh_processing::does_self_intersect() `!CGAL::Polygon_mesh_processing::does_self_intersect(tm)` \endlink + * + * @tparam TriangleMesh a model of `MutableFaceGraph`, `HalfedgeListGraph` and `FaceListGraph`. + * An internal property map for `CGAL::vertex_point_t` must be available. + * + * @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" + * + * @param tm input triangulated surface mesh + * @param iso_cuboid iso-cuboid used to clip `tm`. + * @param np optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below + * + * \cgalNamedParamsBegin + * \cgalParamBegin{visitor} a class model of `PMPCorefinementVisitor` + * that is used to track the creation of new faces. + * \cgalParamEnd + * \cgalParamBegin{throw_on_self_intersection} if `true`, + * the set of triangles closed to the intersection of `tm` and `iso_cuboid` will be + * checked for self-intersections and `CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception` + * will be thrown if at least one is found. + * \cgalParamEnd + * \cgalParamBegin{clip_volume} if `true` and `tm` is closed, the clipping will be done on + * the volume \link coref_def_subsec bounded \endlink by `tm` rather than on its surface + * (i.e., `tm` will be kept closed). + * \cgalParamEnd + * \cgalParamBegin{use_compact_clipper} if `false` and `clip_volume` is `false` and `tm` is open, the parts of `tm` coplanar with `is_cuboid` + * will not be part of the output. + * \cgalNamedParamsEnd + * + * @return `true` if the output surface mesh is manifold. + * If `false` is returned `tm` is only refined by the intersection with `iso_cuboid`. + */ +template +bool clip(TriangleMesh& tm, +#ifdef DOXYGEN_RUNNING + const Iso_cuboid_3& iso_cuboid, +#else + const typename GetGeomTraits::type::Iso_cuboid_3& iso_cuboid, +#endif + const NamedParameters& np) +{ + if(boost::begin(faces(tm))==boost::end(faces(tm))) return true; + TriangleMesh clipper; + + make_hexahedron(iso_cuboid[0], iso_cuboid[1], iso_cuboid[2], iso_cuboid[3], + iso_cuboid[4], iso_cuboid[5], iso_cuboid[6], iso_cuboid[7], + clipper); + triangulate_faces(clipper); + + return clip(tm, clipper, np, parameters::all_default()); +} /*! * \ingroup PMP_corefinement_grp @@ -635,9 +694,6 @@ void split(TriangleMesh& tm, * @param np optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below * * \cgalNamedParamsBegin - * \cgalParamBegin{vertex_point_map} - * the property map with the points associated to the vertices of `tm`. - * \cgalParamEnd * \cgalParamBegin{visitor} a class model of `PMPCorefinementVisitor` * that is used to track the creation of new faces. * \cgalParamEnd @@ -680,23 +736,22 @@ void split(TriangleMesh& tm, //else nothing to do, no intersection. } + /** * \ingroup PMP_corefinement_grp - * clips `tm` by keeping the part that is inside `iso_cuboid`. - * If `tm` is closed, the clipped part can be closed too if the named parameter `clip_volume` is set to `true`. - * See Subsection \ref coref_clip for more details. + * adds intersection edges of `iso_cuboid` and `tm` in `tm` and duplicates those edges. * - * \note In the current implementation it is not possible to set the vertex point map and the default will be used. `Iso_cuboid_3` must be - * from the same %Kernel as the point of the vertex point map. + * \note In the current implementation it is not possible to set the vertex point map and the default will be used. + * \note `Iso_cuboid_3` must be from the same %Kernel as the point of the vertex point map. * \pre \link CGAL::Polygon_mesh_processing::does_self_intersect() `!CGAL::Polygon_mesh_processing::does_self_intersect(tm)` \endlink * - * @tparam TriangleMesh a model of `MutableFaceGraph`, `HalfedgeListGraph` and `FaceListGraph`. + * @tparam TriangleMesh a model of `MutableFaceGraph`, `HalfedgeListGraph` and `FaceListGraph` * An internal property map for `CGAL::vertex_point_t` must be available. * * @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" * * @param tm input triangulated surface mesh - * @param iso_cuboid iso-cuboid used to clip `tm`. + * @param iso_cuboid iso-cuboid used to split `tm`. * @param np optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below * * \cgalNamedParamsBegin @@ -708,36 +763,26 @@ void split(TriangleMesh& tm, * checked for self-intersections and `CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception` * will be thrown if at least one is found. * \cgalParamEnd - * \cgalParamBegin{clip_volume} if `true` and `tm` is closed, the clipping will be done on - * the volume \link coref_def_subsec bounded \endlink by `tm` rather than on its surface - * (i.e., `tm` will be kept closed). - * \cgalParamEnd - * \cgalParamBegin{use_compact_clipper} if `false` and `clip_volume` is `false` and `tm` is open, the parts of `tm` coplanar with `is_cuboid` - * will not be part of the output. * \cgalNamedParamsEnd - * - * @return `true` if the output surface mesh is manifold. - * If `false` is returned `tm` is only refined by the intersection with `iso_cuboid`. */ template -bool clip(TriangleMesh& tm, -#ifdef DOXYGEN_RUNNING - const Iso_cuboid_3& iso_cuboid, -#else - const typename GetGeomTraits::type::Iso_cuboid_3& iso_cuboid, -#endif - const NamedParameters& np) +void split(TriangleMesh& tm, + #ifdef DOXYGEN_RUNNING + const Iso_cuboid_3& iso_cuboid, + #else + const typename GetGeomTraits::type::Iso_cuboid_3& iso_cuboid, + #endif + const NamedParameters& np) { - if(boost::begin(faces(tm))==boost::end(faces(tm))) return true; - TriangleMesh clipper; + TriangleMesh splitter; make_hexahedron(iso_cuboid[0], iso_cuboid[1], iso_cuboid[2], iso_cuboid[3], - iso_cuboid[4], iso_cuboid[5], iso_cuboid[6], iso_cuboid[7], - clipper); - triangulate_faces(clipper); + iso_cuboid[4], iso_cuboid[5], iso_cuboid[6], iso_cuboid[7], + splitter); + triangulate_faces(splitter); - return clip(tm, clipper, np, parameters::all_default()); + split(tm, splitter, np, parameters::all_default()); } /// \cond SKIP_IN_MANUAL @@ -806,6 +851,13 @@ void split(TriangleMesh& tm, split(tm, plane, parameters::all_default()); } +template +void split(TriangleMesh& tm, + const typename GetGeomTraits::type::Iso_cuboid_3& iso_cuboid) +{ + split(tm, iso_cuboid, parameters::all_default()); +} + /// \endcond } } //end of namespace CGAL::Polygon_mesh_processing diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/compute_normal.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/compute_normal.h index 680bf2ec252..b812cf7ae31 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/compute_normal.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/compute_normal.h @@ -98,6 +98,12 @@ void sum_normals(const PM& pmesh, const Point_ref pvnn = get(vpmap, target(next(he, pmesh), pmesh)); const Vector n = internal::triangle_normal(pv, pvn, pvnn, traits); + +#ifdef CGAL_PMP_COMPUTE_NORMAL_DEBUG_PP + std::cout << "Normal of " << f << " pts: " << pv << " ; " << pvn << " ; " << pvnn << std::endl; + std::cout << " --> " << n << std::endl; +#endif + sum = traits.construct_sum_of_vectors_3_object()(sum, n); he = next(he, pmesh); @@ -206,7 +212,7 @@ void compute_face_normals(const PolygonMesh& pmesh, { typename Kernel::Vector_3 vec = compute_face_normal(f, pmesh, np); put(face_normals, f, vec); -#ifdef CGAL_PMP_COMPUTE_NORMAL_DEBUG +#ifdef CGAL_PMP_COMPUTE_NORMAL_DEBUG_PP std::cout << "normal at face " << f << " is " << get(face_normals, f) << std::endl; #endif } @@ -522,6 +528,10 @@ compute_vertex_normal_as_sum_of_weighted_normals(typename boost::graph_traits::vertex_descript const bool must_compute_face_normals = is_default_parameter(get_parameter(np, internal_np::face_normal)); #ifdef CGAL_PMP_COMPUTE_NORMAL_DEBUG_PP - std::cout << std::endl << std::endl; - std::cout << "----------------------------------------------------------------------" << std::endl; - std::cout << "compute vertex at " << get(vpmap, v) + std::cout << "<----- compute vertex normal at " << get(vpmap, v) << ", must compute face normals? " << must_compute_face_normals << std::endl; #endif @@ -654,7 +668,7 @@ compute_vertex_normal(typename boost::graph_traits::vertex_descript } } -#ifdef CGAL_PMP_COMPUTE_NORMAL_DEBUG +#ifdef CGAL_PMP_COMPUTE_NORMAL_DEBUG_PP std::cout << "Incident face normals:" << std::endl; for(halfedge_descriptor h : CGAL::halfedges_around_target(v, pmesh)) { diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h index e5d25bc983a..b98a70c63c5 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h @@ -299,7 +299,8 @@ template Face_property_tag; + typedef typename boost::graph_traits::faces_size_type faces_size_type; + typedef CGAL::dynamic_face_property_t Face_property_tag; typedef typename boost::property_map::const_type Patch_ids_map; Patch_ids_map patch_ids_map = get(Face_property_tag(), pmesh); @@ -931,23 +932,49 @@ struct No_mark template < class PolygonMesh, class PolygonMeshRange, class FIMap, class VIMap, - class HIMap, class Ecm > + class HIMap, class Ecm, class NamedParameters > void split_connected_components_impl(FIMap fim, HIMap him, VIMap vim, Ecm ecm, PolygonMeshRange& range, - const PolygonMesh& tm) + const PolygonMesh& tm, + const NamedParameters& np) { - typename boost::template property_map< - PolygonMesh, CGAL::dynamic_face_property_t >::const_type - pidmap = get(CGAL::dynamic_face_property_t(), tm); + typedef typename boost::graph_traits::faces_size_type faces_size_type; + typedef typename internal_np::Lookup_named_param_def < + internal_np::face_patch_t, + NamedParameters, + typename boost::template property_map< + PolygonMesh, CGAL::dynamic_face_property_t >::const_type> ::type + Fpm; - int nb_patches = CGAL::Polygon_mesh_processing::connected_components( - tm, pidmap, CGAL::parameters::face_index_map(fim) - .edge_is_constrained_map(ecm)); + using parameters::choose_parameter; + using parameters::get_parameter; + using parameters::is_default_parameter; - for(int i=0; i(), tm)); + + faces_size_type nb_patches = 0; + if(is_default_parameter(get_parameter(np, internal_np::face_patch))) + { + nb_patches = CGAL::Polygon_mesh_processing::connected_components( + tm, pidmap, CGAL::parameters::face_index_map(fim) + .edge_is_constrained_map(ecm)); + } + else + { + for(const auto& f : faces(tm)) + { + faces_size_type patch_id = get(pidmap, f); + if(patch_id > nb_patches) + nb_patches = patch_id; + } + nb_patches+=1; + } + + for(faces_size_type i=0; i filter_graph(tm, i, pidmap, CGAL::parameters::face_index_map(fim) @@ -986,6 +1013,11 @@ void split_connected_components_impl(FIMap fim, * \cgalNPBegin{halfedge_index_map} * a property map containing a unique index for each halfedge initialized 0 to `num_halfedges(pm)` * \cgalNPEnd + * \cgalParamBegin{face_patch_map} a property map with the patch id's associated to the + faces of `pm`. Instance of a class model of `ReadPropertyMap`. + If not provided, an internal map will be filled with a call to + `connected_components()` with `edge_is_constrained_map()` (if provided). +* \cgalParamEnd * \cgalNamedParamsEnd * */ @@ -1009,7 +1041,7 @@ void split_connected_components(const PolygonMesh& pm, internal::split_connected_components_impl(CGAL::get_initialized_face_index_map(pm, np), CGAL::get_initialized_halfedge_index_map(pm, np), CGAL::get_initialized_vertex_index_map(pm, np), - ecm, cc_meshes, pm); + ecm, cc_meshes, pm, np); } template diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/distance.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/distance.h index d4c6291c33b..0f8aeb511a0 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/distance.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/distance.h @@ -15,9 +15,9 @@ #include +#include +#include -#include -#include #include #include #include @@ -26,11 +26,8 @@ #include #include #include - #include -#include -#include #ifdef CGAL_LINKED_WITH_TBB #include #include @@ -39,16 +36,21 @@ #include -namespace CGAL{ +#include +#include +#include + +namespace CGAL { namespace Polygon_mesh_processing { -namespace internal{ -template -OutputIterator -triangle_grid_sampling( const typename Kernel::Point_3& p0, - const typename Kernel::Point_3& p1, - const typename Kernel::Point_3& p2, - double distance, - OutputIterator out) +namespace internal { + +template +PointOutputIterator +triangle_grid_sampling(const typename Kernel::Point_3& p0, + const typename Kernel::Point_3& p1, + const typename Kernel::Point_3& p2, + double distance, + PointOutputIterator out) { typename Kernel::Compute_squared_distance_3 squared_distance; const double d_p0p1 = to_double(approximate_sqrt( squared_distance(p0, p1) )); @@ -57,16 +59,17 @@ triangle_grid_sampling( const typename Kernel::Point_3& p0, const double n = (std::max)(std::ceil( d_p0p1 / distance ), std::ceil( d_p0p2 / distance )); - for (double i=1; i::Kernel::Compute_squared_distance_3 squared_distance; double d = to_double(CGAL::approximate_sqrt( squared_distance(hint,*(sample_points.begin() + i)) )); - if (d>hdist) hdist=d; + if(d > hdist) + hdist=d; } // update max value stored in distance @@ -128,7 +132,7 @@ double approximate_Hausdorff_distance_impl( CGAL_static_assertion_msg (!(boost::is_convertible::value), "Parallel_tag is enabled but TBB is unavailable."); #else - if (boost::is_convertible::value) + if(boost::is_convertible::value) { std::atomic distance; distance=0; @@ -153,19 +157,170 @@ double approximate_Hausdorff_distance_impl( } } -} //end of namespace internal +template +struct Triangle_structure_sampler_base +{ + const NamedParameters np; + GeomTraits gt; + PointOutputIterator& out; + + Triangle_structure_sampler_base(PointOutputIterator& out, + const NamedParameters& np) + : np(np), out(out) + {} + + void sample_points(); + double get_minimum_edge_length(); + template + double get_tr_area(const Tr&); + + template + std::array get_tr_points(const Tr& tr); + + void ms_edges_sample(const std::size_t& nb_points_per_edge, + const std::size_t& nb_pts_l_u); + void ru_edges_sample(); + void internal_sample_triangles(double, bool, bool); + + Randomizer get_randomizer(); + std::pair get_range(); + std::size_t get_points_size(); + + void procede() + { + using parameters::choose_parameter; + using parameters::get_parameter; + using parameters::is_default_parameter; + + gt = choose_parameter(get_parameter(np, internal_np::geom_traits)); + + + bool use_rs = choose_parameter(get_parameter(np, internal_np::random_uniform_sampling), true); + bool use_gs = choose_parameter(get_parameter(np, internal_np::grid_sampling), false); + bool use_ms = choose_parameter(get_parameter(np, internal_np::monte_carlo_sampling), false); + + if(use_gs || use_ms) + if(is_default_parameter(get_parameter(np, internal_np::random_uniform_sampling))) + use_rs = false; + + bool smpl_vrtcs = choose_parameter(get_parameter(np, internal_np::do_sample_vertices), true); + bool smpl_dgs = choose_parameter(get_parameter(np, internal_np::do_sample_edges), true); + bool smpl_fcs = choose_parameter(get_parameter(np, internal_np::do_sample_faces), true); + double nb_pts_a_u = choose_parameter(get_parameter(np, internal_np::nb_points_per_area_unit), 0.); + double nb_pts_l_u = choose_parameter(get_parameter(np, internal_np::nb_points_per_distance_unit), 0.); + + // sample vertices + if(smpl_vrtcs) + static_cast(this)->sample_points(); + + // grid sampling + if(use_gs) + { + double grid_spacing_ = choose_parameter(get_parameter(np, internal_np::grid_spacing), 0.); + + if(grid_spacing_ == 0.) + { + // set grid spacing to the shortest edge length + grid_spacing_ = static_cast(this)->get_minimum_edge_length(); + } + + static_cast(this)->internal_sample_triangles(grid_spacing_, smpl_fcs, smpl_dgs); + } + + // monte carlo sampling + if(use_ms) + { + double min_sq_edge_length = (std::numeric_limits::max)(); + + std::size_t nb_points_per_face = + choose_parameter(get_parameter(np, internal_np::number_of_points_per_face), 0); + + std::size_t nb_points_per_edge = + choose_parameter(get_parameter(np, internal_np::number_of_points_per_edge), 0); + + if((nb_points_per_face == 0 && nb_pts_a_u == 0.) || + (nb_points_per_edge == 0 && nb_pts_l_u == 0.)) + { + min_sq_edge_length = static_cast(this)->get_minimum_edge_length(); + } + + // sample faces + if(smpl_fcs) + { + // set default value + if(nb_points_per_face == 0 && nb_pts_a_u == 0.) + nb_pts_a_u = 2. / min_sq_edge_length; + + for(const auto& tr : make_range(static_cast(this)->get_range())) + { + std::size_t nb_points = nb_points_per_face; + if(nb_points == 0) + { + nb_points = (std::max)( + static_cast( + std::ceil(static_cast(this)->get_tr_area(tr)) + *nb_pts_a_u), std::size_t(1)); + } + + // extract triangle face points + std::arraypoints = static_cast(this)->get_tr_points(tr); + + Random_points_in_triangle_3 g(points[0], points[1], points[2]); + out = std::copy_n(g, nb_points, out); + } + } + + // sample edges + if(smpl_dgs) + static_cast(this)->ms_edges_sample(nb_points_per_edge, nb_pts_l_u); + } + + // random uniform sampling + if(use_rs) + { + // sample faces + if(smpl_fcs) + { + std::size_t nb_points + = choose_parameter(get_parameter(np, internal_np::number_of_points_on_faces), 0); + + typename Derived::Randomizer g = static_cast(this)->get_randomizer(); + if(nb_points == 0) + { + if(nb_pts_a_u == 0.) + nb_points = static_cast(this)->get_points_size(); + else + nb_points = static_cast(std::ceil(g.sum_of_weights()*nb_pts_a_u)); + } + out = std::copy_n(g, nb_points, out); + } + + // sample edges + if(smpl_dgs) + static_cast(this)->ru_edges_sample(nb_pts_l_u,nb_pts_a_u); + } + } +}; + +} // namespace internal template -OutputIterator + class PointOutputIterator> +PointOutputIterator sample_triangles(const FaceRange& triangles, const TriangleMesh& tm, VertexPointMap vpm, double distance, - OutputIterator out, + PointOutputIterator out, bool sample_faces, bool sample_edges, bool add_vertices) @@ -183,54 +338,372 @@ sample_triangles(const FaceRange& triangles, { // sample edges but skip endpoints halfedge_descriptor hd = halfedge(fd, tm); - for (int i=0;i<3; ++i) + for(int i=0;i<3; ++i) { - if (sample_edges && sampled_edges.insert(edge(hd, tm)).second ) + if(sample_edges && sampled_edges.insert(edge(hd, tm)).second ) { Point_ref p0 = get(vpm, source(hd, tm)); Point_ref p1 = get(vpm, target(hd, tm)); typename Kernel::Compute_squared_distance_3 squared_distance; - const double d_p0p1 = to_double(approximate_sqrt( squared_distance(p0, p1) )); + const double d_p0p1 = to_double(approximate_sqrt(squared_distance(p0, p1))); const double nb_pts = std::ceil( d_p0p1 / distance ); const Vector_3 step_vec = typename Kernel::Construct_scaled_vector_3()( typename Kernel::Construct_vector_3()(p0, p1), typename Kernel::FT(1)/typename Kernel::FT(nb_pts)); - for (double i=1; i(p0, p1, p2, distance, out); + out = internal::triangle_grid_sampling(p0, p1, p2, distance, out); } } return out; } +namespace internal { + +template +struct Triangle_structure_sampler_for_triangle_mesh + : Triangle_structure_sampler_base::face_iterator, + Random_points_in_triangle_mesh_3, + Creator, + Triangle_structure_sampler_for_triangle_mesh > +{ + typedef Triangle_structure_sampler_for_triangle_mesh Self; + typedef Triangle_structure_sampler_base::face_iterator, + Random_points_in_triangle_mesh_3, + Creator, + Self> Base; + + typedef boost::graph_traits GT; + typedef typename GT::halfedge_descriptor halfedge_descriptor; + typedef typename GT::edge_descriptor edge_descriptor; + typedef typename GT::face_descriptor face_descriptor; + + typedef Random_points_in_triangle_mesh_3 Randomizer; + typedef typename boost::graph_traits::face_iterator TriangleIterator; + + Vpm pmap; + double min_sq_edge_length; + const Mesh& tm; + + Triangle_structure_sampler_for_triangle_mesh(const Mesh& m, + PointOutputIterator& out, + const NamedParameters& np) + : Base(out, np), tm(m) + { + using parameters::choose_parameter; + using parameters::get_parameter; + + pmap = choose_parameter(get_parameter(np, internal_np::vertex_point), + get_const_property_map(vertex_point, tm)); + min_sq_edge_length = (std::numeric_limits::max)(); + } + + std::pair get_range() + { + return std::make_pair(faces(tm).begin(), faces(tm).end()); + } + + void sample_points() + { + Property_map_to_unary_function unary(pmap); + this->out = std::copy(boost::make_transform_iterator(boost::begin(vertices(tm)), unary), + boost::make_transform_iterator(boost::end(vertices(tm)), unary), + this->out); + } + + double get_minimum_edge_length() + { + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + + if(min_sq_edge_length != (std::numeric_limits::max)()) + return min_sq_edge_length; + + for(edge_descriptor ed : edges(tm)) + { + const double sq_el = CGAL::to_double( + typename GeomTraits::Compute_squared_distance_3()(get(pmap, source(ed, tm)), + get(pmap, target(ed, tm)))); + + if(sq_el > 0. && sq_el < min_sq_edge_length) + min_sq_edge_length = sq_el; + } + + return min_sq_edge_length; + } + + double get_tr_area(const typename boost::graph_traits::face_descriptor& tr) + { + return to_double(face_area(tr,tm,parameters::geom_traits(this->gt))); + } + + template//tr = face_descriptor here + std::array get_tr_points(const Tr& tr) + { + std::array points; + halfedge_descriptor hd(halfedge(tr,tm)); + for(int i=0; i<3; ++i) + { + points[i] = get(pmap, target(hd, tm)); + hd = next(hd, tm); + } + return points; + } + + void ms_edges_sample(std::size_t nb_points_per_edge, + double nb_pts_l_u) + { + typename GeomTraits::Compute_squared_distance_3 squared_distance = this->gt.compute_squared_distance_3_object(); + + if(nb_points_per_edge == 0 && nb_pts_l_u == 0.) + nb_pts_l_u = 1. / CGAL::sqrt(min_sq_edge_length); + + for(edge_descriptor ed : edges(tm)) + { + std::size_t nb_points = nb_points_per_edge; + if(nb_points == 0) + { + nb_points = (std::max)( + static_cast(std::ceil(std::sqrt(to_double( + squared_distance(get(pmap, source(ed, tm)), + get(pmap, target(ed, tm))))) * nb_pts_l_u)), + std::size_t(1)); + } + + // now do the sampling of the edge + Random_points_on_segment_3 + g(get(pmap, source(ed,tm)), get(pmap, target(ed, tm))); + this->out = std::copy_n(g, nb_points, this->out); + } + } + void ru_edges_sample(double nb_pts_l_u, + double nb_pts_a_u) + { + using parameters::choose_parameter; + using parameters::get_parameter; + + std::size_t nb_points = choose_parameter(get_parameter(this->np, internal_np::number_of_points_on_edges), 0); + Random_points_on_edge_list_graph_3 g(tm, pmap); + if(nb_points == 0) + { + if(nb_pts_l_u == 0) + nb_points = num_vertices(tm); + else + nb_points = static_cast(std::ceil(g.mesh_length() * nb_pts_a_u)); + } + this->out = std::copy_n(g, nb_points, this->out); + } + + Randomizer get_randomizer() + { + return Randomizer(tm, pmap); + } + + void internal_sample_triangles(double grid_spacing_, bool smpl_fcs, bool smpl_dgs) + { + this->out = sample_triangles(faces(tm), tm, pmap, grid_spacing_, this->out, smpl_fcs, smpl_dgs, false); + } + + std::size_t get_points_size() + { + return num_vertices(tm); + } +}; + +template +struct Triangle_structure_sampler_for_triangle_soup + : Triangle_structure_sampler_base, + Creator, + Triangle_structure_sampler_for_triangle_soup > +{ + typedef typename TriangleRange::value_type TriangleType; + typedef Triangle_structure_sampler_for_triangle_soup Self; + + typedef Triangle_structure_sampler_base, + Creator, + Self> Base; + + typedef typename GeomTraits::Point_3 Point_3; + + typedef Random_points_in_triangle_soup Randomizer; + typedef typename TriangleRange::const_iterator TriangleIterator; + + double min_sq_edge_length; + const PointRange& points; + const TriangleRange& triangles; + + Triangle_structure_sampler_for_triangle_soup(const PointRange& pts, + const TriangleRange& trs, + PointOutputIterator& out, + const NamedParameters& np) + : Base(out, np), points(pts), triangles(trs) + { + min_sq_edge_length = (std::numeric_limits::max)(); + } + + std::pair get_range() + { + return std::make_pair(triangles.begin(), triangles.end()); + } + + void sample_points() + { + this->out = std::copy(points.begin(), points.end(), this->out); + } + + double get_minimum_edge_length() + { + if(min_sq_edge_length != (std::numeric_limits::max)()) + return min_sq_edge_length; + + for(const auto& tr : triangles) + { + for(std::size_t i = 0; i< 3; ++i) + { + const Point_3& a = points[tr[i]]; + const Point_3& b = points[tr[(i+1)%3]]; + + const double sq_el = CGAL::to_double(typename GeomTraits::Compute_squared_distance_3()(a, b)); + if(sq_el > 0. && sq_el < min_sq_edge_length) + min_sq_edge_length = sq_el; + } + } + + return min_sq_edge_length; + } + + template + double get_tr_area(const Tr& tr) + { + return to_double(approximate_sqrt( + this->gt.compute_squared_area_3_object()( + points[tr[0]], points[tr[1]], points[tr[2]]))); + } + + template + std::array get_tr_points(const Tr& tr) + { + std::array points; + for(int i=0; i<3; ++i) + { + points[i] = this->points[tr[i]]; + } + return points; + } + + void ms_edges_sample(std::size_t, double) + { + // don't sample edges in soup. + } + + void ru_edges_sample(double, double) + { + // don't sample edges in soup. + } + + Randomizer get_randomizer() + { + return Randomizer(triangles, points); + } + + void internal_sample_triangles(double distance, bool, bool) + { + for(const auto& tr : triangles) + { + const Point_3& p0 = points[tr[0]]; + const Point_3& p1 = points[tr[1]]; + const Point_3& p2 = points[tr[2]]; + + this->out = internal::triangle_grid_sampling(p0, p1, p2, distance, this->out); + } + } + + std::size_t get_points_size() + { + return points.size(); + } +}; + +} // namespace internal + /** \ingroup PMP_distance_grp - * generates points taken on `tm` and outputs them to `out`, the sampling method - * is selected using named parameters. - * @tparam TriangleMesh a model of the concept `FaceListGraph` - * @tparam OutputIterator a model of `OutputIterator` - * holding objects of the same point type as - * the value type of the internal vertex point map of `tm` * - * @param tm the triangle mesh that will be sampled - * @param out output iterator to be filled with sampled points + * generates points on `tm` and outputs them to `out`; the sampling method + * is selected using named parameters. + * + * @tparam TriangleMesh a model of the concepts `EdgeListGraph` and `FaceListGraph` + * @tparam PointOutputIterator a model of `OutputIterator` + * holding objects of the same point type as + * the value type of the point type associated to the mesh `tm`, i.e. the value type of the vertex + * point map property map, if provided, or the value type of the internal point property map otherwise + * @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" + * + * @param tm the triangle mesh to be sampled + * @param out output iterator to be filled with sample points * @param np an optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below * * \cgalNamedParamsBegin @@ -241,52 +714,55 @@ sample_triangles(const FaceRange& triangles, * \cgalParamEnd * \cgalParamBegin{geom_traits} a model of `PMPDistanceTraits`. \cgalParamEnd * \cgalParamBegin{use_random_uniform_sampling} - * if `true` is passed (the default), points are generated in a random + * if `true` (default value), points are generated in a random * and uniform way on the surface of `tm`, and/or on edges of `tm`. * For faces, the number of sample points is the value passed to the named - * parameter `number_of_points_on_faces()`. If not set, - * the value passed to the named parameter `number_of_points_per_area_unit()` + * parameter `number_of_points_on_faces`. If not set, + * the value passed to the named parameter `number_of_points_per_area_unit` * is multiplied by the area of `tm` to get the number of sample points. * If none of these parameters is set, the number of points sampled is `num_vertices(tm)`. * For edges, the number of the number of sample points is the value passed to the named - * parameter `number_of_points_on_edges()`. If not set, - * the value passed to the named parameter `number_of_points_per_distance_unit()` + * parameter `number_of_points_on_edges`. If not set, + * the value passed to the named parameter `number_of_points_per_distance_unit` * is multiplied by the sum of the length of edges of `tm` to get the number of sample points. * If none of these parameters is set, the number of points sampled is `num_vertices(tm)`. * \cgalParamEnd * \cgalParamBegin{use_grid_sampling} - * if `true` is passed, points are generated on a grid in each triangle, + * if `true`, points are generated on a grid in each triangle, * with a minimum of one point per triangle. The distance between * two consecutive points in the grid is that of the length of the * smallest non-null edge of `tm` or the value passed to - * the named parameter `grid_spacing()`. Edges are also split using the + * the named parameter `grid_spacing`. Edges are also split using the * same distance, if requested. * \cgalParamEnd * \cgalParamBegin{use_monte_carlo_sampling} - * if `true` is passed, points are generated randomly in each triangle and/or + * if `true`, points are generated randomly in each triangle and/or * on each edge. * For faces, the number of points per triangle is the value passed to the named - * parameter `number_of_points_per_face()`. If not set, the value passed - * to the named parameter `number_of_points_per_area_unit()` is + * parameter `number_of_points_per_face`. If not set, the value passed + * to the named parameter `number_of_points_per_area_unit` is * used to pick a number of points per face proportional to the triangle * area with a minimum of one point per face. If none of these parameters * is set, 2 divided by the square of the length of the smallest non-null * edge of `tm` is used as if it was passed to - * `number_of_points_per_area_unit()`. + * `number_of_points_per_area_unit`. * For edges, the number of points per edge is the value passed to the named - * parameter `number_of_points_per_edge()`. If not set, the value passed - * to the named parameter `number_of_points_per_distance_unit()` is + * parameter `number_of_points_per_edge`. If not set, the value passed + * to the named parameter `number_of_points_per_distance_unit` is * used to pick a number of points per edge proportional to the length of * the edge with a minimum of one point per face. If none of these parameters * is set, 1 divided by the length of the smallest non-null edge of `tm` - * is used as if it was passed to `number_of_points_per_distance_unit()`. + * is used as if it was passed to `number_of_points_per_distance_unit`. + * \cgalParamEnd + * \cgalParamBegin{sample_vertices} + * if `true` (default value), vertices of `tm` are put into `out`. + * \cgalParamEnd + * \cgalParamBegin{sample_edges} + * if `true` (default value), edges of `tm` are sampled. + * \cgalParamEnd + * \cgalParamBegin{sample_faces} + * if `true` (default value), faces of `tm` are sampled. * \cgalParamEnd - * \cgalParamBegin{sample_vertices} if `true` is passed (default value), - * vertices of `tm` are put into `out`.\cgalParamEnd - * \cgalParamBegin{sample_edges} if `true` is passed (default value), - * edges of `tm` are sampled.\cgalParamEnd - * \cgalParamBegin{sample_faces} if `true` is passed (default value), - * faces of `tm` are sampled.\cgalParamEnd * \cgalParamBegin{grid_spacing} a double value used as the grid spacing * for the grid sampling method. * \cgalParamEnd @@ -316,212 +792,155 @@ sample_triangles(const FaceRange& triangles, * and the number of points per face. * \cgalParamEnd * \cgalNamedParamsEnd + * + * @see `CGAL::Polygon_mesh_processing::sample_triangle_soup()` */ -template -OutputIterator +template +PointOutputIterator sample_triangle_mesh(const TriangleMesh& tm, - OutputIterator out, - NamedParameters np) + PointOutputIterator out, + const NamedParameters& np) { - typedef typename GetGeomTraits::type Geom_traits; + typedef typename GetGeomTraits::type GeomTraits; + typedef typename GetVertexPointMap::const_type Vpm; - typedef typename GetVertexPointMap::const_type Vpm; + internal::Triangle_structure_sampler_for_triangle_mesh, + Vpm, + NamedParameters> performer(tm, out, np); + performer.procede(); - typedef boost::graph_traits GT; - typedef typename GT::face_descriptor face_descriptor; - typedef typename GT::halfedge_descriptor halfedge_descriptor; - typedef typename GT::edge_descriptor edge_descriptor; - - using parameters::choose_parameter; - using parameters::get_parameter; - using parameters::is_default_parameter; - - Vpm pmap = choose_parameter(get_parameter(np, internal_np::vertex_point), - get_const_property_map(vertex_point, tm)); - typedef Creator_uniform_3 Creator; - - Geom_traits geomtraits = choose_parameter(get_parameter(np, internal_np::geom_traits)); - - bool use_rs = choose_parameter(get_parameter(np, internal_np::random_uniform_sampling), true); - bool use_gs = choose_parameter(get_parameter(np, internal_np::grid_sampling), false); - bool use_ms = choose_parameter(get_parameter(np, internal_np::monte_carlo_sampling), false); - - if (use_gs || use_ms) - if (is_default_parameter(get_parameter(np, internal_np::random_uniform_sampling))) - use_rs=false; - - bool smpl_vrtcs = choose_parameter(get_parameter(np, internal_np::do_sample_vertices), true); - bool smpl_dgs = choose_parameter(get_parameter(np, internal_np::do_sample_edges), true); - bool smpl_fcs = choose_parameter(get_parameter(np, internal_np::do_sample_faces), true); - - double nb_pts_a_u = choose_parameter(get_parameter(np, internal_np::nb_points_per_area_unit), 0.); - double nb_pts_l_u = choose_parameter(get_parameter(np, internal_np::nb_points_per_distance_unit), 0.); - - // sample vertices - if (smpl_vrtcs) - { - Property_map_to_unary_function unary(pmap); - out = std::copy( - boost::make_transform_iterator(boost::begin(vertices(tm)), unary), - boost::make_transform_iterator(boost::end(vertices(tm)), unary), - out); - } - - // grid sampling - if (use_gs) - { - double grid_spacing_ = choose_parameter(get_parameter(np, internal_np::grid_spacing), 0.); - if (grid_spacing_==0.) - { - // set grid spacing to the shortest edge length - double grid_spacing_ = (std::numeric_limits::max)(); - typedef typename boost::graph_traits - ::edge_descriptor edge_descriptor; - for(edge_descriptor ed : edges(tm)) - { - double el = std::sqrt( - to_double( typename Geom_traits::Compute_squared_distance_3()( - get(pmap, source(ed, tm)), get(pmap, target(ed, tm)) ))); - if (el > 0 && el < grid_spacing_) - grid_spacing_ = el; - } - } - out=sample_triangles( - faces(tm), tm, pmap, grid_spacing_, out,smpl_fcs, smpl_dgs, false); - } - - // monte carlo sampling - if (use_ms) - { - typename Geom_traits::Compute_squared_distance_3 squared_distance; - double min_edge_length = (std::numeric_limits::max)(); - - std::size_t nb_points_per_face = - choose_parameter(get_parameter(np, internal_np::number_of_points_per_face), 0); - std::size_t nb_points_per_edge = - choose_parameter(get_parameter(np, internal_np::number_of_points_per_edge), 0); - - if ((nb_points_per_face == 0 && nb_pts_a_u ==0.) || - (nb_points_per_edge == 0 && nb_pts_l_u ==0.) ) - { - typedef typename boost::graph_traits - ::edge_descriptor edge_descriptor; - for(edge_descriptor ed : edges(tm)) - { - double el = std::sqrt( - to_double( squared_distance(get(pmap, source(ed, tm)), - get(pmap, target(ed, tm)) ))); - if (min_edge_length > 0 && el < min_edge_length) - min_edge_length = el; - } - } - - // sample faces - if (smpl_fcs) - { - // set default value - if (nb_points_per_face == 0 && nb_pts_a_u ==0.) - nb_pts_a_u = 2. / CGAL::square(min_edge_length); - - for(face_descriptor f : faces(tm)) - { - std::size_t nb_points = nb_points_per_face; - if (nb_points == 0) - { - nb_points = (std::max)( - static_cast( - std::ceil(to_double( - face_area(f,tm,parameters::geom_traits(geomtraits)))*nb_pts_a_u)) - ,std::size_t(1)); - } - // extract triangle face points - typename Geom_traits::Point_3 points[3]; - halfedge_descriptor hd(halfedge(f,tm)); - for(int i=0; i<3; ++i) - { - points[i] = get(pmap, target(hd, tm)); - hd = next(hd, tm); - } - // sample the triangle face - Random_points_in_triangle_3 - g(points[0], points[1], points[2]); - out=std::copy_n(g, nb_points, out); - } - } - // sample edges - if (smpl_dgs) - { - if (nb_points_per_edge == 0 && nb_pts_l_u == 0) - nb_pts_l_u = 1. / min_edge_length; - for(edge_descriptor ed : edges(tm)) - { - std::size_t nb_points = nb_points_per_edge; - if (nb_points == 0) - { - nb_points = (std::max)( - static_cast( std::ceil( std::sqrt( to_double( - squared_distance(get(pmap, source(ed, tm)), - get(pmap, target(ed, tm)) )) )*nb_pts_l_u ) ), - std::size_t(1)); - } - // now do the sampling of the edge - Random_points_on_segment_3 - g(get(pmap, source(ed,tm)), get(pmap, target(ed,tm))); - out=std::copy_n(g, nb_points, out); - } - } - } - - // random uniform sampling - if (use_rs) - { - // sample faces - if(smpl_fcs) - { - std::size_t nb_points = choose_parameter(get_parameter(np, internal_np::number_of_points_on_faces), 0); - Random_points_in_triangle_mesh_3 g(tm, pmap); - if (nb_points == 0) - { - if (nb_pts_a_u == 0.) - nb_points = num_vertices(tm); - else - nb_points = static_cast( - std::ceil(g.mesh_area()*nb_pts_a_u) ); - } - out = std::copy_n(g, nb_points, out); - } - // sample edges - if (smpl_dgs) - { - std::size_t nb_points = - choose_parameter(get_parameter(np, internal_np::number_of_points_on_edges), 0); - Random_points_on_edge_list_graph_3 g(tm, pmap); - if (nb_points == 0) - { - if (nb_pts_l_u == 0) - nb_points = num_vertices(tm); - else - nb_points = static_cast( - std::ceil( g.mesh_length()*nb_pts_a_u) ); - } - out = std::copy_n(g, nb_points, out); - } - } - - return out; + return performer.out; } -template -OutputIterator +/** \ingroup PMP_distance_grp + * + * generates points on a triangle soup and puts them to `out`; the sampling method + * is selected using named parameters. + * + * @tparam PointRange a model of the concept `RandomAccessContainer` whose value type is the point type. + * @tparam TriangleRange a model of the concept `RandomAccessContainer` + * whose value_type is itself a model of the concept `RandomAccessContainer` + * whose value_type is an unsigned integral value. + * @tparam PointOutputIterator a model of `OutputIterator` holding objects of the same type as `PointRange`'s value type + * @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" + * + * @param points the points of the soup + * @param triangles a `TriangleRange` containing the triangles of the soup to be sampled + * @param out output iterator to be filled with sample points + * @param np an optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below + * + * \cgalNamedParamsBegin + * \cgalParamBegin{geom_traits} a model of `PMPDistanceTraits`, whose `%Point_3` type is the same + * as `PointRange`'s `value_type`. + * \cgalParamEnd + * \cgalParamBegin{use_random_uniform_sampling} + * if `true`, points are generated in a random + * and uniform way over the triangles of the soup. + * The number of sample points is the value passed to the named + * parameter `number_of_points_on_faces`. If not set, + * the value passed to the named parameter `number_of_points_per_area_unit` + * is multiplied by the area of the soup to get the number of sample points. + * If none of these parameters is set, the number of points sampled is `points.size()`. + * + * %Default is `true`. + * \cgalParamEnd + * \cgalParamBegin{use_grid_sampling} + * if `true`, points are generated on a grid in each triangle, + * with a minimum of one point per triangle. The distance between + * two consecutive points in the grid is that of the length of the + * smallest non-null edge of the soup, or the value passed to + * the named parameter `grid_spacing`. + * + * %Default is `false`. + * \cgalParamEnd + * \cgalParamBegin{use_monte_carlo_sampling} + * if `true`, points are generated randomly in each triangle. + * The number of points per triangle is the value passed to the named + * parameter `number_of_points_per_face`. If not set, the value passed + * to the named parameter `number_of_points_per_area_unit` is + * used to pick a number of points per face proportional to the triangle + * area with a minimum of one point per face. If none of these parameters + * is set, the number of points per area unit is set to 2 divided + * by the square of the length of the smallest non-null edge of the soup. + * + * %Default is `false`. + * \cgalParamEnd + * \cgalParamBegin{sample_vertices} + * if `true`, the points of `points` are put into `out`. + * + * %Default is `true`. + * \cgalParamEnd + * \cgalParamBegin{grid_spacing} a double value used as the grid spacing + * for the grid sampling method. + * \cgalParamEnd + * \cgalParamBegin{number_of_points_on_faces} an unsigned integral value used + * for the random sampling method as the number of points to pick on the surface. + * \cgalParamEnd + * \cgalParamBegin{number_of_points_per_face} an unsigned integral value + * used by the Monte-Carlo sampling method as the number of points per triangle + * to pick. + * \cgalParamEnd + * \cgalParamBegin{number_of_points_per_area_unit} a double value + * used for the random sampling and the Monte Carlo sampling methods to + * repectively determine the total number of points inside triangles + * and the number of points per triangle. + * \cgalParamEnd + * \cgalNamedParamsEnd + * + * \attention Contrary to `sample_triangle_mesh()`, this method does not allow to sample edges. + * + * @see `CGAL::Polygon_mesh_processing::sample_triangle_mesh()` + */ +template +PointOutputIterator +sample_triangle_soup(const PointRange& points, + const TriangleRange& triangles, + PointOutputIterator out, + const NamedParameters& np) +{ + typedef typename PointRange::value_type Point_3; + typedef typename Kernel_traits::Kernel GeomTraits; + + static_assert(std::is_same::value, "Wrong point type."); + + internal::Triangle_structure_sampler_for_triangle_soup, + NamedParameters> performer(points, triangles, out, np); + performer.procede(); + + return performer.out; +} + +template +PointOutputIterator sample_triangle_mesh(const TriangleMesh& tm, - OutputIterator out) + PointOutputIterator out) { return sample_triangle_mesh(tm, out, parameters::all_default()); } +template +PointOutputIterator +sample_triangle_soup(const PointRange& points, + const TriangleRange& triangles, + PointOutputIterator out) +{ + return sample_triangle_soup(points, triangles, out, parameters::all_default()); +} + template sample_points; - sample_triangle_mesh( - tm1, - std::back_inserter(sample_points), - np); - return approximate_Hausdorff_distance(sample_points, tm2, vpm_2); + std::vector sample_points; + sample_triangle_mesh(tm1, std::back_inserter(sample_points), np); + + return approximate_Hausdorff_distance(sample_points, tm2, vpm_2); } // documented functions @@ -589,7 +1006,7 @@ double approximate_Hausdorff_distance( * * @tparam Concurrency_tag enables sequential versus parallel algorithm. * Possible values are `Sequential_tag`, `Parallel_tag`, and `Parallel_if_available_tag`. - * @tparam TriangleMesh a model of the concept `FaceListGraph` + * @tparam TriangleMesh a model of the concepts `EdgeListGraph` and `FaceListGraph` * @tparam NamedParameters1 a sequence of \ref pmp_namedparameters "Named Parameters" for `tm1` * @tparam NamedParameters2 a sequence of \ref pmp_namedparameters "Named Parameters" for `tm2` * @@ -605,8 +1022,9 @@ double approximate_Hausdorff_distance( * and in all places where `vertex_point_map` is used. * \cgalParamEnd * \cgalNamedParamsEnd + * * The function `CGAL::parameters::all_default()` can be used to indicate to use the default values for - * `np1` and specify custom values for `np2` + * `np1` and specify custom values for `np2`. */ template< class Concurrency_tag, class TriangleMesh, @@ -618,9 +1036,9 @@ double approximate_Hausdorff_distance( const TriangleMesh& tm1, const NamedParameters2& np2) { typedef typename GetGeomTraits::type Geom_traits; + NamedParameters1>::type GeomTraits; - return approximate_Hausdorff_distance( + return approximate_Hausdorff_distance( tm1, tm2, np1, parameters::choose_parameter(parameters::get_parameter(np2, internal_np::vertex_point), get_const_property_map(vertex_point, tm2))); } @@ -649,11 +1067,12 @@ double approximate_symmetric_Hausdorff_distance( /** * \ingroup PMP_distance_grp - * returns the distance to `tm` of the point from `points` - * that is the furthest from `tm`. + * returns the distance to `tm` of the point from `points` that is the furthest from `tm`. + * * @tparam PointRange a range of `Point_3`, model of `Range`. Its iterator type is `RandomAccessIterator`. - * @tparam TriangleMesh a model of the concept `FaceListGraph` + * @tparam TriangleMesh a model of the concepts `EdgeListGraph` and `FaceListGraph` * @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" + * * @param points the range of points of interest * @param tm the triangle mesh to compute the distance to * @param np an optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below @@ -675,9 +1094,9 @@ double max_distance_to_triangle_mesh(const PointRange& points, const NamedParameters& np) { typedef typename GetGeomTraits::type Geom_traits; + NamedParameters>::type GeomTraits; - return approximate_Hausdorff_distance + return approximate_Hausdorff_distance (points,tm,parameters::choose_parameter(parameters::get_parameter(np, internal_np::vertex_point), get_const_property_map(vertex_point, tm))); } @@ -685,9 +1104,11 @@ double max_distance_to_triangle_mesh(const PointRange& points, /*! *\ingroup PMP_distance_grp * returns an approximation of the distance between `points` and the point lying on `tm` that is the farthest from `points` + * * @tparam PointRange a range of `Point_3`, model of `Range`. * @tparam TriangleMesh a model of the concept `FaceListGraph` * @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" + * * @param tm a triangle mesh * @param points a range of points * @param precision for each triangle of `tm`, the distance of its farthest point from `points` is bounded. @@ -712,16 +1133,16 @@ double approximate_max_distance_to_point_set(const TriangleMesh& tm, const NamedParameters& np) { typedef typename GetGeomTraits::type Geom_traits; + NamedParameters>::type GeomTraits; typedef boost::graph_traits GT; - typedef Orthogonal_k_neighbor_search > Knn; + typedef Orthogonal_k_neighbor_search > Knn; typedef typename Knn::Tree Tree; Tree tree(points.begin(), points.end()); - CRefiner ref; + CRefiner ref; for(typename GT::face_descriptor f : faces(tm)) { - typename Geom_traits::Point_3 points[3]; + typename GeomTraits::Point_3 points[3]; typename GT::halfedge_descriptor hd(halfedge(f,tm)); for(int i=0; i<3; ++i) { diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Smoothing/curvature_flow_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Smoothing/curvature_flow_impl.h index e95b46b5ee4..a201c6e8dde 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Smoothing/curvature_flow_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Smoothing/curvature_flow_impl.h @@ -170,7 +170,7 @@ public: // calls compute once to factorize with the preconditioner if(!solver.factor(A, D)) { -#ifdef CGAL_PMP_SMOOTHING_VERBOSE +#ifdef CGAL_PMP_SMOOTHING_DEBUG std::cerr << "Could not factorize linear system with preconditioner." << std::endl; #endif return false; @@ -180,7 +180,7 @@ public: !solver.linear_solver(by, Xy) || !solver.linear_solver(bz, Xz)) { -#ifdef CGAL_PMP_SMOOTHING_VERBOSE +#ifdef CGAL_PMP_SMOOTHING_DEBUG std::cerr << "Could not solve linear system." << std::endl; #endif return false; diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Smoothing/mesh_smoothing_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Smoothing/mesh_smoothing_impl.h index fb40469e47d..16f8de52d49 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Smoothing/mesh_smoothing_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Smoothing/mesh_smoothing_impl.h @@ -21,7 +21,7 @@ #endif #include -#include +#include #include #include @@ -50,9 +50,11 @@ namespace Polygon_mesh_processing { namespace internal { template -double get_radian_angle(const V& v1, const V& v2, const GT& gt) +typename GT::FT get_radian_angle(const V& v1, const V& v2, const GT& gt) { - return gt.compute_approximate_angle_3_object()(v1, v2) * CGAL_PI / 180.; + typedef typename GT::FT FT; + + return gt.compute_approximate_angle_3_object()(v1, v2) * CGAL_PI / FT(180); } // super naive for now. Not sure it even makes sense to do something like that for surfaces @@ -68,6 +70,7 @@ class Delaunay_edge_flipper typedef typename boost::graph_traits::face_descriptor face_descriptor; typedef typename boost::property_traits::reference Point_ref; + typedef typename GeomTraits::FT FT; typedef typename GeomTraits::Vector_3 Vector; public: @@ -86,20 +89,13 @@ public: const halfedge_descriptor h = halfedge(e, mesh_); const halfedge_descriptor opp_h = opposite(h, mesh_); - vertex_descriptor v0 = source(h, mesh_); - vertex_descriptor v1 = target(h, mesh_); - vertex_descriptor v2 = target(next(h, mesh_), mesh_); - vertex_descriptor v3 = target(next(opp_h, mesh_), mesh_); - const Point_ref p0 = get(vpmap_, v0); - const Point_ref p1 = get(vpmap_, v1); - const Point_ref p2 = get(vpmap_, v2); - const Point_ref p3 = get(vpmap_, v3); + const vertex_descriptor v0 = source(h, mesh_); + const vertex_descriptor v1 = target(h, mesh_); + const vertex_descriptor v2 = target(next(h, mesh_), mesh_); + const vertex_descriptor v3 = target(next(opp_h, mesh_), mesh_); - double alpha = get_radian_angle(Vector(p0 - p2), Vector(p1 - p2), traits_); - double beta = get_radian_angle(Vector(p1 - p3), Vector(p0 - p3), traits_); - - // not local Delaunay if the sum of the angles is greater than pi - if(alpha + beta <= CGAL_PI) + std::set unique_vs { v0, v1, v2, v3 }; + if(unique_vs.size() != 4) return false; // Don't want to flip if the other diagonal already exists @@ -108,7 +104,16 @@ public: if(other_hd_already_exists.second) return false; - return true; + // not local Delaunay := sum of the opposite angles is greater than pi + const Point_ref p0 = get(vpmap_, v0); + const Point_ref p1 = get(vpmap_, v1); + const Point_ref p2 = get(vpmap_, v2); + const Point_ref p3 = get(vpmap_, v3); + + FT alpha = get_radian_angle(Vector(p0 - p2), Vector(p1 - p2), traits_); + FT beta = get_radian_angle(Vector(p1 - p3), Vector(p0 - p3), traits_); + + return (alpha + beta > CGAL_PI); } template @@ -127,6 +132,10 @@ public: template void operator()(const FaceRange& face_range) { +#ifdef CGAL_PMP_SMOOTHING_DEBUG + std::cout << "Flipping edges" << std::endl; +#endif + // edges to consider std::vector edge_range; edge_range.reserve(3 * face_range.size()); @@ -166,6 +175,10 @@ public: ++flipped_n; halfedge_descriptor h = halfedge(e, mesh_); + +#ifdef CGAL_PMP_SMOOTHING_DEBUG_PP + std::cout << "Flipping " << edge(h, mesh_) << std::endl; +#endif Euler::flip_edge(h, mesh_); add_to_stack_if_unmarked(edge(next(h, mesh_), mesh_), marks, edge_range); @@ -174,6 +187,10 @@ public: add_to_stack_if_unmarked(edge(prev(opposite(h, mesh_), mesh_), mesh_), marks, edge_range); } } + +#ifdef CGAL_PMP_SMOOTHING_DEBUG + std::cout << flipped_n << " flips" << std::endl; +#endif } private: @@ -190,6 +207,7 @@ class Angle_smoother typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; typedef typename boost::property_traits::reference Point_ref; + typedef typename GeomTraits::FT FT; typedef typename GeomTraits::Vector_3 Vector; typedef std::pair He_pair; @@ -231,7 +249,7 @@ public: Vector operator()(const vertex_descriptor v) const { Vector move = CGAL::NULL_VECTOR; - double weights_sum = 0.; + FT weights_sum = FT(0); for(halfedge_descriptor main_he : halfedges_around_source(v, mesh_)) { @@ -249,26 +267,30 @@ public: Vector right_v(pt, right_pt); // rotate - double angle = get_radian_angle(right_v, left_v, traits_); - CGAL_warning(angle != 0.); // no degenerate faces is a precondition - if(angle == 0.) - continue; - Vector bisector = rotate_edge(main_he, incident_pair); - double scaling_factor = CGAL::approximate_sqrt( - traits_.compute_squared_distance_3_object()(get(vpmap_, source(main_he, mesh_)), - get(vpmap_, target(main_he, mesh_)))); + FT scaling_factor = CGAL::approximate_sqrt( + traits_.compute_squared_distance_3_object()(get(vpmap_, source(main_he, mesh_)), + get(vpmap_, target(main_he, mesh_)))); bisector = traits_.construct_scaled_vector_3_object()(bisector, scaling_factor); Vector ps_psi(ps, traits_.construct_translated_point_3_object()(pt, bisector)); + FT angle = get_radian_angle(right_v, left_v, traits_); + if(angle == FT(0)) + { + // no degenerate faces is a precondition, angle can be 0 but it should be a numerical error + CGAL_warning(!is_degenerate_triangle_face(face(main_he, mesh_), mesh_)); + + return ps_psi; // since a small angle gives more weight, a null angle give priority (?) + } + // small angles carry more weight - double weight = 1. / (angle*angle); + FT weight = 1. / CGAL::square(angle); weights_sum += weight; move += weight * ps_psi; } - if(weights_sum != 0.) + if(weights_sum != FT(0)) move /= weights_sum; return move; @@ -288,6 +310,7 @@ class Area_smoother typedef typename boost::property_traits::value_type Point; typedef typename boost::property_traits::reference Point_ref; + typedef typename GeomTraits::FT FT; typedef typename GeomTraits::Vector_3 Vector; public: @@ -298,27 +321,27 @@ public: { } private: - double element_area(const vertex_descriptor v1, - const vertex_descriptor v2, - const vertex_descriptor v3) const + FT element_area(const vertex_descriptor v1, + const vertex_descriptor v2, + const vertex_descriptor v3) const { - return CGAL::to_double(CGAL::approximate_sqrt(traits_.compute_squared_area_3_object()(get(vpmap_, v1), - get(vpmap_, v2), - get(vpmap_, v3)))); + return CGAL::approximate_sqrt(traits_.compute_squared_area_3_object()(get(vpmap_, v1), + get(vpmap_, v2), + get(vpmap_, v3))); } - double element_area(const Point& P, - const vertex_descriptor v2, - const vertex_descriptor v3) const + FT element_area(const Point& P, + const vertex_descriptor v2, + const vertex_descriptor v3) const { - return CGAL::to_double(CGAL::approximate_sqrt(traits_.compute_squared_area_3_object()(P, - get(vpmap_, v2), - get(vpmap_, v3)))); + return CGAL::approximate_sqrt(traits_.compute_squared_area_3_object()(P, + get(vpmap_, v2), + get(vpmap_, v3))); } - double compute_average_area_around(const vertex_descriptor v) const + FT compute_average_area_around(const vertex_descriptor v) const { - double sum_areas = 0.; + FT sum_areas = 0; unsigned int number_of_edges = 0; for(halfedge_descriptor h : halfedges_around_source(v, mesh_)) @@ -327,7 +350,7 @@ private: vertex_descriptor vi = source(next(h, mesh_), mesh_); vertex_descriptor vj = target(next(h, mesh_), mesh_); - double S = element_area(v, vi, vj); + FT S = element_area(v, vi, vj); sum_areas += S; ++number_of_edges; } @@ -337,7 +360,7 @@ private: struct Face_energy { - Face_energy(const Point& pi, const Point& pj, const double s_av) + Face_energy(const Point& pi, const Point& pj, const FT s_av) : qx(pi.x()), qy(pi.y()), qz(pi.z()), rx(pj.x()), ry(pj.y()), rz(pj.z()), @@ -346,7 +369,7 @@ private: // next two functions are just for convencience, the only thing ceres cares about is the operator() template - double area(const T x, const T y, const T z) const + FT area(const T x, const T y, const T z) const { return CGAL::approximate_sqrt(CGAL::squared_area(Point(x, y, z), Point(qx, qy, qz), @@ -354,7 +377,7 @@ private: } template - double evaluate(const T x, const T y, const T z) const { return area(x, y, z) - s_av; } + FT evaluate(const T x, const T y, const T z) const { return area(x, y, z) - s_av; } template bool operator()(const T* const x, const T* const y, const T* const z, @@ -390,9 +413,9 @@ private: } private: - const double qx, qy, qz; - const double rx, ry, rz; - const double s_av; + const FT qx, qy, qz; + const FT rx, ry, rz; + const FT s_av; }; public: @@ -401,12 +424,12 @@ public: #ifdef CGAL_PMP_USE_CERES_SOLVER const Point_ref vp = get(vpmap_, v); - const double S_av = compute_average_area_around(v); + const FT S_av = compute_average_area_around(v); - const double initial_x = vp.x(); - const double initial_y = vp.y(); - const double initial_z = vp.z(); - double x = initial_x, y = initial_y, z = initial_z; + const FT initial_x = vp.x(); + const FT initial_y = vp.y(); + const FT initial_z = vp.z(); + FT x = initial_x, y = initial_y, z = initial_z; ceres::Problem problem; @@ -523,7 +546,8 @@ public: Optimizer compute_move(mesh_, vpmap_, traits_); #ifdef CGAL_PMP_SMOOTHING_DEBUG - double total_displacement = 0; + FT total_displacement = 0; + std::cout << "apply_moves_in_single_batch: " << apply_moves_in_single_batch << std::endl; #endif std::size_t moved_points = 0; @@ -532,6 +556,10 @@ public: if(is_border(v, mesh_) || is_constrained(v)) continue; +#ifdef CGAL_PMP_SMOOTHING_DEBUG_PP + std::cout << "Considering " << v << " pos: " << get(vpmap_, v) << std::endl; +#endif + // compute normal to v Vector vn = compute_vertex_normal(v, mesh_, CGAL::parameters::vertex_point_map(vpmap_) .geom_traits(traits_)); @@ -542,17 +570,17 @@ public: // Gram Schmidt so that the new location is on the tangent plane of v (i.e. do mv -= (mv*n)*n) const FT sp = traits_.compute_scalar_product_3_object()(vn, move); - move = traits_.construct_sum_of_vectors_3_object()(move, - traits_.construct_scaled_vector_3_object()(vn, - sp)); + move = traits_.construct_sum_of_vectors_3_object()( + move, traits_.construct_scaled_vector_3_object()(vn, - sp)); - Point new_pos = pos + move; - - if((!use_sanity_checks || !does_move_create_bad_faces(v, new_pos)) && + const Point new_pos = pos + move; + if(move != CGAL::NULL_VECTOR && + !does_move_create_degenerate_faces(v, new_pos) && + (!use_sanity_checks || !does_move_create_bad_faces(v, new_pos)) && (!enforce_no_min_angle_regression || does_improve_min_angle_in_star(v, new_pos))) { -#ifdef CGAL_PMP_SMOOTHING_DEBUG +#ifdef CGAL_PMP_SMOOTHING_DEBUG_PP std::cout << "moving " << get(vpmap_, v) << " to " << new_pos << std::endl; - total_displacement += CGAL::approximate_sqrt(traits_.compute_squared_length_3_object()(move)); #endif if(apply_moves_in_single_batch) @@ -560,11 +588,15 @@ public: else put(vpmap_, v, new_pos); +#ifdef CGAL_PMP_SMOOTHING_DEBUG + total_displacement += CGAL::approximate_sqrt(traits_.compute_squared_length_3_object()(move)); +#endif + ++moved_points; } else // some sanity check failed { -#ifdef CGAL_PMP_SMOOTHING_DEBUG +#ifdef CGAL_PMP_SMOOTHING_DEBUG_PP std::cout << "move is rejected!" << std::endl; #endif if(apply_moves_in_single_batch) @@ -607,6 +639,10 @@ public: Point_ref p_query = get(vpmap_, v); const Point projected = tree.closest_point(p_query); +#ifdef CGAL_PMP_SMOOTHING_DEBUG_PP + std::cout << p_query << " to " << projected << std::endl; +#endif + put(vpmap_, v, projected); } } @@ -617,11 +653,10 @@ private: return get(vcmap_, v); } - // check for degenerate or inversed faces - bool does_move_create_bad_faces(const vertex_descriptor v, - const Point& new_pos) const + // Null faces are bad because they make normal computation difficult + bool does_move_create_degenerate_faces(const vertex_descriptor v, + const Point& new_pos) const { - // check for null faces and face inversions for(halfedge_descriptor main_he : halfedges_around_source(v, mesh_)) { const halfedge_descriptor prev_he = prev(main_he, mesh_); @@ -630,6 +665,23 @@ private: if(traits_.collinear_3_object()(lpt, rpt, new_pos)) return true; + } + + return false; + } + + // check for degenerate or inversed faces + bool does_move_create_bad_faces(const vertex_descriptor v, + const Point& new_pos) const + { + // check for face inversions + for(halfedge_descriptor main_he : halfedges_around_source(v, mesh_)) + { + const halfedge_descriptor prev_he = prev(main_he, mesh_); + const Point_ref lpt = get(vpmap_, target(main_he, mesh_)); + const Point_ref rpt = get(vpmap_, source(prev_he, mesh_)); + + CGAL_assertion(!traits_.collinear_3_object()(lpt, rpt, new_pos)); // checked above const Point_ref old_pos = get(vpmap_, v); Vector ov_1 = traits_.construct_vector_3_object()(old_pos, lpt); @@ -641,7 +693,7 @@ private: if(!is_positive(traits_.compute_scalar_product_3_object()(old_n, new_n))) { -#ifdef CGAL_PMP_SMOOTHING_DEBUG +#ifdef CGAL_PMP_SMOOTHING_DEBUG_PP std::cout << "Moving vertex would result in the inversion of a face normal!" << std::endl; #endif return true; @@ -655,7 +707,7 @@ private: const Point& new_pos) const { // check if the minimum angle of the star has not deteriorated - double old_min_angle = CGAL_PI; + FT old_min_angle = CGAL_PI; for(halfedge_descriptor main_he : halfedges_around_source(v, mesh_)) { const Point_ref old_pos = get(vpmap_, v); @@ -683,7 +735,7 @@ private: get_radian_angle(Vector(lpt, rpt), Vector(lpt, new_pos), traits_) < old_min_angle || get_radian_angle(Vector(rpt, new_pos), Vector(rpt, lpt), traits_) < old_min_angle) { -#ifdef CGAL_PMP_SMOOTHING_DEBUG +#ifdef CGAL_PMP_SMOOTHING_DEBUG_PP const Point_ref old_pos = get(vpmap_, v); std::cout << "deterioration of min angle in the star!" << std::endl; diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Smoothing/smoothing_evaluation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Smoothing/smoothing_evaluation.h index f2c2ccbe000..420a654bedf 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Smoothing/smoothing_evaluation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Smoothing/smoothing_evaluation.h @@ -61,7 +61,7 @@ public: angles_.push_back(traits_.compute_approximate_angle_3_object()(a, b, c)); } -#ifdef CGAL_PMP_SMOOTHING_VERBOSE +#ifdef CGAL_PMP_SMOOTHING_DEBUG std::cout << "angles_ size = " << angles_.size() << std::endl; #endif } @@ -79,7 +79,7 @@ public: for(face_descriptor f : faces(mesh_)) areas_.push_back(face_area(f, mesh_)); -#ifdef CGAL_PMP_SMOOTHING_VERBOSE +#ifdef CGAL_PMP_SMOOTHING_DEBUG std::cout << "areas_ size = " << areas_.size() << std::endl; #endif } @@ -97,7 +97,7 @@ public: for(face_descriptor f : faces(mesh_)) aspect_ratios_.push_back(CGAL::Polygon_mesh_processing::face_aspect_ratio(f, mesh_)); -#ifdef CGAL_PMP_SMOOTHING_VERBOSE +#ifdef CGAL_PMP_SMOOTHING_DEBUG std::cout << "aspect_ratios_ size = " << aspect_ratios_.size() << std::endl; #endif } diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Snapping/snap.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Snapping/snap.h index 6601dd0317a..393281d3da6 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Snapping/snap.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Snapping/snap.h @@ -1185,13 +1185,13 @@ std::size_t snap_borders(TriangleMesh& tm_A, { typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - std::vector border_vertices_1; - border_halfedges(tm_A, std::back_inserter(border_vertices_1)); - std::vector border_vertices_2; - border_halfedges(tm_B, std::back_inserter(border_vertices_2)); + std::vector border_vertices_A; + border_halfedges(tm_A, std::back_inserter(border_vertices_A)); + std::vector border_vertices_B; + border_halfedges(tm_B, std::back_inserter(border_vertices_B)); - return internal::snap_non_conformal(border_vertices_1, tm_A, tolerance_map_A, - border_vertices_2, tm_B, tolerance_map_B, + return internal::snap_non_conformal(border_vertices_A, tm_A, tolerance_map_A, + border_vertices_B, tm_B, tolerance_map_B, false /*not self snapping*/, np_A, np_B); } @@ -1211,19 +1211,19 @@ std::size_t snap_borders(TriangleMesh& tm_A, typedef CGAL::dynamic_vertex_property_t Vertex_property_tag; typedef typename boost::property_map::type Tolerance_map; - std::vector border_vertices_1; - std::vector border_vertices_2; - border_halfedges(tm_A, std::back_inserter(border_vertices_1)); - border_halfedges(tm_B, std::back_inserter(border_vertices_2)); + std::vector border_vertices_A; + std::vector border_vertices_B; + border_halfedges(tm_A, std::back_inserter(border_vertices_A)); + border_halfedges(tm_B, std::back_inserter(border_vertices_B)); const FT tol_mx(std::numeric_limits::max()); Tolerance_map tolerance_map_A = get(Vertex_property_tag(), tm_A); - internal::assign_tolerance_with_local_edge_length_bound(border_vertices_1, tolerance_map_A, tol_mx, tm_A, np_A); - Tolerance_map tolerance_map_B = get(Vertex_property_tag(), tm_A); - internal::assign_tolerance_with_local_edge_length_bound(border_vertices_2, tolerance_map_B, tol_mx, tm_B, np_B); + internal::assign_tolerance_with_local_edge_length_bound(border_vertices_A, tolerance_map_A, tol_mx, tm_A, np_A); + Tolerance_map tolerance_map_B = get(Vertex_property_tag(), tm_B); + internal::assign_tolerance_with_local_edge_length_bound(border_vertices_B, tolerance_map_B, tol_mx, tm_B, np_B); - return internal::snap_non_conformal(border_vertices_1, tm_A, tolerance_map_A, - border_vertices_2, tm_B, tolerance_map_B, + return internal::snap_non_conformal(border_vertices_A, tm_A, tolerance_map_A, + border_vertices_B, tm_B, tolerance_map_B, false /*no self snapping*/, np_A, np_B); } diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/remove_degeneracies.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/remove_degeneracies.h deleted file mode 100644 index b6f868bef14..00000000000 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/remove_degeneracies.h +++ /dev/null @@ -1,709 +0,0 @@ -// Copyright (c) 2019 GeometryFactory (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) : Sebastien Loriot, -// Mael Rouxel-Labbé - -#ifndef CGAL_POLYGON_MESH_PROCESSING_REMOVE_DEGENERACIES_H -#define CGAL_POLYGON_MESH_PROCESSING_REMOVE_DEGENERACIES_H - -#include - -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include -#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES -#include -#include -#endif - -namespace CGAL { -namespace Polygon_mesh_processing { -namespace internal { - -template -std::array::halfedge_descriptor, 2> -is_badly_shaped(const typename boost::graph_traits::face_descriptor f, - TriangleMesh& tmesh, - const VPM& vpm, - const ECM& ecm, - const Traits& gt, - const double cap_threshold, // angle over 160° ==> cap - const double needle_threshold, // longest edge / shortest edge over this ratio ==> needle - const double collapse_length_threshold) // max length of edges allowed to be collapsed -{ - namespace PMP = CGAL::Polygon_mesh_processing; - - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - - const halfedge_descriptor null_h = boost::graph_traits::null_halfedge(); - - halfedge_descriptor res = PMP::is_needle_triangle_face(f, tmesh, needle_threshold, - parameters::vertex_point_map(vpm) - .geom_traits(gt)); - if(res != null_h && !get(ecm, edge(res, tmesh))) - { - // don't want to collapse edges that are too large - if(collapse_length_threshold == 0 || - edge_length(res, tmesh, parameters::vertex_point_map(vpm).geom_traits(gt)) <= collapse_length_threshold) - { - return make_array(res, null_h); - } - } - else // let's not make it possible to have a face be both a cap and a needle (for now) - { - res = PMP::is_cap_triangle_face(f, tmesh, cap_threshold, parameters::vertex_point_map(vpm).geom_traits(gt)); - if(res != null_h && !get(ecm, edge(res, tmesh))) - return make_array(null_h, res); - } - - return make_array(null_h, null_h); -} - -template -void collect_badly_shaped_triangles(const typename boost::graph_traits::face_descriptor f, - TriangleMesh& tmesh, - const VPM& vpm, - const ECM& ecm, - const Traits& gt, - const double cap_threshold, // angle over this threshold (as a cosine) ==> cap - const double needle_threshold, // longest edge / shortest edge over this ratio ==> needle - const double collapse_length_threshold, // max length of edges allowed to be collapsed - EdgeContainer& edges_to_collapse, - EdgeContainer& edges_to_flip) -{ - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - - std::array res = is_badly_shaped(f, tmesh, vpm, ecm, gt, cap_threshold, - needle_threshold, collapse_length_threshold); - - if(res[0] != boost::graph_traits::null_halfedge()) - { -#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES - std::cout << "add new needle: " << edge(res[0], tmesh) << std::endl; -#endif - edges_to_collapse.insert(edge(res[0], tmesh)); - } - else // let's not make it possible to have a face be both a cap and a needle (for now) - { - if(res[1] != boost::graph_traits::null_halfedge()) - { -#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES - std::cout << "add new cap: " << edge(res[1],tmesh) << std::endl; -#endif - edges_to_flip.insert(edge(res[1], tmesh)); - } - } -} - -/* -// Following Ronfard et al. 96 we look at variation of the normal after the collapse -// the collapse must be topologically valid -template -bool is_collapse_geometrically_valid(typename boost::graph_traits::halfedge_descriptor h, - const TriangleMesh& tmesh, - const NamedParameters& np) -{ - using CGAL::parameters::choose_parameter; - using CGAL::parameters::get_parameter; - - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - typedef typename GetVertexPointMap::const_type VPM; - typedef typename boost::property_traits::reference Point_ref; - typedef typename GetGeomTraits::type Traits; - - VPM vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), - get_const_property_map(vertex_point, tmesh)); - Traits gt = choose_parameter(get_parameter(np, internal_np::geom_traits)); - -/// @todo handle boundary edges - - h = opposite(h, tmesh); // Euler::collapse edge keeps the target and removes the source - - // source is kept, target is removed - CGAL_assertion(target(h, tmesh) == vertex_removed); - Point_ref kept = get(vpm, source(h, tmesh)); - Point_ref removed= get(vpm, target(h, tmesh)); - - // consider triangles incident to the vertex removed - halfedge_descriptor stop = prev(opposite(h, tmesh), tmesh); - halfedge_descriptor hi = opposite(next(h, tmesh), tmesh); - - std::vector triangles; - while(hi != stop) - { - if(!is_border(hi, tmesh)) - { - Point_ref a = get(vpm, target(next(hi, tmesh), tmesh)); - Point_ref b = get(vpm, source(hi, tmesh)); - - //ack a-b-point_remove and a-b-point_kept has a compatible orientation - /// @todo use a predicate - typename Traits::Vector_3 n1 = gt.construct_cross_product_vector_3_object()(removed-a, b-a); - typename Traits::Vector_3 n2 = gt.construct_cross_product_vector_3_object()(kept-a, b-a); - if(gt.compute_scalar_product_3_object()(n1, n2) <= 0) - return false; - } - - hi = opposite(next(hi, tmesh), tmesh); - } - - return true; -} -*/ - -template -boost::optional get_collapse_volume(typename boost::graph_traits::halfedge_descriptor h, - const TriangleMesh& tmesh, - const VPM& vpm, - const Traits& gt) -{ - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - - typedef typename boost::property_traits::reference Point_ref; - typedef typename Traits::Vector_3 Vector_3; - - const typename Traits::Point_3 origin(ORIGIN); - -/// @todo handle boundary edges - - h = opposite(h, tmesh); // Euler::collapse edge keeps the target and removes the source - - // source is kept, target is removed - Point_ref kept = get(vpm, source(h, tmesh)); - Point_ref removed= get(vpm, target(h, tmesh)); - - // init volume with incident triangles (reversed orientation - double delta_vol = volume(removed, kept, get(vpm, target(next(h, tmesh), tmesh)), origin) + - volume(kept, removed, get(vpm, target(next(opposite(h, tmesh), tmesh), tmesh)), origin); - - // consider triangles incident to the vertex removed - halfedge_descriptor stop = prev(opposite(h, tmesh), tmesh); - halfedge_descriptor hi = opposite(next(h, tmesh), tmesh); - - std::vector triangles; - while(hi != stop) - { - if(!is_border(hi, tmesh)) - { - Point_ref a = get(vpm, target(next(hi, tmesh), tmesh)); - Point_ref b = get(vpm, source(hi, tmesh)); - - //ack a-b-point_remove and a-b-point_kept has a compatible orientation - /// @todo use a predicate - Vector_3 v_ab = gt.construct_vector_3_object()(a, b); - Vector_3 v_ar = gt.construct_vector_3_object()(a, removed); - Vector_3 v_ak = gt.construct_vector_3_object()(a, kept); - - Vector_3 n1 = gt.construct_cross_product_vector_3_object()(v_ar, v_ab); - Vector_3 n2 = gt.construct_cross_product_vector_3_object()(v_ak, v_ab); - if(gt.compute_scalar_product_3_object()(n1, n2) <= 0) - return boost::none; - - delta_vol += volume(b, a, removed, origin) + volume(a, b, kept, origin); // opposite orientation - } - - hi = opposite(next(hi, tmesh), tmesh); - } - - return CGAL::abs(delta_vol); -} - -template -typename boost::graph_traits::halfedge_descriptor -get_best_edge_orientation(typename boost::graph_traits::edge_descriptor e, - const TriangleMesh& tmesh, - const VPM& vpm, - const VCM& vcm, - const Traits& gt) -{ - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - - halfedge_descriptor h = halfedge(e, tmesh), ho = opposite(h, tmesh); - - CGAL_assertion(!get(vcm, source(h, tmesh)) || !get(vcm, target(h, tmesh))); - - boost::optional dv1 = get_collapse_volume(h, tmesh, vpm, gt); - boost::optional dv2 = get_collapse_volume(ho, tmesh, vpm, gt); - - // the resulting point of the collapse of a halfedge is the target of the halfedge before collapse - if(get(vcm, source(h, tmesh))) - return dv2 != boost::none ? ho - : boost::graph_traits::null_halfedge(); - - if(get(vcm, target(h, tmesh))) - return dv1 != boost::none ? h - : boost::graph_traits::null_halfedge(); - - if(dv1 != boost::none) - { - if(dv2 != boost::none) - return (*dv1 < *dv2) ? h : ho; - - return h; - } - - if(dv2 != boost::none) - return ho; - - return boost::graph_traits::null_halfedge(); -} - -// adapted from triangulate_faces -template -bool should_flip(typename boost::graph_traits::edge_descriptor e, - const TriangleMesh& tmesh, - const VPM& vpm, - const Traits& gt) -{ - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - - typedef typename boost::property_traits::reference Point_ref; - typedef typename Traits::Vector_3 Vector_3; - - CGAL_precondition(!is_border(e, tmesh)); - - halfedge_descriptor h = halfedge(e, tmesh); - - Point_ref p0 = get(vpm, target(h, tmesh)); - Point_ref p1 = get(vpm, target(next(h, tmesh), tmesh)); - Point_ref p2 = get(vpm, source(h, tmesh)); - Point_ref p3 = get(vpm, target(next(opposite(h, tmesh), tmesh), tmesh)); - - /* Chooses the diagonal that will split the quad in two triangles that maximize - * the scalar product of of the un-normalized normals of the two triangles. - * The lengths of the un-normalized normals (computed using cross-products of two vectors) - * are proportional to the area of the triangles. - * Maximize the scalar product of the two normals will avoid skinny triangles, - * and will also taken into account the cosine of the angle between the two normals. - * In particular, if the two triangles are oriented in different directions, - * the scalar product will be negative. - */ - -// CGAL::cross_product(p2-p1, p3-p2) * CGAL::cross_product(p0-p3, p1-p0); -// CGAL::cross_product(p1-p0, p1-p2) * CGAL::cross_product(p3-p2, p3-p0); - - const Vector_3 v01 = gt.construct_vector_3_object()(p0, p1); - const Vector_3 v12 = gt.construct_vector_3_object()(p1, p2); - const Vector_3 v23 = gt.construct_vector_3_object()(p2, p3); - const Vector_3 v30 = gt.construct_vector_3_object()(p3, p0); - - const double p1p3 = gt.compute_scalar_product_3_object()( - gt.construct_cross_product_vector_3_object()(v12, v23), - gt.construct_cross_product_vector_3_object()(v30, v01)); - - const Vector_3 v21 = gt.construct_opposite_vector_3_object()(v12); - const Vector_3 v03 = gt.construct_opposite_vector_3_object()(v30); - - const double p0p2 = gt.compute_scalar_product_3_object()( - gt.construct_cross_product_vector_3_object()(v01, v21), - gt.construct_cross_product_vector_3_object()(v23, v03)); - - return p0p2 <= p1p3; -} - -} // namespace internal - -namespace experimental { - -// @todo check what to use as priority queue with removable elements, set might not be optimal -template -bool remove_almost_degenerate_faces(const FaceRange& face_range, - TriangleMesh& tmesh, - const double cap_threshold, - const double needle_threshold, - const double collapse_length_threshold, - const NamedParameters& np) -{ - using CGAL::parameters::choose_parameter; - using CGAL::parameters::get_parameter; - - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - typedef typename boost::graph_traits::edge_descriptor edge_descriptor; - typedef typename boost::graph_traits::face_descriptor face_descriptor; - - typedef Constant_property_map Default_VCM; - typedef typename internal_np::Lookup_named_param_def::type VCM; - VCM vcm_np = choose_parameter(get_parameter(np, internal_np::vertex_is_constrained), Default_VCM(false)); - - typedef Constant_property_map Default_ECM; - typedef typename internal_np::Lookup_named_param_def::type ECM; - ECM ecm = choose_parameter(get_parameter(np, internal_np::edge_is_constrained), Default_ECM(false)); - - typedef typename GetVertexPointMap::const_type VPM; - VPM vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), - get_const_property_map(vertex_point, tmesh)); - - typedef typename GetGeomTraits::type Traits; - Traits gt = choose_parameter(get_parameter(np, internal_np::geom_traits)); - - // Vertex property map that combines the VCM and the fact that extremities of a constrained edge should be constrained - typedef CGAL::dynamic_vertex_property_t Vertex_property_tag; - typedef typename boost::property_map::type DVCM; - DVCM vcm = get(Vertex_property_tag(), tmesh); - - for(face_descriptor f : face_range) - { - if(f == boost::graph_traits::null_face()) - continue; - - for(halfedge_descriptor h : CGAL::halfedges_around_face(halfedge(f, tmesh), tmesh)) - { - if(get(ecm, edge(h, tmesh))) - { - put(vcm, source(h, tmesh), true); - put(vcm, target(h, tmesh), true); - } - else if(get(vcm_np, target(h, tmesh))) - { - put(vcm, target(h, tmesh), true); - } - } - } - - // Start the process of removing bad elements - std::set edges_to_collapse; - std::set edges_to_flip; - - // @todo could probably do something a bit better by looping edges, consider the incident faces - // f1 / f2 and look at f1 if f1 next_edges_to_collapse; - std::set next_edges_to_flip; - - // treat needles -#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA - int kk=0; - std::ofstream(std::string("tmp/n-00000.off")) << tmesh; -#endif - while(!edges_to_collapse.empty()) - { - edge_descriptor e = *edges_to_collapse.begin(); - edges_to_collapse.erase(edges_to_collapse.begin()); - - CGAL_assertion(!get(ecm, e)); - - if(get(vcm, source(e, tmesh)) && get(vcm, target(e, tmesh))) - continue; - -#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES - std::cout << " treat needle: " << e << " (" << tmesh.point(source (e, tmesh)) - << " --- " << tmesh.point(target(e, tmesh)) << ")" << std::endl; -#endif - if(CGAL::Euler::does_satisfy_link_condition(e, tmesh)) - { - // the following edges are removed by the collapse - halfedge_descriptor h = halfedge(e, tmesh); - CGAL_assertion(!is_border(h, tmesh)); // because extracted from a face - - std::array nc = - internal::is_badly_shaped(face(h, tmesh), tmesh, vpm, ecm, gt, - cap_threshold, needle_threshold, collapse_length_threshold); - - if(nc[0] != h) - { -#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES - std::cerr << "Warning: Needle criteria no longer verified " << tmesh.point(source(e, tmesh)) << " " - << tmesh.point(target(e, tmesh)) << std::endl; -#endif - // the opposite edge might also have been inserted in the set and might still be a needle - h = opposite(h, tmesh); - if(is_border(h, tmesh)) - continue; - - nc = internal::is_badly_shaped(face(h, tmesh), tmesh, vpm, ecm, gt, - cap_threshold, needle_threshold, - collapse_length_threshold); - if(nc[0] != h) - continue; - } - - for(int i=0; i<2; ++i) - { - if(!is_border(h, tmesh)) - { - edge_descriptor pe = edge(prev(h, tmesh), tmesh); - edges_to_flip.erase(pe); - next_edges_to_collapse.erase(pe); - edges_to_collapse.erase(pe); - } - - h = opposite(h, tmesh); - } - - // pick the orientation of edge to keep the vertex minimizing the volume variation - h = internal::get_best_edge_orientation(e, tmesh, vpm, vcm, gt); - - if(h == boost::graph_traits::null_halfedge()) - { -#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES - std::cerr << "Warning: geometrically invalid edge collapse! " - << tmesh.point(source(e, tmesh)) << " " - << tmesh.point(target(e, tmesh)) << std::endl; -#endif - next_edges_to_collapse.insert(e); - continue; - } - - edges_to_flip.erase(e); - next_edges_to_collapse.erase(e); // for edges added in faces incident to a vertex kept after a collapse -#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA - std::cerr << " " << kk << " -- Collapsing " << tmesh.point(source(h, tmesh)) << " " - << tmesh.point(target(h, tmesh)) << std::endl; -#endif - // moving to the midpoint is not a good idea. On a circle for example you might endpoint with - // a bad geometry because you iteratively move one point - // auto mp = midpoint(tmesh.point(source(h, tmesh)), tmesh.point(target(h, tmesh))); - - vertex_descriptor v = Euler::collapse_edge(edge(h, tmesh), tmesh); - - //tmesh.point(v) = mp; - // examine all faces incident to the vertex kept - for(halfedge_descriptor hv : halfedges_around_target(v, tmesh)) - { - if(!is_border(hv, tmesh)) - { - internal::collect_badly_shaped_triangles(face(hv, tmesh), tmesh, vpm, ecm, gt, - cap_threshold, needle_threshold, collapse_length_threshold, - edges_to_collapse, edges_to_flip); - } - } - -#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA - std::string nb = std::to_string(++kk); - if(kk<10) nb = std::string("0")+nb; - if(kk<100) nb = std::string("0")+nb; - if(kk<1000) nb = std::string("0")+nb; - if(kk<10000) nb = std::string("0")+nb; - std::ofstream(std::string("tmp/n-")+nb+std::string(".off")) << tmesh; -#endif - something_was_done = true; - } - else - { -#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES - std::cerr << "Warning: uncollapsable edge! " << tmesh.point(source(e, tmesh)) << " " - << tmesh.point(target(e, tmesh)) << std::endl; -#endif - next_edges_to_collapse.insert(e); - } - } - - // treat caps -#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA - kk=0; - std::ofstream(std::string("tmp/c-000.off")) << tmesh; -#endif - while(!edges_to_flip.empty()) - { - edge_descriptor e = *edges_to_flip.begin(); - edges_to_flip.erase(edges_to_flip.begin()); - - CGAL_assertion(!get(ecm, e)); - - if(get(vcm, source(e, tmesh)) && get(vcm, target(e, tmesh))) - continue; - -#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES - std::cout << "treat cap: " << e << " (" << tmesh.point(source(e, tmesh)) - << " --- " << tmesh.point(target(e, tmesh)) << ")" << std::endl; -#endif - - halfedge_descriptor h = halfedge(e, tmesh); - std::array nc = internal::is_badly_shaped(face(h, tmesh), tmesh, vpm, ecm, gt, - cap_threshold, needle_threshold, - collapse_length_threshold); - // First check the triangle is still a cap - if(nc[1] != h) - { -#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES - std::cerr << "Warning: Cap criteria no longer verified " << tmesh.point(source(e, tmesh)) << " --- " - << tmesh.point(target(e, tmesh)) << std::endl; -#endif - // the opposite edge might also have been inserted in the set and might still be a cap - h = opposite(h, tmesh); - if(is_border(h, tmesh)) - continue; - - nc = internal::is_badly_shaped(face(h, tmesh), tmesh, vpm, ecm, gt, - cap_threshold, needle_threshold, collapse_length_threshold); - if(nc[1] != h) - continue; - } - - // special case on the border - if(is_border(opposite(h, tmesh), tmesh)) - { - // remove the triangle - edges_to_flip.erase(edge(prev(h, tmesh), tmesh)); - edges_to_flip.erase(edge(next(h, tmesh), tmesh)); - next_edges_to_collapse.erase(edge(prev(h, tmesh), tmesh)); - next_edges_to_collapse.erase(edge(next(h, tmesh), tmesh)); - Euler::remove_face(h, tmesh); - something_was_done = true; - continue; - } - - // condition for the flip to be valid (the edge to be created does not already exist) - if(!halfedge(target(next(h, tmesh), tmesh), - target(next(opposite(h, tmesh), tmesh), tmesh), tmesh).second) - { - - if(!internal::should_flip(e, tmesh, vpm, gt)) - { -#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES - std::cout << "Flipping prevented: not the best diagonal" << std::endl; -#endif - next_edges_to_flip.insert(e); - continue; - } - -#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES - std::cout << "Flipping" << std::endl; -#endif -#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA - std::cerr << "step " << kk << "\n"; - std::cerr << " Flipping " << tmesh.point(source(h, tmesh)) << " " - << tmesh.point(target(h, tmesh)) << std::endl; -#endif - Euler::flip_edge(h, tmesh); - CGAL_assertion(edge(h, tmesh) == e); - - // handle face updates - for(int i=0; i<2; ++i) - { - CGAL_assertion(!is_border(h, tmesh)); - std::array nc = - internal::is_badly_shaped(face(h, tmesh), tmesh, vpm, ecm, gt, - cap_threshold, needle_threshold, collapse_length_threshold); - - if(nc[1] != boost::graph_traits::null_halfedge()) - { - if(edge(nc[1], tmesh) != e) - next_edges_to_flip.insert(edge(nc[1], tmesh)); - } - else - { - if(nc[0] != boost::graph_traits::null_halfedge()) - { - next_edges_to_collapse.insert(edge(nc[0], tmesh)); - } - } - h = opposite(h, tmesh); - } - something_was_done = true; - } -#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES - else - { - std::cerr << "Warning: unflippable edge! " << tmesh.point(source(h, tmesh)) << " --- " - << tmesh.point(target(h, tmesh)) << std::endl; - next_edges_to_flip.insert(e); - } -#endif -#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA - std::string nb = std::to_string(++kk); - if(kk<10) nb = std::string("0")+nb; - if(kk<100) nb = std::string("0")+nb; - if(kk<1000) nb = std::string("0")+nb; - if(kk<10000) nb = std::string("0")+nb; - std::ofstream(std::string("tmp/c-")+nb+std::string(".off")) << tmesh; -#endif - } - - std::swap(edges_to_collapse, next_edges_to_collapse); - std::swap(edges_to_flip, next_edges_to_flip); - - if(!something_was_done) - return false; - } - - return false; -} - -template -bool remove_almost_degenerate_faces(const FaceRange& face_range, - TriangleMesh& tmesh, - const double cap_threshold, - const double needle_threshold, - const double collapse_length_threshold) -{ - return remove_almost_degenerate_faces(face_range, tmesh, - cap_threshold, needle_threshold, collapse_length_threshold, - CGAL::parameters::all_default()); -} - -template -bool remove_almost_degenerate_faces(TriangleMesh& tmesh, - const double cap_threshold, - const double needle_threshold, - const double collapse_length_threshold, - const CGAL_PMP_NP_CLASS& np) -{ - return remove_almost_degenerate_faces(faces(tmesh), tmesh, cap_threshold, needle_threshold, - collapse_length_threshold, np); -} - -template -bool remove_almost_degenerate_faces(TriangleMesh& tmesh, - const double cap_threshold, - const double needle_threshold, - const double collapse_length_threshold) -{ - return remove_almost_degenerate_faces(faces(tmesh), tmesh, - cap_threshold, needle_threshold, collapse_length_threshold, - CGAL::parameters::all_default()); -} - -} // namespace experimental -} // namespace Polygon_mesh_processing -} // namespace CGAL - -#endif // CGAL_POLYGON_MESH_PROCESSING_REMOVE_DEGENERACIES_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/manifoldness.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/manifoldness.h new file mode 100644 index 00000000000..5ffa00ffeda --- /dev/null +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/manifoldness.h @@ -0,0 +1,461 @@ +// Copyright (c) 2015-2019 GeometryFactory (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) : Sebastien Loriot, +// Mael Rouxel-Labbé +// +#ifndef CGAL_POLYGON_MESH_PROCESSING_MANIFOLDNESS_H +#define CGAL_POLYGON_MESH_PROCESSING_MANIFOLDNESS_H + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace CGAL { +namespace Polygon_mesh_processing { + +/// \ingroup PMP_repairing_grp +/// checks whether a vertex of a polygon mesh is non-manifold. +/// +/// @tparam PolygonMesh a model of `HalfedgeListGraph` +/// +/// @param v a vertex of `pm` +/// @param pm a triangle mesh containing `v` +/// +/// \warning This function has linear runtime with respect to the size of the mesh. +/// +/// \sa `duplicate_non_manifold_vertices()` +/// +/// \return `true` if the vertex is non-manifold, `false` otherwise. +template +bool is_non_manifold_vertex(typename boost::graph_traits::vertex_descriptor v, + const PolygonMesh& pm) +{ + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + + typedef CGAL::dynamic_halfedge_property_t Halfedge_property_tag; + typedef typename boost::property_map::const_type Visited_halfedge_map; + + // Dynamic pmaps do not have default initialization values (yet) + Visited_halfedge_map visited_halfedges = get(Halfedge_property_tag(), pm); + for(halfedge_descriptor h : halfedges(pm)) + put(visited_halfedges, h, false); + + std::size_t incident_null_faces_counter = 0; + for(halfedge_descriptor h : halfedges_around_target(v, pm)) + { + put(visited_halfedges, h, true); + if(CGAL::is_border(h, pm)) + ++incident_null_faces_counter; + } + + if(incident_null_faces_counter > 1) + { + // The vertex is the sole connection between two connected components --> non-manifold + return true; + } + + for(halfedge_descriptor h : halfedges(pm)) + { + if(v == target(h, pm)) + { + // Haven't seen that halfedge yet ==> more than one umbrella incident to 'v' ==> non-manifold + if(!get(visited_halfedges, h)) + return true; + } + } + + return false; +} + +namespace internal { + +template +struct Vertex_collector +{ + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + + bool has_old_vertex(const vertex_descriptor v) const { return collections.count(v) != 0; } + void tag_old_vertex(const vertex_descriptor v) + { + CGAL_precondition(!has_old_vertex(v)); + collections[v]; + } + + void collect_vertices(vertex_descriptor v1, vertex_descriptor v2) + { + std::vector& verts = collections[v1]; + if(verts.empty()) + verts.push_back(v1); + verts.push_back(v2); + } + + template + void dump(OutputIterator out) + { + typedef std::pair > Pair_type; + for(const Pair_type& p : collections) + *out++ = p.second; + } + + void dump(Emptyset_iterator) { } + + std::map > collections; +}; + +template +typename boost::graph_traits::vertex_descriptor +create_new_vertex_for_sector(typename boost::graph_traits::halfedge_descriptor sector_begin_h, + typename boost::graph_traits::halfedge_descriptor sector_last_h, + PolygonMesh& pm, + const VPM& vpm, + const ConstraintMap& cmap) +{ + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + + vertex_descriptor old_vd = target(sector_begin_h, pm); + vertex_descriptor new_vd = add_vertex(pm); + put(vpm, new_vd, get(vpm, old_vd)); + + put(cmap, new_vd, true); + + set_halfedge(new_vd, sector_begin_h, pm); + halfedge_descriptor h = sector_begin_h; + do + { + set_target(h, new_vd, pm); + + if(h == sector_last_h) + break; + else + h = prev(opposite(h, pm), pm); + } + while(h != sector_begin_h); // for safety + CGAL_assertion(h != sector_begin_h); + + return new_vd; +} + +template +std::size_t make_umbrella_manifold(typename boost::graph_traits::halfedge_descriptor h, + PolygonMesh& pm, + internal::Vertex_collector& dmap, + const NamedParameters& np) +{ + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + + using parameters::get_parameter; + using parameters::choose_parameter; + + typedef typename GetVertexPointMap::type VertexPointMap; + VertexPointMap vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), + get_property_map(vertex_point, pm)); + + typedef typename internal_np::Lookup_named_param_def // default (no constraint pmap) + >::type VerticesMap; + VerticesMap cmap = choose_parameter(get_parameter(np, internal_np::vertex_is_constrained), + Constant_property_map(false)); + + std::size_t nb_new_vertices = 0; + + vertex_descriptor old_v = target(h, pm); + put(cmap, old_v, true); // store the duplicates + + // count the number of borders + int border_counter = 0; + halfedge_descriptor ih = h, done = ih, border_h = h; + do + { + if(is_border(ih, pm)) + { + border_h = ih; + ++border_counter; + } + + ih = prev(opposite(ih, pm), pm); + } + while(ih != done); + + bool is_non_manifold_within_umbrella = (border_counter > 1); + if(!is_non_manifold_within_umbrella) + { + const bool first_time_meeting_v = !dmap.has_old_vertex(old_v); + if(first_time_meeting_v) + { + // The star is manifold, so if it is the first time we have met that vertex, + // there is nothing to do, we just keep the same vertex. + set_halfedge(old_v, h, pm); // to ensure halfedge(old_v, pm) stays valid + dmap.tag_old_vertex(old_v); // so that we know we have met old_v already, next time, we'll have to duplicate + } + else + { + // This is not the canonical star associated to 'v'. + // Create a new vertex, and move the whole star to that new vertex + halfedge_descriptor last_h = opposite(next(h, pm), pm); + vertex_descriptor new_v = create_new_vertex_for_sector(h, last_h, pm, vpm, cmap); + dmap.collect_vertices(old_v, new_v); + nb_new_vertices = 1; + } + } + // if there is more than one sector, look at each sector and split them away from the main one + else + { + // the first manifold sector, described by two halfedges + halfedge_descriptor sector_start_h = border_h; + CGAL_assertion(is_border(border_h, pm)); + + bool should_stop = false; + bool is_main_sector = true; + do + { + CGAL_assertion(is_border(sector_start_h, pm)); + + // collect the sector and split it away if it must be + halfedge_descriptor sector_last_h = sector_start_h; + do + { + halfedge_descriptor next_h = prev(opposite(sector_last_h, pm), pm); + + if(is_border(next_h, pm)) + break; + + sector_last_h = next_h; + } + while(sector_last_h != sector_start_h); + CGAL_assertion(!is_border(sector_last_h, pm)); + CGAL_assertion(sector_last_h != sector_start_h); + + halfedge_descriptor next_start_h = prev(opposite(sector_last_h, pm), pm); + + // there are multiple CCs incident to this particular vertex, and we should create a new vertex + // if it's not the first umbrella around 'old_v' or not the first sector, but only not if it's + // both the first umbrella and first sector. + bool must_create_new_vertex = (!is_main_sector || dmap.has_old_vertex(old_v)); + + // In any case, we must set up the next pointer correctly + set_next(sector_start_h, opposite(sector_last_h, pm), pm); + + if(must_create_new_vertex) + { + vertex_descriptor new_v = create_new_vertex_for_sector(sector_start_h, sector_last_h, pm, vpm, cmap); + dmap.collect_vertices(old_v, new_v); + ++nb_new_vertices; + } + else + { + // We are in the first sector and first star, ensure that halfedge(old_v, pm) stays valid + set_halfedge(old_v, sector_start_h, pm); + } + + is_main_sector = false; + sector_start_h = next_start_h; + should_stop = (sector_start_h == border_h); + } + while(!should_stop); + } + + return nb_new_vertices; +} + +} // end namespace internal + +/// \ingroup PMP_repairing_grp +/// collects the non-manifold vertices (if any) present in the mesh. A non-manifold vertex `v` is returned +/// via one incident halfedge `h` such that `target(h, pm) = v` for all the umbrellas that `v` apppears in +/// (an umbrella being the set of faces incident to all the halfedges reachable by walking around `v` +/// using `hnext = prev(opposite(h, pm), pm)`, starting from `h`). +/// +/// @tparam PolygonMesh a model of `HalfedgeListGraph` +/// @tparam OutputIterator a model of `OutputIterator` holding objects of type +/// `boost::graph_traits::%halfedge_descriptor` +/// +/// @param pm a triangle mesh +/// @param out the output iterator that collects halfedges incident to `v` +/// +/// \sa `is_non_manifold_vertex()` +/// \sa `duplicate_non_manifold_vertices()` +/// +/// \return the output iterator. +template +OutputIterator non_manifold_vertices(const PolygonMesh& pm, + OutputIterator out) +{ + // Non-manifoldness can appear either: + // - if 'pm' is pinched at a vertex. While traversing the incoming halfedges at this vertex, + // we will meet strictly more than one border halfedge. + // - if there are multiple umbrellas around a vertex. In that case, we will find a non-visited + // halfedge that has for target a vertex that is already visited. + + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + + typedef CGAL::dynamic_vertex_property_t Vertex_bool_tag; + typedef typename boost::property_map::const_type Known_manifold_vertex_map; + typedef CGAL::dynamic_vertex_property_t Vertex_halfedge_tag; + typedef typename boost::property_map::const_type Visited_vertex_map; + typedef CGAL::dynamic_halfedge_property_t Halfedge_property_tag; + typedef typename boost::property_map::const_type Visited_halfedge_map; + + Known_manifold_vertex_map known_nm_vertices = get(Vertex_bool_tag(), pm); + Visited_vertex_map visited_vertices = get(Vertex_halfedge_tag(), pm); + Visited_halfedge_map visited_halfedges = get(Halfedge_property_tag(), pm); + + halfedge_descriptor null_h = boost::graph_traits::null_halfedge(); + + // Dynamic pmaps do not have default initialization values (yet) + for(vertex_descriptor v : vertices(pm)) + { + put(known_nm_vertices, v, false); + put(visited_vertices, v, null_h); + } + for(halfedge_descriptor h : halfedges(pm)) + put(visited_halfedges, h, false); + + for(halfedge_descriptor h : halfedges(pm)) + { + // If 'h' is not visited yet, we walk around the target of 'h' and mark these + // halfedges as visited. Thus, if we are here and the target is already marked as visited, + // it means that the vertex is non manifold. + if(!get(visited_halfedges, h)) + { + put(visited_halfedges, h, true); + bool is_non_manifold = false; + + vertex_descriptor v = target(h, pm); + + if(get(visited_vertices, v) != null_h) // already seen this vertex, but not from this star + { + is_non_manifold = true; + + // if this is the second time we visit that vertex and the first star was manifold, we have + // never reported the first star, but we must now + if(!get(known_nm_vertices, v)) + *out++ = get(visited_vertices, v); // that's a halfedge of the first star we've seen 'v' in + } + else + { + // first time we meet this vertex, just mark it so, and keep the halfedge we found the vertex with in memory + put(visited_vertices, v, h); + } + + // While walking the star of this halfedge, if we meet a border halfedge more than once, + // it means the mesh is pinched and we are also in the case of a non-manifold situation + halfedge_descriptor ih = h, done = ih; + int border_counter = 0; + do + { + put(visited_halfedges, ih, true); + if(is_border(ih, pm)) + ++border_counter; + + ih = prev(opposite(ih, pm), pm); + } + while(ih != done); + + if(border_counter > 1) + is_non_manifold = true; + + if(is_non_manifold) + { + *out++ = h; + put(known_nm_vertices, v, true); + } + } + } + + return out; +} + +/// \ingroup PMP_repairing_grp +/// duplicates all the non-manifold vertices of the input mesh. +/// +/// @tparam PolygonMesh a model of `HalfedgeListGraph` and `MutableHalfedgeGraph` +/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" +/// +/// @param pm the surface mesh to be repaired +/// @param np optional \ref pmp_namedparameters "Named Parameters" described below +/// +/// \cgalNamedParamsBegin +/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. +/// The type of this map is model of `ReadWritePropertyMap`. +/// If this parameter is omitted, an internal property map for +/// `CGAL::vertex_point_t` should be available in `PolygonMesh` +/// \cgalParamEnd +/// \cgalParamBegin{vertex_is_constrained_map} a writable property map with `vertex_descriptor` +/// as key and `bool` as `value_type`. `put(pmap, v, true)` will be called for each duplicated +/// vertices, as well as the original non-manifold vertex in the input mesh. +/// \cgalParamEnd +/// \cgalParamBegin{output_iterator} a model of `OutputIterator` with value type +/// `std::vector`. The first vertex of each vector is a non-manifold vertex +/// of the input mesh, followed by the new vertices that were created to fix this precise +/// non-manifold configuration. +/// \cgalParamEnd +/// \cgalNamedParamsEnd +/// +/// \return the number of vertices created. +template +std::size_t duplicate_non_manifold_vertices(PolygonMesh& pm, + const NamedParameters& np) +{ + using parameters::get_parameter; + using parameters::choose_parameter; + + typedef boost::graph_traits GT; + typedef typename GT::halfedge_descriptor halfedge_descriptor; + + typedef typename internal_np::Lookup_named_param_def::type Output_iterator; + + Output_iterator out = choose_parameter(get_parameter(np, internal_np::output_iterator), + Emptyset_iterator()); + + std::vector non_manifold_cones; + non_manifold_vertices(pm, std::back_inserter(non_manifold_cones)); + + internal::Vertex_collector dmap; + std::size_t nb_new_vertices = 0; + if(!non_manifold_cones.empty()) + { + for(halfedge_descriptor h : non_manifold_cones) + nb_new_vertices += internal::make_umbrella_manifold(h, pm, dmap, np); + + dmap.dump(out); + } + + return nb_new_vertices; +} + +template +std::size_t duplicate_non_manifold_vertices(PolygonMesh& pm) +{ + return duplicate_non_manifold_vertices(pm, parameters::all_default()); +} + +} // namespace Polygon_mesh_processing +} // namespace CGAL + +#endif // CGAL_POLYGON_MESH_PROCESSING_MANIFOLDNESS_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/orient_polygon_soup_extension.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/orient_polygon_soup_extension.h new file mode 100644 index 00000000000..f7fee2b3dc7 --- /dev/null +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/orient_polygon_soup_extension.h @@ -0,0 +1,196 @@ +// Copyright (c) 2019 GeometryFactory (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) : Sebastien Loriot and Maxime Gimeno + +#ifndef CGAL_ORIENT_POLYGON_SOUP_EXTENSION_H +#define CGAL_ORIENT_POLYGON_SOUP_EXTENSION_H + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#ifdef CGAL_LINKED_WITH_TBB +#include +#endif // CGAL_LINKED_WITH_TBB + + +namespace CGAL { + +namespace Polygon_mesh_processing { + +/*! + * \ingroup PMP_orientation_grp + * duplicates each point \a p at which the intersection + * of an infinitesimally small ball centered at \a p + * with the polygons incident to it is not a topological disk. + * + * @tparam PointRange a model of the concepts `RandomAccessContainer` + * and `BackInsertionSequence` whose `value_type` is the point type. + * @tparam PolygonRange a model of the concept `RandomAccessContainer` + * whose `value_type` is a model of the concept `RandomAccessContainer` + * whose `value_type` is `std::size_t`, and is also a model of `BackInsertionSequence`. + * + * @param points points of the soup of polygons. Some additional points might be pushed back to resolve + * non-manifoldness or non-orientability issues. + * @param polygons each element in the vector describes a polygon using the indices of the points in `points`. + * If needed the order of the indices of a polygon might be reversed. + * @return `false` if some points were duplicated, thus producing a self-intersecting surface mesh. + * @return `true` otherwise. + * @sa `orient_polygon_soup()` + */ +template +bool +duplicate_non_manifold_edges_in_polygon_soup(PointRange& points, + PolygonRange& polygons) +{ + std::size_t inital_nb_pts = points.size(); + typedef CGAL::Polygon_mesh_processing::internal:: + Polygon_soup_orienter Orienter; + + Orienter orienter(points, polygons); + orienter.fill_edge_map(); + // make edges to duplicate + for(std::size_t i1=0;i1 1) + orienter.set_edge_marked(i1,i2_and_pids.first,orienter.marked_edges); + orienter.duplicate_singular_vertices(); + + return inital_nb_pts==points.size(); +} + +/*! + * \ingroup PMP_orientation_grp + * orients each triangle of a triangle soup using the orientation of its + * closest non degenerate triangle in `tm_ref`. + * \tparam Concurrency_tag enables sequential versus parallel orientation. + Possible values are `Sequential_tag` (the default), + `Parallel_if_available_tag`, and `Parallel_tag`. + * \tparam PointRange a model of the concepts `RandomAccessContainer` + * and `BackInsertionSequence` whose value type is the point type. + * @tparam TriangleRange a model of the concept `RandomAccessContainer` + * whose `value_type` is a model of the concept `RandomAccessContainer` + * whose `value_type` is `std::size_t`and of size 3. + * @tparam TriangleMesh a model of `FaceListGraph` and `MutableFaceGraph` . + * + * \param tm_ref the reference triangle_mesh. + * \param points the points of the soup. + * \param triangles the triangles of the soup. + * @param np optional sequence of \ref pmp_namedparameters among the ones listed below + * + * \cgalNamedParamsBegin + * \cgalParamBegin{vertex_point_map} + * the property map with the points associated to the vertices of `tm_ref`. + * If this parameter is omitted, an internal property map for + * `CGAL::vertex_point_t` must be available in `TriangleMesh` + * \cgalParamEnd + * \cgalParamBegin{geom_traits} a geometric traits class instance. + * The traits class must provide the nested functor `Collinear_3` + * to check whether three points are collinear. + * \cgalParamEnd + * \cgalNamedParamsEnd + * + * \attention The types of points in `PointRange`, `geom_traits` and `vertex_point_map` must be the same. + */ + +template +void +orient_triangle_soup_with_reference_triangle_mesh( + const TriangleMesh& tm_ref, + PointRange& points, + TriangleRange& triangles, + const NamedParameters& np) +{ + namespace PMP = CGAL::Polygon_mesh_processing; + + typedef boost::graph_traits GrT; + typedef typename GrT::face_descriptor face_descriptor; + typedef typename PointRange::value_type Point_3; + typedef typename GetGeomTraits::type K; + typedef typename + GetVertexPointMap::const_type Vpm; + + Vpm vpm = parameters::choose_parameter(parameters::get_parameter(np, internal_np::vertex_point), + get_const_property_map(CGAL::vertex_point, tm_ref)); + + + typedef std::function Face_predicate; + Face_predicate is_not_deg = + [&tm_ref, np](face_descriptor f) + { + return !PMP::is_degenerate_triangle_face(f, tm_ref, np); + }; + + // build a tree filtering degenerate faces + typedef CGAL::AABB_face_graph_triangle_primitive Primitive; + typedef CGAL::AABB_traits Tree_traits; + + boost::filter_iterator + begin(is_not_deg, faces(tm_ref).begin(), faces(tm_ref).end()), + end(is_not_deg, faces(tm_ref).end(), faces(tm_ref).end()); + + CGAL::AABB_tree tree(begin, end, tm_ref, vpm); + + // now orient the faces + tree.build(); + tree.accelerate_distance_queries(); + auto process_facet = + [&points, &tree, &tm_ref, &triangles](std::size_t fid) { + const Point_3& p0 = points[triangles[fid][0]]; + const Point_3& p1 = points[triangles[fid][1]]; + const Point_3& p2 = points[triangles[fid][2]]; + const Point_3 mid = CGAL::centroid(p0, p1, p2); + std::pair pt_and_f = + tree.closest_point_and_primitive(mid); + auto face_ref_normal = PMP::compute_face_normal(pt_and_f.second, tm_ref); + if(face_ref_normal * cross_product(p1-p0, p2-p0) < 0) { + std::swap(triangles[fid][1], triangles[fid][2]); + } + }; + +#if !defined(CGAL_LINKED_WITH_TBB) + CGAL_static_assertion_msg (!(boost::is_convertible::value), + "Parallel_tag is enabled but TBB is unavailable."); +#else + if (boost::is_convertible::value) + tbb::parallel_for(std::size_t(0), triangles.size(), std::size_t(1), process_facet); + else +#endif + std::for_each( + boost::counting_iterator (0), + boost::counting_iterator (triangles.size()), + process_facet); +} + + +template +void +orient_triangle_soup_with_reference_triangle_mesh( + const TriangleMesh& tm_ref, + PointRange& points, + TriangleRange& triangles) +{ + orient_triangle_soup_with_reference_triangle_mesh(tm_ref, points, triangles, CGAL::parameters::all_default()); +} + +}}//end namespace CGAL::Polygon_mesh_processing +#endif // CGAL_ORIENT_POLYGON_SOUP_EXTENSION_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/orientation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/orientation.h index 447d0d0f069..f9e9a5d8bd5 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/orientation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/orientation.h @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -1337,6 +1338,206 @@ void orient_to_bound_a_volume(TriangleMesh& tm) { orient_to_bound_a_volume(tm, parameters::all_default()); } + +/*! + * \ingroup PMP_orientation_grp + * reverses the connected components of `tm` having compatible boundary cycles + * that could be merged if their orientation were made compatible, and stitches them. + * Connected components are examined by increasing number of faces. + * + * @tparam PolygonMesh a model of `MutableFaceGraph`, `HalfedgeListGraph` and `FaceListGraph`. + * @tparam NamedParameters a sequence of \ref pmp_namedparameters + * + * @param pm a surface mesh + * @param np optional sequence of \ref pmp_namedparameters among the ones listed below + * + * \cgalNamedParamsBegin + * \cgalParamBegin{vertex_point_map} + * the property map with the points associated to the vertices of `pm`. + * If this parameter is omitted, an internal property map for + * `CGAL::vertex_point_t` must be available in `PolygonMesh` + * \cgalParamEnd + * \cgalParamBegin{face_index_map} + * a property map containing an index for each face initialized from 0 to num_faces(pm). + * \cgalParamEnd + * \cgalParamBegin{maximum_number_of_faces} + * if not 0 (default), a connected component is considered reversible only + * if it has no more faces than the value given. Otherwise, it is always considered reversible. + * \cgalParamEnd + * \cgalNamedParamsEnd + */ +template +void merge_reversible_connected_components(PolygonMesh& pm, + const NamedParameters& np) +{ + typedef boost::graph_traits GrT; + typedef typename GrT::face_descriptor face_descriptor; + typedef typename GrT::halfedge_descriptor halfedge_descriptor; + + typedef typename GetVertexPointMap::const_type Vpm; + + typedef typename boost::property_traits::value_type Point_3; + Vpm vpm = parameters::choose_parameter(parameters::get_parameter(np, internal_np::vertex_point), + get_const_property_map(vertex_point, pm)); + + typedef std::size_t F_cc_id; + typedef std::size_t B_cc_id; + + + typedef typename CGAL::GetInitializedFaceIndexMap::const_type Fidmap; + Fidmap fim = CGAL::get_initialized_face_index_map(pm, np); + + typedef dynamic_face_property_t Face_property_tag; + typedef typename boost::property_map::type Face_cc_map; + Face_cc_map f_cc_ids = get(Face_property_tag(), pm); + F_cc_id nb_cc = connected_components(pm, f_cc_ids, parameters::face_index_map(fim)); + + std::vector nb_faces_per_cc(nb_cc, 0); + for (face_descriptor f : faces(pm)) + nb_faces_per_cc[ get(f_cc_ids, f) ]+=1; + + std::map< std::pair, std::vector > border_hedges_map; + std::vector border_hedges; + typedef typename boost::property_map >::type H_to_bcc_id; + H_to_bcc_id h_bcc_ids = get(dynamic_halfedge_property_t(), pm); + const B_cc_id base_value(-1); + const B_cc_id FILTERED_OUT(-2); + + // collect border halfedges + for (halfedge_descriptor h : halfedges(pm)) + if ( is_border(h, pm) ) + { + put(h_bcc_ids, h, base_value); + border_hedges.push_back(h); + } + + // compute the border cc id of all halfedges and mark those duplicated in their own cycle + B_cc_id bcc_id=0; + for (halfedge_descriptor h : border_hedges) + { + if (get(h_bcc_ids,h) == base_value) + { + typedef std::map< std::pair, halfedge_descriptor> Hmap; + Hmap hmap; + for (halfedge_descriptor hh : halfedges_around_face(h, pm)) + { + std::pair< typename Hmap::iterator, bool > insert_res = + hmap.insert( + std::make_pair( + make_sorted_pair(get(vpm, source(hh, pm)), + get(vpm, target(hh,pm))), hh) ); + if (insert_res.second) + put(h_bcc_ids, hh, bcc_id); + else + { + put(h_bcc_ids, hh, FILTERED_OUT); + put(h_bcc_ids, insert_res.first->second, FILTERED_OUT); + } + } + ++bcc_id; + } + } + + // fill endpoints -> hedges + for (halfedge_descriptor h : border_hedges) + { + if ( get(h_bcc_ids, h) != FILTERED_OUT) + border_hedges_map[std::make_pair(get(vpm, source(h, pm)), get(vpm, target(h, pm)))].push_back(h); + } + + // max nb of faces for a CC to be reversed + const std::size_t threshold = + parameters::choose_parameter( parameters::get_parameter(np, internal_np::maximum_number_of_faces), 0); + + std::vector border_cycle_to_ignore(bcc_id, false); + std::vector cycle_f_cc_id(bcc_id); + std::vector< std::vector > patch_neighbors(nb_cc); + + for (const auto& p : border_hedges_map) + { + const std::vector& hedges = p.second; + switch(hedges.size()) + { + case 1: + // isolated border hedge nothing to do + break; + case 2: + { + F_cc_id cc_id_0 = get(f_cc_ids, face(opposite(hedges[0], pm), pm)), + cc_id_1 = get(f_cc_ids, face(opposite(hedges[1], pm), pm)); + + if (cc_id_0!=cc_id_1) + { + cycle_f_cc_id[ get(h_bcc_ids, hedges[0]) ] = cc_id_0; + cycle_f_cc_id[ get(h_bcc_ids, hedges[1]) ] = cc_id_1; + // WARNING: we might have duplicates here but it is not important for our usage + patch_neighbors[cc_id_0].push_back(cc_id_1); + patch_neighbors[cc_id_1].push_back(cc_id_0); + break; + } + CGAL_FALLTHROUGH; + } + default: + for (halfedge_descriptor h : hedges) + border_cycle_to_ignore[get(h_bcc_ids, h)]=true; + } + } + + // sort the connected components with potential matches using their number + // of faces (sorted by decreasing number of faces) + std::set ccs_to_reverse; + std::vector reversible(nb_cc, false); + std::set< F_cc_id, std::function > queue( + [&nb_faces_per_cc](F_cc_id i, F_cc_id j) + {return nb_faces_per_cc[i]==nb_faces_per_cc[j] ? inb_faces_per_cc[j];} + ); + + for (B_cc_id i=0; i= nb_faces_per_cc[id])) + { + CGAL_assertion( nb_faces_per_cc[f_cc_id] >= nb_faces_per_cc[id] ); + ccs_to_reverse.insert(id); + reversible[id]=false; + queue.erase(id); + } + } + } + + // reverse ccs and stitches boundaries + std::vector faces_to_reverse; + for (face_descriptor f : faces(pm)) + if ( ccs_to_reverse.count( get(f_cc_ids, f) ) != 0 ) + faces_to_reverse.push_back(f); + + if ( !faces_to_reverse.empty() ) + { + reverse_face_orientations(faces_to_reverse, pm); + stitch_borders(pm, np); + } +} + +template +void merge_reversible_connected_components(PolygonMesh& pm) +{ + merge_reversible_connected_components(pm, parameters::all_default()); +} } // namespace Polygon_mesh_processing } // namespace CGAL #endif // CGAL_ORIENT_POLYGON_MESH_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h index 03a615956c1..84fc617c14c 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h @@ -257,7 +257,7 @@ void polygon_soup_to_polygon_mesh(const PointRange& points, const NamedParameters_PM& np_pm) { CGAL_precondition_msg(is_polygon_soup_a_polygon_mesh(polygons), - "Input soup needs to be a polygon mesh!"); + "Input soup needs to define a valid polygon mesh! See is_polygon_soup_a_polygon_mesh() for further information."); using parameters::choose_parameter; using parameters::get_parameter; diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h index db274f3d59c..127f6c1b31c 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h @@ -16,50 +16,16 @@ #include -#include -#include -#include -#include -#include -#include - -// headers for self-intersection removal -#include -#include -#include - -#include +#include +#include +#include #include -#include -#include #include -#include -#include +#include -#include - -#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG -#include -#include -#endif - -#include -#include -#include - -#include -#include -#include -#include -#include #include -#ifdef DOXYGEN_RUNNING -#define CGAL_PMP_NP_TEMPLATE_PARAMETERS NamedParameters -#define CGAL_PMP_NP_CLASS NamedParameters -#endif - namespace CGAL { namespace Polygon_mesh_processing { @@ -333,2899 +299,7 @@ std::size_t remove_connected_components_of_negligible_size(TriangleMesh& tmesh) return remove_connected_components_of_negligible_size(tmesh, parameters::all_default()); } -namespace debug{ - - template - std::ostream& dump_edge_neighborhood( - typename boost::graph_traits::edge_descriptor ed, - TriangleMesh& tmesh, - const VertexPointMap& vpmap, - std::ostream& out) - { - typedef boost::graph_traits GT; - typedef typename GT::halfedge_descriptor halfedge_descriptor; - typedef typename GT::vertex_descriptor vertex_descriptor; - typedef typename GT::face_descriptor face_descriptor; - - halfedge_descriptor h = halfedge(ed, tmesh); - - std::map vertices; - std::set faces; - int vindex=0; - for(halfedge_descriptor hd : halfedges_around_target(h, tmesh)) - { - if ( vertices.insert(std::make_pair(source(hd, tmesh), vindex)).second ) - ++vindex; - if (!is_border(hd, tmesh)) - faces.insert( face(hd, tmesh) ); - } - - h=opposite(h, tmesh); - for(halfedge_descriptor hd : halfedges_around_target(h, tmesh)) - { - if ( vertices.insert(std::make_pair(source(hd, tmesh), vindex)).second ) - ++vindex; - if (!is_border(hd, tmesh)) - faces.insert( face(hd, tmesh) ); - } - - std::vector ordered_vertices(vertices.size()); - typedef std::pair Pair_type; - for(const Pair_type& p : vertices) - ordered_vertices[p.second]=p.first; - - out << "OFF\n" << ordered_vertices.size() << " " << faces.size() << " 0\n"; - for(vertex_descriptor vd : ordered_vertices) - out << get(vpmap, vd) << "\n"; - for(face_descriptor fd : faces) - { - out << "3"; - h=halfedge(fd,tmesh); - for(halfedge_descriptor hd : halfedges_around_face(h, tmesh)) - out << " " << vertices[target(hd, tmesh)]; - out << "\n"; - } - return out; - } - - template - void dump_cc_faces(const FaceRange& cc_faces, const TriangleMesh& tm, std::ostream& output) - { - typedef typename boost::property_map::const_type Vpm; - typedef typename boost::property_traits::value_type Point_3; - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename boost::graph_traits::face_descriptor face_descriptor; - - Vpm vpm = get(boost::vertex_point, tm); - - int id=0; - std::map vids; - for(face_descriptor f : cc_faces) - { - if ( vids.insert( std::make_pair( target(halfedge(f, tm), tm), id) ).second ) ++id; - if ( vids.insert( std::make_pair( target(next(halfedge(f, tm), tm), tm), id) ).second ) ++id; - if ( vids.insert( std::make_pair( target(next(next(halfedge(f, tm), tm), tm), tm), id) ).second ) ++id; - } - output << std::setprecision(17); - output << "OFF\n" << vids.size() << " " << cc_faces.size() << " 0\n"; - std::vector points(vids.size()); - typedef std::pair Pair_type; - for(Pair_type p : vids) - points[p.second]=get(vpm, p.first); - for(Point_3 p : points) - output << p << "\n"; - for(face_descriptor f : cc_faces) - { - output << "3 " - << vids[ target(halfedge(f, tm), tm) ] << " " - << vids[ target(next(halfedge(f, tm), tm), tm) ] << " " - << vids[ target(next(next(halfedge(f, tm), tm), tm), tm) ] << "\n"; - } - } - -} //end of namespace debug - -namespace internal { - -template -struct Less_vertex_point{ - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - const Traits& m_traits; - const VertexPointMap& m_vpmap; - Less_vertex_point(const Traits& traits, const VertexPointMap& vpmap) - : m_traits(traits) - , m_vpmap(vpmap) {} - bool operator()(vertex_descriptor v1, vertex_descriptor v2) const{ - return m_traits.less_xyz_3_object()(get(m_vpmap, v1), get(m_vpmap, v2)); - } -}; - -} // end namespace internal - -/// \ingroup PMP_repairing_grp -/// collects the degenerate edges within a given range of edges. -/// -/// @tparam EdgeRange a model of `Range` with value type `boost::graph_traits::%edge_descriptor` -/// @tparam TriangleMesh a model of `HalfedgeGraph` -/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" -/// -/// @param edges a subset of edges of `tm` -/// @param tm a triangle mesh -/// @param out an output iterator in which the degenerate edges are written -/// @param np optional \ref pmp_namedparameters "Named Parameters" described below -/// -/// \cgalNamedParamsBegin -/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `tm`. -/// The type of this map is model of `ReadWritePropertyMap`. -/// If this parameter is omitted, an internal property map for -/// `CGAL::vertex_point_t` should be available in `TriangleMesh` -/// \cgalParamEnd -/// \cgalParamBegin{geom_traits} a geometric traits class instance. -/// The traits class must provide the nested type `Point_3`, -/// and the nested functor `Equal_3` to check whether two points are identical. -/// \cgalParamEnd -/// \cgalNamedParamsEnd -template -OutputIterator degenerate_edges(const EdgeRange& edges, - const TriangleMesh& tm, - OutputIterator out, - const NamedParameters& np) -{ - typedef typename boost::graph_traits::edge_descriptor edge_descriptor; - - for(edge_descriptor ed : edges) - { - if(is_degenerate_edge(ed, tm, np)) - *out++ = ed; - } - return out; -} - -template -OutputIterator degenerate_edges(const EdgeRange& edges, - const TriangleMesh& tm, - OutputIterator out, - typename boost::enable_if< - typename boost::has_range_iterator - >::type* = 0) -{ - return degenerate_edges(edges, tm, out, CGAL::parameters::all_default()); -} - -/// \ingroup PMP_repairing_grp -/// calls the function `degenerate_edges()` with the range: `edges(tm)`. -/// -/// See above for the comprehensive description of the parameters. -/// -template -OutputIterator degenerate_edges(const TriangleMesh& tm, - OutputIterator out, - const NamedParameters& np -#ifndef DOXYGEN_RUNNING - , typename boost::disable_if< - boost::has_range_iterator - >::type* = 0 -#endif - ) -{ - return degenerate_edges(edges(tm), tm, out, np); -} - -template -OutputIterator -degenerate_edges(const TriangleMesh& tm, OutputIterator out) -{ - return degenerate_edges(edges(tm), tm, out, CGAL::parameters::all_default()); -} - -/// \ingroup PMP_repairing_grp -/// collects the degenerate faces within a given range of faces. -/// -/// @tparam FaceRange a model of `Range` with value type `boost::graph_traits::%face_descriptor` -/// @tparam TriangleMesh a model of `FaceGraph` -/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" -/// -/// @param faces a subset of faces of `tm` -/// @param tm a triangle mesh -/// @param out an output iterator in which the degenerate faces are put -/// @param np optional \ref pmp_namedparameters "Named Parameters" described below -/// -/// \cgalNamedParamsBegin -/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `tm`. -/// The type of this map is model of `ReadWritePropertyMap`. -/// If this parameter is omitted, an internal property map for -/// `CGAL::vertex_point_t` should be available in `TriangleMesh` -/// \cgalParamEnd -/// \cgalParamBegin{geom_traits} a geometric traits class instance. -/// The traits class must provide the nested functor `Collinear_3` -/// to check whether three points are collinear. -/// \cgalParamEnd -/// \cgalNamedParamsEnd -/// -template -OutputIterator degenerate_faces(const FaceRange& faces, - const TriangleMesh& tm, - OutputIterator out, - const NamedParameters& np) -{ - typedef typename boost::graph_traits::face_descriptor face_descriptor; - - for(face_descriptor fd : faces) - { - if(is_degenerate_triangle_face(fd, tm, np)) - *out++ = fd; - } - return out; -} - -template -OutputIterator degenerate_faces(const FaceRange& faces, - const TriangleMesh& tm, - OutputIterator out, - typename boost::enable_if< - boost::has_range_iterator - >::type* = 0) -{ - return degenerate_faces(faces, tm, out, CGAL::parameters::all_default()); -} - -/// \ingroup PMP_repairing_grp -/// calls the function `degenerate_faces()` with the range: `faces(tm)`. -/// -/// See above for the comprehensive description of the parameters. -/// -template -OutputIterator degenerate_faces(const TriangleMesh& tm, - OutputIterator out, - const NamedParameters& np -#ifndef DOXYGEN_RUNNING - , typename boost::disable_if< - boost::has_range_iterator - >::type* = 0 -#endif - ) -{ - return degenerate_faces(faces(tm), tm, out, np); -} - -template -OutputIterator degenerate_faces(const TriangleMesh& tm, OutputIterator out) -{ - return degenerate_faces(faces(tm), tm, out, CGAL::parameters::all_default()); -} - -// this function removes a border edge even if it does not satisfy the link condition. -// null_vertex() is returned if the removal changes the topology of the input -template -typename boost::graph_traits::vertex_descriptor -remove_a_border_edge(typename boost::graph_traits::edge_descriptor ed, - TriangleMesh& tm, - EdgeSet& edge_set, - FaceSet& face_set) -{ - typedef boost::graph_traits GT; - typedef typename GT::edge_descriptor edge_descriptor; - typedef typename GT::halfedge_descriptor halfedge_descriptor; - typedef typename GT::face_descriptor face_descriptor; - typedef typename GT::vertex_descriptor vertex_descriptor; - - halfedge_descriptor h=halfedge(ed,tm); - - if ( is_border(h,tm) ) - h=opposite(h,tm); - - halfedge_descriptor opp_h = opposite(h,tm); - CGAL_assertion(is_border(opp_h,tm)); - CGAL_assertion(!is_border(h,tm)); - - CGAL_assertion(next(next(opp_h, tm), tm) !=opp_h); // not working for a hole made of 2 edges - CGAL_assertion(next(next(next(opp_h, tm), tm), tm) !=opp_h); // not working for a hole make of 3 edges - - if (CGAL::Euler::does_satisfy_link_condition(edge(h,tm),tm)) - { - edge_set.erase(ed); - halfedge_descriptor h=halfedge(ed, tm); - if ( is_border(h, tm) ) - h = opposite(h, tm); - - edge_set.erase(edge(prev(h, tm), tm)); - face_set.erase(face(h, tm)); - - return CGAL::Euler::collapse_edge(ed, tm); - } - - // collect edges that have one vertex in the link of - // the vertices of h and one of the vertex of h as other vertex - std::set common_incident_edges; - for(halfedge_descriptor hos : halfedges_around_source(h, tm)) - for(halfedge_descriptor hot : halfedges_around_target(h, tm)) - { - if( target(hos, tm) == source(hot, tm) ) - { - common_incident_edges.insert( edge(hot, tm) ); - common_incident_edges.insert( edge(hos, tm) ); - } - } - - CGAL_assertion(common_incident_edges.size() >=2 ); - - // in the following loop, we visit define a connected component of - // faces bounded by edges in common_incident_edges and h. We look - // for the maximal one. This set of faces is the one that will - // disappear while collapsing ed - std::set marked_faces; - - std::vector queue; - queue.push_back( opposite(next(h,tm), tm) ); - queue.push_back( opposite(prev(h,tm), tm) ); - marked_faces.insert( face(h, tm) ); - - do - { - std::vector boundary; - while(!queue.empty()) - { - halfedge_descriptor back=queue.back(); - queue.pop_back(); - face_descriptor fback=face(back,tm); - if (common_incident_edges.count(edge(back,tm))) - { - boundary.push_back(back); - continue; - } - - if ( fback==GT::null_face() || !marked_faces.insert(fback).second ) - continue; - - queue.push_back( opposite(next(back,tm), tm) ); - if ( is_border(queue.back(), tm) ) - { -#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG - std::cout << "Boundary reached during exploration, the region to be removed is not a topological disk, not handled for now.\n"; -#endif - return GT::null_vertex(); - } - - queue.push_back( opposite(prev(back,tm), tm) ); - if ( is_border(queue.back(), tm) ) - { -#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG - std::cout << "Boundary reached during exploration, the region to be removed is not a topological disk, not handled for now.\n"; -#endif - return GT::null_vertex(); - } - } - - CGAL_assertion( boundary.size() == 2 ); - common_incident_edges.erase( edge(boundary[0], tm) ); - common_incident_edges.erase( edge(boundary[1], tm) ); - if (!is_border(boundary[0], tm) || common_incident_edges.empty()) - queue.push_back(boundary[0]); - if (!is_border(boundary[1], tm) || common_incident_edges.empty()) - queue.push_back(boundary[1]); - } - while(!common_incident_edges.empty()); - - // hk1 and hk2 are bounding the region that will be removed. - // The edge of hk2 will be removed and hk2 will be replaced - // by the opposite edge of hk1 - halfedge_descriptor hk1=queue.front(); - halfedge_descriptor hk2=queue.back(); - if ( target(hk1,tm)!=source(hk2,tm) ) - std::swap(hk1, hk2); - - CGAL_assertion( target(hk1,tm)==source(hk2,tm) ); - CGAL_assertion( source(hk1,tm)==source(h,tm) ); - CGAL_assertion( target(hk2,tm)==target(h,tm) ); - - CGAL_assertion(is_valid_polygon_mesh(tm)); - if (!is_selection_a_topological_disk(marked_faces, tm)) - { -#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG - std::cout << "The region to be removed is not a topological disk, not handled for now.\n"; -#endif - return GT::null_vertex(); - - } - - if (is_border(hk1, tm) && is_border(hk2, tm)) - { -#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG - std::cout << "The region to be removed is an isolated region, not handled for now.\n"; -#endif - return GT::null_vertex(); - } - - // collect vertices and edges to remove and do remove faces - std::set edges_to_remove; - std::set vertices_to_remove; - for(face_descriptor fd : marked_faces) - { - halfedge_descriptor hd=halfedge(fd, tm); - for(int i=0; i<3; ++i) - { - edges_to_remove.insert( edge(hd, tm) ); - vertices_to_remove.insert( target(hd,tm) ); - hd=next(hd, tm); - } - } - - vertex_descriptor vkept=source(hk1,tm); - - //back-up next, prev halfedge pointers to be restored after removal - halfedge_descriptor hp=prev(opp_h, tm); - halfedge_descriptor hn=next(opp_h, tm); - halfedge_descriptor hk1_opp_next = next(hk2, tm); - halfedge_descriptor hk1_opp_prev = prev(hk2, tm); - face_descriptor hk1_opp_face = face(hk2,tm); - - // we will remove the target of hk2, update vertex pointers - for(halfedge_descriptor hot : halfedges_around_target(hk2, tm)) - set_target(hot, vkept, tm); - - // update halfedge pointers since hk2 will be removed - set_halfedge(vkept, opposite(hk1, tm), tm); - set_halfedge(target(hk1,tm), hk1, tm); - - // do not remove hk1 and its vertices - vertices_to_remove.erase( vkept ); - vertices_to_remove.erase( target(hk1, tm) ); - edges_to_remove.erase( edge(hk1,tm) ); - - bool hk2_equals_hp = hk2==hp; - CGAL_assertion( is_border(hk2, tm) == hk2_equals_hp ); - - /* - - case hk2!=hp - - /\ / - hk1/ \hk2 / - / \ / - ____/______\/____ - hn h_opp hp - - - case hk2==hp - - /\ - hk1/ \hk2 == hp - / \ - ____/______\ - hn h_opp - */ - - // remove vertices - for(vertex_descriptor vd : vertices_to_remove) - remove_vertex(vd, tm); - - // remove edges - for(edge_descriptor ed : edges_to_remove) - { - edge_set.erase(ed); - remove_edge(ed, tm); - } - - // remove faces - for(face_descriptor fd : marked_faces) - { - face_set.erase(fd); - remove_face(fd, tm); - } - - // now update pointers - set_face(opposite(hk1, tm), hk1_opp_face, tm); - if (!hk2_equals_hp) - { - set_next(hp, hn, tm); - set_next(opposite(hk1, tm), hk1_opp_next, tm); - set_next(hk1_opp_prev, opposite(hk1, tm), tm); - set_halfedge(hk1_opp_face, opposite(hk1, tm), tm); - } - else - { - set_next(hk1_opp_prev, opposite(hk1, tm), tm); - set_next(opposite(hk1, tm), hn, tm); - } - - CGAL_assertion(is_valid_polygon_mesh(tm)); - return vkept; -} - -template -typename boost::graph_traits::vertex_descriptor -remove_a_border_edge(typename boost::graph_traits::edge_descriptor ed, - TriangleMesh& tm) -{ - std::set::edge_descriptor> edge_set; - std::set::face_descriptor> face_set; - - return remove_a_border_edge(ed, tm, edge_set, face_set); -} - -template -bool remove_degenerate_edges(const EdgeRange& edge_range, - TriangleMesh& tmesh, - FaceSet& face_set, - const NamedParameters& np) -{ - CGAL_assertion(CGAL::is_triangle_mesh(tmesh)); - CGAL_assertion(CGAL::is_valid_polygon_mesh(tmesh)); - - using parameters::get_parameter; - using parameters::choose_parameter; - - typedef TriangleMesh TM; - typedef typename boost::graph_traits GT; - typedef typename GT::edge_descriptor edge_descriptor; - typedef typename GT::halfedge_descriptor halfedge_descriptor; - typedef typename GT::face_descriptor face_descriptor; - typedef typename GT::vertex_descriptor vertex_descriptor; - - typedef typename GetVertexPointMap::type VertexPointMap; - VertexPointMap vpmap = choose_parameter(get_parameter(np, internal_np::vertex_point), - get_property_map(vertex_point, tmesh)); - - typedef typename GetGeomTraits::type Traits; - - std::size_t nb_deg_faces = 0; - bool all_removed=false; - bool some_removed=true; - - bool preserve_genus = parameters::choose_parameter(parameters::get_parameter(np, internal_np::preserve_genus), true); - - // collect edges of length 0 - while(some_removed && !all_removed) - { - some_removed=false; - all_removed=true; - std::set degenerate_edges_to_remove; - degenerate_edges(edge_range, tmesh, std::inserter(degenerate_edges_to_remove, - degenerate_edges_to_remove.end())); - -#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG - std::cout << "Found " << degenerate_edges_to_remove.size() << " null edges.\n"; -#endif - - // first try to remove all collapsable edges - typename std::set::iterator it = degenerate_edges_to_remove.begin(); - while (it!=degenerate_edges_to_remove.end()) - { - edge_descriptor e = *it; - if (CGAL::Euler::does_satisfy_link_condition(e,tmesh)) - { - halfedge_descriptor h = halfedge(e, tmesh); - degenerate_edges_to_remove.erase(it); - - // remove edges that could also be set for removal - if ( face(h, tmesh)!=GT::null_face() ) - { - ++nb_deg_faces; - degenerate_edges_to_remove.erase(edge(prev(h, tmesh), tmesh)); - face_set.erase(face(h, tmesh)); - } - - if (face(opposite(h, tmesh), tmesh)!=GT::null_face()) - { - ++nb_deg_faces; - degenerate_edges_to_remove.erase(edge(prev(opposite(h, tmesh), tmesh), tmesh)); - face_set.erase(face(opposite(h, tmesh), tmesh)); - } - - //now remove the edge - CGAL::Euler::collapse_edge(e, tmesh); - - // some_removed is not updated on purpose because if nothing - // happens below then nothing can be done - it = degenerate_edges_to_remove.begin(); - } - else - ++it; - } - - CGAL_assertion( is_valid_polygon_mesh(tmesh) ); -#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG - std::cout << "Remaining " << degenerate_edges_to_remove.size() << " null edges to be handled.\n"; -#endif - - while (!degenerate_edges_to_remove.empty()) - { - edge_descriptor ed = *degenerate_edges_to_remove.begin(); - degenerate_edges_to_remove.erase(degenerate_edges_to_remove.begin()); - - halfedge_descriptor h = halfedge(ed, tmesh); - - if (CGAL::Euler::does_satisfy_link_condition(ed,tmesh)) - { - // remove edges that could also be set for removal - if ( face(h, tmesh)!=GT::null_face() ) - { - ++nb_deg_faces; - degenerate_edges_to_remove.erase(edge(prev(h, tmesh), tmesh)); - face_set.erase(face(h, tmesh)); - } - - if (face(opposite(h, tmesh), tmesh)!=GT::null_face()) - { - ++nb_deg_faces; - degenerate_edges_to_remove.erase(edge(prev(opposite(h, tmesh), tmesh), tmesh)); - face_set.erase(face(opposite(h, tmesh), tmesh)); - } - - //now remove the edge - CGAL::Euler::collapse_edge(ed, tmesh); - some_removed = true; - } - else - { - //handle the case when the edge is incident to a triangle hole - //we first fill the hole and try again - if ( is_border(ed, tmesh) ) - { - halfedge_descriptor hd = halfedge(ed,tmesh); - if (!is_border(hd,tmesh)) - hd=opposite(hd,tmesh); - - if (is_triangle(hd, tmesh)) - { - if (!preserve_genus) - { - Euler::fill_hole(hd, tmesh); - degenerate_edges_to_remove.insert(ed); // reinsert the edge for future processing - } - else - { - all_removed=false; - } - continue; - } - -#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG - std::cout << "Calling remove_a_border_edge\n"; -#endif - - vertex_descriptor vd = remove_a_border_edge(ed, tmesh, degenerate_edges_to_remove, face_set); - if (vd == GT::null_vertex()) - { - // @todo: if some border edges are later removed, the edge might be processable later - // for example if it belongs to boundary cycle of edges where the number of non-degenerate - // edges is 2. That's what happen with fused_vertices.off in the testsuite where the edges - // are not processed the same way with Polyhedron and Surface_mesh. In the case of Polyhedron - // more degenerate edges could be removed. - all_removed=false; - } - else - some_removed=true; - - continue; - } - else - { - halfedge_descriptor hd = halfedge(ed,tmesh); - // if both vertices are boundary vertices we can't do anything - bool impossible = false; - for(halfedge_descriptor h : halfedges_around_target(hd, tmesh)) - { - if (is_border(h, tmesh)) - { - impossible = true; - break; - } - } - - if (impossible) - { - impossible=false; - for(halfedge_descriptor h : halfedges_around_source(hd, tmesh)) - { - if (is_border(h, tmesh)) - { - impossible = true; - break; - } - } - - if (impossible) - { - all_removed=false; - continue; - } - } - } - - // When the edge does not satisfy the link condition, it means that it cannot be - // collapsed as is. In the following if there is a topological issue - // with contracting the edge (component or geometric feature that disappears), - // nothing is done. - // We start by marking the faces that are incident to an edge endpoint. - // If the set of marked faces is a topologically disk, then we simply remove all the simplicies - // inside the disk and star the hole with the edge vertex kept. - // If the set of marked faces is not a topological disk, it has some non-manifold vertices - // on its boundary. We need to mark additional faces to make it a topological disk. - // We can then apply the star hole procedure. - // Right now we additionally mark the smallest connected components of non-marked faces - // (using the number of faces) - - //backup central point - typename Traits::Point_3 pt = get(vpmap, source(ed, tmesh)); - - // mark faces of the link of each endpoints of the edge which collapse is not topologically valid - std::set marked_faces; - - // first endpoint - for(halfedge_descriptor hd : CGAL::halfedges_around_target(halfedge(ed,tmesh), tmesh) ) - if (!is_border(hd,tmesh)) marked_faces.insert( face(hd, tmesh) ); - - // second endpoint - for(halfedge_descriptor hd : CGAL::halfedges_around_target(opposite(halfedge(ed, tmesh), tmesh), tmesh) ) - if (!is_border(hd,tmesh)) marked_faces.insert( face(hd, tmesh) ); - - // extract the halfedges on the boundary of the marked region - std::vector border; - for(face_descriptor fd : marked_faces) - { - for(halfedge_descriptor hd : CGAL::halfedges_around_face(halfedge(fd,tmesh), tmesh)) - { - halfedge_descriptor hd_opp = opposite(hd, tmesh); - if ( is_border(hd_opp, tmesh) || - marked_faces.count( face(hd_opp, tmesh) ) == 0 ) - { - border.push_back( hd ); - } - } - } - - if (border.empty() ) - { - // a whole connected component (without boundary) got selected and will disappear (not handled for now) -#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG - std::cout << "Trying to remove a whole connected component, not handled yet\n"; -#endif - all_removed=false; - continue; - } - - // define cc of border halfedges: two halfedges are in the same cc - // if they are on the border of the cc of non-marked faces. - typedef CGAL::Union_find UF_ds; - UF_ds uf; - std::map handles; - // one cc per border halfedge - for(halfedge_descriptor hd : border) - handles.insert( std::make_pair(hd, uf.make_set(hd)) ); - - // join cc's - for(halfedge_descriptor hd : border) - { - CGAL_assertion( marked_faces.count( face( hd, tmesh) ) > 0); - CGAL_assertion( marked_faces.count( face( opposite(hd, tmesh), tmesh) ) == 0 ); - halfedge_descriptor candidate = hd; - - do { - candidate = prev( opposite(candidate, tmesh), tmesh ); - } while( !marked_faces.count( face( opposite(candidate, tmesh), tmesh) ) ); - - uf.unify_sets( handles[hd], handles[opposite(candidate, tmesh)] ); - } - - std::size_t nb_cc = uf.number_of_sets(); - if ( nb_cc != 1 ) - { - // if more than one connected component is found then the patch - // made of marked faces contains "non-manifold" vertices. - // The smallest components need to be marked so that the patch - // made of marked faces is a topological disk - - // we will explore in parallel the connected components and will stop - // when all but one connected component have been entirely explored. - // We add one face at a time for each cc in order to not explore a - // potentially very large cc. - std::vector< std::vector > stacks_per_cc(nb_cc); - std::vector< std::set > faces_per_cc(nb_cc); - std::vector< bool > exploration_finished(nb_cc, false); - - // init the stacks of halfedges using the cc of the boundary - std::size_t index=0; - std::map< halfedge_descriptor, std::size_t > ccs; - typedef std::pair Pair_type; - for(Pair_type p : handles) - { - halfedge_descriptor opp_hedge = opposite(p.first, tmesh); - if (is_border(opp_hedge, tmesh)) continue; // nothing to do on the boundary - - typedef typename std::map< halfedge_descriptor, std::size_t >::iterator Map_it; - std::pair insert_res= - ccs.insert( std::make_pair(*uf.find( p.second ), index) ); - - if (insert_res.second) - ++index; - - stacks_per_cc[ insert_res.first->second ].push_back( prev(opp_hedge, tmesh) ); - stacks_per_cc[ insert_res.first->second ].push_back( next(opp_hedge, tmesh) ); - faces_per_cc[ insert_res.first->second ].insert( face(opp_hedge, tmesh) ); - } - - if (index != nb_cc) - { - // most probably, one cc is a cycle of border edges -#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG - std::cout << "Trying to remove a component with a cycle of halfedges (nested hole or whole component), not handled yet.\n"; -#endif - all_removed=false; - continue; - } - - std::size_t nb_ccs_to_be_explored = nb_cc; - index=0; - //explore the cc's - do - { - // try to extract one more face for a given cc - do - { - CGAL_assertion( !exploration_finished[index] ); - CGAL_assertion( !stacks_per_cc.empty() ); - CGAL_assertion( !stacks_per_cc[index].empty() ); - halfedge_descriptor hd = stacks_per_cc[index].back(); - stacks_per_cc[index].pop_back(); - hd = opposite(hd, tmesh); - if ( !is_border(hd,tmesh) && !marked_faces.count(face(hd, tmesh) ) ) - { - if ( faces_per_cc[index].insert( face(hd, tmesh) ).second ) - { - stacks_per_cc[index].push_back( next(hd, tmesh) ); - stacks_per_cc[index].push_back( prev(hd, tmesh) ); - break; - } - } - if (stacks_per_cc[index].empty()) break; - } - while(true); - - // the exploration of a cc is finished when its stack is empty - exploration_finished[index]=stacks_per_cc[index].empty(); - if ( exploration_finished[index] ) - --nb_ccs_to_be_explored; - if ( nb_ccs_to_be_explored==1 ) - break; - while ( exploration_finished[(++index)%nb_cc] ); - index=index%nb_cc; - } - while(true); - - // @todo use the area criteria? this means maybe continue exploration of larger cc - // mark faces of completetly explored cc - for (index=0; index< nb_cc; ++index) - { - if( exploration_finished[index] ) - { - for(face_descriptor fd : faces_per_cc[index]) - marked_faces.insert(fd); - } - } - } - - // make sure the selection is a topological disk (otherwise we need another treatment) - if (!is_selection_a_topological_disk(marked_faces, tmesh)) - { -#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG - std::cout << "Trying to handle a non-topological disk, do nothing\n"; -#endif - all_removed=false; - continue; - } - - some_removed = true; - // collect simplices to be removed - std::set vertices_to_keep; - std::set halfedges_to_keep; - for(halfedge_descriptor hd : border) - { - if ( !marked_faces.count(face(opposite(hd, tmesh), tmesh)) ) - { - halfedges_to_keep.insert( hd ); - vertices_to_keep.insert( target(hd, tmesh) ); - } - } - - // backup next,prev relationships to set after patch removal - std::vector< std::pair > next_prev_halfedge_pairs; - halfedge_descriptor first_border_hd=*( halfedges_to_keep.begin() ); - halfedge_descriptor current_border_hd=first_border_hd; - do - { - halfedge_descriptor prev_border_hd=current_border_hd; - current_border_hd=next(current_border_hd, tmesh); - while( marked_faces.count( face( opposite(current_border_hd, tmesh), tmesh) ) ) - current_border_hd=next(opposite(current_border_hd, tmesh), tmesh); - next_prev_halfedge_pairs.push_back( std::make_pair(prev_border_hd, current_border_hd) ); - } - while(current_border_hd!=first_border_hd); - - // collect vertices and edges to remove and do remove faces - std::set edges_to_remove; - std::set vertices_to_remove; - for(face_descriptor fd : marked_faces) - { - halfedge_descriptor hd=halfedge(fd, tmesh); - for(int i=0; i<3; ++i) - { - if ( !halfedges_to_keep.count(hd) ) - edges_to_remove.insert( edge(hd, tmesh) ); - - if ( !vertices_to_keep.count(target(hd,tmesh)) ) - vertices_to_remove.insert( target(hd,tmesh) ); - - hd=next(hd, tmesh); - } - - remove_face(fd, tmesh); - face_set.erase(fd); - } - - // remove vertices - for(vertex_descriptor vd : vertices_to_remove) - remove_vertex(vd, tmesh); - - // remove edges - for(edge_descriptor ed : edges_to_remove) - { - degenerate_edges_to_remove.erase(ed); - remove_edge(ed, tmesh); - } - - // add a new face, set all border edges pointing to it - // and update halfedge vertex of patch boundary vertices - face_descriptor new_face = add_face(tmesh); - typedef std::pair Pair_type; - for(const Pair_type& p : next_prev_halfedge_pairs) - { - set_face(p.first, new_face, tmesh); - set_next(p.first, p.second, tmesh); - set_halfedge(target(p.first, tmesh), p.first, tmesh); - } - - set_halfedge(new_face, first_border_hd, tmesh); - // triangulate the new face and update the coordinate of the central vertex - halfedge_descriptor new_hd=Euler::add_center_vertex(first_border_hd, tmesh); - put(vpmap, target(new_hd, tmesh), pt); - - for(halfedge_descriptor hd : halfedges_around_target(new_hd, tmesh)) - { - if(is_degenerate_edge(edge(hd, tmesh), tmesh, np)) - degenerate_edges_to_remove.insert(edge(hd, tmesh)); - - if(face(hd, tmesh) != GT::null_face() && is_degenerate_triangle_face(face(hd, tmesh), tmesh)) - face_set.insert(face(hd, tmesh)); - } - - CGAL_assertion( is_valid_polygon_mesh(tmesh) ); - } - } - } - - return all_removed; -} - -template -bool remove_degenerate_edges(const EdgeRange& edge_range, - TriangleMesh& tmesh, - const CGAL_PMP_NP_CLASS& np) -{ - std::set::face_descriptor> face_set; - return remove_degenerate_edges(edge_range, tmesh, face_set, np); -} - -template -bool remove_degenerate_edges(TriangleMesh& tmesh, - const CGAL_PMP_NP_CLASS& np) -{ - std::set::face_descriptor> face_set; - return remove_degenerate_edges(edges(tmesh), tmesh, face_set, np); -} - -template -bool remove_degenerate_edges(const EdgeRange& edge_range, - TriangleMesh& tmesh) -{ - std::set::face_descriptor> face_set; - return remove_degenerate_edges(edge_range, tmesh, face_set, parameters::all_default()); -} - -template -bool remove_degenerate_edges(TriangleMesh& tmesh) -{ - std::set::face_descriptor> face_set; - return remove_degenerate_edges(edges(tmesh), tmesh, face_set, parameters::all_default()); -} - -// \ingroup PMP_repairing_grp -// removes the degenerate faces from a triangulated surface mesh. -// A face is considered degenerate if two of its vertices share the same location, -// or more generally if all its vertices are collinear. -// -// @pre `CGAL::is_triangle_mesh(tmesh)` -// -// @tparam TriangleMesh a model of `FaceListGraph` and `MutableFaceGraph` -// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" -// -// @param tmesh the triangulated surface mesh to be repaired -// @param np optional \ref pmp_namedparameters "Named Parameters" described below -// -// \cgalNamedParamsBegin -// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. -// The type of this map is model of `ReadWritePropertyMap`. -// If this parameter is omitted, an internal property map for -// `CGAL::vertex_point_t` must be available in `TriangleMesh` -// \cgalParamEnd -// \cgalParamBegin{geom_traits} a geometric traits class instance. -// The traits class must provide the nested type `Point_3`, -// and the nested functors: -// - `Compare_distance_3` to compute the distance between 2 points -// - `Collinear_3` to check whether 3 points are collinear -// - `Less_xyz_3` to compare lexicographically two points -// - `Equal_3` to check whether 2 points are identical. -// For each functor Foo, a function `Foo foo_object()` must be provided. -// \cgalParamEnd -// \cgalNamedParamsEnd -// -// @todo the function might not be able to remove all degenerate faces. -// We should probably do something with the return type. -// -// \return `true` if all degenerate faces were successfully removed, and `false` otherwise. -template -bool remove_degenerate_faces(const FaceRange& face_range, - TriangleMesh& tmesh, - const NamedParameters& np) -{ - CGAL_assertion(CGAL::is_triangle_mesh(tmesh)); - - using parameters::get_parameter; - using parameters::choose_parameter; - - typedef TriangleMesh TM; - typedef typename boost::graph_traits GT; - typedef typename GT::edge_descriptor edge_descriptor; - typedef typename GT::halfedge_descriptor halfedge_descriptor; - typedef typename GT::face_descriptor face_descriptor; - typedef typename GT::vertex_descriptor vertex_descriptor; - - typedef typename GetVertexPointMap::type VertexPointMap; - VertexPointMap vpmap = choose_parameter(get_parameter(np, internal_np::vertex_point), - get_property_map(vertex_point, tmesh)); - typedef typename GetGeomTraits::type Traits; - Traits traits = choose_parameter(get_parameter(np, internal_np::geom_traits)); - - typedef typename boost::property_traits::value_type Point_3; - typedef typename boost::property_traits::reference Point_ref; - - std::set degenerate_face_set; - degenerate_faces(face_range, tmesh, std::inserter(degenerate_face_set, degenerate_face_set.begin()), np); - - const std::size_t faces_size = faces(tmesh).size(); - - if(degenerate_face_set.empty()) - return true; - - if(degenerate_face_set.size() == faces_size) - { - clear(tmesh); - return true; - } - - // Sanitize the face range by adding adjacent degenerate faces - const std::size_t range_size = face_range.size(); - bool is_range_full_mesh = (range_size == faces_size); - if(!is_range_full_mesh) - { - std::list faces_to_visit(degenerate_face_set.begin(), degenerate_face_set.end()); - - while(!faces_to_visit.empty()) - { - face_descriptor fd = faces_to_visit.front(); - faces_to_visit.pop_front(); - - for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, tmesh), tmesh)) - { - for(halfedge_descriptor inc_hd : halfedges_around_target(hd, tmesh)) - { - face_descriptor adj_fd = face(inc_hd, tmesh); - if(adj_fd == GT::null_face() || adj_fd == fd) - continue; - - if(is_degenerate_triangle_face(adj_fd, tmesh)) - { - if(degenerate_face_set.insert(adj_fd).second) - { - // successful insertion means we did not know about this face before - faces_to_visit.push_back(adj_fd); - } - } - } - } - } - } - - // Note that there can't be any null edge incident to the degenerate faces range, - // otherwise we would have a null face incident to the face range, and that is not possible - // after the sanitization above - std::set edge_range; - for(face_descriptor fd : degenerate_face_set) - for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, tmesh), tmesh)) - edge_range.insert(edge(hd, tmesh)); - - // First remove edges of length 0 - bool all_removed = remove_degenerate_edges(edge_range, tmesh, degenerate_face_set, np); - - CGAL_assertion_code(for(face_descriptor fd : degenerate_face_set) {) - CGAL_assertion(is_degenerate_triangle_face(fd, tmesh)); - CGAL_assertion_code(}) - -#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG - { - std::cout <<"Done with null edges.\n"; - std::ofstream output("/tmp/no_null_edges.off"); - output << std::setprecision(17) << tmesh << "\n"; - output.close(); - } -#endif - - // Then, remove triangles made of 3 collinear points - - // start by filtering out border faces - // @todo: shall we avoid doing that in case a non-manifold vertex on the boundary or if a whole component disappear? - std::set border_deg_faces; - for(face_descriptor f : degenerate_face_set) - { - halfedge_descriptor h = halfedge(f, tmesh); - for (int i=0; i<3; ++i) - { - if ( is_border( opposite(h, tmesh), tmesh) ) - { - border_deg_faces.insert(f); - break; - } - h = next(h, tmesh); - } - } - - while( !border_deg_faces.empty() ) - { - face_descriptor f_to_rm = *border_deg_faces.begin(); - border_deg_faces.erase(border_deg_faces.begin()); - - halfedge_descriptor h = halfedge(f_to_rm, tmesh); - for (int i=0; i<3; ++i) - { - face_descriptor f = face(opposite(h, tmesh), tmesh); - if ( f!=GT::null_face() ) - { - if (is_degenerate_triangle_face(f, tmesh, np) ) - border_deg_faces.insert(f); - } - h = next(h, tmesh); - } - - while( !is_border(opposite(h, tmesh), tmesh) ) - { - h = next(h, tmesh); - } - - degenerate_face_set.erase(f_to_rm); - Euler::remove_face(h, tmesh); - } - - // Ignore faces with null edges - if(!all_removed) - { - std::map are_degenerate_edges; - - for(face_descriptor fd : degenerate_face_set) - { - for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, tmesh), tmesh)) - { - edge_descriptor ed = edge(hd, tmesh); - std::pair::iterator, bool> is_insert_successful = - are_degenerate_edges.insert(std::make_pair(ed, false)); - - bool is_degenerate = false; - if(is_insert_successful.second) - { - // did not previously exist in the map, so actually have to check if it is degenerate - if(traits.equal_3_object()(get(vpmap, target(ed, tmesh)), get(vpmap, source(ed, tmesh)))) - is_degenerate = true; - } - - is_insert_successful.first->second = is_degenerate; - - if(is_degenerate) - { - halfedge_descriptor h = halfedge(ed, tmesh); - if(!is_border(h, tmesh)) - degenerate_face_set.erase(face(h, tmesh)); - - h = opposite(h, tmesh); - if(!is_border(h, tmesh)) - degenerate_face_set.erase(face(h, tmesh)); - } - } - } - } - - // first remove degree 3 vertices that are part of a cap - // (only the vertex in the middle of the opposite edge) - // This removal does not change the shape of the mesh. - while (!degenerate_face_set.empty()) - { - std::set vertices_to_remove; - for(face_descriptor fd : degenerate_face_set) - { - for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, tmesh), tmesh)) - { - vertex_descriptor vd = target(hd, tmesh); - if (degree(vd, tmesh) == 3 && is_border(vd, tmesh)==GT::null_halfedge()) - { - vertices_to_remove.insert(vd); - break; - } - } - } - - for(vertex_descriptor vd : vertices_to_remove) - { - halfedge_descriptor hd=halfedge(vd, tmesh); - for(halfedge_descriptor hd2 : halfedges_around_target(hd, tmesh)) - if (!is_border(hd2, tmesh)) - degenerate_face_set.erase( face(hd2, tmesh) ); - - // remove the central vertex and check if the new face is degenerated - hd=CGAL::Euler::remove_center_vertex(hd, tmesh); - if (is_degenerate_triangle_face(face(hd, tmesh), tmesh, np)) - { - degenerate_face_set.insert( face(hd, tmesh) ); - } - } - - if (vertices_to_remove.empty()) - break; - } - - while (!degenerate_face_set.empty()) - { -#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG - std::cout << "Loop on removing deg faces\n"; - // ensure the mesh is not broken - { - std::ofstream out("/tmp/out.off"); - out << tmesh; - out.close(); - - std::vector points; - std::vector > triangles; - std::ifstream in("/tmp/out.off"); - CGAL::read_OFF(in, points, triangles); - if (!CGAL::Polygon_mesh_processing::is_polygon_soup_a_polygon_mesh(triangles)) - { - std::cerr << "Warning: got a polygon soup (may simply be a non-manifold vertex)!\n"; - } - } -#endif - - face_descriptor fd = *degenerate_face_set.begin(); - - // look whether an incident triangle is also degenerate - bool detect_cc_of_degenerate_triangles = false; - for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, tmesh), tmesh) ) - { - face_descriptor adjacent_face = face( opposite(hd, tmesh), tmesh ); - if ( adjacent_face!=GT::null_face() && - degenerate_face_set.count(adjacent_face) ) - { - detect_cc_of_degenerate_triangles = true; - break; - } - } - - if (!detect_cc_of_degenerate_triangles) - { -#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG - std::cout << " no degenerate neighbors, using a flip.\n"; -#endif - degenerate_face_set.erase(degenerate_face_set.begin()); - - // flip the longest edge of the triangle - Point_ref p1 = get(vpmap, target( halfedge(fd, tmesh), tmesh) ); - Point_ref p2 = get(vpmap, target(next(halfedge(fd, tmesh), tmesh), tmesh) ); - Point_ref p3 = get(vpmap, source( halfedge(fd, tmesh), tmesh) ); - - CGAL_assertion(p1!=p2 && p1!=p3 && p2!=p3); - - typename Traits::Compare_distance_3 compare_distance = traits.compare_distance_3_object(); - - halfedge_descriptor edge_to_flip; - if (compare_distance(p1,p2, p1,p3) != CGAL::SMALLER) // p1p2 > p1p3 - { - if (compare_distance(p1,p2, p2,p3) != CGAL::SMALLER) // p1p2 > p2p3 - // flip p1p2 - edge_to_flip = next( halfedge(fd, tmesh), tmesh ); - else - // flip p2p3 - edge_to_flip = prev( halfedge(fd, tmesh), tmesh ); - } - else - if (compare_distance(p1,p3, p2,p3) != CGAL::SMALLER) // p1p3>p2p3 - //flip p3p1 - edge_to_flip = halfedge(fd, tmesh); - else - //flip p2p3 - edge_to_flip = prev( halfedge(fd, tmesh), tmesh ); - - face_descriptor opposite_face=face( opposite(edge_to_flip, tmesh), tmesh); - if ( opposite_face == GT::null_face() ) - { - // simply remove the face - Euler::remove_face(edge_to_flip, tmesh); - } - else - { - // condition for the flip to be valid (the edge to be created do not already exists) - if ( !halfedge(target(next(edge_to_flip, tmesh), tmesh), - target(next(opposite(edge_to_flip, tmesh), tmesh), tmesh), - tmesh).second ) - { - Euler::flip_edge(edge_to_flip, tmesh); - } - else - { - all_removed=false; -#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG - std::cout << " WARNING: flip is not possible\n"; - // @todo Let p and q be the vertices opposite to `edge_to_flip`, and let - // r be the vertex of `edge_to_flip` that is the furthest away from - // the edge `pq`. In that case I think we should remove all the triangles - // so that the triangle pqr is in the mesh. -#endif - } - } - } - else - { - // Process a connected component of degenerate faces - // get all the faces from the connected component - // and the boundary edges - std::set cc_faces; - std::vector queue; - std::vector boundary_hedges; - std::vector inside_hedges; - queue.push_back(fd); - cc_faces.insert(fd); - - while(!queue.empty()) - { - face_descriptor top=queue.back(); - queue.pop_back(); - for(halfedge_descriptor hd : halfedges_around_face(halfedge(top, tmesh), tmesh) ) - { - face_descriptor adjacent_face = face( opposite(hd, tmesh), tmesh ); - if ( adjacent_face==GT::null_face() || - degenerate_face_set.count(adjacent_face)==0 ) - { - boundary_hedges.push_back(hd); - } - else - { - if (cc_faces.insert(adjacent_face).second) - queue.push_back(adjacent_face); - - if ( hd < opposite(hd, tmesh) ) - inside_hedges.push_back(hd); - } - } - } - -#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG - std::cout << " Deal with a cc of " << cc_faces.size() << " degenerate faces.\n"; - /// dump cc_faces - { - int id=0; - std::map vids; - for(face_descriptor f : cc_faces) - { - if ( vids.insert( std::make_pair( target(halfedge(f, tmesh), tmesh), id) ).second ) ++id; - if ( vids.insert( std::make_pair( target(next(halfedge(f, tmesh), tmesh), tmesh), id) ).second ) ++id; - if ( vids.insert( std::make_pair( target(next(next(halfedge(f, tmesh), tmesh), tmesh), tmesh), id) ).second ) ++id; - } - - std::ofstream output("/tmp/cc_faces.off"); - output << std::setprecision(17); - output << "OFF\n" << vids.size() << " " << cc_faces.size() << " 0\n"; - std::vector points(vids.size()); - typedef std::pair Pair_type; - for(Pair_type p : vids) - - points[p.second]=get(vpmap, p.first); - for(typename Traits::Point_3 p : points) - output << p << "\n"; - - for(face_descriptor f : cc_faces) - { - output << "3 " - << vids[ target(halfedge(f, tmesh), tmesh) ] << " " - << vids[ target(next(halfedge(f, tmesh), tmesh), tmesh) ] << " " - << vids[ target(next(next(halfedge(f, tmesh), tmesh), tmesh), tmesh) ] << "\n"; - } - - for (std::size_t pid=2; pid!=points.size(); ++pid) - { - CGAL_assertion(collinear(points[0], points[1], points[pid])); - } - } -#endif - - // find vertices strictly inside the cc - std::set boundary_vertices; - for(halfedge_descriptor hd : boundary_hedges) - boundary_vertices.insert( target(hd, tmesh) ); - - std::set inside_vertices; - for(halfedge_descriptor hd : inside_hedges) - { - if (!boundary_vertices.count( target(hd, tmesh) )) - inside_vertices.insert( target(hd, tmesh) ); - if (!boundary_vertices.count( source(hd, tmesh) )) - inside_vertices.insert( source(hd, tmesh) ); - } - - // v-e+f = 1 for a topological disk and e = (3f+#boundary_edges)/2 - if (boundary_vertices.size()+inside_vertices.size() - - (cc_faces.size()+boundary_hedges.size())/2 != 1) - { - //cc_faces does not define a topological disk - // @todo Find to way to handle that case -#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG - std::cout << " WARNING: Cannot remove the component of degenerate faces: not a topological disk.\n"; -#endif - - for(face_descriptor f : cc_faces) - degenerate_face_set.erase(f); - - continue; - } - - // preliminary step to check if the operation is possible - // sort the boundary points along the common supporting line - // we first need a reference point - typedef internal::Less_vertex_point Less_vertex; - std::pair< - typename std::set::iterator, - typename std::set::iterator > ref_vertices = - boost::minmax_element( boundary_vertices.begin(), - boundary_vertices.end(), - Less_vertex(traits, vpmap) ); - - // and then we sort the vertices using this reference point - typedef std::set Sorted_point_set; - Sorted_point_set sorted_points; - for(vertex_descriptor v : boundary_vertices) - sorted_points.insert( get(vpmap,v) ); - - CGAL_assertion(sorted_points.size()== - std::set(sorted_points.begin(), - sorted_points.end()).size()); - - CGAL_assertion( get( vpmap, *ref_vertices.first)==*sorted_points.begin() ); - CGAL_assertion( get( vpmap, *ref_vertices.second)==*std::prev(sorted_points.end()) ); - - const typename Traits::Point_3& xtrm1 = *sorted_points.begin(); - const typename Traits::Point_3& xtrm2 = *std::prev(sorted_points.end()); - - // recover halfedges on the hole, bounded by the reference vertices - std::vector side_one, side_two; - - // look for the outgoing border halfedge of the first extreme point - for(halfedge_descriptor hd : boundary_hedges) - { - if ( get(vpmap, source(hd, tmesh)) == xtrm1 ) - { - side_one.push_back(hd); - break; - } - } - CGAL_assertion(side_one.size()==1); - - bool non_monotone_border = false; - - while( get(vpmap, target(side_one.back(), tmesh)) != xtrm2 ) - { - vertex_descriptor prev_vertex = target(side_one.back(), tmesh); - for(halfedge_descriptor hd : boundary_hedges) - { - if ( source(hd, tmesh) == prev_vertex ) - { - if ( get(vpmap, target(hd, tmesh)) < get(vpmap, prev_vertex) ) - non_monotone_border = true; - - side_one.push_back(hd); - break; - } - } - - if (non_monotone_border) - break; - } - - if (non_monotone_border) - { -#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG - std::cout << " WARNING: Cannot remove the component of degenerate faces: border not a monotonic cycle.\n"; -#endif - - for(face_descriptor f : cc_faces) - degenerate_face_set.erase(f); - - continue; - } - - // look for the outgoing border halfedge of second extreme vertex - for(halfedge_descriptor hd : boundary_hedges) - { - if ( source(hd, tmesh) == target(side_one.back(), tmesh) ) - { - side_two.push_back(hd); - break; - } - } - CGAL_assertion(side_two.size()==1); - - while( target(side_two.back(), tmesh) != source(side_one.front(), tmesh) ) - { - vertex_descriptor prev_vertex = target(side_two.back(), tmesh); - for(halfedge_descriptor hd : boundary_hedges) - { - if ( source(hd, tmesh) == prev_vertex ) - { - if ( get(vpmap, target(hd, tmesh)) > get(vpmap, prev_vertex) ) - non_monotone_border = true; - - side_two.push_back(hd); - break; - } - } - - if (non_monotone_border) - break; - } - - if (non_monotone_border) - { -#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG - std::cout << " WARNING: Cannot remove the component of degenerate faces: border not a monotonic cycle.\n"; -#endif - - for(face_descriptor f : cc_faces) - degenerate_face_set.erase(f); - - continue; - } - - CGAL_assertion( side_one.size()+side_two.size()==boundary_hedges.size() ); - - // reverse the order of the second side so as to follow - // the same order than side one - std::reverse(side_two.begin(), side_two.end()); - for(halfedge_descriptor& h : side_two) - h=opposite(h, tmesh); - - //make sure the points of the vertices along side_one are correctly sorted - std::vector side_points; - side_points.reserve(side_one.size()+1); - side_points.push_back(get(vpmap,source(side_one.front(), tmesh))); - - for(halfedge_descriptor h : side_one) - side_points.push_back(get(vpmap,target(h, tmesh))); - - CGAL_assertion(get(vpmap,source(side_one.front(), tmesh))==side_points.front()); - CGAL_assertion(get(vpmap,target(side_one.back(), tmesh))==side_points.back()); - - // @todo the reordering could lead to the apparition of null edges. - std::sort(side_points.begin(), side_points.end()); - - CGAL_assertion(std::unique(side_points.begin(), side_points.end())==side_points.end()); - for(std::size_t i=0;i::iterator side_one_it = side_one.begin(); - typename std::vector::iterator side_two_it = side_two.begin(); - for(;it_pt!=it_pt_end;++it_pt) - { - // check if it_pt is the point of the target of one or two halfedges - bool target_of_side_one = get(vpmap, target(*side_one_it, tmesh))==*it_pt; - bool target_of_side_two = get(vpmap, target(*side_two_it, tmesh))==*it_pt; - - if (target_of_side_one && target_of_side_two) - { - for(halfedge_descriptor h : halfedges_around_target(*side_one_it, tmesh)) - { - if (source(h, tmesh)==target(*side_two_it, tmesh)) - { - non_collapsable=true; - break; - } - } - } - else - { - CGAL_assertion(target_of_side_one || target_of_side_two); - vertex_descriptor v1 = target_of_side_one ? target(*side_one_it, tmesh) - : target(*side_two_it, tmesh); - vertex_descriptor v2 = target_of_side_two ? target(next(opposite(*side_one_it, tmesh), tmesh), tmesh) - : target(next(*side_two_it, tmesh), tmesh); - for(halfedge_descriptor h : halfedges_around_target(v1, tmesh)) - { - if (source(h, tmesh)==v2) - { - non_collapsable=true; - break; - } - } - } - - if(non_collapsable) break; - if (target_of_side_one) ++side_one_it; - if (target_of_side_two) ++side_two_it; - } - - if (non_collapsable) - { - for(face_descriptor f : cc_faces) - degenerate_face_set.erase(f); - -#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG - std::cout << " WARNING: cannot remove a connected components of degenerate faces.\n"; -#endif - continue; - } - - // now proceed to the fix - // update the face and halfedge vertex pointers on the boundary - for(halfedge_descriptor h : boundary_hedges) - { - set_face(h, GT::null_face(), tmesh); - set_halfedge(target(h,tmesh), h, tmesh); - } - - // update next/prev pointers of boundary_hedges - for(halfedge_descriptor h : boundary_hedges) - { - halfedge_descriptor next_candidate = next( h, tmesh); - while (face(next_candidate, tmesh)!=GT::null_face()) - next_candidate = next( opposite( next_candidate, tmesh), tmesh); - - set_next(h, next_candidate, tmesh); - } - - // remove degenerate faces - for(face_descriptor f : cc_faces) - { - degenerate_face_set.erase(f); - remove_face(f, tmesh); - } - - // remove interior edges - for(halfedge_descriptor h : inside_hedges) - remove_edge(edge(h, tmesh), tmesh); - - // remove interior vertices - for(vertex_descriptor v : inside_vertices) - remove_vertex(v, tmesh); - -#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG - std::cout << " side_one.size() " << side_one.size() << "\n"; - std::cout << " side_two.size() " << side_two.size() << "\n"; -#endif - - CGAL_assertion( source(side_one.front(), tmesh) == *ref_vertices.first ); - CGAL_assertion( source(side_two.front(), tmesh) == *ref_vertices.first ); - CGAL_assertion( target(side_one.back(), tmesh) == *ref_vertices.second ); - CGAL_assertion( target(side_two.back(), tmesh) == *ref_vertices.second ); - - // now split each side to contains the same sequence of points - // first side - int hi=0; - for (typename Sorted_point_set::iterator it=std::next(sorted_points.begin()), - it_end=sorted_points.end(); it!=it_end; ++it) - { - CGAL_assertion( *std::prev(it) == get(vpmap, source(side_one[hi], tmesh) ) ); - if( *it != get(vpmap, target(side_one[hi], tmesh) ) ) - { - // split the edge and update the point - halfedge_descriptor h1 = next(opposite(side_one[hi], tmesh), tmesh); - put(vpmap, - target(Euler::split_edge(side_one[hi], tmesh), tmesh), - *it); - - // split_edge updates the halfedge of the source vertex of h, - // since we reuse later the halfedge of the first refernce vertex - // we must set it as we need. - if ( source(h1,tmesh) == *ref_vertices.first) - set_halfedge(*ref_vertices.first, prev( prev(side_one[hi], tmesh), tmesh), tmesh ); - - // retriangulate the opposite face - if ( face(h1, tmesh) != GT::null_face()) - Euler::split_face(h1, opposite(side_one[hi], tmesh), tmesh); - } - else - { - ++hi; - } - } - - // second side - hi=0; - for (typename Sorted_point_set::iterator it=std::next(sorted_points.begin()), - it_end=sorted_points.end(); it!=it_end; ++it) - { - CGAL_assertion( *std::prev(it) == get(vpmap, source(side_two[hi], tmesh) ) ); - if( *it != get(vpmap, target(side_two[hi], tmesh) ) ) - { - // split the edge and update the point - halfedge_descriptor h2 = Euler::split_edge(side_two[hi], tmesh); - put(vpmap, target(h2, tmesh), *it); - - // split_edge updates the halfedge of the source vertex of h, - // since we reuse later the halfedge of the first refernce vertex - // we must set it as we need. - if ( source(h2,tmesh) == *ref_vertices.first) - set_halfedge(*ref_vertices.first, opposite( h2, tmesh), tmesh ); - - // retriangulate the face - if ( face(h2, tmesh) != GT::null_face()) - Euler::split_face(h2, next(side_two[hi], tmesh), tmesh); - } - else - { - ++hi; - } - } - - CGAL_assertion( target(halfedge(*ref_vertices.first, tmesh), tmesh) == *ref_vertices.first ); - CGAL_assertion( face(halfedge(*ref_vertices.first, tmesh), tmesh) == GT::null_face() ); - -#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG - { - halfedge_descriptor h_side2 = halfedge(*ref_vertices.first, tmesh); - halfedge_descriptor h_side1 = next(h_side2, tmesh); - - do - { - CGAL_assertion( get(vpmap, source(h_side1, tmesh)) == get(vpmap, target(h_side2, tmesh)) ); - CGAL_assertion( get(vpmap, target(h_side1, tmesh)) == get(vpmap, source(h_side2, tmesh)) ); - - if ( target(next(opposite(h_side1, tmesh), tmesh), tmesh) == - target(next(opposite(h_side2, tmesh), tmesh), tmesh) ) - { - CGAL_error_msg("Forbidden simplification"); - } - - h_side2 = prev(h_side2, tmesh); - h_side1 = next(h_side1, tmesh); - } - while( target(h_side1, tmesh) != *ref_vertices.second ); - } -#endif - - // remove side1 and replace its opposite hedges by those of side2 - halfedge_descriptor h_side2 = halfedge(*ref_vertices.first, tmesh); - halfedge_descriptor h_side1 = next(h_side2, tmesh); - while(true) - { - CGAL_assertion( get(vpmap, source(h_side1, tmesh)) == get(vpmap, target(h_side2, tmesh)) ); - CGAL_assertion( get(vpmap, target(h_side1, tmesh)) == get(vpmap, source(h_side2, tmesh)) ); - - // backup target vertex - vertex_descriptor vertex_to_remove = target(h_side1, tmesh); - if (vertex_to_remove!=*ref_vertices.second) - { - vertex_descriptor replacement_vertex = source(h_side2, tmesh); - // replace the incident vertex - for(halfedge_descriptor hd : halfedges_around_target(h_side1, tmesh)) - set_target(hd, replacement_vertex, tmesh); - } - - // prev side2 hedge for next loop - halfedge_descriptor h_side2_for_next_turn = prev(h_side2, tmesh); - - // replace the opposite of h_side1 by h_side2 - halfedge_descriptor opposite_h_side1 = opposite( h_side1, tmesh); - face_descriptor the_face = face(opposite_h_side1, tmesh); - set_face(h_side2, the_face, tmesh); - - if (the_face!=GT::null_face()) - set_halfedge(the_face, h_side2, tmesh); - - set_next(h_side2, next(opposite_h_side1, tmesh), tmesh); - set_next(prev(opposite_h_side1, tmesh), h_side2, tmesh); - - // take the next hedges - edge_descriptor edge_to_remove = edge(h_side1, tmesh); - h_side1 = next(h_side1, tmesh); - - // now remove the extra edge - remove_edge(edge_to_remove, tmesh); - - // ... and the extra vertex if it's not the second reference - if (vertex_to_remove==*ref_vertices.second) - { - // update the halfedge pointer of the last vertex (others were already from side 2) - CGAL_assertion( target(opposite(h_side2, tmesh), tmesh) == vertex_to_remove ); - set_halfedge(vertex_to_remove, opposite(h_side2, tmesh), tmesh); - break; - } - else - { - remove_vertex(vertex_to_remove , tmesh); - } - - h_side2 = h_side2_for_next_turn; - } - } - } - - return all_removed; -} - -template -bool remove_degenerate_faces(const FaceRange& face_range, - TriangleMesh& tmesh) -{ - return remove_degenerate_faces(face_range, tmesh, parameters::all_default()); -} - -template -bool remove_degenerate_faces(TriangleMesh& tmesh, - const CGAL_PMP_NP_CLASS& np) -{ - return remove_degenerate_faces(faces(tmesh), tmesh, np); -} - -template -bool remove_degenerate_faces(TriangleMesh& tmesh) -{ - return remove_degenerate_faces(tmesh, - CGAL::Polygon_mesh_processing::parameters::all_default()); -} - -/// \ingroup PMP_repairing_grp -/// checks whether a vertex of a polygon mesh is non-manifold. -/// -/// @tparam PolygonMesh a model of `HalfedgeListGraph` -/// -/// @param v a vertex of `pm` -/// @param pm a triangle mesh containing `v` -/// -/// \warning This function has linear runtime with respect to the size of the mesh. -/// -/// \sa `duplicate_non_manifold_vertices()` -/// -/// \return `true` if the vertex is non-manifold, `false` otherwise. -template -bool is_non_manifold_vertex(typename boost::graph_traits::vertex_descriptor v, - const PolygonMesh& pm) -{ - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - - typedef CGAL::dynamic_halfedge_property_t Halfedge_property_tag; - typedef typename boost::property_map::const_type Visited_halfedge_map; - - // Dynamic pmaps do not have default initialization values (yet) - Visited_halfedge_map visited_halfedges = get(Halfedge_property_tag(), pm); - for(halfedge_descriptor h : halfedges(pm)) - put(visited_halfedges, h, false); - - std::size_t incident_null_faces_counter = 0; - for(halfedge_descriptor h : halfedges_around_target(v, pm)) - { - put(visited_halfedges, h, true); - if(CGAL::is_border(h, pm)) - ++incident_null_faces_counter; - } - - if(incident_null_faces_counter > 1) - { - // The vertex is the sole connection between two connected components --> non-manifold - return true; - } - - for(halfedge_descriptor h : halfedges(pm)) - { - if(v == target(h, pm)) - { - // Haven't seen that halfedge yet ==> more than one umbrella incident to 'v' ==> non-manifold - if(!get(visited_halfedges, h)) - return true; - } - } - - return false; -} - -namespace internal { - -template -struct Vertex_collector -{ - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - - bool has_old_vertex(const vertex_descriptor v) const { return collections.count(v) != 0; } - void tag_old_vertex(const vertex_descriptor v) - { - CGAL_precondition(!has_old_vertex(v)); - collections[v]; - } - - void collect_vertices(vertex_descriptor v1, vertex_descriptor v2) - { - std::vector& verts = collections[v1]; - if(verts.empty()) - verts.push_back(v1); - verts.push_back(v2); - } - - template - void dump(OutputIterator out) - { - typedef std::pair > Pair_type; - for(const Pair_type& p : collections) - *out++ = p.second; - } - - void dump(Emptyset_iterator) { } - - std::map > collections; -}; - -template -typename boost::graph_traits::vertex_descriptor -create_new_vertex_for_sector(typename boost::graph_traits::halfedge_descriptor sector_begin_h, - typename boost::graph_traits::halfedge_descriptor sector_last_h, - PolygonMesh& pm, - const VPM& vpm, - const ConstraintMap& cmap) -{ - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - - vertex_descriptor old_vd = target(sector_begin_h, pm); - vertex_descriptor new_vd = add_vertex(pm); - put(vpm, new_vd, get(vpm, old_vd)); - - put(cmap, new_vd, true); - - set_halfedge(new_vd, sector_begin_h, pm); - halfedge_descriptor h = sector_begin_h; - do - { - set_target(h, new_vd, pm); - - if(h == sector_last_h) - break; - else - h = prev(opposite(h, pm), pm); - } - while(h != sector_begin_h); // for safety - CGAL_assertion(h != sector_begin_h); - - return new_vd; -} - -template -std::size_t make_umbrella_manifold(typename boost::graph_traits::halfedge_descriptor h, - PolygonMesh& pm, - internal::Vertex_collector& dmap, - const NamedParameters& np) -{ - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - - using parameters::get_parameter; - using parameters::choose_parameter; - - typedef typename GetVertexPointMap::type VertexPointMap; - VertexPointMap vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), - get_property_map(vertex_point, pm)); - - typedef typename internal_np::Lookup_named_param_def // default (no constraint pmap) - >::type VerticesMap; - VerticesMap cmap = choose_parameter(get_parameter(np, internal_np::vertex_is_constrained), - Constant_property_map(false)); - - std::size_t nb_new_vertices = 0; - - vertex_descriptor old_v = target(h, pm); - put(cmap, old_v, true); // store the duplicates - - // count the number of borders - int border_counter = 0; - halfedge_descriptor ih = h, done = ih, border_h = h; - do - { - if(is_border(ih, pm)) - { - border_h = ih; - ++border_counter; - } - - ih = prev(opposite(ih, pm), pm); - } - while(ih != done); - - const bool is_non_manifold_within_umbrella = (border_counter > 1); - if(!is_non_manifold_within_umbrella) - { - const bool first_time_meeting_v = !dmap.has_old_vertex(old_v); - if(first_time_meeting_v) - { - // The star is manifold, so if it is the first time we have met that vertex, - // there is nothing to do, we just keep the same vertex. - set_halfedge(old_v, h, pm); // to ensure halfedge(old_v, pm) stays valid - dmap.tag_old_vertex(old_v); // so that we know we have met old_v already, next time, we'll have to duplicate - } - else - { - // This is not the canonical star associated to 'v'. - // Create a new vertex, and move the whole star to that new vertex - halfedge_descriptor last_h = opposite(next(h, pm), pm); - vertex_descriptor new_v = create_new_vertex_for_sector(h, last_h, pm, vpm, cmap); - dmap.collect_vertices(old_v, new_v); - nb_new_vertices = 1; - } - } - // if there is more than one sector, look at each sector and split them away from the main one - else - { - // the first manifold sector, described by two halfedges - halfedge_descriptor sector_start_h = border_h; - CGAL_assertion(is_border(border_h, pm)); - - bool should_stop = false; - bool is_main_sector = true; - do - { - CGAL_assertion(is_border(sector_start_h, pm)); - - // collect the sector and split it away if it must be - halfedge_descriptor sector_last_h = sector_start_h; - do - { - halfedge_descriptor next_h = prev(opposite(sector_last_h, pm), pm); - - if(is_border(next_h, pm)) - break; - - sector_last_h = next_h; - } - while(sector_last_h != sector_start_h); - CGAL_assertion(!is_border(sector_last_h, pm)); - CGAL_assertion(sector_last_h != sector_start_h); - - halfedge_descriptor next_start_h = prev(opposite(sector_last_h, pm), pm); - - // there are multiple CCs incident to this particular vertex, and we should create a new vertex - // if it's not the first umbrella around 'old_v' or not the first sector, but only not if it's - // both the first umbrella and first sector. - const bool must_create_new_vertex = (!is_main_sector || dmap.has_old_vertex(old_v)); - - // In any case, we must set up the next pointer correctly - set_next(sector_start_h, opposite(sector_last_h, pm), pm); - - if(must_create_new_vertex) - { - vertex_descriptor new_v = create_new_vertex_for_sector(sector_start_h, sector_last_h, pm, vpm, cmap); - dmap.collect_vertices(old_v, new_v); - ++nb_new_vertices; - } - else - { - // Ensure that halfedge(old_v, pm) stays valid - set_halfedge(old_v, sector_start_h, pm); - } - - is_main_sector = false; - sector_start_h = next_start_h; - should_stop = (sector_start_h == border_h); - } - while(!should_stop); - } - - return nb_new_vertices; -} - -} // end namespace internal - -/// \ingroup PMP_repairing_grp -/// collects the non-manifold vertices (if any) present in the mesh. A non-manifold vertex `v` is returned -/// via one incident halfedge `h` such that `target(h, pm) = v` for all the umbrellas that `v` apppears in -/// (an umbrella being the set of faces incident to all the halfedges reachable by walking around `v` -/// using `hnext = prev(opposite(h, pm), pm)`, starting from `h`). -/// -/// @tparam PolygonMesh a model of `HalfedgeListGraph` -/// @tparam OutputIterator a model of `OutputIterator` holding objects of type -/// `boost::graph_traits::%halfedge_descriptor` -/// -/// @param pm a triangle mesh -/// @param out the output iterator that collects halfedges incident to `v` -/// -/// \sa `is_non_manifold_vertex()` -/// \sa `duplicate_non_manifold_vertices()` -/// -/// \return the output iterator. -template -OutputIterator non_manifold_vertices(const PolygonMesh& pm, - OutputIterator out) -{ - // Non-manifoldness can appear either: - // - if 'pm' is pinched at a vertex. While traversing the incoming halfedges at this vertex, - // we will meet strictly more than one border halfedge. - // - if there are multiple umbrellas around a vertex. In that case, we will find a non-visited - // halfedge that has for target a vertex that is already visited. - - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - - typedef CGAL::dynamic_vertex_property_t Vertex_bool_tag; - typedef typename boost::property_map::const_type Known_manifold_vertex_map; - typedef CGAL::dynamic_vertex_property_t Vertex_halfedge_tag; - typedef typename boost::property_map::const_type Visited_vertex_map; - typedef CGAL::dynamic_halfedge_property_t Halfedge_property_tag; - typedef typename boost::property_map::const_type Visited_halfedge_map; - - Known_manifold_vertex_map known_nm_vertices = get(Vertex_bool_tag(), pm); - Visited_vertex_map visited_vertices = get(Vertex_halfedge_tag(), pm); - Visited_halfedge_map visited_halfedges = get(Halfedge_property_tag(), pm); - - const halfedge_descriptor null_h = boost::graph_traits::null_halfedge(); - - // Dynamic pmaps do not have default initialization values (yet) - for(vertex_descriptor v : vertices(pm)) - { - put(known_nm_vertices, v, false); - put(visited_vertices, v, null_h); - } - for(halfedge_descriptor h : halfedges(pm)) - put(visited_halfedges, h, false); - - for(halfedge_descriptor h : halfedges(pm)) - { - // If 'h' is not visited yet, we walk around the target of 'h' and mark these - // halfedges as visited. Thus, if we are here and the target is already marked as visited, - // it means that the vertex is non manifold. - if(!get(visited_halfedges, h)) - { - put(visited_halfedges, h, true); - bool is_non_manifold = false; - - vertex_descriptor v = target(h, pm); - if(get(visited_vertices, v) != null_h) // already seen this vertex, but not from this star - { - is_non_manifold = true; - // if this is the second time we visit that vertex and the first star was manifold, we have - // never reported the first star, but we must now - if(!get(known_nm_vertices, v)) - *out++ = get(visited_vertices, v); // that's a halfedge of the first star we've seen 'v' in - } - else - { - // first time we meet this vertex, just mark it so, and keep the halfedge we found the vertex with in memory - put(visited_vertices, v, h); - } - - // While walking the star of this halfedge, if we meet a border halfedge more than once, - // it means the mesh is pinched and we are also in the case of a non-manifold situation - halfedge_descriptor ih = h, done = ih; - int border_counter = 0; - do - { - put(visited_halfedges, ih, true); - if(is_border(ih, pm)) - ++border_counter; - - ih = prev(opposite(ih, pm), pm); - } - while(ih != done); - - if(border_counter > 1) - is_non_manifold = true; - - if(is_non_manifold) - { - *out++ = h; - put(known_nm_vertices, v, true); - } - } - } - - return out; -} - -/// \ingroup PMP_repairing_grp -/// duplicates all the non-manifold vertices of the input mesh. -/// -/// @tparam PolygonMesh a model of `HalfedgeListGraph` and `MutableHalfedgeGraph` -/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" -/// -/// @param pm the surface mesh to be repaired -/// @param np optional \ref pmp_namedparameters "Named Parameters" described below -/// -/// \cgalNamedParamsBegin -/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. -/// The type of this map is model of `ReadWritePropertyMap`. -/// If this parameter is omitted, an internal property map for -/// `CGAL::vertex_point_t` should be available in `PolygonMesh` -/// \cgalParamEnd -/// \cgalParamBegin{vertex_is_constrained_map} a writable property map with `vertex_descriptor` -/// as key and `bool` as `value_type`. `put(pmap, v, true)` will be called for each duplicated -/// vertices, as well as the original non-manifold vertex in the input mesh. -/// \cgalParamEnd -/// \cgalParamBegin{output_iterator} a model of `OutputIterator` with value type -/// `std::vector`. The first vertex of each vector is a non-manifold vertex -/// of the input mesh, followed by the new vertices that were created to fix this precise -/// non-manifold configuration. -/// \cgalParamEnd -/// \cgalNamedParamsEnd -/// -/// \return the number of vertices created. -template -std::size_t duplicate_non_manifold_vertices(PolygonMesh& pm, - const NamedParameters& np) -{ - using parameters::get_parameter; - using parameters::choose_parameter; - - typedef boost::graph_traits GT; - typedef typename GT::halfedge_descriptor halfedge_descriptor; - - typedef typename internal_np::Lookup_named_param_def < - internal_np::output_iterator_t, - NamedParameters, - Emptyset_iterator - > ::type Output_iterator; - - Output_iterator out = choose_parameter(get_parameter(np, internal_np::output_iterator)); - - std::vector non_manifold_cones; - non_manifold_vertices(pm, std::back_inserter(non_manifold_cones)); - - internal::Vertex_collector dmap; - std::size_t nb_new_vertices = 0; - if(!non_manifold_cones.empty()) - { - for(halfedge_descriptor h : non_manifold_cones) - nb_new_vertices += internal::make_umbrella_manifold(h, pm, dmap, np); - - dmap.dump(out); - } - - return nb_new_vertices; -} - -template -std::size_t duplicate_non_manifold_vertices(PolygonMesh& pm) -{ - return duplicate_non_manifold_vertices(pm, parameters::all_default()); -} - -/// \cond SKIP_IN_MANUAL -template -std::pair< bool, bool > -remove_self_intersections_one_step(TriangleMesh& tm, - std::set& faces_to_remove, - VertexPointMap& vpmap, - int step, - bool preserve_genus, - bool verbose) -{ - std::set faces_to_remove_copy = faces_to_remove; - - if (verbose) - std::cout << "DEBUG: running remove_self_intersections_one_step, step " << step - << " with " << faces_to_remove.size() << " intersecting faces\n"; - - CGAL_assertion(tm.is_valid()); - - typedef boost::graph_traits graph_traits; - typedef typename graph_traits::vertex_descriptor vertex_descriptor; - typedef typename graph_traits::edge_descriptor edge_descriptor; - typedef typename graph_traits::halfedge_descriptor halfedge_descriptor; - - bool something_was_done = false; // indicates if a region was successfully remeshed - bool all_fixed = true; // indicates if all removal went well - // indicates if a removal was not possible because the region handle has - // some boundary cycle of halfedges - bool topology_issue = false; - - if (verbose) - { - std::cout << " DEBUG: is_valid in one_step(tm)? "; - std::cout.flush(); - std::cout << is_valid_polygon_mesh(tm) << "\n"; - } - - if(!faces_to_remove.empty()){ - - while(!faces_to_remove.empty()) - { - // Process a connected component of faces to remove. - // collect all the faces from the connected component - std::set cc_faces; - std::vector queue(1, *faces_to_remove.begin()); // temporary queue - cc_faces.insert(queue.back()); - while(!queue.empty()) - { - face_descriptor top=queue.back(); - queue.pop_back(); - halfedge_descriptor h = halfedge(top,tm); - for (int i=0;i<3; ++i) - { - face_descriptor adjacent_face = face( opposite(h, tm), tm ); - if ( adjacent_face!=boost::graph_traits::null_face()) - { - if (faces_to_remove.count(adjacent_face) != 0 && - cc_faces.insert(adjacent_face).second) - queue.push_back(adjacent_face); - } - h = next(h, tm); - } - } - - // expand the region to be filled - if (step > 0) - expand_face_selection(cc_faces, tm, step, - make_boolean_property_map(cc_faces), - Emptyset_iterator()); - - // try to compactify the selection region by also selecting all the faces included - // in the bounding box of the initial selection - std::vector stack_for_expension; - Bbox_3 bb; - for(face_descriptor fd : cc_faces) - { - for(halfedge_descriptor h : halfedges_around_face(halfedge(fd, tm), tm)) - { - bb += get(vpmap, target(h, tm)).bbox(); - face_descriptor nf = face(opposite(h, tm), tm); - if (nf != boost::graph_traits::null_face() && - cc_faces.count(nf)==0) - { - stack_for_expension.push_back(opposite(h, tm)); - } - } - } - - while(!stack_for_expension.empty()) - { - halfedge_descriptor h=stack_for_expension.back(); - stack_for_expension.pop_back(); - if ( cc_faces.count(face(h,tm))==1) continue; - if ( do_overlap(bb, get(vpmap, target(next(h, tm), tm)).bbox()) ) - { - cc_faces.insert(face(h,tm)); - halfedge_descriptor candidate = opposite(next(h, tm), tm); - if ( face(candidate, tm) != boost::graph_traits::null_face() ) - stack_for_expension.push_back( candidate ); - candidate = opposite(prev(h, tm), tm); - if ( face(candidate, tm) != boost::graph_traits::null_face() ) - stack_for_expension.push_back( candidate ); - } - } - - // remove faces from the set to process - for(face_descriptor f : cc_faces) - faces_to_remove.erase(f); - - if (cc_faces.size()==1) continue; // it is a triangle nothing better can be done - - //Check for non-manifold vertices in the selection and remove them by selecting all incident faces: - // extract the set of halfedges that is on the boundary of the holes to be - // made. In addition, we make sure no hole to be created contains a vertex - // visited more than once along a hole border (pinched surface) - // We save the size of boundary_hedges to make sur halfedges added - // from non_filled_hole are not removed. - bool non_manifold_vertex_remaining_in_selection = false; - do{ - bool non_manifold_vertex_removed = false; //here non-manifold is for the 1D polyline - std::vector boundary_hedges; - for(face_descriptor fh : cc_faces) - { - halfedge_descriptor h = halfedge(fh,tm); - for (int i=0;i<3; ++i) - { - if ( is_border( opposite(h, tm), tm) || - cc_faces.count( face( opposite(h, tm), tm) ) == 0) - { - boundary_hedges.push_back(h); - } - h=next(h, tm); - } - } - - // detect vertices visited more than once along - // a hole border. We then remove all faces incident - // to such a vertex to force the removal of the vertex. - // Actually even if two holes are sharing a vertex, this - // vertex will be removed. It is not needed but since - // we do not yet have one halfedge per hole it is simpler - // and does not harm - std::set border_vertices; - for(halfedge_descriptor h : boundary_hedges) - { - if (!border_vertices.insert(target(h,tm)).second){ - bool any_face_added = false; - for(halfedge_descriptor hh : halfedges_around_target(h,tm)){ - if (!is_border(hh, tm)) - { - any_face_added |= cc_faces.insert(face(hh, tm)).second; // add the face to the current selection - faces_to_remove.erase(face(hh, tm)); - } - } - if (any_face_added) - non_manifold_vertex_removed=true; - else - non_manifold_vertex_remaining_in_selection=true; - } - } - - if (!non_manifold_vertex_removed) - break; - } - while(true); - - if (preserve_genus && non_manifold_vertex_remaining_in_selection) - { - topology_issue = true; - if(verbose) - std::cout << " DEBUG: CC not handled due to the presence at least one non-manifold vertex\n"; - continue; // cannot replace a patch containing a nm vertex by a disk - } - - // before running this function if preserve_genus=false, we duplicated - // all of them - CGAL_assertion( !non_manifold_vertex_remaining_in_selection ); - - - // Collect halfedges on the boundary of the region to be selected - // (pointing inside the domain to be remeshed) - std::vector cc_border_hedges; - for(face_descriptor fd : cc_faces) - { - halfedge_descriptor h = halfedge(fd, tm); - for (int i=0; i<3;++i) - { - if ( is_border(opposite(h, tm), tm) || - cc_faces.count( face(opposite(h, tm), tm) )== 0) - { - cc_border_hedges.push_back(h); - } - h=next(h, tm); - } - } - - if(!is_selection_a_topological_disk(cc_faces, tm)) - { - // check if the selection contains cycles of border halfedges - bool only_border_edges = true; - std::set mesh_border_hedge; - - for(halfedge_descriptor h : cc_border_hedges) - { - if ( !is_border(opposite(h, tm), tm) ) - only_border_edges = false; - else - mesh_border_hedge.insert( opposite(h, tm) ); - } - int nb_cycles=0; - while(!mesh_border_hedge.empty()) - { - // we must count the number of cycle of boundary edges - halfedge_descriptor h_b = *mesh_border_hedge.begin(), h=h_b; - mesh_border_hedge.erase( mesh_border_hedge.begin() ); - do{ - h=next(h, tm); - if (h==h_b) - { - // found a cycle - ++nb_cycles; - break; - } - else - { - typename std::set::iterator it = - mesh_border_hedge.find(h); - if ( it == mesh_border_hedge.end() ) - break; // not a cycle - mesh_border_hedge.erase(it); - } - }while(true); - } - - if(nb_cycles > (only_border_edges ? 1 : 0) ) - { - if(verbose) - std::cout << " DEBUG: CC not handled due to the presence of " - << nb_cycles << " of boundary edges\n"; - topology_issue = true; - continue; - } - else - { - if (preserve_genus) - { - if(verbose) - std::cout << " DEBUG: CC not handled because it is not a topological disk (preserve_genus=true)\n"; - all_fixed = false; - continue; - } - // count the number of cycles of halfedges of the boundary - std::map bhs; - for(halfedge_descriptor h : cc_border_hedges) - { - bhs[source(h, tm)]=target(h, tm); - } - int nbc=0; - while(!bhs.empty()) - { - ++nbc; - std::pair top=*bhs.begin(); - bhs.erase(bhs.begin()); - do - { - typename std::map::iterator - it_find = bhs.find(top.second); - if (it_find == bhs.end()) break; - top = *it_find; - bhs.erase(it_find); - } - while(true); - } - if (nbc!=1){ - if(verbose) - std::cout << " DEBUG: CC not handled because it is not a topological disk(" - << nbc << " boundary cycles)\n"; - all_fixed = false; - continue; - } - else - { - if(verbose) - std::cout << " DEBUG: CC that is not a topological disk but has only one boundary cycle(preserve_genus=false)\n"; - } - } - } - - // sort halfedges so that they describe the sequence - // of halfedges of the hole to be made - CGAL_assertion( cc_border_hedges.size() > 2 ); - for(std::size_t i=0; i < cc_border_hedges.size()-2; ++i) - { - vertex_descriptor tgt = target(cc_border_hedges[i], tm); - for(std::size_t j=i+1; j cc_interior_vertices; - std::set cc_interior_edges; - - // first collect all vertices and edges incident to the faces to remove - for(face_descriptor fh : cc_faces) - { - for(halfedge_descriptor h : halfedges_around_face(halfedge(fh,tm),tm)) - { - if (halfedge(target(h, tm), tm)==h) // limit the number of insertions - cc_interior_vertices.insert(target(h, tm)); - cc_interior_edges.insert(edge(h,tm)); - } - } - // and then remove those on the boundary - for(halfedge_descriptor h : cc_border_hedges) - { - cc_interior_vertices.erase(target(h, tm)); - cc_interior_edges.erase(edge(h,tm)); - } - - if (verbose) - { - std::cout << " DEBUG: is_valid(tm) in one_step, before mesh changes? "; - std::cout << is_valid_polygon_mesh(tm) << std::endl; - } - - //try hole_filling. - typedef CGAL::Triple Face_indices; - typedef typename boost::property_traits::value_type Point; - std::vector hole_points, third_points; - hole_points.reserve(cc_border_hedges.size()); - third_points.reserve(cc_border_hedges.size()); - std::vector border_vertices; - for(halfedge_descriptor h : cc_border_hedges) - { - vertex_descriptor v = source(h, tm); - hole_points.push_back( get(vpmap, v) ); - border_vertices.push_back(v); - third_points.push_back(get(vpmap, target(next(opposite(h, tm), tm), tm))); // @todo fix me for mesh border edges - } - CGAL_assertion(hole_points.size() >= 3); - - // try to triangulate the hole using default parameters - //(using Delaunay search space if CGAL_HOLE_FILLING_DO_NOT_USE_DT3 is not defined) - std::vector patch; - if (hole_points.size()>3) - triangulate_hole_polyline(hole_points, - third_points, - std::back_inserter(patch)); - else - patch.push_back(Face_indices(0,1,2)); // trivial hole filling - - if(patch.empty()) - { -#ifndef CGAL_HOLE_FILLING_DO_NOT_USE_DT3 - if (verbose) - std::cout << " DEBUG: Failed to fill a hole using Delaunay search space.\n"; - triangulate_hole_polyline(hole_points, - third_points, - std::back_inserter(patch), - parameters::use_delaunay_triangulation(false)); -#endif - if (patch.empty()) - { - if (verbose) - std::cout << " DEBUG: Failed to fill a hole using the whole search space.\n"; - all_fixed = false; - continue; - } - } - - // make sure that the hole filling is valid, we check that no - // edge already in the mesh is present in patch. - bool non_manifold_edge_found = false; - for(const Face_indices& triangle : patch) - { - std::array edges = - make_array(triangle.first, triangle.second, - triangle.second, triangle.third, - triangle.third, triangle.first); - for (int k=0; k<3; ++k) - { - int vi=edges[2*k], vj=edges[2*k+1]; - // ignore boundary edges - if (vi+1==vj || (vj==0 && static_cast(vi)==border_vertices.size()-1) ) - continue; - halfedge_descriptor h = halfedge(border_vertices[vi], border_vertices[vj], tm).first; - if (h!=boost::graph_traits::null_halfedge() && - cc_interior_edges.count(edge(h, tm))==0) - { - non_manifold_edge_found=true; - break; - } - } - if (non_manifold_edge_found) break; - } - if (non_manifold_edge_found) - { - if (verbose) - std::cout << " DEBUG: Triangulation produced is non-manifold when plugged into the mesh.\n"; - all_fixed = false; - continue; - } - - // plug the new triangles in the mesh, reusing previous edges and faces - std::vector edge_stack(cc_interior_edges.begin(), cc_interior_edges.end()); - std::vector face_stack(cc_faces.begin(), cc_faces.end()); - - std::map< std::pair, halfedge_descriptor > halfedge_map; - int i=0; - // register border halfedges - for(halfedge_descriptor h : cc_border_hedges) - { - int j = static_cast( std::size_t(i+1)%cc_border_hedges.size() ); - halfedge_map.insert(std::make_pair( std::make_pair(i, j), h) ); - set_halfedge(target(h, tm), h, tm); // update vertex halfedge pointer - CGAL_assertion( border_vertices[i] == source(h, tm) && - border_vertices[j] == target(h, tm) ); - ++i; - } - - std::vector hedges; - hedges.reserve(4); - face_descriptor f = boost::graph_traits::null_face(); - for(const Face_indices& triangle : patch) - { - // get the new face - if (face_stack.empty()) - f=add_face(tm); - else - { - f=face_stack.back(); - face_stack.pop_back(); - } - - std::array indices = - make_array( triangle.first, - triangle.second, - triangle.third, - triangle.first ); - for (int i=0; i<3; ++i) - { - // get the corresponding halfedge (either a new one or an already created) - typename std::map< std::pair , halfedge_descriptor >::iterator insert_res = - halfedge_map.insert( - std::make_pair( std::make_pair(indices[i], indices[i+1]), - boost::graph_traits::null_halfedge() ) ).first; - if (insert_res->second == boost::graph_traits::null_halfedge()) - { - if (edge_stack.empty()) - insert_res->second = halfedge(add_edge(tm), tm); - else - { - insert_res->second = halfedge(edge_stack.back(), tm); - edge_stack.pop_back(); - } - - halfedge_map[std::make_pair(indices[i+1], indices[i])] = - opposite(insert_res->second, tm); - } - hedges.push_back(insert_res->second); - } - hedges.push_back(hedges.front()); - // update halfedge connections + face pointers - for(int i=0; i<3;++i) - { - set_next(hedges[i], hedges[i+1], tm); - set_face(hedges[i], f, tm); - set_target(hedges[i], border_vertices[indices[i+1]], tm); - } - set_halfedge(f, hedges[0], tm); - hedges.clear(); - } - - // now remove remaining edges, - for(edge_descriptor e : edge_stack) - remove_edge(e, tm); - // vertices, - for(vertex_descriptor vh : cc_interior_vertices) - remove_vertex(vh, tm); - // and remaning faces - for(face_descriptor f : face_stack) - remove_face(f, tm); - - if (verbose) - std::cout << " DEBUG: " << cc_faces.size() << " triangles removed, " - << patch.size() << " created\n"; - - CGAL_assertion(is_valid_polygon_mesh(tm)); - - something_was_done = true; - } - } - if (!something_was_done) - { - faces_to_remove.swap(faces_to_remove_copy); - if (verbose) - std::cout<<" DEBUG: Nothing was changed during this step, self-intersections won't be recomputed."< -bool remove_self_intersections(TriangleMesh& tm, const NamedParameters& np) -{ - typedef boost::graph_traits graph_traits; - typedef typename graph_traits::face_descriptor face_descriptor; - - // named parameter extraction - typedef typename GetVertexPointMap::type VertexPointMap; - VertexPointMap vpm = parameters::choose_parameter(parameters::get_parameter(np, internal_np::vertex_point), - get_property_map(vertex_point, tm)); - - const int max_steps = parameters::choose_parameter(parameters::get_parameter(np, internal_np::number_of_iterations), 7); - bool verbose = parameters::choose_parameter(parameters::get_parameter(np, internal_np::verbosity_level), 0) > 0; - bool preserve_genus = parameters::choose_parameter(parameters::get_parameter(np, internal_np::preserve_genus), true); - - if (verbose) - std::cout << "DEBUG: Starting remove_self_intersections, is_valid(tm)? " << is_valid_polygon_mesh(tm) << "\n"; - - // first handle the removal of degenerate faces - remove_degenerate_faces(tm, np); - - if (!preserve_genus) - duplicate_non_manifold_vertices(tm, np); - - if (verbose) - std::cout << "DEBUG: After degenerate faces removal, is_valid(tm)? " << is_valid_polygon_mesh(tm) << "\n"; - - // Look for self-intersections in the polyhedron and remove them - int step=-1; - bool all_fixed = true; // indicates if the filling of all created holes went fine - bool topology_issue = false; // indicates if some boundary cycles of edges are blocking the fixing - std::set faces_to_remove; - while( ++step Face_pair; - std::vector self_inter; - // @todo : possible optimization to reduce the range to check with the bbox - // of the previous patches or something. - self_intersections(tm, std::back_inserter(self_inter)); - - for(Face_pair fp : self_inter) - { - faces_to_remove.insert(fp.first); - faces_to_remove.insert(fp.second); - } - } - - if ( faces_to_remove.empty() && all_fixed){ - if (verbose) - std::cout<<"DEBUG: There is no more face to remove."< -bool remove_self_intersections(TriangleMesh& tm) -{ - return remove_self_intersections(tm, parameters::all_default()); -} -/// \endcond - -} } // end of CGAL::Polygon_mesh_processing +} // namespace Polygon_mesh_processing +} // namespace CGAL #endif // CGAL_POLYGON_MESH_PROCESSING_REPAIR_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair_degeneracies.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair_degeneracies.h new file mode 100644 index 00000000000..5ef95e053f6 --- /dev/null +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair_degeneracies.h @@ -0,0 +1,2397 @@ +// Copyright (c) 2015-2019 GeometryFactory (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) : Sebastien Loriot, +// Mael Rouxel-Labbé +// +#ifndef CGAL_POLYGON_MESH_PROCESSING_REPAIR_DEGENERACIES_H +#define CGAL_POLYGON_MESH_PROCESSING_REPAIR_DEGENERACIES_H + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG +#include +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// First part of the file: remove_ALMOST_degenerate_faces (needles/caps) +// Second part of the file: remove_degenerate_edges/faces + +namespace CGAL { +namespace Polygon_mesh_processing { +namespace internal { + +template +std::array::halfedge_descriptor, 2> +is_badly_shaped(const typename boost::graph_traits::face_descriptor f, + TriangleMesh& tmesh, + const VPM& vpm, + const ECM& ecm, + const Traits& gt, + const double cap_threshold, // angle over 160° ==> cap + const double needle_threshold, // longest edge / shortest edge over this ratio ==> needle + const double collapse_length_threshold) // max length of edges allowed to be collapsed +{ + namespace PMP = CGAL::Polygon_mesh_processing; + + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + + const halfedge_descriptor null_h = boost::graph_traits::null_halfedge(); + + halfedge_descriptor res = PMP::is_needle_triangle_face(f, tmesh, needle_threshold, + parameters::vertex_point_map(vpm) + .geom_traits(gt)); + if(res != null_h && !get(ecm, edge(res, tmesh))) + { + // don't want to collapse edges that are too large + if(collapse_length_threshold == 0 || + edge_length(res, tmesh, parameters::vertex_point_map(vpm).geom_traits(gt)) <= collapse_length_threshold) + { + return make_array(res, null_h); + } + } + else // let's not make it possible to have a face be both a cap and a needle (for now) + { + res = PMP::is_cap_triangle_face(f, tmesh, cap_threshold, parameters::vertex_point_map(vpm).geom_traits(gt)); + if(res != null_h && !get(ecm, edge(res, tmesh))) + return make_array(null_h, res); + } + + return make_array(null_h, null_h); +} + +template +void collect_badly_shaped_triangles(const typename boost::graph_traits::face_descriptor f, + TriangleMesh& tmesh, + const VPM& vpm, + const ECM& ecm, + const Traits& gt, + const double cap_threshold, // angle over this threshold (as a cosine) ==> cap + const double needle_threshold, // longest edge / shortest edge over this ratio ==> needle + const double collapse_length_threshold, // max length of edges allowed to be collapsed + EdgeContainer& edges_to_collapse, + EdgeContainer& edges_to_flip) +{ + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + + std::array res = is_badly_shaped(f, tmesh, vpm, ecm, gt, cap_threshold, + needle_threshold, collapse_length_threshold); + + if(res[0] != boost::graph_traits::null_halfedge()) + { +#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES + std::cout << "add new needle: " << edge(res[0], tmesh) << std::endl; +#endif + edges_to_collapse.insert(edge(res[0], tmesh)); + } + else // let's not make it possible to have a face be both a cap and a needle (for now) + { + if(res[1] != boost::graph_traits::null_halfedge()) + { +#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES + std::cout << "add new cap: " << edge(res[1],tmesh) << std::endl; +#endif + edges_to_flip.insert(edge(res[1], tmesh)); + } + } +} + +/* +// Following Ronfard et al. 96 we look at variation of the normal after the collapse +// the collapse must be topologically valid +template +bool is_collapse_geometrically_valid(typename boost::graph_traits::halfedge_descriptor h, + const TriangleMesh& tmesh, + const NamedParameters& np) +{ + using CGAL::parameters::choose_parameter; + using CGAL::parameters::get_parameter; + + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename GetVertexPointMap::const_type VPM; + typedef typename boost::property_traits::reference Point_ref; + typedef typename GetGeomTraits::type Traits; + + VPM vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), + get_const_property_map(vertex_point, tmesh)); + Traits gt = choose_parameter(get_parameter(np, internal_np::geom_traits), Traits()); + + // @todo handle boundary edges + + h = opposite(h, tmesh); // Euler::collapse edge keeps the target and removes the source + + // source is kept, target is removed + CGAL_assertion(target(h, tmesh) == vertex_removed); + Point_ref kept = get(vpm, source(h, tmesh)); + Point_ref removed= get(vpm, target(h, tmesh)); + + // consider triangles incident to the vertex removed + halfedge_descriptor stop = prev(opposite(h, tmesh), tmesh); + halfedge_descriptor hi = opposite(next(h, tmesh), tmesh); + + std::vector triangles; + while(hi != stop) + { + if(!is_border(hi, tmesh)) + { + Point_ref a = get(vpm, target(next(hi, tmesh), tmesh)); + Point_ref b = get(vpm, source(hi, tmesh)); + + //ack a-b-point_remove and a-b-point_kept has a compatible orientation + // @todo use a predicate + typename Traits::Vector_3 n1 = gt.construct_cross_product_vector_3_object()(removed-a, b-a); + typename Traits::Vector_3 n2 = gt.construct_cross_product_vector_3_object()(kept-a, b-a); + if(gt.compute_scalar_product_3_object()(n1, n2) <= 0) + return false; + } + + hi = opposite(next(hi, tmesh), tmesh); + } + + return true; +} +*/ + +template +boost::optional get_collapse_volume(typename boost::graph_traits::halfedge_descriptor h, + const TriangleMesh& tmesh, + const VPM& vpm, + const Traits& gt) +{ + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + + typedef typename boost::property_traits::reference Point_ref; + typedef typename Traits::Vector_3 Vector_3; + + const typename Traits::Point_3 origin(ORIGIN); + +// @todo handle boundary edges + + h = opposite(h, tmesh); // Euler::collapse edge keeps the target and removes the source + + // source is kept, target is removed + Point_ref kept = get(vpm, source(h, tmesh)); + Point_ref removed= get(vpm, target(h, tmesh)); + + // init volume with incident triangles (reversed orientation + double delta_vol = volume(removed, kept, get(vpm, target(next(h, tmesh), tmesh)), origin) + + volume(kept, removed, get(vpm, target(next(opposite(h, tmesh), tmesh), tmesh)), origin); + + // consider triangles incident to the vertex removed + halfedge_descriptor stop = prev(opposite(h, tmesh), tmesh); + halfedge_descriptor hi = opposite(next(h, tmesh), tmesh); + + std::vector triangles; + while(hi != stop) + { + if(!is_border(hi, tmesh)) + { + Point_ref a = get(vpm, target(next(hi, tmesh), tmesh)); + Point_ref b = get(vpm, source(hi, tmesh)); + + //ack a-b-point_remove and a-b-point_kept has a compatible orientation + // @todo use a predicate + Vector_3 v_ab = gt.construct_vector_3_object()(a, b); + Vector_3 v_ar = gt.construct_vector_3_object()(a, removed); + Vector_3 v_ak = gt.construct_vector_3_object()(a, kept); + + Vector_3 n1 = gt.construct_cross_product_vector_3_object()(v_ar, v_ab); + Vector_3 n2 = gt.construct_cross_product_vector_3_object()(v_ak, v_ab); + if(gt.compute_scalar_product_3_object()(n1, n2) <= 0) + return boost::none; + + delta_vol += volume(b, a, removed, origin) + volume(a, b, kept, origin); // opposite orientation + } + + hi = opposite(next(hi, tmesh), tmesh); + } + + return CGAL::abs(delta_vol); +} + +template +typename boost::graph_traits::halfedge_descriptor +get_best_edge_orientation(typename boost::graph_traits::edge_descriptor e, + const TriangleMesh& tmesh, + const VPM& vpm, + const VCM& vcm, + const Traits& gt) +{ + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + + halfedge_descriptor h = halfedge(e, tmesh), ho = opposite(h, tmesh); + + CGAL_assertion(!get(vcm, source(h, tmesh)) || !get(vcm, target(h, tmesh))); + + boost::optional dv1 = get_collapse_volume(h, tmesh, vpm, gt); + boost::optional dv2 = get_collapse_volume(ho, tmesh, vpm, gt); + + // the resulting point of the collapse of a halfedge is the target of the halfedge before collapse + if(get(vcm, source(h, tmesh))) + return dv2 != boost::none ? ho + : boost::graph_traits::null_halfedge(); + + if(get(vcm, target(h, tmesh))) + return dv1 != boost::none ? h + : boost::graph_traits::null_halfedge(); + + if(dv1 != boost::none) + { + if(dv2 != boost::none) + return (*dv1 < *dv2) ? h : ho; + + return h; + } + + if(dv2 != boost::none) + return ho; + + return boost::graph_traits::null_halfedge(); +} + +// adapted from triangulate_faces +template +bool should_flip(typename boost::graph_traits::edge_descriptor e, + const TriangleMesh& tmesh, + const VPM& vpm, + const Traits& gt) +{ + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + + typedef typename boost::property_traits::reference Point_ref; + typedef typename Traits::Vector_3 Vector_3; + + CGAL_precondition(!is_border(e, tmesh)); + + halfedge_descriptor h = halfedge(e, tmesh); + + Point_ref p0 = get(vpm, target(h, tmesh)); + Point_ref p1 = get(vpm, target(next(h, tmesh), tmesh)); + Point_ref p2 = get(vpm, source(h, tmesh)); + Point_ref p3 = get(vpm, target(next(opposite(h, tmesh), tmesh), tmesh)); + + /* Chooses the diagonal that will split the quad in two triangles that maximize + * the scalar product of of the un-normalized normals of the two triangles. + * The lengths of the un-normalized normals (computed using cross-products of two vectors) + * are proportional to the area of the triangles. + * Maximize the scalar product of the two normals will avoid skinny triangles, + * and will also taken into account the cosine of the angle between the two normals. + * In particular, if the two triangles are oriented in different directions, + * the scalar product will be negative. + */ + +// CGAL::cross_product(p2-p1, p3-p2) * CGAL::cross_product(p0-p3, p1-p0); +// CGAL::cross_product(p1-p0, p1-p2) * CGAL::cross_product(p3-p2, p3-p0); + + const Vector_3 v01 = gt.construct_vector_3_object()(p0, p1); + const Vector_3 v12 = gt.construct_vector_3_object()(p1, p2); + const Vector_3 v23 = gt.construct_vector_3_object()(p2, p3); + const Vector_3 v30 = gt.construct_vector_3_object()(p3, p0); + + const double p1p3 = gt.compute_scalar_product_3_object()( + gt.construct_cross_product_vector_3_object()(v12, v23), + gt.construct_cross_product_vector_3_object()(v30, v01)); + + const Vector_3 v21 = gt.construct_opposite_vector_3_object()(v12); + const Vector_3 v03 = gt.construct_opposite_vector_3_object()(v30); + + const double p0p2 = gt.compute_scalar_product_3_object()( + gt.construct_cross_product_vector_3_object()(v01, v21), + gt.construct_cross_product_vector_3_object()(v23, v03)); + + return p0p2 <= p1p3; +} + +} // namespace internal + +namespace experimental { + +// @todo check what to use as priority queue with removable elements, set might not be optimal +template +bool remove_almost_degenerate_faces(const FaceRange& face_range, + TriangleMesh& tmesh, + const double cap_threshold, + const double needle_threshold, + const double collapse_length_threshold, + const NamedParameters& np) +{ + using CGAL::parameters::choose_parameter; + using CGAL::parameters::get_parameter; + + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + typedef typename boost::graph_traits::face_descriptor face_descriptor; + + typedef Constant_property_map Default_VCM; + typedef typename internal_np::Lookup_named_param_def::type VCM; + VCM vcm_np = choose_parameter(get_parameter(np, internal_np::vertex_is_constrained), Default_VCM(false)); + + typedef Constant_property_map Default_ECM; + typedef typename internal_np::Lookup_named_param_def::type ECM; + ECM ecm = choose_parameter(get_parameter(np, internal_np::edge_is_constrained), Default_ECM(false)); + + typedef typename GetVertexPointMap::const_type VPM; + VPM vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), + get_const_property_map(vertex_point, tmesh)); + + typedef typename GetGeomTraits::type Traits; + Traits gt = choose_parameter(get_parameter(np, internal_np::geom_traits), Traits()); + + // Vertex property map that combines the VCM and the fact that extremities of a constrained edge should be constrained + typedef CGAL::dynamic_vertex_property_t Vertex_property_tag; + typedef typename boost::property_map::type DVCM; + DVCM vcm = get(Vertex_property_tag(), tmesh); + + for(face_descriptor f : face_range) + { + if(f == boost::graph_traits::null_face()) + continue; + + for(halfedge_descriptor h : CGAL::halfedges_around_face(halfedge(f, tmesh), tmesh)) + { + if(get(ecm, edge(h, tmesh))) + { + put(vcm, source(h, tmesh), true); + put(vcm, target(h, tmesh), true); + } + else if(get(vcm_np, target(h, tmesh))) + { + put(vcm, target(h, tmesh), true); + } + } + } + + // Start the process of removing bad elements + std::set edges_to_collapse; + std::set edges_to_flip; + + // @todo could probably do something a bit better by looping edges, consider the incident faces + // f1 / f2 and look at f1 if f1 next_edges_to_collapse; + std::set next_edges_to_flip; + + // treat needles +#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA + int kk=0; + std::ofstream(std::string("tmp/n-00000.off")) << tmesh; +#endif + while(!edges_to_collapse.empty()) + { + edge_descriptor e = *edges_to_collapse.begin(); + edges_to_collapse.erase(edges_to_collapse.begin()); + + CGAL_assertion(!get(ecm, e)); + + if(get(vcm, source(e, tmesh)) && get(vcm, target(e, tmesh))) + continue; + +#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES + std::cout << " treat needle: " << e << " (" << tmesh.point(source (e, tmesh)) + << " --- " << tmesh.point(target(e, tmesh)) << ")" << std::endl; +#endif + if(CGAL::Euler::does_satisfy_link_condition(e, tmesh)) + { + // the following edges are removed by the collapse + halfedge_descriptor h = halfedge(e, tmesh); + CGAL_assertion(!is_border(h, tmesh)); // because extracted from a face + + std::array nc = + internal::is_badly_shaped(face(h, tmesh), tmesh, vpm, ecm, gt, + cap_threshold, needle_threshold, collapse_length_threshold); + + if(nc[0] != h) + { +#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES + std::cerr << "Warning: Needle criteria no longer verified " << tmesh.point(source(e, tmesh)) << " " + << tmesh.point(target(e, tmesh)) << std::endl; +#endif + // the opposite edge might also have been inserted in the set and might still be a needle + h = opposite(h, tmesh); + if(is_border(h, tmesh)) + continue; + + nc = internal::is_badly_shaped(face(h, tmesh), tmesh, vpm, ecm, gt, + cap_threshold, needle_threshold, + collapse_length_threshold); + if(nc[0] != h) + continue; + } + + for(int i=0; i<2; ++i) + { + if(!is_border(h, tmesh)) + { + edge_descriptor pe = edge(prev(h, tmesh), tmesh); + edges_to_flip.erase(pe); + next_edges_to_collapse.erase(pe); + edges_to_collapse.erase(pe); + } + + h = opposite(h, tmesh); + } + + // pick the orientation of edge to keep the vertex minimizing the volume variation + h = internal::get_best_edge_orientation(e, tmesh, vpm, vcm, gt); + + if(h == boost::graph_traits::null_halfedge()) + { +#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES + std::cerr << "Warning: geometrically invalid edge collapse! " + << tmesh.point(source(e, tmesh)) << " " + << tmesh.point(target(e, tmesh)) << std::endl; +#endif + next_edges_to_collapse.insert(e); + continue; + } + + edges_to_flip.erase(e); + next_edges_to_collapse.erase(e); // for edges added in faces incident to a vertex kept after a collapse +#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA + std::cerr << " " << kk << " -- Collapsing " << tmesh.point(source(h, tmesh)) << " " + << tmesh.point(target(h, tmesh)) << std::endl; +#endif + // moving to the midpoint is not a good idea. On a circle for example you might endpoint with + // a bad geometry because you iteratively move one point + // auto mp = midpoint(tmesh.point(source(h, tmesh)), tmesh.point(target(h, tmesh))); + + vertex_descriptor v = Euler::collapse_edge(edge(h, tmesh), tmesh); + + //tmesh.point(v) = mp; + // examine all faces incident to the vertex kept + for(halfedge_descriptor hv : halfedges_around_target(v, tmesh)) + { + if(!is_border(hv, tmesh)) + { + internal::collect_badly_shaped_triangles(face(hv, tmesh), tmesh, vpm, ecm, gt, + cap_threshold, needle_threshold, collapse_length_threshold, + edges_to_collapse, edges_to_flip); + } + } + +#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA + std::string nb = std::to_string(++kk); + if(kk<10) nb = std::string("0")+nb; + if(kk<100) nb = std::string("0")+nb; + if(kk<1000) nb = std::string("0")+nb; + if(kk<10000) nb = std::string("0")+nb; + std::ofstream(std::string("tmp/n-")+nb+std::string(".off")) << tmesh; +#endif + something_was_done = true; + } + else + { +#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES + std::cerr << "Warning: uncollapsable edge! " << tmesh.point(source(e, tmesh)) << " " + << tmesh.point(target(e, tmesh)) << std::endl; +#endif + next_edges_to_collapse.insert(e); + } + } + + // treat caps +#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA + kk=0; + std::ofstream(std::string("tmp/c-000.off")) << tmesh; +#endif + while(!edges_to_flip.empty()) + { + edge_descriptor e = *edges_to_flip.begin(); + edges_to_flip.erase(edges_to_flip.begin()); + + CGAL_assertion(!get(ecm, e)); + + if(get(vcm, source(e, tmesh)) && get(vcm, target(e, tmesh))) + continue; + +#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES + std::cout << "treat cap: " << e << " (" << tmesh.point(source(e, tmesh)) + << " --- " << tmesh.point(target(e, tmesh)) << ")" << std::endl; +#endif + + halfedge_descriptor h = halfedge(e, tmesh); + std::array nc = internal::is_badly_shaped(face(h, tmesh), tmesh, vpm, ecm, gt, + cap_threshold, needle_threshold, + collapse_length_threshold); + // First check the triangle is still a cap + if(nc[1] != h) + { +#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES + std::cerr << "Warning: Cap criteria no longer verified " << tmesh.point(source(e, tmesh)) << " --- " + << tmesh.point(target(e, tmesh)) << std::endl; +#endif + // the opposite edge might also have been inserted in the set and might still be a cap + h = opposite(h, tmesh); + if(is_border(h, tmesh)) + continue; + + nc = internal::is_badly_shaped(face(h, tmesh), tmesh, vpm, ecm, gt, + cap_threshold, needle_threshold, collapse_length_threshold); + if(nc[1] != h) + continue; + } + + // special case on the border + if(is_border(opposite(h, tmesh), tmesh)) + { + // remove the triangle + edges_to_flip.erase(edge(prev(h, tmesh), tmesh)); + edges_to_flip.erase(edge(next(h, tmesh), tmesh)); + next_edges_to_collapse.erase(edge(prev(h, tmesh), tmesh)); + next_edges_to_collapse.erase(edge(next(h, tmesh), tmesh)); + Euler::remove_face(h, tmesh); + something_was_done = true; + continue; + } + + // condition for the flip to be valid (the edge to be created does not already exist) + if(!halfedge(target(next(h, tmesh), tmesh), + target(next(opposite(h, tmesh), tmesh), tmesh), tmesh).second) + { + + if(!internal::should_flip(e, tmesh, vpm, gt)) + { +#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES + std::cout << "Flipping prevented: not the best diagonal" << std::endl; +#endif + next_edges_to_flip.insert(e); + continue; + } + +#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES + std::cout << "Flipping" << std::endl; +#endif +#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA + std::cerr << "step " << kk << "\n"; + std::cerr << " Flipping " << tmesh.point(source(h, tmesh)) << " " + << tmesh.point(target(h, tmesh)) << std::endl; +#endif + Euler::flip_edge(h, tmesh); + CGAL_assertion(edge(h, tmesh) == e); + + // handle face updates + for(int i=0; i<2; ++i) + { + CGAL_assertion(!is_border(h, tmesh)); + std::array nc = + internal::is_badly_shaped(face(h, tmesh), tmesh, vpm, ecm, gt, + cap_threshold, needle_threshold, collapse_length_threshold); + + if(nc[1] != boost::graph_traits::null_halfedge()) + { + if(edge(nc[1], tmesh) != e) + next_edges_to_flip.insert(edge(nc[1], tmesh)); + } + else + { + if(nc[0] != boost::graph_traits::null_halfedge()) + { + next_edges_to_collapse.insert(edge(nc[0], tmesh)); + } + } + h = opposite(h, tmesh); + } + something_was_done = true; + } +#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES + else + { + std::cerr << "Warning: unflippable edge! " << tmesh.point(source(h, tmesh)) << " --- " + << tmesh.point(target(h, tmesh)) << std::endl; + next_edges_to_flip.insert(e); + } +#endif +#ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA + std::string nb = std::to_string(++kk); + if(kk<10) nb = std::string("0")+nb; + if(kk<100) nb = std::string("0")+nb; + if(kk<1000) nb = std::string("0")+nb; + if(kk<10000) nb = std::string("0")+nb; + std::ofstream(std::string("tmp/c-")+nb+std::string(".off")) << tmesh; +#endif + } + + std::swap(edges_to_collapse, next_edges_to_collapse); + std::swap(edges_to_flip, next_edges_to_flip); + + if(!something_was_done) + return false; + } + + return false; +} + +template +bool remove_almost_degenerate_faces(const FaceRange& face_range, + TriangleMesh& tmesh, + const double cap_threshold, + const double needle_threshold, + const double collapse_length_threshold) +{ + return remove_almost_degenerate_faces(face_range, tmesh, + cap_threshold, needle_threshold, collapse_length_threshold, + CGAL::parameters::all_default()); +} + +template +bool remove_almost_degenerate_faces(TriangleMesh& tmesh, + const double cap_threshold, + const double needle_threshold, + const double collapse_length_threshold, + const CGAL_PMP_NP_CLASS& np) +{ + return remove_almost_degenerate_faces(faces(tmesh), tmesh, cap_threshold, needle_threshold, + collapse_length_threshold, np); +} + +template +bool remove_almost_degenerate_faces(TriangleMesh& tmesh, + const double cap_threshold, + const double needle_threshold, + const double collapse_length_threshold) +{ + return remove_almost_degenerate_faces(faces(tmesh), tmesh, + cap_threshold, needle_threshold, collapse_length_threshold, + CGAL::parameters::all_default()); +} + +} // namespace experimental + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// Remove degenerate_edges/faces + +namespace internal { + +template +struct Less_vertex_point +{ + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + + Less_vertex_point(const Traits& traits, const VertexPointMap& vpmap) + : m_traits(traits), m_vpmap(vpmap) + {} + + bool operator()(vertex_descriptor v1, vertex_descriptor v2) const { + return m_traits.less_xyz_3_object()(get(m_vpmap, v1), get(m_vpmap, v2)); + } + + const Traits& m_traits; + const VertexPointMap& m_vpmap; +}; + +} // end namespace internal + +// this function removes a border edge even if it does not satisfy the link condition. +// null_vertex() is returned if the removal changes the topology of the input +template +typename boost::graph_traits::vertex_descriptor +remove_a_border_edge(typename boost::graph_traits::edge_descriptor ed, + TriangleMesh& tm, + EdgeSet& edge_set, + FaceSet& face_set) +{ + typedef boost::graph_traits GT; + typedef typename GT::vertex_descriptor vertex_descriptor; + typedef typename GT::halfedge_descriptor halfedge_descriptor; + typedef typename GT::edge_descriptor edge_descriptor; + typedef typename GT::face_descriptor face_descriptor; + + halfedge_descriptor h = halfedge(ed, tm); + + if(is_border(h, tm)) + h = opposite(h, tm); + + halfedge_descriptor opp_h = opposite(h, tm); + CGAL_assertion(is_border(opp_h, tm)); + CGAL_assertion(!is_border(h, tm)); + + CGAL_assertion(next(next(opp_h, tm), tm) != opp_h); // not working for a hole made of 2 edges + CGAL_assertion(next(next(next(opp_h, tm), tm), tm) != opp_h); // not working for a hole make of 3 edges + + if(CGAL::Euler::does_satisfy_link_condition(edge(h, tm), tm)) + { + edge_set.erase(ed); + halfedge_descriptor h = halfedge(ed, tm); + if(is_border(h, tm)) + h = opposite(h, tm); + + edge_set.erase(edge(prev(h, tm), tm)); + face_set.erase(face(h, tm)); + + return CGAL::Euler::collapse_edge(ed, tm); + } + + // collect edges that have one vertex in the link of + // the vertices of h and one of the vertex of h as other vertex + std::set common_incident_edges; + for(halfedge_descriptor hos : halfedges_around_source(h, tm)) + { + for(halfedge_descriptor hot : halfedges_around_target(h, tm)) + { + if(target(hos, tm) == source(hot, tm)) + { + common_incident_edges.insert(edge(hot, tm)); + common_incident_edges.insert(edge(hos, tm)); + } + } + } + + CGAL_assertion(common_incident_edges.size() >= 2); + + // in the following loop, we visit define a connected component of + // faces bounded by edges in common_incident_edges and h. We look + // for the maximal one. This set of faces is the one that will + // disappear while collapsing ed + std::set marked_faces; + + std::vector queue; + queue.push_back(opposite(next(h, tm), tm)); + queue.push_back(opposite(prev(h, tm), tm)); + marked_faces.insert(face(h, tm)); + + do + { + std::vector boundary; + while(!queue.empty()) + { + halfedge_descriptor back=queue.back(); + queue.pop_back(); + face_descriptor fback=face(back, tm); + if(common_incident_edges.count(edge(back, tm))) + { + boundary.push_back(back); + continue; + } + + if(fback==GT::null_face() || !marked_faces.insert(fback).second) + continue; + + queue.push_back(opposite(next(back, tm), tm)); + if(is_border(queue.back(), tm)) + { +#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG + std::cout << "Boundary reached during exploration, the region to be removed is not a topological disk, not handled for now.\n"; +#endif + return GT::null_vertex(); + } + + queue.push_back(opposite(prev(back, tm), tm)); + if(is_border(queue.back(), tm)) + { +#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG + std::cout << "Boundary reached during exploration, the region to be removed is not a topological disk, not handled for now.\n"; +#endif + return GT::null_vertex(); + } + } + + CGAL_assertion(boundary.size() == 2); + common_incident_edges.erase(edge(boundary[0], tm)); + common_incident_edges.erase(edge(boundary[1], tm)); + if(!is_border(boundary[0], tm) || common_incident_edges.empty()) + queue.push_back(boundary[0]); + if(!is_border(boundary[1], tm) || common_incident_edges.empty()) + queue.push_back(boundary[1]); + } + while(!common_incident_edges.empty()); + + // hk1 and hk2 are bounding the region that will be removed. + // The edge of hk2 will be removed and hk2 will be replaced + // by the opposite edge of hk1 + halfedge_descriptor hk1 = queue.front(); + halfedge_descriptor hk2 = queue.back(); + if(target(hk1, tm)!=source(hk2, tm)) + std::swap(hk1, hk2); + + CGAL_assertion(target(hk1, tm) == source(hk2, tm)); + CGAL_assertion(source(hk1, tm) == source(h, tm)); + CGAL_assertion(target(hk2, tm) == target(h, tm)); + + CGAL_assertion(is_valid_polygon_mesh(tm)); + if(!is_selection_a_topological_disk(marked_faces, tm)) + { +#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG + std::cout << "The region to be removed is not a topological disk, not handled for now.\n"; +#endif + return GT::null_vertex(); + } + + if(is_border(hk1, tm) && is_border(hk2, tm)) + { +#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG + std::cout << "The region to be removed is an isolated region, not handled for now.\n"; +#endif + return GT::null_vertex(); + } + + // collect vertices and edges to remove and do remove faces + std::set edges_to_remove; + std::set vertices_to_remove; + for(face_descriptor fd : marked_faces) + { + halfedge_descriptor hd = halfedge(fd, tm); + for(int i=0; i<3; ++i) + { + edges_to_remove.insert(edge(hd, tm)); + vertices_to_remove.insert(target(hd, tm)); + hd = next(hd, tm); + } + } + + vertex_descriptor vkept = source(hk1, tm); + + //back-up next, prev halfedge pointers to be restored after removal + halfedge_descriptor hp = prev(opp_h, tm); + halfedge_descriptor hn = next(opp_h, tm); + halfedge_descriptor hk1_opp_next = next(hk2, tm); + halfedge_descriptor hk1_opp_prev = prev(hk2, tm); + face_descriptor hk1_opp_face = face(hk2, tm); + + // we will remove the target of hk2, update vertex pointers + for(halfedge_descriptor hot : halfedges_around_target(hk2, tm)) + set_target(hot, vkept, tm); + + // update halfedge pointers since hk2 will be removed + set_halfedge(vkept, opposite(hk1, tm), tm); + set_halfedge(target(hk1, tm), hk1, tm); + + // do not remove hk1 and its vertices + vertices_to_remove.erase(vkept); + vertices_to_remove.erase(target(hk1, tm)); + edges_to_remove.erase(edge(hk1, tm)); + + bool hk2_equals_hp = (hk2 == hp); + CGAL_assertion(is_border(hk2, tm) == hk2_equals_hp); + + /* + - case hk2!=hp + + /\ / + hk1/ \hk2 / + / \ / + ____/______\/____ + hn h_opp hp + + - case hk2==hp + + /\ + hk1/ \hk2 == hp + / \ + ____/______\ + hn h_opp + */ + + // remove vertices + for(vertex_descriptor vd : vertices_to_remove) + remove_vertex(vd, tm); + + // remove edges + for(edge_descriptor ed : edges_to_remove) + { + edge_set.erase(ed); + remove_edge(ed, tm); + } + + // remove faces + for(face_descriptor fd : marked_faces) + { + face_set.erase(fd); + remove_face(fd, tm); + } + + // now update pointers + set_face(opposite(hk1, tm), hk1_opp_face, tm); + if(!hk2_equals_hp) + { + set_next(hp, hn, tm); + set_next(opposite(hk1, tm), hk1_opp_next, tm); + set_next(hk1_opp_prev, opposite(hk1, tm), tm); + set_halfedge(hk1_opp_face, opposite(hk1, tm), tm); + } + else + { + set_next(hk1_opp_prev, opposite(hk1, tm), tm); + set_next(opposite(hk1, tm), hn, tm); + } + + CGAL_assertion(is_valid_polygon_mesh(tm)); + return vkept; +} + +template +typename boost::graph_traits::vertex_descriptor +remove_a_border_edge(typename boost::graph_traits::edge_descriptor ed, + TriangleMesh& tm) +{ + std::set::edge_descriptor> edge_set; + std::set::face_descriptor> face_set; + + return remove_a_border_edge(ed, tm, edge_set, face_set); +} + +template +bool remove_degenerate_edges(const EdgeRange& edge_range, + TriangleMesh& tmesh, + FaceSet& face_set, + const NamedParameters& np) +{ + CGAL_assertion(CGAL::is_triangle_mesh(tmesh)); + CGAL_assertion(CGAL::is_valid_polygon_mesh(tmesh)); + + using parameters::get_parameter; + using parameters::choose_parameter; + + typedef TriangleMesh TM; + typedef typename boost::graph_traits GT; + typedef typename GT::vertex_descriptor vertex_descriptor; + typedef typename GT::halfedge_descriptor halfedge_descriptor; + typedef typename GT::edge_descriptor edge_descriptor; + typedef typename GT::face_descriptor face_descriptor; + + typedef typename GetVertexPointMap::type VertexPointMap; + VertexPointMap vpmap = choose_parameter(get_parameter(np, internal_np::vertex_point), + get_property_map(vertex_point, tmesh)); + + typedef typename GetGeomTraits::type Traits; + + std::size_t nb_deg_faces = 0; + bool all_removed = false; + bool some_removed = true; + bool preserve_genus = choose_parameter(get_parameter(np, internal_np::preserve_genus), true); + + // collect edges of length 0 + while(some_removed && !all_removed) + { + some_removed = false; + all_removed = true; + std::set degenerate_edges_to_remove; + degenerate_edges(edge_range, tmesh, std::inserter(degenerate_edges_to_remove, + degenerate_edges_to_remove.end())); + +#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG + std::cout << "Found " << degenerate_edges_to_remove.size() << " null edges.\n"; +#endif + + // first try to remove all collapsable edges + typename std::set::iterator it = degenerate_edges_to_remove.begin(); + while(it != degenerate_edges_to_remove.end()) + { + edge_descriptor e = *it; + if(CGAL::Euler::does_satisfy_link_condition(e, tmesh)) + { + halfedge_descriptor h = halfedge(e, tmesh); + degenerate_edges_to_remove.erase(it); + + // remove edges that could also be set for removal + if(face(h, tmesh) != GT::null_face()) + { + ++nb_deg_faces; + degenerate_edges_to_remove.erase(edge(prev(h, tmesh), tmesh)); + face_set.erase(face(h, tmesh)); + } + + if(face(opposite(h, tmesh), tmesh) != GT::null_face()) + { + ++nb_deg_faces; + degenerate_edges_to_remove.erase(edge(prev(opposite(h, tmesh), tmesh), tmesh)); + face_set.erase(face(opposite(h, tmesh), tmesh)); + } + + //now remove the edge + CGAL::Euler::collapse_edge(e, tmesh); + + // some_removed is not updated on purpose because if nothing + // happens below then nothing can be done + it = degenerate_edges_to_remove.begin(); + } + else // link condition not satisfied + { + ++it; + } + } + + CGAL_assertion(is_valid_polygon_mesh(tmesh)); +#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG + std::cout << "Remaining " << degenerate_edges_to_remove.size() << " null edges to be handled.\n"; +#endif + + while(!degenerate_edges_to_remove.empty()) + { + edge_descriptor ed = *degenerate_edges_to_remove.begin(); + degenerate_edges_to_remove.erase(degenerate_edges_to_remove.begin()); + halfedge_descriptor h = halfedge(ed, tmesh); + + if(CGAL::Euler::does_satisfy_link_condition(ed, tmesh)) + { + // remove edges that could also be set for removal + if(face(h, tmesh) != GT::null_face()) + { + ++nb_deg_faces; + degenerate_edges_to_remove.erase(edge(prev(h, tmesh), tmesh)); + face_set.erase(face(h, tmesh)); + } + + if(face(opposite(h, tmesh), tmesh)!=GT::null_face()) + { + ++nb_deg_faces; + degenerate_edges_to_remove.erase(edge(prev(opposite(h, tmesh), tmesh), tmesh)); + face_set.erase(face(opposite(h, tmesh), tmesh)); + } + + //now remove the edge + CGAL::Euler::collapse_edge(ed, tmesh); + some_removed = true; + } + else // link condition not satisfied + { + // handle the case when the edge is incident to a triangle hole + // we first fill the hole and try again + if(is_border(ed, tmesh)) + { + halfedge_descriptor hd = halfedge(ed, tmesh); + if(!is_border(hd, tmesh)) + hd = opposite(hd, tmesh); + + if(is_triangle(hd, tmesh)) + { + if(!preserve_genus) + { + Euler::fill_hole(hd, tmesh); + degenerate_edges_to_remove.insert(ed); // reinsert the edge for future processing + } + else + { + all_removed=false; + } + + continue; + } + +#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG + std::cout << "Calling remove_a_border_edge\n"; +#endif + + vertex_descriptor vd = remove_a_border_edge(ed, tmesh, degenerate_edges_to_remove, face_set); + if(vd == GT::null_vertex()) + { + // @todo: if some border edges are later removed, the edge might be processable later + // for example if it belongs to boundary cycle of edges where the number of non-degenerate + // edges is 2. That's what happen with fused_vertices.off in the testsuite where the edges + // are not processed the same way with Polyhedron and Surface_mesh. In the case of Polyhedron + // more degenerate edges could be removed. + all_removed = false; + } + else + some_removed = true; + + continue; + } + else + { + halfedge_descriptor hd = halfedge(ed, tmesh); + // if both vertices are boundary vertices we can't do anything + bool impossible = false; + for(halfedge_descriptor h : halfedges_around_target(hd, tmesh)) + { + if(is_border(h, tmesh)) + { + impossible = true; + break; + } + } + + if(impossible) + { + impossible = false; + for(halfedge_descriptor h : halfedges_around_source(hd, tmesh)) + { + if(is_border(h, tmesh)) + { + impossible = true; + break; + } + } + + if(impossible) + { + all_removed = false; + continue; + } + } + } + + // When the edge does not satisfy the link condition, it means that it cannot be + // collapsed as is. In the following if there is a topological issue + // with contracting the edge (component or geometric feature that disappears), + // nothing is done. + // We start by marking the faces that are incident to an edge endpoint. + // If the set of marked faces is a topologically disk, then we simply remove all the simplicies + // inside the disk and star the hole with the edge vertex kept. + // If the set of marked faces is not a topological disk, it has some non-manifold vertices + // on its boundary. We need to mark additional faces to make it a topological disk. + // We can then apply the star hole procedure. + // Right now we additionally mark the smallest connected components of non-marked faces + // (using the number of faces) + + //backup central point + typename Traits::Point_3 pt = get(vpmap, source(ed, tmesh)); + + // mark faces of the link of each endpoints of the edge which collapse is not topologically valid + std::set marked_faces; + + // first endpoint + for(halfedge_descriptor hd : CGAL::halfedges_around_target(halfedge(ed, tmesh), tmesh)) + if(!is_border(hd, tmesh)) + marked_faces.insert(face(hd, tmesh)); + + // second endpoint + for(halfedge_descriptor hd : CGAL::halfedges_around_target(opposite(halfedge(ed, tmesh), tmesh), tmesh)) + if(!is_border(hd, tmesh)) + marked_faces.insert(face(hd, tmesh)); + + // extract the halfedges on the boundary of the marked region + std::vector border; + for(face_descriptor fd : marked_faces) + { + for(halfedge_descriptor hd : CGAL::halfedges_around_face(halfedge(fd, tmesh), tmesh)) + { + halfedge_descriptor hd_opp = opposite(hd, tmesh); + if(is_border(hd_opp, tmesh) || marked_faces.count(face(hd_opp, tmesh)) == 0) + { + border.push_back(hd); + } + } + } + + if(border.empty()) + { + // a whole connected component (without boundary) got selected and will disappear (not handled for now) +#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG + std::cout << "Trying to remove a whole connected component, not handled yet\n"; +#endif + all_removed = false; + continue; + } + + // define cc of border halfedges: two halfedges are in the same cc + // if they are on the border of the cc of non-marked faces. + typedef CGAL::Union_find UF_ds; + UF_ds uf; + std::map handles; + + // one cc per border halfedge + for(halfedge_descriptor hd : border) + handles.insert(std::make_pair(hd, uf.make_set(hd))); + + // join cc's + for(halfedge_descriptor hd : border) + { + CGAL_assertion(marked_faces.count(face(hd, tmesh)) > 0); + CGAL_assertion(marked_faces.count(face(opposite(hd, tmesh), tmesh)) == 0); + halfedge_descriptor candidate = hd; + + do + { + candidate = prev(opposite(candidate, tmesh), tmesh); + } + while(!marked_faces.count(face(opposite(candidate, tmesh), tmesh))); + + uf.unify_sets(handles[hd], handles[opposite(candidate, tmesh)]); + } + + std::size_t nb_cc = uf.number_of_sets(); + if(nb_cc != 1) + { + // if more than one connected component is found then the patch + // made of marked faces contains "non-manifold" vertices. + // The smallest components need to be marked so that the patch + // made of marked faces is a topological disk + + // we will explore in parallel the connected components and will stop + // when all but one connected component have been entirely explored. + // We add one face at a time for each cc in order to not explore a + // potentially very large cc. + std::vector > stacks_per_cc(nb_cc); + std::vector > faces_per_cc(nb_cc); + std::vector exploration_finished(nb_cc, false); + + // init the stacks of halfedges using the cc of the boundary + std::size_t index = 0; + std::map ccs; + + typedef std::pair Pair_type; + for(const Pair_type& p : handles) + { + halfedge_descriptor opp_hedge = opposite(p.first, tmesh); + if(is_border(opp_hedge, tmesh)) + continue; // nothing to do on the boundary + + typedef typename std::map::iterator Map_it; + std::pair insert_res = ccs.insert(std::make_pair(*uf.find(p.second), index)); + + if(insert_res.second) + ++index; + + stacks_per_cc[insert_res.first->second].push_back(prev(opp_hedge, tmesh)); + stacks_per_cc[insert_res.first->second].push_back(next(opp_hedge, tmesh)); + faces_per_cc[insert_res.first->second].insert(face(opp_hedge, tmesh)); + } + + if(index != nb_cc) + { + // most probably, one cc is a cycle of border edges +#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG + std::cout << "Trying to remove a component with a cycle of halfedges (nested hole or whole component), not handled yet.\n"; +#endif + all_removed = false; + continue; + } + + std::size_t nb_ccs_to_be_explored = nb_cc; + index = 0; + //explore the cc's + do + { + // try to extract one more face for a given cc + do + { + CGAL_assertion(!exploration_finished[index]); + CGAL_assertion(!stacks_per_cc.empty()); + CGAL_assertion(!stacks_per_cc[index].empty()); + + halfedge_descriptor hd = stacks_per_cc[index].back(); + stacks_per_cc[index].pop_back(); + hd = opposite(hd, tmesh); + + if(!is_border(hd, tmesh) && !marked_faces.count(face(hd, tmesh))) + { + if(faces_per_cc[index].insert(face(hd, tmesh)).second) + { + stacks_per_cc[index].push_back(next(hd, tmesh)); + stacks_per_cc[index].push_back(prev(hd, tmesh)); + break; + } + } + + if(stacks_per_cc[index].empty()) + break; + } + while(true); + + // the exploration of a cc is finished when its stack is empty + exploration_finished[index]=stacks_per_cc[index].empty(); + if(exploration_finished[index]) + --nb_ccs_to_be_explored; + + if(nb_ccs_to_be_explored==1) + break; + + while(exploration_finished[(++index)%nb_cc]); + + index = index%nb_cc; + } + while(true); + + // @todo use the area criteria? this means maybe continue exploration of larger cc + // mark faces of completetly explored cc + for(index=0; index vertices_to_keep; + std::set halfedges_to_keep; + for(halfedge_descriptor hd : border) + { + if(!marked_faces.count(face(opposite(hd, tmesh), tmesh))) + { + halfedges_to_keep.insert(hd); + vertices_to_keep.insert(target(hd, tmesh)); + } + } + + // backup next,prev relationships to set after patch removal + std::vector > next_prev_halfedge_pairs; + halfedge_descriptor first_border_hd = *(halfedges_to_keep.begin()); + halfedge_descriptor current_border_hd = first_border_hd; + do + { + halfedge_descriptor prev_border_hd = current_border_hd; + current_border_hd = next(current_border_hd, tmesh); + + while(marked_faces.count(face(opposite(current_border_hd, tmesh), tmesh))) + current_border_hd=next(opposite(current_border_hd, tmesh), tmesh); + + next_prev_halfedge_pairs.push_back(std::make_pair(prev_border_hd, current_border_hd)); + } + while(current_border_hd!=first_border_hd); + + // collect vertices and edges to remove and do remove faces + std::set edges_to_remove; + std::set vertices_to_remove; + for(face_descriptor fd : marked_faces) + { + halfedge_descriptor hd=halfedge(fd, tmesh); + for(int i=0; i<3; ++i) + { + if(!halfedges_to_keep.count(hd)) + edges_to_remove.insert(edge(hd, tmesh)); + + if(!vertices_to_keep.count(target(hd, tmesh))) + vertices_to_remove.insert(target(hd, tmesh)); + + hd = next(hd, tmesh); + } + + remove_face(fd, tmesh); + face_set.erase(fd); + } + + // remove vertices + for(vertex_descriptor vd : vertices_to_remove) + remove_vertex(vd, tmesh); + + // remove edges + for(edge_descriptor ed : edges_to_remove) + { + degenerate_edges_to_remove.erase(ed); + remove_edge(ed, tmesh); + } + + // add a new face, set all border edges pointing to it + // and update halfedge vertex of patch boundary vertices + face_descriptor new_face = add_face(tmesh); + typedef std::pair Pair_type; + for(const Pair_type& p : next_prev_halfedge_pairs) + { + set_face(p.first, new_face, tmesh); + set_next(p.first, p.second, tmesh); + set_halfedge(target(p.first, tmesh), p.first, tmesh); + } + + set_halfedge(new_face, first_border_hd, tmesh); + + // triangulate the new face and update the coordinate of the central vertex + halfedge_descriptor new_hd=Euler::add_center_vertex(first_border_hd, tmesh); + put(vpmap, target(new_hd, tmesh), pt); + + for(halfedge_descriptor hd : halfedges_around_target(new_hd, tmesh)) + { + if(is_degenerate_edge(edge(hd, tmesh), tmesh, np)) + degenerate_edges_to_remove.insert(edge(hd, tmesh)); + + if(face(hd, tmesh) != GT::null_face() && is_degenerate_triangle_face(face(hd, tmesh), tmesh)) + face_set.insert(face(hd, tmesh)); + } + + CGAL_assertion(is_valid_polygon_mesh(tmesh)); + } + } + } + + return all_removed; +} + +template +bool remove_degenerate_edges(const EdgeRange& edge_range, + TriangleMesh& tmesh, + const CGAL_PMP_NP_CLASS& np) +{ + std::set::face_descriptor> face_set; + return remove_degenerate_edges(edge_range, tmesh, face_set, np); +} + +template +bool remove_degenerate_edges(TriangleMesh& tmesh, + const CGAL_PMP_NP_CLASS& np) +{ + std::set::face_descriptor> face_set; + return remove_degenerate_edges(edges(tmesh), tmesh, face_set, np); +} + +template +bool remove_degenerate_edges(const EdgeRange& edge_range, + TriangleMesh& tmesh) +{ + std::set::face_descriptor> face_set; + return remove_degenerate_edges(edge_range, tmesh, face_set, parameters::all_default()); +} + +template +bool remove_degenerate_edges(TriangleMesh& tmesh) +{ + std::set::face_descriptor> face_set; + return remove_degenerate_edges(edges(tmesh), tmesh, face_set, parameters::all_default()); +} + +// \ingroup PMP_repairing_grp +// removes the degenerate faces from a triangulated surface mesh. +// A face is considered degenerate if two of its vertices share the same location, +// or more generally if all its vertices are collinear. +// +// @pre `CGAL::is_triangle_mesh(tmesh)` +// +// @tparam TriangleMesh a model of `FaceListGraph` and `MutableFaceGraph` +// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" +// +// @param tmesh the triangulated surface mesh to be repaired +// @param np optional \ref pmp_namedparameters "Named Parameters" described below +// +// \cgalNamedParamsBegin +// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. +// The type of this map is model of `ReadWritePropertyMap`. +// If this parameter is omitted, an internal property map for +// `CGAL::vertex_point_t` must be available in `TriangleMesh` +// \cgalParamEnd +// \cgalParamBegin{geom_traits} a geometric traits class instance. +// The traits class must provide the nested type `Point_3`, +// and the nested functors: +// - `Compare_distance_3` to compute the distance between 2 points +// - `Collinear_3` to check whether 3 points are collinear +// - `Less_xyz_3` to compare lexicographically two points +// - `Equal_3` to check whether 2 points are identical. +// For each functor Foo, a function `Foo foo_object()` must be provided. +// \cgalParamEnd +// \cgalNamedParamsEnd +// +// \todo the function might not be able to remove all degenerate faces. +// We should probably do something with the return type. +// +// \return `true` if all degenerate faces were successfully removed, and `false` otherwise. +template +bool remove_degenerate_faces(const FaceRange& face_range, + TriangleMesh& tmesh, + const NamedParameters& np) +{ + CGAL_assertion(CGAL::is_triangle_mesh(tmesh)); + + using parameters::get_parameter; + using parameters::choose_parameter; + + typedef TriangleMesh TM; + typedef typename boost::graph_traits GT; + typedef typename GT::vertex_descriptor vertex_descriptor; + typedef typename GT::halfedge_descriptor halfedge_descriptor; + typedef typename GT::edge_descriptor edge_descriptor; + typedef typename GT::face_descriptor face_descriptor; + + typedef typename GetVertexPointMap::type VertexPointMap; + VertexPointMap vpmap = choose_parameter(get_parameter(np, internal_np::vertex_point), + get_property_map(vertex_point, tmesh)); + typedef typename GetGeomTraits::type Traits; + Traits traits = choose_parameter(get_parameter(np, internal_np::geom_traits), Traits()); + + typedef typename boost::property_traits::value_type Point_3; + typedef typename boost::property_traits::reference Point_ref; + + std::set degenerate_face_set; + degenerate_faces(face_range, tmesh, std::inserter(degenerate_face_set, degenerate_face_set.begin()), np); + + const std::size_t faces_size = faces(tmesh).size(); + + if(degenerate_face_set.empty()) + return true; + + if(degenerate_face_set.size() == faces_size) + { + clear(tmesh); + return true; + } + + // Sanitize the face range by adding adjacent degenerate faces + const std::size_t range_size = face_range.size(); + bool is_range_full_mesh = (range_size == faces_size); + if(!is_range_full_mesh) + { + std::list faces_to_visit(degenerate_face_set.begin(), degenerate_face_set.end()); + + while(!faces_to_visit.empty()) + { + face_descriptor fd = faces_to_visit.front(); + faces_to_visit.pop_front(); + + for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, tmesh), tmesh)) + { + for(halfedge_descriptor inc_hd : halfedges_around_target(hd, tmesh)) + { + face_descriptor adj_fd = face(inc_hd, tmesh); + if(adj_fd == GT::null_face() || adj_fd == fd) + continue; + + if(is_degenerate_triangle_face(adj_fd, tmesh)) + { + if(degenerate_face_set.insert(adj_fd).second) + { + // successful insertion means we did not know about this face before + faces_to_visit.push_back(adj_fd); + } + } + } + } + } + } + + // Note that there can't be any null edge incident to the degenerate faces range, + // otherwise we would have a null face incident to the face range, and that is not possible + // after the sanitization above + std::set edge_range; + for(face_descriptor fd : degenerate_face_set) + for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, tmesh), tmesh)) + edge_range.insert(edge(hd, tmesh)); + + // First remove edges of length 0 + bool all_removed = remove_degenerate_edges(edge_range, tmesh, degenerate_face_set, np); + + CGAL_assertion_code(for(face_descriptor fd : degenerate_face_set) {) + CGAL_assertion(is_degenerate_triangle_face(fd, tmesh)); + CGAL_assertion_code(}) + +#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG + { + std::cout <<"Done with null edges.\n"; + std::ofstream output("/tmp/no_null_edges.off"); + output << std::setprecision(17) << tmesh << "\n"; + output.close(); + } +#endif + + // Then, remove triangles made of 3 collinear points + + // start by filtering out border faces + // @todo shall we avoid doing that in case a non-manifold vertex on the boundary or if a whole component disappear? + std::set border_deg_faces; + for(face_descriptor f : degenerate_face_set) + { + halfedge_descriptor h = halfedge(f, tmesh); + for(int i=0; i<3; ++i) + { + if(is_border(opposite(h, tmesh), tmesh)) + { + border_deg_faces.insert(f); + break; + } + + h = next(h, tmesh); + } + } + + while(!border_deg_faces.empty()) + { + face_descriptor f_to_rm = *border_deg_faces.begin(); + border_deg_faces.erase(border_deg_faces.begin()); + + halfedge_descriptor h = halfedge(f_to_rm, tmesh); + for(int i=0; i<3; ++i) + { + face_descriptor f = face(opposite(h, tmesh), tmesh); + if(f!=GT::null_face()) + { + if(is_degenerate_triangle_face(f, tmesh, np)) + border_deg_faces.insert(f); + } + + h = next(h, tmesh); + } + + while(!is_border(opposite(h, tmesh), tmesh)) + { + h = next(h, tmesh); + } + + degenerate_face_set.erase(f_to_rm); + Euler::remove_face(h, tmesh); + } + + // Ignore faces with null edges + if(!all_removed) + { + std::map are_degenerate_edges; + + for(face_descriptor fd : degenerate_face_set) + { + for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, tmesh), tmesh)) + { + edge_descriptor ed = edge(hd, tmesh); + std::pair::iterator, bool> is_insert_successful = + are_degenerate_edges.insert(std::make_pair(ed, false)); + + bool is_degenerate = false; + if(is_insert_successful.second) + { + // did not previously exist in the map, so actually have to check if it is degenerate + if(traits.equal_3_object()(get(vpmap, target(ed, tmesh)), get(vpmap, source(ed, tmesh)))) + is_degenerate = true; + } + + is_insert_successful.first->second = is_degenerate; + + if(is_degenerate) + { + halfedge_descriptor h = halfedge(ed, tmesh); + if(!is_border(h, tmesh)) + degenerate_face_set.erase(face(h, tmesh)); + + h = opposite(h, tmesh); + if(!is_border(h, tmesh)) + degenerate_face_set.erase(face(h, tmesh)); + } + } + } + } + + // first remove degree 3 vertices that are part of a cap + // (only the vertex in the middle of the opposite edge) + // This removal does not change the shape of the mesh. + while(!degenerate_face_set.empty()) + { + std::set vertices_to_remove; + for(face_descriptor fd : degenerate_face_set) + { + for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, tmesh), tmesh)) + { + vertex_descriptor vd = target(hd, tmesh); + if(degree(vd, tmesh) == 3 && is_border(vd, tmesh)==GT::null_halfedge()) + { + vertices_to_remove.insert(vd); + break; + } + } + } + + for(vertex_descriptor vd : vertices_to_remove) + { + for(halfedge_descriptor hd2 : halfedges_around_target(vd, tmesh)) + degenerate_face_set.erase(face(hd2, tmesh)); + + // remove the central vertex and check if the new face is degenerated + halfedge_descriptor hd = CGAL::Euler::remove_center_vertex(halfedge(vd, tmesh), tmesh); + if(is_degenerate_triangle_face(face(hd, tmesh), tmesh, np)) + { + degenerate_face_set.insert(face(hd, tmesh)); + } + } + + if(vertices_to_remove.empty()) + break; + } + + while(!degenerate_face_set.empty()) + { +#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG + std::cout << "Loop on removing deg faces\n"; + + // ensure the mesh is not broken + { + std::ofstream out("/tmp/out.off"); + out << tmesh; + out.close(); + + std::vector points; + std::vector > triangles; + std::ifstream in("/tmp/out.off"); + CGAL::read_OFF(in, points, triangles); + if(!CGAL::Polygon_mesh_processing::is_polygon_soup_a_polygon_mesh(triangles)) + { + std::cerr << "Warning: got a polygon soup (may simply be a non-manifold vertex)!\n"; + } + } +#endif + + face_descriptor fd = *degenerate_face_set.begin(); + + // look whether an incident triangle is also degenerate + bool detect_cc_of_degenerate_triangles = false; + for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, tmesh), tmesh)) + { + face_descriptor adjacent_face = face(opposite(hd, tmesh), tmesh); + if(adjacent_face!=GT::null_face() && degenerate_face_set.count(adjacent_face)) + { + detect_cc_of_degenerate_triangles = true; + break; + } + } + + if(!detect_cc_of_degenerate_triangles) + { +#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG + std::cout << " no degenerate neighbors, using a flip.\n"; +#endif + degenerate_face_set.erase(degenerate_face_set.begin()); + + // flip the longest edge of the triangle + Point_ref p1 = get(vpmap, target(halfedge(fd, tmesh), tmesh)); + Point_ref p2 = get(vpmap, target(next(halfedge(fd, tmesh), tmesh), tmesh)); + Point_ref p3 = get(vpmap, source(halfedge(fd, tmesh), tmesh)); + + CGAL_assertion(p1!=p2 && p1!=p3 && p2!=p3); + + typename Traits::Compare_distance_3 compare_distance = traits.compare_distance_3_object(); + + halfedge_descriptor edge_to_flip; + if(compare_distance(p1,p2, p1,p3) != CGAL::SMALLER) // p1p2 > p1p3 + { + if(compare_distance(p1,p2, p2,p3) != CGAL::SMALLER) // p1p2 > p2p3 + // flip p1p2 + edge_to_flip = next(halfedge(fd, tmesh), tmesh); + else + // flip p2p3 + edge_to_flip = prev(halfedge(fd, tmesh), tmesh); + } + else + { + if(compare_distance(p1,p3, p2,p3) != CGAL::SMALLER) // p1p3>p2p3 + //flip p3p1 + edge_to_flip = halfedge(fd, tmesh); + else + //flip p2p3 + edge_to_flip = prev(halfedge(fd, tmesh), tmesh); + } + + face_descriptor opposite_face=face(opposite(edge_to_flip, tmesh), tmesh); + if(opposite_face == GT::null_face()) + { + // simply remove the face + Euler::remove_face(edge_to_flip, tmesh); + } + else + { + // condition for the flip to be valid (the edge to be created do not already exists) + if(!halfedge(target(next(edge_to_flip, tmesh), tmesh), + target(next(opposite(edge_to_flip, tmesh), tmesh), tmesh), + tmesh).second) + { + Euler::flip_edge(edge_to_flip, tmesh); + } + else + { + all_removed = false; +#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG + std::cout << " WARNING: flip is not possible\n"; + // @todo Let p and q be the vertices opposite to `edge_to_flip`, and let + // r be the vertex of `edge_to_flip` that is the furthest away from + // the edge `pq`. In that case I think we should remove all the triangles + // so that the triangle pqr is in the mesh. +#endif + } + } + } + else + { + // Process a connected component of degenerate faces + // get all the faces from the connected component + // and the boundary edges + std::set cc_faces; + std::vector queue; + std::vector boundary_hedges; + std::vector inside_hedges; + queue.push_back(fd); + cc_faces.insert(fd); + + while(!queue.empty()) + { + face_descriptor top=queue.back(); + queue.pop_back(); + for(halfedge_descriptor hd : halfedges_around_face(halfedge(top, tmesh), tmesh)) + { + face_descriptor adjacent_face = face(opposite(hd, tmesh), tmesh); + if(adjacent_face==GT::null_face() || degenerate_face_set.count(adjacent_face)==0) + { + boundary_hedges.push_back(hd); + } + else + { + if(cc_faces.insert(adjacent_face).second) + queue.push_back(adjacent_face); + + if(hd < opposite(hd, tmesh)) + inside_hedges.push_back(hd); + } + } + } + +#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG + std::cout << " Deal with a cc of " << cc_faces.size() << " degenerate faces.\n"; + /// dump cc_faces + { + int id = 0; + std::map vids; + for(face_descriptor f : cc_faces) + { + if(vids.insert(std::make_pair(target(halfedge(f, tmesh), tmesh), id)).second) ++id; + if(vids.insert(std::make_pair(target(next(halfedge(f, tmesh), tmesh), tmesh), id)).second) ++id; + if(vids.insert(std::make_pair(target(next(next(halfedge(f, tmesh), tmesh), tmesh), tmesh), id)).second) ++id; + } + + std::ofstream output("/tmp/cc_faces.off"); + output << std::setprecision(17); + output << "OFF\n" << vids.size() << " " << cc_faces.size() << " 0\n"; + std::vector points(vids.size()); + typedef std::pair Pair_type; + for(const Pair_type& p : vids) + + points[p.second] = get(vpmap, p.first); + for(const Point_3& p : points) + output << p << "\n"; + + for(face_descriptor f : cc_faces) + { + output << "3 " + << vids[target(halfedge(f, tmesh), tmesh)] << " " + << vids[target(next(halfedge(f, tmesh), tmesh), tmesh)] << " " + << vids[target(next(next(halfedge(f, tmesh), tmesh), tmesh), tmesh)] << "\n"; + } + + for(std::size_t pid=2; pid!=points.size(); ++pid) + { + CGAL_assertion(collinear(points[0], points[1], points[pid])); + } + } +#endif + + // find vertices strictly inside the cc + std::set boundary_vertices; + for(halfedge_descriptor hd : boundary_hedges) + boundary_vertices.insert(target(hd, tmesh)); + + std::set inside_vertices; + for(halfedge_descriptor hd : inside_hedges) + { + if(!boundary_vertices.count(target(hd, tmesh))) + inside_vertices.insert(target(hd, tmesh)); + if(!boundary_vertices.count(source(hd, tmesh))) + inside_vertices.insert(source(hd, tmesh)); + } + + // v-e+f = 1 for a topological disk and e = (3f+#boundary_edges)/2 + if(boundary_vertices.size()+inside_vertices.size() - + (cc_faces.size()+boundary_hedges.size())/2 != 1) + { + //cc_faces does not define a topological disk + // @todo Find to way to handle that case +#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG + std::cout << " WARNING: Cannot remove the component of degenerate faces: not a topological disk.\n"; +#endif + + for(face_descriptor f : cc_faces) + degenerate_face_set.erase(f); + + continue; + } + + // preliminary step to check if the operation is possible + // sort the boundary points along the common supporting line + // we first need a reference point + typedef internal::Less_vertex_point Less_vertex; + std::pair< + typename std::set::iterator, + typename std::set::iterator > ref_vertices = + boost::minmax_element(boundary_vertices.begin(), + boundary_vertices.end(), + Less_vertex(traits, vpmap)); + + // and then we sort the vertices using this reference point + typedef std::set Sorted_point_set; + Sorted_point_set sorted_points; + for(vertex_descriptor v : boundary_vertices) + sorted_points.insert(get(vpmap, v)); + + CGAL_assertion(sorted_points.size()== + std::set(sorted_points.begin(), + sorted_points.end()).size()); + + CGAL_assertion(get(vpmap, *ref_vertices.first) == *sorted_points.begin()); + CGAL_assertion(get(vpmap, *ref_vertices.second) == *std::prev(sorted_points.end())); + + const Point_3& xtrm1 = *sorted_points.begin(); + const Point_3& xtrm2 = *std::prev(sorted_points.end()); + + // recover halfedges on the hole, bounded by the reference vertices + std::vector side_one, side_two; + + // look for the outgoing border halfedge of the first extreme point + for(halfedge_descriptor hd : boundary_hedges) + { + if(get(vpmap, source(hd, tmesh)) == xtrm1) + { + side_one.push_back(hd); + break; + } + } + + CGAL_assertion(side_one.size() == 1); + + bool non_monotone_border = false; + + while(get(vpmap, target(side_one.back(), tmesh)) != xtrm2) + { + vertex_descriptor prev_vertex = target(side_one.back(), tmesh); + for(halfedge_descriptor hd : boundary_hedges) + { + if(source(hd, tmesh) == prev_vertex) + { + if(get(vpmap, target(hd, tmesh)) < get(vpmap, prev_vertex)) + non_monotone_border = true; + + side_one.push_back(hd); + break; + } + } + + if(non_monotone_border) + break; + } + + if(non_monotone_border) + { +#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG + std::cout << " WARNING: Cannot remove the component of degenerate faces: border not a monotonic cycle.\n"; +#endif + + for(face_descriptor f : cc_faces) + degenerate_face_set.erase(f); + + continue; + } + + // look for the outgoing border halfedge of second extreme vertex + for(halfedge_descriptor hd : boundary_hedges) + { + if(source(hd, tmesh) == target(side_one.back(), tmesh)) + { + side_two.push_back(hd); + break; + } + } + + CGAL_assertion(side_two.size() == 1); + + while(target(side_two.back(), tmesh) != source(side_one.front(), tmesh)) + { + vertex_descriptor prev_vertex = target(side_two.back(), tmesh); + for(halfedge_descriptor hd : boundary_hedges) + { + if(source(hd, tmesh) == prev_vertex) + { + if(get(vpmap, target(hd, tmesh)) > get(vpmap, prev_vertex)) + non_monotone_border = true; + + side_two.push_back(hd); + break; + } + } + + if(non_monotone_border) + break; + } + + if(non_monotone_border) + { +#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG + std::cout << " WARNING: Cannot remove the component of degenerate faces: border not a monotonic cycle.\n"; +#endif + + for(face_descriptor f : cc_faces) + degenerate_face_set.erase(f); + + continue; + } + + CGAL_assertion(side_one.size()+side_two.size()==boundary_hedges.size()); + + // reverse the order of the second side so as to follow + // the same order than side one + std::reverse(side_two.begin(), side_two.end()); + for(halfedge_descriptor& h : side_two) + h = opposite(h, tmesh); + + //make sure the points of the vertices along side_one are correctly sorted + std::vector side_points; + side_points.reserve(side_one.size()+1); + side_points.push_back(get(vpmap, source(side_one.front(), tmesh))); + + for(halfedge_descriptor h : side_one) + side_points.push_back(get(vpmap, target(h, tmesh))); + + CGAL_assertion(get(vpmap,source(side_one.front(), tmesh)) == side_points.front()); + CGAL_assertion(get(vpmap,target(side_one.back(), tmesh)) == side_points.back()); + + // @todo the reordering could lead to the apparition of null edges. + std::sort(side_points.begin(), side_points.end()); + + CGAL_assertion(std::unique(side_points.begin(), side_points.end())==side_points.end()); + for(std::size_t i=0; i::iterator side_one_it = side_one.begin(); + typename std::vector::iterator side_two_it = side_two.begin(); + for(;it_pt!=it_pt_end;++it_pt) + { + // check if it_pt is the point of the target of one or two halfedges + bool target_of_side_one = (get(vpmap, target(*side_one_it, tmesh)) == *it_pt); + bool target_of_side_two = (get(vpmap, target(*side_two_it, tmesh)) == *it_pt); + + if(target_of_side_one && target_of_side_two) + { + for(halfedge_descriptor h : halfedges_around_target(*side_one_it, tmesh)) + { + if(source(h, tmesh) == target(*side_two_it, tmesh)) + { + non_collapsable = true; + break; + } + } + } + else + { + CGAL_assertion(target_of_side_one || target_of_side_two); + vertex_descriptor v1 = target_of_side_one ? target(*side_one_it, tmesh) + : target(*side_two_it, tmesh); + vertex_descriptor v2 = target_of_side_two ? target(next(opposite(*side_one_it, tmesh), tmesh), tmesh) + : target(next(*side_two_it, tmesh), tmesh); + for(halfedge_descriptor h : halfedges_around_target(v1, tmesh)) + { + if(source(h, tmesh)==v2) + { + non_collapsable=true; + break; + } + } + } + + if(non_collapsable) break; + if(target_of_side_one) ++side_one_it; + if(target_of_side_two) ++side_two_it; + } + + if(non_collapsable) + { + for(face_descriptor f : cc_faces) + degenerate_face_set.erase(f); + +#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG + std::cout << " WARNING: cannot remove a connected components of degenerate faces.\n"; +#endif + continue; + } + + // now proceed to the fix + // update the face and halfedge vertex pointers on the boundary + for(halfedge_descriptor h : boundary_hedges) + { + set_face(h, GT::null_face(), tmesh); + set_halfedge(target(h, tmesh), h, tmesh); + } + + // update next/prev pointers of boundary_hedges + for(halfedge_descriptor h : boundary_hedges) + { + halfedge_descriptor next_candidate = next(h, tmesh); + while(face(next_candidate, tmesh)!=GT::null_face()) + next_candidate = next(opposite(next_candidate, tmesh), tmesh); + + set_next(h, next_candidate, tmesh); + } + + // remove degenerate faces + for(face_descriptor f : cc_faces) + { + degenerate_face_set.erase(f); + remove_face(f, tmesh); + } + + // remove interior edges + for(halfedge_descriptor h : inside_hedges) + remove_edge(edge(h, tmesh), tmesh); + + // remove interior vertices + for(vertex_descriptor v : inside_vertices) + remove_vertex(v, tmesh); + +#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG + std::cout << " side_one.size() " << side_one.size() << "\n"; + std::cout << " side_two.size() " << side_two.size() << "\n"; +#endif + + CGAL_assertion(source(side_one.front(), tmesh) == *ref_vertices.first); + CGAL_assertion(source(side_two.front(), tmesh) == *ref_vertices.first); + CGAL_assertion(target(side_one.back(), tmesh) == *ref_vertices.second); + CGAL_assertion(target(side_two.back(), tmesh) == *ref_vertices.second); + + // now split each side to contains the same sequence of points + // first side + int hi = 0; + + for(typename Sorted_point_set::iterator it=std::next(sorted_points.begin()), + it_end=sorted_points.end(); it!=it_end; ++it) + { + CGAL_assertion(*std::prev(it) == get(vpmap, source(side_one[hi], tmesh))); + + if(*it != get(vpmap, target(side_one[hi], tmesh))) + { + // split the edge and update the point + halfedge_descriptor h1 = next(opposite(side_one[hi], tmesh), tmesh); + put(vpmap, target(Euler::split_edge(side_one[hi], tmesh), tmesh), *it); + + // split_edge updates the halfedge of the source vertex of h, + // since we reuse later the halfedge of the first refernce vertex + // we must set it as we need. + if(source(h1, tmesh) == *ref_vertices.first) + set_halfedge(*ref_vertices.first, prev(prev(side_one[hi], tmesh), tmesh), tmesh); + + // retriangulate the opposite face + if(face(h1, tmesh) != GT::null_face()) + Euler::split_face(h1, opposite(side_one[hi], tmesh), tmesh); + } + else + { + ++hi; + } + } + + // second side + hi = 0; + for(typename Sorted_point_set::iterator it=std::next(sorted_points.begin()), + it_end=sorted_points.end(); it!=it_end; ++it) + { + CGAL_assertion(*std::prev(it) == get(vpmap, source(side_two[hi], tmesh))); + if(*it != get(vpmap, target(side_two[hi], tmesh))) + { + // split the edge and update the point + halfedge_descriptor h2 = Euler::split_edge(side_two[hi], tmesh); + put(vpmap, target(h2, tmesh), *it); + + // split_edge updates the halfedge of the source vertex of h, + // since we reuse later the halfedge of the first refernce vertex + // we must set it as we need. + if(source(h2, tmesh) == *ref_vertices.first) + set_halfedge(*ref_vertices.first, opposite(h2, tmesh), tmesh); + + // retriangulate the face + if(face(h2, tmesh) != GT::null_face()) + Euler::split_face(h2, next(side_two[hi], tmesh), tmesh); + } + else + { + ++hi; + } + } + + CGAL_assertion(target(halfedge(*ref_vertices.first, tmesh), tmesh) == *ref_vertices.first); + CGAL_assertion(face(halfedge(*ref_vertices.first, tmesh), tmesh) == GT::null_face()); + +#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG + { + halfedge_descriptor h_side2 = halfedge(*ref_vertices.first, tmesh); + halfedge_descriptor h_side1 = next(h_side2, tmesh); + + do + { + CGAL_assertion(get(vpmap, source(h_side1, tmesh)) == get(vpmap, target(h_side2, tmesh))); + CGAL_assertion(get(vpmap, target(h_side1, tmesh)) == get(vpmap, source(h_side2, tmesh))); + + if(target(next(opposite(h_side1, tmesh), tmesh), tmesh) == + target(next(opposite(h_side2, tmesh), tmesh), tmesh)) + { + CGAL_assertion(!"Forbidden simplification"); + } + + h_side2 = prev(h_side2, tmesh); + h_side1 = next(h_side1, tmesh); + } + while(target(h_side1, tmesh) != *ref_vertices.second); + } +#endif + + // remove side1 and replace its opposite hedges by those of side2 + halfedge_descriptor h_side2 = halfedge(*ref_vertices.first, tmesh); + halfedge_descriptor h_side1 = next(h_side2, tmesh); + for(;;) + { + CGAL_assertion(get(vpmap, source(h_side1, tmesh)) == get(vpmap, target(h_side2, tmesh))); + CGAL_assertion(get(vpmap, target(h_side1, tmesh)) == get(vpmap, source(h_side2, tmesh))); + + // backup target vertex + vertex_descriptor vertex_to_remove = target(h_side1, tmesh); + if(vertex_to_remove!=*ref_vertices.second) + { + vertex_descriptor replacement_vertex = source(h_side2, tmesh); + // replace the incident vertex + for(halfedge_descriptor hd : halfedges_around_target(h_side1, tmesh)) + set_target(hd, replacement_vertex, tmesh); + } + + // prev side2 hedge for next loop + halfedge_descriptor h_side2_for_next_turn = prev(h_side2, tmesh); + + // replace the opposite of h_side1 by h_side2 + halfedge_descriptor opposite_h_side1 = opposite(h_side1, tmesh); + face_descriptor the_face = face(opposite_h_side1, tmesh); + set_face(h_side2, the_face, tmesh); + + if(the_face!=GT::null_face()) + set_halfedge(the_face, h_side2, tmesh); + + set_next(h_side2, next(opposite_h_side1, tmesh), tmesh); + set_next(prev(opposite_h_side1, tmesh), h_side2, tmesh); + + // take the next hedges + edge_descriptor edge_to_remove = edge(h_side1, tmesh); + h_side1 = next(h_side1, tmesh); + + // now remove the extra edge + remove_edge(edge_to_remove, tmesh); + + // ... and the extra vertex if it's not the second reference + if(vertex_to_remove == *ref_vertices.second) + { + // update the halfedge pointer of the last vertex (others were already from side 2) + CGAL_assertion(target(opposite(h_side2, tmesh), tmesh) == vertex_to_remove); + set_halfedge(vertex_to_remove, opposite(h_side2, tmesh), tmesh); + break; + } + else + { + remove_vertex(vertex_to_remove , tmesh); + } + + h_side2 = h_side2_for_next_turn; + } + } + } + + return all_removed; +} + +template +bool remove_degenerate_faces(const FaceRange& face_range, + TriangleMesh& tmesh) +{ + return remove_degenerate_faces(face_range, tmesh, CGAL::parameters::all_default()); +} + +template +bool remove_degenerate_faces(TriangleMesh& tmesh, + const CGAL_PMP_NP_CLASS& np) +{ + return remove_degenerate_faces(faces(tmesh), tmesh, np); +} + +template +bool remove_degenerate_faces(TriangleMesh& tmesh) +{ + return remove_degenerate_faces(tmesh, CGAL::parameters::all_default()); +} + +} // namespace Polygon_mesh_processing +} // namespace CGAL + +#endif // CGAL_POLYGON_MESH_PROCESSING_REPAIR_DEGENERACIES_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair_self_intersections.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair_self_intersections.h new file mode 100644 index 00000000000..b67ac5dc5d6 --- /dev/null +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair_self_intersections.h @@ -0,0 +1,1865 @@ +// Copyright (c) 2015-2020 GeometryFactory (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) : Sebastien Loriot, +// Mael Rouxel-Labbé +// +#ifndef CGAL_POLYGON_MESH_PROCESSING_REPAIR_SELF_INTERSECTIONS_H +#define CGAL_POLYGON_MESH_PROCESSING_REPAIR_SELF_INTERSECTIONS_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// #define CGAL_PMP_REMOVE_SELF_INTERSECTIONS_NO_SMOOTHING +// #define CGAL_PMP_REMOVE_SELF_INTERSECTIONS_NO_CONSTRAINTS_IN_HOLE_FILLING + +// Self-intersection removal is done by making a big-enough hole and filling it +// +// Local self-intersection removal is more subtle and only considers self-intersections +// within a connected component. It then tries to fix those by trying successively: +// - smoothing with the sharp edges in the area being constrained +// - smoothing without the sharp edges in the area being constrained +// - hole-filling with the sharp edges in the area being constrained +// - hole-filling without the sharp edges in the area being constrained +// +// The working area grows as long as we haven't been able to fix the self-intersection, +// up to a user-defined number of times. + +namespace CGAL { +namespace Polygon_mesh_processing { +namespace internal { + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG +static int self_intersections_solved_by_constrained_smoothing = 0; +static int self_intersections_solved_by_unconstrained_smoothing = 0; +static int self_intersections_solved_by_constrained_hole_filling = 0; +static int self_intersections_solved_by_unconstrained_hole_filling = 0; +#endif + +// -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- + +// @todo these could be extracted to somewhere else, it's useful in itself +template +FaceOutputIterator replace_faces_with_patch(const std::vector::vertex_descriptor>& border_vertices, + const std::set::vertex_descriptor>& interior_vertices, + const std::vector::halfedge_descriptor>& border_hedges, + const std::set::edge_descriptor>& interior_edges, + const std::set::face_descriptor>& faces, + const std::vector >& patch, + PolygonMesh& pmesh, + VPM& vpm, + FaceOutputIterator out) +{ + CGAL_static_assertion((std::is_same::value_type, Point>::value)); + + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + typedef typename boost::graph_traits::face_descriptor face_descriptor; + + typedef std::vector Point_face; + typedef std::vector Vertex_face; + + CGAL_precondition(is_valid_polygon_mesh(pmesh)); + + // To be used to create new elements + std::vector vertex_stack(interior_vertices.begin(), interior_vertices.end()); + std::vector edge_stack(interior_edges.begin(), interior_edges.end()); + std::vector face_stack(faces.begin(), faces.end()); + + // Introduce new vertices, convert the patch in vertex patches + std::vector patch_with_vertices; + patch_with_vertices.reserve(patch.size()); + + std::map point_to_vs; + + // first, add those for which the vertex will not change + for(const vertex_descriptor v : border_vertices) + point_to_vs[get(vpm, v)] = v; + + // now build a correspondence map and the faces with vertices + const vertex_descriptor null_v = boost::graph_traits::null_vertex(); + for(const Point_face& face : patch) + { + Vertex_face vface; + vface.reserve(face.size()); + + for(const Point& p : face) + { + bool success; + typename std::map::iterator it; + std::tie(it, success) = point_to_vs.insert(std::make_pair(p, null_v)); + vertex_descriptor& v = it->second; + + if(success) // first time we meet that point, means it`s an interior point and we need to make a new vertex + { + if(vertex_stack.empty()) + { + v = add_vertex(pmesh); + } + else + { + v = vertex_stack.back(); + vertex_stack.pop_back(); + } + + put(vpm, v, p); + } + + vface.push_back(v); + } + + patch_with_vertices.push_back(vface); + } + + typedef std::pair Vertex_pair; + typedef std::map Vertex_pair_halfedge_map; + + Vertex_pair_halfedge_map halfedge_map; + + // register border halfedges + int i = 0; + for(halfedge_descriptor h : border_hedges) + { + const vertex_descriptor vs = source(h, pmesh); + const vertex_descriptor vt = target(h, pmesh); + halfedge_map.insert(std::make_pair(std::make_pair(vs, vt), h)); + + set_halfedge(target(h, pmesh), h, pmesh); // update vertex halfedge pointer + ++i; + } + + face_descriptor f = boost::graph_traits::null_face(); +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::vector new_faces; +#endif + + for(const Vertex_face& vface : patch_with_vertices) + { + if(face_stack.empty()) + { + f = add_face(pmesh); + } + else + { + f = face_stack.back(); + face_stack.pop_back(); + } + + *out++ = f; +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + new_faces.push_back(f); +#endif + + std::vector hedges; + hedges.reserve(vface.size()); + + for(std::size_t i=0, n=vface.size(); i::null_halfedge())); + halfedge_descriptor& h = it->second; + + if(success) // this halfedge is an interior halfedge + { + if(edge_stack.empty()) + { + h = halfedge(add_edge(pmesh), pmesh); + } + else + { + h = halfedge(edge_stack.back(), pmesh); + edge_stack.pop_back(); + } + + halfedge_map[std::make_pair(vj, vi)] = opposite(h, pmesh); + } + + hedges.push_back(h); + } + + CGAL_assertion(vface.size() == hedges.size()); + + // update halfedge connections + face pointers + for(std::size_t i=0, n=vface.size(); i +FaceOutputIterator replace_faces_with_patch(const std::set::face_descriptor>& face_range, + const std::vector >& patch, + PolygonMesh& pmesh, + VPM& vpm, + FaceOutputIterator out) +{ + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + typedef typename boost::graph_traits::face_descriptor face_descriptor; + + std::vector border_vertices; + std::set interior_vertices; + std::vector border_hedges; + std::set interior_edges; + + for(face_descriptor fh : face_range) + { + for(halfedge_descriptor h : halfedges_around_face(halfedge(fh, pmesh), pmesh)) + { + if(halfedge(target(h, pmesh), pmesh) == h) // limit the number of insertions + interior_vertices.insert(target(h, pmesh)); + } + } + + for(face_descriptor fh : face_range) + { + for(halfedge_descriptor h : halfedges_around_face(halfedge(fh, pmesh), pmesh)) + { + CGAL_assertion(!is_border(h, pmesh)); + + const edge_descriptor e = edge(h, pmesh); + const halfedge_descriptor opp_h = opposite(h, pmesh); + const face_descriptor opp_f = face(opp_h, pmesh); + + if(is_border(opp_h, pmesh) || face_range.count(opp_f) == 0) + { + vertex_descriptor v = target(h, pmesh); + interior_vertices.erase(v); + border_hedges.push_back(h); + border_vertices.push_back(v); + } + else + { + interior_edges.insert(e); + } + } + } + + return replace_faces_with_patch(border_vertices, interior_vertices, + border_hedges, interior_edges, face_range, patch, + pmesh, vpm, out); +} + +template +void replace_faces_with_patch(const std::set::face_descriptor>& faces, + const std::vector >& patch, + PolygonMesh& pmesh, + VPM& vpm) +{ + CGAL::Emptyset_iterator out; + replace_faces_with_patch(faces, patch, pmesh, vpm, out); +} + +// -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- + +template +void back_up_face_range_as_point_patch(std::vector >& point_patch, + const FaceRange& face_range, + const PolygonMesh& tmesh, + const VertexPointMap vpm) +{ + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::face_descriptor face_descriptor; + + point_patch.reserve(face_range.size()); + + for(const face_descriptor f : face_range) + { + std::vector face_points; + for(const halfedge_descriptor h : CGAL::halfedges_around_face(halfedge(f, tmesh), tmesh)) + face_points.push_back(get(vpm, target(h, tmesh))); + + point_patch.push_back(face_points); + } +} + +// -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- + +template +void constrain_sharp_and_border_edges(const FaceRange& faces, + TriangleMesh& tmesh, + EdgeConstrainMap& eif, + const bool constrain_sharp_edges, + const double dihedral_angle, + const double /*weak_DA*/, + VertexPointMap vpm, + const GeomTraits& gt) +{ + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + typedef typename boost::graph_traits::face_descriptor face_descriptor; + + typedef typename GeomTraits::FT FT; + typedef typename GeomTraits::Vector_3 Vector; + + std::map is_border_of_selection; + for(face_descriptor f : faces) + { + // @fixme what about nm vertices + for(halfedge_descriptor h : CGAL::halfedges_around_face(halfedge(f, tmesh), tmesh)) + { + // Default initialization is guaranteed to be `false`. Thus, meet it once will switch + // the value to `true` and meeting it twice will switch back to `false`. + const edge_descriptor e = edge(h, tmesh); + is_border_of_selection[e] = !(is_border_of_selection[e]); + } + } + +#if 0 // Until detect_features++ is integrated + CGAL::Polygon_mesh_processing::experimental::detect_sharp_edges_pp(faces, tmesh, dihedral_angle, eif, + parameters::weak_dihedral_angle(weak_DA)); + + // borders are also constrained + for(const auto& ep : is_border_of_selection) + if(ep.second) + put(eif, ep.first, true); +#else + // this is basically the code that is in detect_features (at the very bottom) + // but we do not want a folding to be marked as a sharp feature so the dihedral angle is also + // bounded from above + const double bound = dihedral_angle; + const double cos_angle = std::cos(bound * CGAL_PI / 180.); + + for(const auto& ep : is_border_of_selection) + { + bool flag = ep.second; + if(constrain_sharp_edges && !flag) + { + const halfedge_descriptor h = halfedge(ep.first, tmesh); + CGAL_assertion(!is_border(edge(h, tmesh), tmesh)); + + const face_descriptor f1 = face(h, tmesh); + const face_descriptor f2 = face(opposite(h, tmesh), tmesh); + + // @todo cache normals + const Vector n1 = compute_face_normal(f1, tmesh, parameters::vertex_point_map(vpm).geom_traits(gt)); + const Vector n2 = compute_face_normal(f2, tmesh, parameters::vertex_point_map(vpm).geom_traits(gt)); + const FT c = gt.compute_scalar_product_3_object()(n1, n2); + + // Do not mark as sharp edges with a dihedral angle that is almost `pi` because this is likely + // due to a foldness on the mesh rather than a sharp edge that we wish to preserve + // (Ideally this would be pre-treated as part of the flatness treatment) + flag = (c <= cos_angle && c >= -cos_angle); + } + + is_border_of_selection[ep.first] = flag; // Only needed for output, really + put(eif, ep.first, flag); + } +#endif + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_OUTPUT + std::ofstream out("results/constrained_edges.polylines.txt"); + out << std::setprecision(17); + for(edge_descriptor e : edges(tmesh)) + if(get(eif, e)) + out << "2 " << tmesh.point(source(e, tmesh)) << " " << tmesh.point(target(e, tmesh)) << std::endl; + out.close(); +#endif +} + +// -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- + +template +bool remove_self_intersections_with_smoothing(std::set::face_descriptor>& face_range, + TriangleMesh& tmesh, + const bool constrain_sharp_edges, + const double dihedral_angle, + const double weak_DA, + VertexPointMap vpm, + const GeomTraits& gt) +{ + namespace CP = CGAL::parameters; + + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::face_descriptor face_descriptor; + + typedef typename boost::property_traits::value_type Point; + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: repair with smoothing... (constraining sharp edges: "; + std::cout << std::boolalpha << constrain_sharp_edges << ")" << std::endl; +#endif + + CGAL_precondition(does_self_intersect(face_range, tmesh)); + + // Rather than working directly on the mesh, copy a range and work on this instead + const CGAL::Face_filtered_graph ffg(tmesh, face_range); + TriangleMesh local_mesh; + CGAL::copy_face_graph(ffg, local_mesh, CP::vertex_point_map(vpm)); + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_OUTPUT + std::ofstream out_p("results/local_mesh.off"); + out_p << std::setprecision(17) << local_mesh; + out_p.close(); +#endif + + // Constrain sharp and border edges + typedef CGAL::dynamic_edge_property_t Edge_property_tag; + typedef typename boost::property_map::type EIFMap; + EIFMap eif = get(Edge_property_tag(), local_mesh); + + VertexPointMap local_vpm = get_property_map(vertex_point, local_mesh); + + constrain_sharp_and_border_edges(faces(local_mesh), local_mesh, eif, constrain_sharp_edges, + dihedral_angle, weak_DA, local_vpm, gt); + + // @todo choice of number of iterations? Till convergence && max of 100? + Polygon_mesh_processing::smooth_mesh(faces(local_mesh), local_mesh, CP::edge_is_constrained_map(eif) + .number_of_iterations(100) + .use_safety_constraints(false)); + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_OUTPUT + std::ofstream out("results/post_smoothing_local_mesh.off"); + out << std::setprecision(17) << local_mesh; + out.close(); +#endif + + if(does_self_intersect(local_mesh)) + return false; + + std::vector > patch; + for(const face_descriptor f : faces(local_mesh)) + { + halfedge_descriptor h = halfedge(f, local_mesh); + patch.emplace_back(std::initializer_list{get(local_vpm, target(h, local_mesh)), + get(local_vpm, target(next(h, local_mesh), local_mesh)), + get(local_vpm, target(prev(h, local_mesh), local_mesh))}); + } + + std::set new_faces; + replace_faces_with_patch(face_range, patch, tmesh, vpm, std::inserter(new_faces, new_faces.end())); + CGAL_assertion(!does_self_intersect(new_faces, tmesh, parameters::vertex_point_map(vpm))); + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + if(constrain_sharp_edges) + ++self_intersections_solved_by_constrained_smoothing; + else + ++self_intersections_solved_by_unconstrained_smoothing; +#endif + + return true; +} + +// -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- + +template +bool order_border_halfedge_range(std::vector::halfedge_descriptor>& hrange, + const TriangleMesh& tmesh) +{ + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + + CGAL_precondition(hrange.size() > 2); + + for(std::size_t i=0; i +void dump_cc(const FaceContainer& cc_faces, + const TriangleMesh& mesh, + const std::string filename) +{ + typedef typename boost::graph_traits::face_descriptor face_descriptor; + + typedef typename GetVertexPointMap::const_type VertexPointMap; + VertexPointMap vpm =get_const_property_map(vertex_point, mesh); + + std::ofstream out(filename); + out.precision(17); + + out << "OFF\n"; + out << 3*cc_faces.size() << " " << cc_faces.size() << " 0\n"; + + for(const face_descriptor f : cc_faces) + { + out << get(vpm, source(halfedge(f, mesh), mesh)) << "\n"; + out << get(vpm, target(halfedge(f, mesh), mesh)) << "\n"; + out << get(vpm, target(next(halfedge(f, mesh), mesh), mesh)) << "\n"; + } + + int id = 0; + for(const face_descriptor f : cc_faces) + { + CGAL_USE(f); + out << "3 " << id << " " << id+1 << " " << id+2 << "\n"; + id += 3; + } + + out.close(); +} + +template +void dump_tentative_hole(std::vector >& point_patch, + const std::string filename) +{ + std::ofstream out(filename); + out << std::setprecision(17); + + std::map unique_points_with_id; + for(const std::vector& face : point_patch) + for(const Point& p : face) + unique_points_with_id.insert(std::make_pair(p, 0)); + + out << "OFF\n"; + out << unique_points_with_id.size() << " " << point_patch.size() << " 0\n"; + + int unique_id = 0; + for(auto& pp : unique_points_with_id) + { + out << pp.first << "\n"; + pp.second = unique_id++; + } + + for(const std::vector& face : point_patch) + { + out << face.size(); + for(const Point& p : face) + out << " " << unique_points_with_id.at(p); + out << "\n"; + } + + out << std::endl; + out.close(); +} + +#endif // CGAL_PMP_REMOVE_SELF_INTERSECTION_OUTPUT + +// -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- + +// Hole filling can be influenced by setting a third point associated to an edge on the border of the hole. +// This third point is supposed to represent how the mesh continues on the other side of the hole. +// If that edge is a border edge, there is no third point (since the opposite face is the null face). +// Similarly if the edge is an internal sharp edge, we don't really want to use the opposite face because +// there is by definition a strong discontinuity and it might thus mislead the hole filling algorithm. +// +// Rather, we construct an artifical third point that is in the same plane as the face incident to `h`, +// defined as the third point of the imaginary equilateral triangle incident to opp(h, tmesh) +template +typename boost::property_traits::value_type +construct_artificial_third_point(const typename boost::graph_traits::halfedge_descriptor h, + const TriangleMesh& tmesh, + const VertexPointMap vpm, + const GeomTraits& gt) +{ + typedef typename GeomTraits::FT FT; + typedef typename boost::property_traits::value_type Point; + typedef typename boost::property_traits::reference Point_ref; + typedef typename GeomTraits::Vector_3 Vector; + + const Point_ref p1 = get(vpm, source(h, tmesh)); + const Point_ref p2 = get(vpm, target(h, tmesh)); + const Point_ref opp_p = get(vpm, target(next(h, tmesh), tmesh)); + + // sqrt(3)/2 to have an equilateral triangle with p1, p2, and third_point + const FT dist = 0.5 * CGAL::sqrt(3.) * CGAL::approximate_sqrt(gt.compute_squared_distance_3_object()(p1, p2)); + + const Vector ve1 = gt.construct_vector_3_object()(p1, p2); + const Vector ve2 = gt.construct_vector_3_object()(p1, opp_p); + + // gram schmidt + const FT e1e2_sp = gt.compute_scalar_product_3_object()(ve1, ve2); + Vector orthogonalized_ve2 = gt.construct_sum_of_vectors_3_object()( + ve2, gt.construct_scaled_vector_3_object()(ve1, - e1e2_sp)); + Polygon_mesh_processing::internal::normalize(orthogonalized_ve2, gt); + + const Point mid_p1p2 = gt.construct_midpoint_3_object()(p1, p2); + const Point third_p = gt.construct_translated_point_3_object()( + mid_p1p2, gt.construct_scaled_vector_3_object()(orthogonalized_ve2, -dist)); + + return third_p; +} + +template +bool construct_tentative_hole_patch(std::vector::vertex_descriptor>& cc_border_vertices, + std::set::vertex_descriptor>& cc_interior_vertices, + std::set::edge_descriptor>& cc_interior_edges, + const std::vector& hole_points, + const std::vector& third_points, + const std::vector::halfedge_descriptor>& cc_border_hedges, + const std::set::face_descriptor>& cc_faces, + std::vector >& point_patch, + const TriangleMesh& tmesh, + VertexPointMap /*vpm*/, + const GeomTraits& /*gt*/) +{ + CGAL_static_assertion((std::is_same::value_type, Point>::value)); + + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::face_descriptor face_descriptor; + + typedef CGAL::Triple Face_indices; + + CGAL_assertion(cc_border_hedges.size() == cc_border_hedges.size()); + CGAL_assertion(hole_points.size() == third_points.size()); + + // Collect vertices and edges inside the current selection cc: first collect all vertices and + // edges incident to the faces to remove... + for(const face_descriptor f : cc_faces) + { + for(halfedge_descriptor h : halfedges_around_face(halfedge(f, tmesh), tmesh)) + { + if(halfedge(target(h, tmesh), tmesh) == h) // limit the number of insertions + cc_interior_vertices.insert(target(h, tmesh)); + + cc_interior_edges.insert(edge(h, tmesh)); + } + } + + // ... and then remove those on the boundary + for(halfedge_descriptor h : cc_border_hedges) + { + cc_interior_vertices.erase(target(h, tmesh)); + cc_interior_edges.erase(edge(h, tmesh)); + } + + // try to triangulate the hole using default parameters + // (using Delaunay search space if CGAL_HOLE_FILLING_DO_NOT_USE_DT3 is not defined) + std::vector hole_faces; + if(hole_points.size() > 3) + triangulate_hole_polyline(hole_points, third_points, std::back_inserter(hole_faces)); + else + hole_faces.emplace_back(0, 1, 2); // trivial hole filling + + if(hole_faces.empty()) + { +#ifndef CGAL_HOLE_FILLING_DO_NOT_USE_DT3 + #ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: Failed to fill a hole using Delaunay search space.\n"; + #endif + + triangulate_hole_polyline(hole_points, third_points, std::back_inserter(hole_faces), + parameters::use_delaunay_triangulation(false)); +#endif + if(hole_faces.empty()) + { +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: Failed to fill a hole using the whole search space.\n"; +#endif + return false; + } + } + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_OUTPUT + std::cout << hole_faces.size() << " faces in the patch" << std::endl; + std::vector > to_dump; + for(const Face_indices& face : hole_faces) + { + to_dump.emplace_back(std::initializer_list{hole_points[face.first], + hole_points[face.second], + hole_points[face.third]}); + } + + CGAL_assertion(to_dump.size() == hole_faces.size()); + + static int hole_id = 0; + std::stringstream oss; + oss << "results/tentative_hole_" << hole_id++ << ".off" << std::ends; + const std::string filename = oss.str().c_str(); + + dump_tentative_hole(to_dump, filename); +#endif + + // make sure that the hole filling is valid, we check that no + // edge already in the mesh is present in hole_faces. + bool non_manifold_edge_found = false; + for(const Face_indices& triangle : hole_faces) + { + std::array edges = make_array(triangle.first, triangle.second, + triangle.second, triangle.third, + triangle.third, triangle.first); + for(int k=0; k<3; ++k) + { + const int vi = edges[2*k], vj = edges[2*k+1]; + + // ignore boundary edges + if(vi+1 == vj || (vj == 0 && static_cast(vi) == cc_border_vertices.size()-1)) + continue; + + halfedge_descriptor h = halfedge(cc_border_vertices[vi], cc_border_vertices[vj], tmesh).first; + if(h != boost::graph_traits::null_halfedge() && + cc_interior_edges.count(edge(h, tmesh)) == 0) + { + non_manifold_edge_found = true; + break; + } + } + + if(non_manifold_edge_found) + break; + } + + if(non_manifold_edge_found) + { +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: Triangulation produced is non-manifold when plugged into the mesh.\n"; +#endif + + return false; + } + + point_patch.reserve(point_patch.size() + hole_faces.size()); + for(const Face_indices& face : hole_faces) + { + point_patch.emplace_back(std::initializer_list{hole_points[face.first], + hole_points[face.second], + hole_points[face.third]}); + } + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: Found acceptable hole-filling patch.\n"; +#endif + + return true; +} + +// This function constructs the ranges `hole_points` and `third_points`. Note that for a sub-hole, +// these two ranges are constructed in another function because we don't want to set 'third_points' +// for edges that are on the border of the sub-hole but not on the border of the (full) hole. +template +bool construct_tentative_hole_patch(std::vector::vertex_descriptor>& cc_border_vertices, + std::set::vertex_descriptor>& cc_interior_vertices, + std::set::edge_descriptor>& cc_interior_edges, + const std::vector::halfedge_descriptor>& cc_border_hedges, + const std::set::face_descriptor>& cc_faces, + std::vector::value_type> >& patch, + const TriangleMesh& tmesh, + VertexPointMap vpm, + const GeomTraits& gt) +{ + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + + typedef typename boost::property_traits::value_type Point; + + std::vector hole_points, third_points; + hole_points.reserve(cc_border_hedges.size()); + third_points.reserve(cc_border_hedges.size()); + + for(const halfedge_descriptor h : cc_border_hedges) + { + const vertex_descriptor v = source(h, tmesh); + hole_points.push_back(get(vpm, v)); + cc_border_vertices.push_back(v); + + CGAL_assertion(!is_border(h, tmesh)); + + if(is_border_edge(h, tmesh)) + third_points.push_back(construct_artificial_third_point(h, tmesh, vpm, gt)); + else + third_points.push_back(get(vpm, target(next(opposite(h, tmesh), tmesh), tmesh))); + } + + CGAL_postcondition(hole_points.size() >= 3); + + return construct_tentative_hole_patch(cc_border_vertices, cc_interior_vertices, cc_interior_edges, + hole_points, third_points, cc_border_hedges, cc_faces, + patch, tmesh, vpm, gt); +} + +// In that overload, we don't know the border of the patch because the face range is a sub-region +// of the hole. We also construct `hole_points` and `third_points`, but with no third point for internal +// sharp edges because a local self-intersection is usually caused by folding and thus we do not want +// a third point resulting from folding to constrain the way we fill the hole in the wrong way. +template +bool construct_tentative_sub_hole_patch(std::vector::value_type> >& patch, + const std::set::face_descriptor>& sub_cc_faces, + const std::set::face_descriptor>& cc_faces, + TriangleMesh& tmesh, + VertexPointMap vpm, + const GeomTraits& gt) +{ + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + typedef typename boost::graph_traits::face_descriptor face_descriptor; + + typedef typename boost::property_traits::value_type Point; + + // Collect halfedges on the boundary of the region to be selected + // (pointing inside the domain to be remeshed) + std::set internal_hedges; + std::vector cc_border_hedges; + for(const face_descriptor fd : sub_cc_faces) + { + halfedge_descriptor h = halfedge(fd, tmesh); + for(int i=0; i<3;++i) + { + if(is_border(opposite(h, tmesh), tmesh)) + { + cc_border_hedges.push_back(h); + } + else + { + const face_descriptor opp_f = face(opposite(h, tmesh), tmesh); + if(sub_cc_faces.count(opp_f) == 0) + { + cc_border_hedges.push_back(h); + if(cc_faces.count(opp_f) != 0) + internal_hedges.insert(h); + } + } + + h = next(h, tmesh); + } + } + + // Sort halfedges so that they describe the sequence of halfedges of the hole to be made + if(!order_border_halfedge_range(cc_border_hedges, tmesh)) + { +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: More than one border in sub-hole. Not currently handled." << std::endl; +#endif + + return false; + } + + // @todo we don't care about those sets, so instead there could be a system of output iterators + // in construct_tentative_hole_patch() instead (and here would be emptyset iterators). + std::set cc_interior_vertices; + std::set cc_interior_edges; + + std::vector cc_border_vertices; + cc_border_vertices.reserve(cc_border_hedges.size()); + + std::vector hole_points, third_points; + hole_points.reserve(cc_border_hedges.size()); + third_points.reserve(cc_border_hedges.size()); + + for(const halfedge_descriptor h : cc_border_hedges) + { + const vertex_descriptor v = source(h, tmesh); + hole_points.push_back(get(vpm, v)); + cc_border_vertices.push_back(v); + + CGAL_assertion(!is_border(h, tmesh)); + + if(internal_hedges.count(h) == 0 && // `h` is on the border of the full CC + !is_border_edge(h, tmesh)) + { + third_points.push_back(get(vpm, target(next(opposite(h, tmesh), tmesh), tmesh))); + } + else // `h` is on the border of the sub CC but not on the border of the full CC + { + const Point tp = construct_artificial_third_point(h, tmesh, vpm, gt); + third_points.push_back(tp); + } + } + + return construct_tentative_hole_patch(cc_border_vertices, cc_interior_vertices, cc_interior_edges, + hole_points, third_points, cc_border_hedges, sub_cc_faces, + patch, tmesh, vpm, gt); +} + +// -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- + +// This function is only called when the hole is NOT subdivided into smaller holes +template +bool fill_hole(std::vector::halfedge_descriptor>& cc_border_hedges, + std::set::face_descriptor>& cc_faces, + std::set::face_descriptor>& working_face_range, + TriangleMesh& tmesh, + VertexPointMap vpm, + const GeomTraits& gt) +{ + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + typedef typename boost::graph_traits::face_descriptor face_descriptor; + + typedef typename boost::property_traits::value_type Point; + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: Attempting hole-filling (no constraints), " << cc_faces.size() << " faces\n"; +#endif + + if(!order_border_halfedge_range(cc_border_hedges, tmesh)) + { + CGAL_assertion(false); // we shouldn't fail to orient the boundary cycle of the complete hole + return false; + } + + std::set cc_interior_vertices; + std::set cc_interior_edges; + + std::vector cc_border_vertices; + cc_border_vertices.reserve(cc_border_hedges.size()); + + std::vector > patch; + if(!construct_tentative_hole_patch(cc_border_vertices, cc_interior_vertices, cc_interior_edges, + cc_border_hedges, cc_faces, patch, tmesh, vpm, gt)) + { +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: Failed to find acceptable hole patch\n"; +#endif + + return false; + } + + // Could renew the range directly within the patch replacement function + // to avoid erasing and re-adding the same face + for(const face_descriptor f : cc_faces) + working_face_range.erase(f); + + // Plug the new triangles in the mesh, reusing previous edges and faces + replace_faces_with_patch(cc_border_vertices, cc_interior_vertices, + cc_border_hedges, cc_interior_edges, + cc_faces, patch, tmesh, vpm, + std::inserter(working_face_range, working_face_range.end())); + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_OUTPUT + static int filed_hole_id = 0; + std::stringstream oss; + oss << "results/filled_basic_" << filed_hole_id++ << ".off" << std::ends; + std::ofstream(oss.str().c_str()) << std::setprecision(17) << tmesh; +#endif + + CGAL_postcondition(is_valid_polygon_mesh(tmesh)); + + return true; +} + +// Same function as above but border of the hole is not known +template +bool fill_hole(std::set::face_descriptor>& cc_faces, + std::set::face_descriptor>& working_face_range, + TriangleMesh& tmesh, + VertexPointMap vpm, + const GeomTraits& gt) +{ + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::face_descriptor face_descriptor; + + std::vector cc_border_hedges; + for(face_descriptor fd : cc_faces) + { + halfedge_descriptor h = halfedge(fd, tmesh); + for(int i=0; i<3; ++i) + { + if(is_border(opposite(h, tmesh), tmesh) || cc_faces.count(face(opposite(h, tmesh), tmesh)) == 0) + cc_border_hedges.push_back(h); + + h = next(h, tmesh); + } + } + + if(order_border_halfedge_range(cc_border_hedges, tmesh)) + return fill_hole(cc_border_hedges, cc_faces, working_face_range, tmesh, vpm, gt); + else + return false; +} + +// Patch is not valid if: +// - we insert the same face more than once +// - insert (geometric) non-manifold edges +template +bool check_patch_sanity(const std::vector >& patch) +{ + std::set > unique_faces; + std::map, int> unique_edges; + + for(const std::vector& face : patch) + { + if(!unique_faces.emplace(face.begin(), face.end()).second) // this face had already been found + return false; + + int i = (unique_edges.insert(std::make_pair(std::set { face[0], face[1] }, 0)).first->second)++; + if(i == 2) // non-manifold edge + return false; + + i = (unique_edges.insert(std::make_pair(std::set { face[1], face[2] }, 0)).first->second)++; + if(i == 2) // non-manifold edge + return false; + + i = (unique_edges.insert(std::make_pair(std::set { face[2], face[0] }, 0)).first->second)++; + if(i == 2) // non-manifold edge + return false; + } + + // Check for self-intersections + // Don't know anything better than just making a mesh out of the soup for now... + std::vector points; + std::vector > faces; + std::map ids; + + std::size_t c = 0; + for(const std::vector& face : patch) + { + std::vector ps_f; + for(const Point& pt : face) + { + std::size_t id = c; + std::pair::iterator, bool> is_insert_successful = + ids.insert(std::make_pair(pt, c)); + if(is_insert_successful.second) // first time we've seen that point + { + ++c; + points.push_back(pt); + } + else // already seen that point + { + id = is_insert_successful.first->second; + } + + CGAL_assertion(id < points.size()); + ps_f.push_back(id); + } + + faces.push_back(ps_f); + } + + TriangleMesh patch_mesh; + if(is_polygon_soup_a_polygon_mesh(faces)) + polygon_soup_to_polygon_mesh(points, faces, patch_mesh); + else + return false; + + if(does_self_intersect(patch_mesh)) + { +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: Tentative patch has self-intersections." << std::endl; +#endif + + return false; + } + + return true; +} + +template +bool fill_hole_with_constraints(std::vector::halfedge_descriptor>& cc_border_hedges, + std::set::face_descriptor>& cc_faces, + std::set::face_descriptor>& working_face_range, + TriangleMesh& tmesh, + const double dihedral_angle, + const double weak_DA, + VertexPointMap vpm, + const GeomTraits& gt) +{ + typedef typename boost::graph_traits::face_descriptor face_descriptor; + + typedef typename boost::property_traits::value_type Point; + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: Attempting local hole-filling with constrained sharp edges..." << std::endl; +#endif + + // If we are treating self intersections locally, first try to constrain sharp edges in the hole + typedef CGAL::dynamic_edge_property_t Edge_property_tag; + typedef typename boost::property_map::type EIFMap; + EIFMap eif = get(Edge_property_tag(), tmesh); + + constrain_sharp_and_border_edges(cc_faces, tmesh, eif, true /*constrain_sharp_edges*/, dihedral_angle, weak_DA, vpm, gt); + + // Partition the hole using these constrained edges + std::set visited_faces; + std::vector > patch; + + int cc_counter = 0; + for(face_descriptor f : cc_faces) + { + if(!visited_faces.insert(f).second) // already visited that face + continue; + + // gather the faces of the sub-hole + std::set sub_cc; + Polygon_mesh_processing::connected_component(f, tmesh, std::inserter(sub_cc, sub_cc.end()), + CGAL::parameters::edge_is_constrained_map(eif)); + + visited_faces.insert(sub_cc.begin(), sub_cc.end()); + + std::cout << "CC of size " << sub_cc.size() << " (total: " << cc_faces.size() << ")" << std::endl; + ++cc_counter; + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_OUTPUT + dump_cc(sub_cc, tmesh, "results/current_cc.off"); +#endif + + // The mesh is not modified, but 'patch' gets filled + if(!construct_tentative_sub_hole_patch(patch, sub_cc, cc_faces, tmesh, vpm, gt)) + { + // Something went wrong while finding a potential cover for the a sub-hole --> use basic hole-filling + return fill_hole(cc_border_hedges, cc_faces, working_face_range, tmesh, vpm, gt); + } + } + + std::cout << cc_counter << " independent sub holes" << std::endl; + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_OUTPUT + std::ofstream out("results/hole_fillers.off"); + out.precision(17); + + out << "OFF\n"; + out << 3*patch.size() << " " << patch.size() << " 0\n"; + + for(const auto& f : patch) + { + for(const auto& pt : f) + out << pt << "\n"; + } + + int id = 0; + for(std::size_t i=0; i(patch)) + { + std::cout << "Unhealthy patch, use base fill_hole" << std::endl; + return fill_hole(cc_border_hedges, cc_faces, working_face_range, tmesh, vpm, gt); + } + + // Plug the hole-filling patch in the mesh + std::set new_faces; + replace_faces_with_patch(cc_faces, patch, tmesh, vpm, std::inserter(new_faces, new_faces.end())); + + // Otherwise it should have failed the sanity check + CGAL_assertion(!does_self_intersect(new_faces, tmesh, parameters::vertex_point_map(vpm))); + + // Update working range with the new faces + for(const face_descriptor f : cc_faces) + working_face_range.erase(f); + + working_face_range.insert(new_faces.begin(), new_faces.end()); + + return true; +} + +template +bool remove_self_intersections_with_hole_filling(std::vector::halfedge_descriptor>& cc_border_hedges, + std::set::face_descriptor>& cc_faces, + std::set::face_descriptor>& working_face_range, + TriangleMesh& tmesh, + bool local_self_intersection_removal, + const double strong_dihedral_angle, + const double weak_dihedral_angle, + VertexPointMap vpm, + const GeomTraits& gt) +{ +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_OUTPUT + std::ofstream out("results/zone_border.polylines.txt"); + out << std::setprecision(17); + for(const auto& h : cc_border_hedges) + out << "2 " << tmesh.point(source(h, tmesh)) << " " << tmesh.point(target(h, tmesh)) << std::endl; + out.close(); +#endif + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTIONS_NO_CONSTRAINTS_IN_HOLE_FILLING + // Do not try to impose sharp edge constraints if we are not doing local-only self intersections removal + local_self_intersection_removal = false; +#endif + + bool success = false; + if(local_self_intersection_removal) + { + success = fill_hole_with_constraints(cc_border_hedges, cc_faces, working_face_range, tmesh, + strong_dihedral_angle, weak_dihedral_angle, vpm, gt); + } + else + { + success = fill_hole(cc_border_hedges, cc_faces, working_face_range, tmesh, vpm, gt); + } + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + if(success) + { + if(local_self_intersection_removal) + ++self_intersections_solved_by_constrained_hole_filling; + else + ++self_intersections_solved_by_unconstrained_hole_filling; + } +#endif + + return success; +} + +// the parameter `step` controls how many extra layers of faces we take around the range `faces_to_remove` +template +std::pair +remove_self_intersections_one_step(std::set::face_descriptor>& faces_to_remove, + std::set::face_descriptor>& working_face_range, + TriangleMesh& tmesh, + const int step, + const bool preserve_genus, + const bool only_treat_self_intersections_locally, + const double strong_dihedral_angle, + const double weak_dihedral_angle, + VertexPointMap vpm, + const GeomTraits& gt) +{ + typedef boost::graph_traits graph_traits; + typedef typename graph_traits::vertex_descriptor vertex_descriptor; + typedef typename graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename graph_traits::face_descriptor face_descriptor; + + std::set faces_to_remove_copy = faces_to_remove; + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << "DEBUG: running remove_self_intersections_one_step, step " << step + << " with " << faces_to_remove.size() << " intersecting faces\n"; +#endif + + bool something_was_done = false; // indicates if a region was successfully remeshed + bool all_fixed = true; // indicates if all removal went well + // indicates if a removal was not possible because the region handle has + // some boundary cycle of halfedges + bool topology_issue = false; + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: is_valid in one_step(tmesh)? "; + std::cout.flush(); + std::cout << is_valid_polygon_mesh(tmesh) << "\n"; +#endif + + while(!faces_to_remove.empty()) + { +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: --------------- " << faces_to_remove.size() << " faces to remove (step: " << step << ")\n"; +#endif + + // Process a connected component of faces to remove. + // collect all the faces from the connected component + std::set cc_faces; + std::vector queue(1, *faces_to_remove.begin()); // temporary queue + cc_faces.insert(queue.back()); + while(!queue.empty()) + { + face_descriptor top = queue.back(); + queue.pop_back(); + halfedge_descriptor h = halfedge(top, tmesh); + for(int i=0; i<3; ++i) + { + face_descriptor adjacent_face = face(opposite(h, tmesh), tmesh); + if(adjacent_face!=boost::graph_traits::null_face()) + { + if(faces_to_remove.count(adjacent_face) != 0 && cc_faces.insert(adjacent_face).second) + queue.push_back(adjacent_face); + } + + h = next(h, tmesh); + } + } + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: " << cc_faces.size() << " faces in CC\n"; + std::cout << " DEBUG: first face: " << get(vpm, source(halfedge(*(cc_faces.begin()), tmesh), tmesh)) << " " + << get(vpm, target(halfedge(*(cc_faces.begin()), tmesh), tmesh)) << " " + << get(vpm, target(next(halfedge(*(cc_faces.begin()), tmesh), tmesh), tmesh)) << "\n"; +#endif + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_OUTPUT + static int ini_cc_id = 0; + std::stringstream ini_oss, mesh_oss; + std::cout << "Output initial CC #" << ini_cc_id << std::endl; + ini_oss << "results/initial_cc_" << ini_cc_id << ".off" << std::ends; + dump_cc(cc_faces, tmesh, ini_oss.str().c_str()); + + mesh_oss << "results/mesh_at_cc_ " << ini_cc_id++ << ".off" << std::ends; + std::ofstream mout(mesh_oss.str().c_str()); + mout << std::setprecision(17) << tmesh; + mout.close(); +#endif + + // expand the region to be filled + if(step > 0) + { + expand_face_selection(cc_faces, tmesh, step, + make_boolean_property_map(cc_faces), + Emptyset_iterator()); + } + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_OUTPUT + static int exp_cc_id = 0; + std::stringstream oss; + std::cout << "Output expanded CC #" << exp_cc_id << std::endl; + oss << "results/expanded_cc_" << exp_cc_id++ << ".off" << std::ends; + dump_cc(cc_faces, tmesh, oss.str().c_str()); +#endif + + // try to compactify the selection region by also selecting all the faces included + // in the bounding box of the initial selection + std::vector stack_for_expension; + Bbox_3 bb; + for(face_descriptor fd : cc_faces) + { + for(halfedge_descriptor h : halfedges_around_face(halfedge(fd, tmesh), tmesh)) + { + bb += get(vpm, target(h, tmesh)).bbox(); + face_descriptor nf = face(opposite(h, tmesh), tmesh); + if(nf != boost::graph_traits::null_face() && cc_faces.count(nf) == 0) + { + stack_for_expension.push_back(opposite(h, tmesh)); + } + } + } + + while(!stack_for_expension.empty()) + { + halfedge_descriptor h = stack_for_expension.back(); + stack_for_expension.pop_back(); + if(cc_faces.count(face(h, tmesh)) == 1) + continue; + + if(do_overlap(bb, get(vpm, target(next(h, tmesh), tmesh)).bbox())) + { + cc_faces.insert(face(h, tmesh)); + halfedge_descriptor candidate = opposite(next(h, tmesh), tmesh); + if(face(candidate, tmesh) != boost::graph_traits::null_face()) + stack_for_expension.push_back(candidate); + + candidate = opposite(prev(h, tmesh), tmesh); + if(face(candidate, tmesh) != boost::graph_traits::null_face()) + stack_for_expension.push_back(candidate); + } + } + + if(only_treat_self_intersections_locally) + { + if(!does_self_intersect(cc_faces, tmesh, parameters::vertex_point_map(vpm).geom_traits(gt))) + { +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: No self-intersection in CC\n"; +#endif + + for(const face_descriptor f : cc_faces) + faces_to_remove.erase(f); + + continue; + } + } + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: " << cc_faces.size() << " faces in expanded CC\n"; +#endif + + // remove faces from the set to process + for(const face_descriptor f : cc_faces) + faces_to_remove.erase(f); + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_OUTPUT + std::stringstream ex_oss; + std::cout << "Output FULLY expanded CC #" << exp_cc_id-1 << std::endl; + ex_oss << "results/fully_expanded_cc_" << exp_cc_id-1 << ".off" << std::ends; + dump_cc(cc_faces, tmesh, ex_oss.str().c_str()); +#endif + + //Check for non-manifold vertices in the selection and remove them by selecting all incident faces: + // extract the set of halfedges that is on the boundary of the holes to be + // made. In addition, we make sure no hole to be created contains a vertex + // visited more than once along a hole border (pinched surface) + // We save the size of boundary_hedges to make sur halfedges added + // from non_filled_hole are not removed. + bool non_manifold_vertex_remaining_in_selection = false; + do + { + bool non_manifold_vertex_removed = false; //here non-manifold is for the 1D polyline + std::vector boundary_hedges; + for(face_descriptor fh : cc_faces) + { + halfedge_descriptor h = halfedge(fh, tmesh); + for(int i=0; i<3; ++i) + { + if(is_border(opposite(h, tmesh), tmesh) || cc_faces.count(face(opposite(h, tmesh), tmesh)) == 0) + boundary_hedges.push_back(h); + + h = next(h, tmesh); + } + } + + // detect vertices visited more than once along + // a hole border. We then remove all faces incident + // to such a vertex to force the removal of the vertex. + // Actually even if two holes are sharing a vertex, this + // vertex will be removed. It is not needed but since + // we do not yet have one halfedge per hole it is simpler + // and does not harm + std::set border_vertices; + for(halfedge_descriptor h : boundary_hedges) + { + if(!border_vertices.insert(target(h, tmesh)).second) + { + bool any_face_added = false; + for(halfedge_descriptor hh : halfedges_around_target(h, tmesh)) + { + if(!is_border(hh, tmesh)) + { + // add the face to the current selection + any_face_added |= cc_faces.insert(face(hh, tmesh)).second; + faces_to_remove.erase(face(hh, tmesh)); + } + } + + if(any_face_added) + non_manifold_vertex_removed = true; + else + non_manifold_vertex_remaining_in_selection = true; + } + } + + if(!non_manifold_vertex_removed) + break; + } + while(true); + + if(preserve_genus && non_manifold_vertex_remaining_in_selection) + { + topology_issue = true; +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: CC not handled due to the presence at least one non-manifold vertex\n"; +#endif + + continue; // cannot replace a patch containing a nm vertex by a disk + } + + // before running this function if preserve_genus=false, we duplicated + // all of them + CGAL_assertion(!non_manifold_vertex_remaining_in_selection); + + // Collect halfedges on the boundary of the region to be selected + // (pointing inside the domain to be remeshed) + std::vector cc_border_hedges; + for(face_descriptor fd : cc_faces) + { + halfedge_descriptor h = halfedge(fd, tmesh); + for(int i=0; i<3; ++i) + { + if(is_border(opposite(h, tmesh), tmesh) || cc_faces.count(face(opposite(h, tmesh), tmesh)) == 0) + cc_border_hedges.push_back(h); + + h = next(h, tmesh); + } + } + + if(cc_faces.size() == 1) // it is a triangle nothing better can be done + continue; + + working_face_range.insert(cc_faces.begin(), cc_faces.end()); + + // Now, we have a proper selection that we can work on. + +#ifndef CGAL_PMP_REMOVE_SELF_INTERSECTIONS_NO_SMOOTHING + // First, try to smooth if we only care about local self-intersections + // Two different approaches: + // - First, try to constrain edges that are in the zone to smooth and whose dihedral angle is large, + // but not too large (we don't want to constrain edges that are created by foldings) + // - If that fails, try to smooth without any constraints, but make sure that the deviation from + // the first zone is small + // + // If smoothing fails, the face patch is restored to its pre-smoothing state. + // + // Note that there is no need to update the working range because smoothing doesn`t change + // the number of faces (and old faces are re-used). + bool fixed_by_smoothing = false; + + if(only_treat_self_intersections_locally) + { + fixed_by_smoothing = remove_self_intersections_with_smoothing(cc_faces, tmesh, true, + strong_dihedral_angle, + weak_dihedral_angle, vpm, gt); + + if(!fixed_by_smoothing) // try again, but without constraining sharp edges + { + #ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: Could not be solved via smoothing with constraints\n"; + #endif + + fixed_by_smoothing = remove_self_intersections_with_smoothing(cc_faces, tmesh, false, + strong_dihedral_angle, + weak_dihedral_angle, vpm, gt); + } + } + + if(fixed_by_smoothing) + { + #ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: Solved with smoothing!\n"; + #endif + + something_was_done = true; + continue; + } + #ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + else + { + std::cout << " DEBUG: Could not be solved via smoothing\n"; + } + #endif +#endif // ndef CGAL_PMP_REMOVE_SELF_INTERSECTIONS_NO_SMOOTHING + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: Trying hole-filling based approach\n"; +#endif + + if(!is_selection_a_topological_disk(cc_faces, tmesh)) + { + // check if the selection contains cycles of border halfedges + bool only_border_edges = true; + std::set mesh_border_hedge; + + for(halfedge_descriptor h : cc_border_hedges) + { + if(!is_border(opposite(h, tmesh), tmesh)) + only_border_edges = false; + else + mesh_border_hedge.insert(opposite(h, tmesh)); + } + + int nb_cycles = 0; + while(!mesh_border_hedge.empty()) + { + // we must count the number of cycle of boundary edges + halfedge_descriptor h_b = *mesh_border_hedge.begin(), h=h_b; + mesh_border_hedge.erase(mesh_border_hedge.begin()); + do + { + h = next(h, tmesh); + if(h == h_b) + { + // found a cycle + ++nb_cycles; + break; + } + else + { + typename std::set::iterator it = mesh_border_hedge.find(h); + if(it == mesh_border_hedge.end()) + break; // not a cycle + + mesh_border_hedge.erase(it); + } + } + while(true); + } + + if(nb_cycles > (only_border_edges ? 1 : 0)) + { +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: CC not handled due to the presence of " + << nb_cycles << " of boundary edges\n"; +#endif + + topology_issue = true; + continue; + } + else + { + if(preserve_genus) + { +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: CC not handled because it is not a topological disk (preserve_genus=true)\n"; +#endif + + all_fixed = false; + continue; + } + + // count the number of cycles of halfedges of the boundary + std::map bhs; + for(halfedge_descriptor h : cc_border_hedges) + bhs[source(h, tmesh)] = target(h, tmesh); + + int nbc=0; + while(!bhs.empty()) + { + ++nbc; + std::pair top=*bhs.begin(); + bhs.erase(bhs.begin()); + + do + { + typename std::map::iterator it_find = bhs.find(top.second); + if(it_find == bhs.end()) + break; + + top = *it_find; + bhs.erase(it_find); + } + while(true); + } + + if(nbc != 1) + { +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: CC not handled because it is not a topological disk(" + << nbc << " boundary cycles)\n"; +#endif + + all_fixed = false; + continue; + } + else + { +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: CC that is not a topological disk but has only one boundary cycle(preserve_genus=false)\n"; +#endif + } + } + } + + if(!remove_self_intersections_with_hole_filling(cc_border_hedges, cc_faces, working_face_range, + tmesh, only_treat_self_intersections_locally, + strong_dihedral_angle, weak_dihedral_angle, + vpm, gt)) + { +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: Failed to fill hole\n"; +#endif + + all_fixed = false; + } + else + { + something_was_done = true; + } + } + + if(!something_was_done) + { + faces_to_remove.swap(faces_to_remove_copy); +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << " DEBUG: Nothing was changed during this step, self-intersections won`t be recomputed." << std::endl; +#endif + } + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_OUTPUT + std::stringstream oss; + oss << "results/after_step_" << step << ".off" << std::ends; + std::ofstream(oss.str().c_str()) << std::setprecision(17) << tmesh; +#endif + + return std::make_pair(all_fixed, topology_issue); +} + +} // namespace internal + +namespace experimental { + +template +bool remove_self_intersections(const FaceRange& face_range, + TriangleMesh& tmesh, + const NamedParameters& np) +{ + using parameters::choose_parameter; + using parameters::get_parameter; + + typedef boost::graph_traits graph_traits; + typedef typename graph_traits::face_descriptor face_descriptor; + + // named parameter extraction + typedef typename GetVertexPointMap::type VertexPointMap; + VertexPointMap vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), + get_property_map(vertex_point, tmesh)); + + typedef typename GetGeomTraits::type GeomTraits; + GeomTraits gt = choose_parameter(get_parameter(np, internal_np::geom_traits)); + + bool preserve_genus = choose_parameter(get_parameter(np, internal_np::preserve_genus), true); + const bool only_treat_self_intersections_locally = choose_parameter(get_parameter(np, internal_np::apply_per_connected_component), false); + + // When treating intersections locally, we don't want to grow the working range too much as + // either the solution is found fast, or it's too difficult and neither local smoothing or local + // hole filling are going to provide nice results. + const int default_max_step = only_treat_self_intersections_locally ? 2 : 7; + const int max_steps = choose_parameter(get_parameter(np, internal_np::number_of_iterations), default_max_step); + + // @fixme give it its own named parameter rather than abusing 'with_dihedral_angle'? + const double strong_dihedral_angle = choose_parameter(get_parameter(np, internal_np::with_dihedral_angle), 60.); + + // detect_feature_pp NP (unused for now) + const double weak_dihedral_angle = 0.; // choose_parameter(get_parameter(np, internal_np::weak_dihedral_angle), 20.); + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << "DEBUG: Starting remove_self_intersections, is_valid(tmesh)? " << is_valid_polygon_mesh(tmesh) << "\n"; + std::cout << "\tpreserve_genus: " << preserve_genus << std::endl; + std::cout << "\tonly_treat_self_intersections_locally: " << only_treat_self_intersections_locally << std::endl; + std::cout << "\tmax_steps: " << max_steps << std::endl; + std::cout << "\tstrong_dihedral_angle: " << strong_dihedral_angle << std::endl; + std::cout << "\tweak_dihedral_angle: " << weak_dihedral_angle << std::endl; +#endif + + if(!preserve_genus) + duplicate_non_manifold_vertices(tmesh, np); + + // Look for self-intersections in the mesh and remove them + int step = -1; + bool all_fixed = true; // indicates if the filling of all created holes went fine + bool topology_issue = false; // indicates if some boundary cycles of edges are blocking the fixing + std::set faces_to_remove; + std::set working_face_range(face_range.begin(), face_range.end()); + + while(++step < max_steps) + { + if(faces_to_remove.empty()) // the previous round might have been blocked due to topological constraints + { + typedef std::pair Face_pair; + std::vector self_inter; + + // TODO : possible optimization to reduce the range to check with the bbox + // of the previous patches or something. + self_intersections(working_face_range, tmesh, std::back_inserter(self_inter)); + + std::cout << self_inter.size() << " intersecting pairs" << std::endl; + + for(const Face_pair& fp : self_inter) + { + faces_to_remove.insert(fp.first); + faces_to_remove.insert(fp.second); + } + } + + if(faces_to_remove.empty() && all_fixed) + { +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << "DEBUG: There are no more faces to remove." << std::endl; +#endif + break; + } + + std::tie(all_fixed, topology_issue) = + internal::remove_self_intersections_one_step( + faces_to_remove, working_face_range, tmesh, + step, preserve_genus, only_treat_self_intersections_locally, + strong_dihedral_angle, weak_dihedral_angle, vpm, gt); + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + if(all_fixed && topology_issue) + std::cout << "DEBUG: boundary cycles of boundary edges involved in self-intersections.\n"; +#endif + } + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_DEBUG + std::cout << "solved by constrained smoothing: " << internal::self_intersections_solved_by_constrained_smoothing << std::endl; + std::cout << "solved by unconstrained smoothing: " << internal::self_intersections_solved_by_unconstrained_smoothing << std::endl; + std::cout << "solved by constrained hole-filling: " << internal::self_intersections_solved_by_constrained_hole_filling << std::endl; + std::cout << "solved by unconstrained hole-filling: " << internal::self_intersections_solved_by_unconstrained_hole_filling << std::endl; +#endif + +#ifdef CGAL_PMP_REMOVE_SELF_INTERSECTION_OUTPUT + std::ofstream("results/final.off") << std::setprecision(17) << tmesh; +#endif + + return step < max_steps; +} + +template +bool remove_self_intersections(const FaceRange& face_range, TriangleMesh& tmesh) +{ + return remove_self_intersections(face_range, tmesh, parameters::all_default()); +} + +template +bool remove_self_intersections(TriangleMesh& tmesh, const CGAL_PMP_NP_CLASS& np) +{ + return remove_self_intersections(faces(tmesh), tmesh, np); +} + +template +bool remove_self_intersections(TriangleMesh& tmesh) +{ + return remove_self_intersections(tmesh, parameters::all_default()); +} + +} // namespace experimental +} // namespace Polygon_mesh_processing +} // namespace CGAL + +#endif // CGAL_POLYGON_MESH_PROCESSING_REPAIR_SELF_INTERSECTIONS_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/shape_predicates.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/shape_predicates.h index 8004766c805..3e43d8fc6d2 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/shape_predicates.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/shape_predicates.h @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -85,6 +86,81 @@ bool is_degenerate_edge(typename boost::graph_traits::edge_descript return is_degenerate_edge(e, pm, parameters::all_default()); } +/// \ingroup PMP_repairing_grp +/// collects the degenerate edges within a given range of edges. +/// +/// @tparam EdgeRange a model of `Range` with value type `boost::graph_traits::%edge_descriptor` +/// @tparam TriangleMesh a model of `HalfedgeGraph` +/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" +/// +/// @param edges a subset of edges of `tm` +/// @param tm a triangle mesh +/// @param out an output iterator in which the degenerate edges are written +/// @param np optional \ref pmp_namedparameters "Named Parameters" described below +/// +/// \cgalNamedParamsBegin +/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `tm`. +/// The type of this map is model of `ReadWritePropertyMap`. +/// If this parameter is omitted, an internal property map for +/// `CGAL::vertex_point_t` should be available in `TriangleMesh` +/// \cgalParamEnd +/// \cgalParamBegin{geom_traits} a geometric traits class instance. +/// The traits class must provide the nested type `Point_3`, +/// and the nested functor `Equal_3` to check whether two points are identical. +/// \cgalParamEnd +/// \cgalNamedParamsEnd +template +OutputIterator degenerate_edges(const EdgeRange& edges, + const TriangleMesh& tm, + OutputIterator out, + const NamedParameters& np) +{ + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + + for(edge_descriptor ed : edges) + if(is_degenerate_edge(ed, tm, np)) + *out++ = ed; + + return out; +} + +template +OutputIterator degenerate_edges(const EdgeRange& edges, + const TriangleMesh& tm, + OutputIterator out, + typename boost::enable_if< + typename boost::has_range_iterator + >::type* = 0) +{ + return degenerate_edges(edges, tm, out, CGAL::parameters::all_default()); +} + +/// \ingroup PMP_repairing_grp +/// calls the function `degenerate_edges()` with the range: `edges(tm)`. +/// +/// See above for the comprehensive description of the parameters. +/// +template +OutputIterator degenerate_edges(const TriangleMesh& tm, + OutputIterator out, + const NamedParameters& np +#ifndef DOXYGEN_RUNNING + , + typename boost::disable_if< + boost::has_range_iterator + >::type* = 0 +#endif + ) +{ + return degenerate_edges(edges(tm), tm, out, np); +} + +template +OutputIterator degenerate_edges(const TriangleMesh& tm, OutputIterator out) +{ + return degenerate_edges(edges(tm), tm, out, CGAL::parameters::all_default()); +} + /// \ingroup PMP_repairing_grp /// checks whether a triangle face is degenerate. /// A triangle face is considered degenerate if the geometric positions of its vertices are collinear. @@ -142,6 +218,83 @@ bool is_degenerate_triangle_face(typename boost::graph_traits::fac return CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(f, tm, parameters::all_default()); } +/// \ingroup PMP_repairing_grp +/// collects the degenerate faces within a given range of faces. +/// +/// @tparam FaceRange a model of `Range` with value type `boost::graph_traits::%face_descriptor` +/// @tparam TriangleMesh a model of `FaceGraph` +/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" +/// +/// @param faces a subset of faces of `tm` +/// @param tm a triangle mesh +/// @param out an output iterator in which the degenerate faces are put +/// @param np optional \ref pmp_namedparameters "Named Parameters" described below +/// +/// \cgalNamedParamsBegin +/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `tm`. +/// The type of this map is model of `ReadWritePropertyMap`. +/// If this parameter is omitted, an internal property map for +/// `CGAL::vertex_point_t` should be available in `TriangleMesh` +/// \cgalParamEnd +/// \cgalParamBegin{geom_traits} a geometric traits class instance. +/// The traits class must provide the nested functor `Collinear_3` +/// to check whether three points are collinear. +/// \cgalParamEnd +/// \cgalNamedParamsEnd +/// +template +OutputIterator degenerate_faces(const FaceRange& faces, + const TriangleMesh& tm, + OutputIterator out, + const NamedParameters& np) +{ + typedef typename boost::graph_traits::face_descriptor face_descriptor; + + for(face_descriptor fd : faces) + { + if(is_degenerate_triangle_face(fd, tm, np)) + *out++ = fd; + } + return out; +} + +template +OutputIterator degenerate_faces(const FaceRange& faces, + const TriangleMesh& tm, + OutputIterator out, + typename boost::enable_if< + boost::has_range_iterator + >::type* = 0) +{ + return degenerate_faces(faces, tm, out, CGAL::parameters::all_default()); +} + +/// \ingroup PMP_repairing_grp +/// calls the function `degenerate_faces()` with the range: `faces(tm)`. +/// +/// See above for the comprehensive description of the parameters. +/// +template +OutputIterator degenerate_faces(const TriangleMesh& tm, + OutputIterator out, + const NamedParameters& np +#ifndef DOXYGEN_RUNNING + , + typename boost::disable_if< + boost::has_range_iterator + >::type* = 0 +#endif + ) +{ + return degenerate_faces(faces(tm), tm, out, np); +} + +template +OutputIterator degenerate_faces(const TriangleMesh& tm, OutputIterator out) +{ + return degenerate_faces(faces(tm), tm, out, CGAL::parameters::all_default()); +} + /// \ingroup PMP_repairing_grp /// checks whether a triangle face is needle. /// A triangle is said to be a needle if its longest edge is much longer than its shortest edge. diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/smooth_mesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/smooth_mesh.h index 865d4332268..60cdbf355be 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/smooth_mesh.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/smooth_mesh.h @@ -182,7 +182,7 @@ void smooth_mesh(const FaceRange& faces, } ECMap ecmap = choose_parameter(get_parameter(np, internal_np::edge_is_constrained), - Constant_property_map(false)); + Constant_property_map(false)); // a constrained edge has constrained extremities for(face_descriptor f : faces) @@ -234,8 +234,16 @@ void smooth_mesh(const FaceRange& faces, for(unsigned int i=0; i std::size_t stitch_borders(PolygonMesh& pmesh, - const HalfedgePairsRange& hedge_pairs_to_stitch) + const HalfedgePairsRange& hedge_pairs_to_stitch) { return stitch_borders(pmesh, hedge_pairs_to_stitch, CGAL::parameters::all_default()); } @@ -890,9 +890,9 @@ std::size_t stitch_borders(PolygonMesh& pmesh, halfedge_descriptor; std::vector< std::pair > hedge_pairs_to_stitch; - typedef typename GetVertexPointMap::const_type VPMap; - VPMap vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), - get_const_property_map(vertex_point, pmesh)); + typedef typename GetVertexPointMap::const_type VPM; + VPM vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), + get_const_property_map(vertex_point, pmesh)); #ifdef CGAL_PMP_STITCHING_DEBUG std::cout << "------- Stitch cycles..." << std::endl; @@ -907,7 +907,7 @@ std::size_t stitch_borders(PolygonMesh& pmesh, internal::collect_duplicated_stitchable_boundary_edges(pmesh, std::back_inserter(hedge_pairs_to_stitch), - internal::Less_for_halfedge(pmesh, vpm), + internal::Less_for_halfedge(pmesh, vpm), vpm, np); res += stitch_borders(pmesh, hedge_pairs_to_stitch, np); diff --git a/Polygon_mesh_processing/include/CGAL/polygon_mesh_processing.h b/Polygon_mesh_processing/include/CGAL/polygon_mesh_processing.h index 843cb500e75..dcea23d7177 100644 --- a/Polygon_mesh_processing/include/CGAL/polygon_mesh_processing.h +++ b/Polygon_mesh_processing/include/CGAL/polygon_mesh_processing.h @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include #include @@ -46,7 +48,7 @@ #include #include #include -#include +#include // the named parameter header being not documented the doc is put here for now #ifdef DOXYGEN_RUNNING diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt index e4e2b21f80b..f18ccb8bd61 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt @@ -97,13 +97,16 @@ endif() create_single_source_cgal_program("test_pmp_locate.cpp") create_single_source_cgal_program("test_pmp_snapping.cpp") create_single_source_cgal_program("test_pmp_non_conforming_snapping.cpp") - create_single_source_cgal_program("remove_degeneracies_test.cpp") + create_single_source_cgal_program("test_pmp_repair_degeneracies.cpp") create_single_source_cgal_program("test_pmp_manifoldness.cpp") create_single_source_cgal_program("test_mesh_smoothing.cpp") create_single_source_cgal_program("test_remove_caps_needles.cpp") +# create_single_source_cgal_program("test_pmp_repair_self_intersections.cpp") if( TBB_FOUND ) + include(CGAL_target_use_TBB) CGAL_target_use_TBB(test_pmp_distance) + CGAL_target_use_TBB(orient_polygon_soup_test) CGAL_target_use_TBB(self_intersection_surface_mesh_test) else() message( STATUS "NOTICE: Intel TBB was not found. test_pmp_distance will use sequential code." ) @@ -118,6 +121,9 @@ find_package(Ceres QUIET) if(TARGET ceres) target_compile_definitions( test_mesh_smoothing PRIVATE CGAL_PMP_USE_CERES_SOLVER ) target_link_libraries( test_mesh_smoothing PRIVATE ceres ) + +# target_compile_definitions( test_pmp_repair_self_intersections PRIVATE CGAL_PMP_USE_CERES_SOLVER ) +# target_link_libraries( test_pmp_repair_self_intersections PRIVATE ceres ) endif(TARGET ceres) if(BUILD_TESTING) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/orient_polygon_soup_test.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/orient_polygon_soup_test.cpp index eabf7f14538..2469c494ad2 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/orient_polygon_soup_test.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/orient_polygon_soup_test.cpp @@ -4,7 +4,9 @@ #include #include +#include #include +#include #include #include @@ -134,9 +136,70 @@ int test_orient(const bool save_oriented) { return 0; } + +template +int test_pipeline() +{ + typedef typename K::Point_3 Point_3; + typedef CGAL::Polyhedron_3 Polyhedron; + + std::vector points; + std::vector< std::vector > polygons; + Polyhedron ref1; + + shuffle_off("data/elephant.off", "elephant-shuffled.off"); + std::ifstream input("elephant-shuffled.off"); + if ( !input || !read_soup(input, points, polygons)){ + std::cerr << "Error: can not shuffled file.\n"; + return 1; + } + input.close(); + input.open("data/elephant.off"); + if ( !input || !(input >> ref1)){ + std::cerr << "Error: can not read reference file.\n"; + return 1; + } + input.close(); + CGAL::Polygon_mesh_processing::orient_triangle_soup_with_reference_triangle_mesh(ref1, points, polygons); + + CGAL::Polygon_mesh_processing::duplicate_non_manifold_edges_in_polygon_soup(points, polygons); + + Polyhedron poly; + CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh( + points, polygons, poly); + typedef typename boost::property_map >::type Fccmap; + Fccmap fim = get(CGAL::dynamic_face_property_t(), poly); + std::size_t id =0; + for(auto f : faces(poly)) + { + put(fim, f, id++); + } + CGAL::Polygon_mesh_processing:: + merge_reversible_connected_components(poly, + CGAL::parameters::face_index_map(fim)); + + Fccmap fccmap = get(CGAL::dynamic_face_property_t(), poly); + + assert(CGAL::Polygon_mesh_processing:: + connected_components(poly, fccmap, + CGAL::parameters::face_index_map(fim)) == 1); + return 0; +} + int main() { assert(test_orient(false) == 0); assert(test_orient(false) == 0); + + int res = test_pipeline(); + assert(res == 0); + res = test_pipeline(); + assert(res == 0); +#if defined(CGAL_LINKED_WITH_TBB) + res = test_pipeline(); + assert(res == 0); + //res = test_pipeline(); + //assert(res == 0); +#endif return 0; } diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_clip.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_clip.cpp index 512724ba23d..a20745227b7 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_clip.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_clip.cpp @@ -528,9 +528,16 @@ void test_split() std::vector meshes; PMP::split(tm1, tm2); + //try with np + typedef typename boost::graph_traits::faces_size_type faces_size_type; + typename boost::template property_map< + TriangleMesh, CGAL::dynamic_face_property_t >::type + pidmap = get(CGAL::dynamic_face_property_t(), tm1); + CGAL::Polygon_mesh_processing::connected_components( + tm1, pidmap, CGAL::parameters::all_default()); PMP::split_connected_components(tm1, meshes, - params::all_default()); + params::face_patch_map(pidmap)); CGAL_assertion(meshes.size() == 5); //if the order is not deterministc, put the num_vertices in a list and check @@ -540,6 +547,7 @@ void test_split() CGAL_assertion(num_vertices(meshes[2]) == 142); CGAL_assertion(num_vertices(meshes[3]) == 83); CGAL_assertion(num_vertices(meshes[4]) == 104); + CGAL_assertion(tm1.is_valid()); CGAL::clear(tm1); CGAL::clear(tm2); @@ -580,14 +588,59 @@ void test_split() //if the list does contain all those numbers. CGAL_assertion(num_vertices(meshes[0]) == 588); CGAL_assertion(num_vertices(meshes[1]) == 50); + CGAL_assertion(tm1.is_valid()); CGAL::clear(tm1); CGAL::clear(tm2); meshes.clear(); } +template +void test_isocuboid() +{ + TriangleMesh tm; + //closed intersection curves + std::ifstream input("data-coref/elephant.off"); + input >> tm; + + if(!input) + { + std::cerr<<"File not found. Aborting."< meshes; + K::Iso_cuboid_3 splitter(K::Point_3(-0.3, -0.45, -0.25), + K::Point_3( 0.3, 0.45, 0.25)); + PMP::split(tm, splitter); + + PMP::split_connected_components(tm, + meshes); + + CGAL_assertion(meshes.size() == 10); + //if the order is not deterministc, put the num_vertices in a list and check + //if the list does contain all those numbers. + CGAL_assertion(num_vertices(meshes[0]) == 2657); + CGAL_assertion(num_vertices(meshes[1]) == 131 ); + CGAL_assertion(num_vertices(meshes[2]) == 32 ); + CGAL_assertion(num_vertices(meshes[3]) == 123 ); + CGAL_assertion(num_vertices(meshes[4]) == 220 ); + CGAL_assertion(num_vertices(meshes[5]) == 107 ); + CGAL_assertion(num_vertices(meshes[6]) == 121 ); + CGAL_assertion(num_vertices(meshes[7]) == 56 ); + CGAL_assertion(num_vertices(meshes[8]) == 49 ); + CGAL_assertion(num_vertices(meshes[9]) == 13 ); + CGAL_assertion(tm.is_valid()); + + CGAL::clear(tm); + meshes.clear(); +} int main() { + std::cout << "Surface Mesh" << std::endl; test(); @@ -597,6 +650,9 @@ int main() std::cout << "running test_split with Surface_mesh\n"; test_split(); + std::cout << "running test_iso_cuboid with Surface_mesh\n"; + test_isocuboid(); + std::cout << "running test_split_plane with Surface_mesh\n"; test_split_plane(); @@ -606,6 +662,8 @@ int main() std::cout << "running test_split_plane with Polyhedron\n"; test_split_plane(); + std::cout << "running test_iso_cuboid with Polyhedron\n"; + test_isocuboid(); std::cout << "Done!" << std::endl; return EXIT_SUCCESS; diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_distance.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_distance.cpp index 437da882d74..dbcbce4db8a 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_distance.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_pmp_distance.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include @@ -258,6 +259,11 @@ void general_tests(const TriangleMesh& m1, std::cout << "Max distance to triangle mesh (sequential) " << PMP::max_distance_to_triangle_mesh(points,m1) << "\n"; + + std::vector samples; + PMP::sample_triangle_mesh(m1, std::back_inserter(samples)); + std::cout << samples.size()<<" points sampled on mesh."<(m1,m2); } -int main(int, char** argv) +int main(int argc, char** argv) { + if(argc != 3) + { + std::cerr << "Missing input meshes" << std::endl; + return EXIT_FAILURE; + } + Mesh m1,m2; std::ifstream input(argv[1]); input >> m1; input.close(); - input.open(argv[2]); input >> m2; - + input.close(); std::cout << "First mesh has " << num_faces(m1) << " faces\n"; std::cout << "Second mesh has " << num_faces(m2) << " faces\n"; @@ -304,5 +315,16 @@ int main(int, char** argv) test_concept(); + std::vector > faces; + std::vector points; + input.open(argv[1]); + CGAL::read_OFF(input, points, faces); + input.close(); + + std::vector samples; + PMP::sample_triangle_soup(points, faces, std::back_inserter(samples)); + std::cout< +#include + +#include +#include + +#include +#include +#include + +namespace PMP = ::CGAL::Polygon_mesh_processing; +namespace CP = ::CGAL::parameters; + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; + +typedef CGAL::Surface_mesh Surface_mesh; + +typedef boost::graph_traits::edge_descriptor edge_descriptor; +typedef boost::graph_traits::face_descriptor face_descriptor; + +void fix_self_intersections(const char* mesh_filename, + const char* mesh_selection_filename = nullptr) +{ + std::cout << std::endl << "---------------" << std::endl; + std::cout << "Test " << mesh_filename << std::endl; + if(mesh_selection_filename) + std::cout << "With selection " << mesh_selection_filename << std::endl; + + std::ifstream input(mesh_filename); + Surface_mesh mesh; + if(!input || !(input >> mesh) || mesh.is_empty()) + { + std::cerr << "Error: " << mesh_filename << " is not a valid off file.\n"; + std::exit(1); + } + + std::list selected_faces; + + if(mesh_selection_filename) + { + std::ifstream selection_input(mesh_selection_filename); + std::string line; + // skip the first line (faces are on the second line) + if(!selection_input || !std::getline(selection_input, line) || !std::getline(selection_input, line)) + { + std::cerr << "Error: could not read selection: " << mesh_selection_filename << std::endl; + std::exit(1); + } + + std::istringstream face_line(line); + std::size_t face_id; + while(face_line >> face_id) + selected_faces.push_back(*(faces(mesh).begin() + face_id)); + std::cout << selected_faces.size() << " faces selected" << std::endl; + + PMP::experimental::remove_self_intersections(selected_faces, mesh); + } + else + { + PMP::experimental::remove_self_intersections(mesh); + } + + std::ofstream out("post_repair.off"); + out.precision(17); + out << mesh; + out.close(); + + assert(CGAL::is_valid_polygon_mesh(mesh)); + assert(!PMP::does_self_intersect(mesh)); +} + +void fix_local_self_intersections(const char* fname) +{ + std::cout << std::endl << "-----LOCAL------" << std::endl; + std::cout << "Test " << fname << std::endl; + + std::ifstream input(fname); + Surface_mesh mesh; + if(!input || !(input >> mesh) || mesh.is_empty()) + { + std::cerr << "Error: " << fname << " is not a valid off file.\n"; + std::exit(1); + } + + PMP::experimental::remove_self_intersections(mesh, CP::apply_per_connected_component(true)); + + std::ofstream out("post_local_repair.off"); + out.precision(17); + out << mesh; + out.close(); + + assert(CGAL::is_valid_polygon_mesh(mesh)); + assert(!PMP::does_self_intersect(mesh)); +} + +int main(int, char**) +{ + std::cout.precision(17); + std::cerr.precision(17); + +#if 0 + fix_local_self_intersections("data_repair/brain.off"); + fix_self_intersections("data_repair/brain.off") + + fix_self_intersections("data_repair/flute.off"); + fix_self_intersections("data_repair/dinosaur.off"); + fix_self_intersections("data_repair/hemispheres.off"); + + // selection is adjacent to a self-intersection but does not contain any intersection + fix_self_intersections("data_repair/brain.off", "data_repair/brain-complete.selection.txt"); + + // selection covers nicely a self-intersection + fix_self_intersections("data_repair/brain.off", "data_repair/brain-adjacent.selection.txt"); + + // selection contains part of a self intersection + fix_self_intersections("data_repair/brain.off", "data_repair/brain-partial.selection.txt"); + + // selection contains disjoint parts of a self intersection + fix_self_intersections("data_repair/brain.off", "data_repair/brain-disjoint.selection.txt"); + + // Remove only self-intersections within a single hemisphere + fix_self_intersections("data_repair/hemispheres.off"); + fix_self_intersections("data_repair/hemispheres.off", "data_repair/hemispheres-half.selection.txt"); +#endif + + return 0; +} diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_remove_caps_needles.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_remove_caps_needles.cpp index f6e418b23a7..397f4ffd7e4 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_remove_caps_needles.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_remove_caps_needles.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_shape_smoothing.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_shape_smoothing.cpp index 7a2f18c919d..52ed2a4b8b3 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_shape_smoothing.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_shape_smoothing.cpp @@ -1,4 +1,4 @@ -#define CGAL_PMP_SMOOTHING_VERBOSE +#define CGAL_PMP_SMOOTHING_DEBUG #include @@ -31,7 +31,7 @@ bool equal_doubles(double d1, double d2, double e) template void test_implicit_constrained_devil(Mesh mesh) { -#ifdef CGAL_PMP_SMOOTHING_VERBOSE +#ifdef CGAL_PMP_SMOOTHING_DEBUG std::cout << "-- test_implicit_constrained_devil --" << std::endl; #endif @@ -69,7 +69,7 @@ void test_implicit_constrained_devil(Mesh mesh) ++i; } -#ifdef CGAL_PMP_SMOOTHING_VERBOSE +#ifdef CGAL_PMP_SMOOTHING_DEBUG std::ofstream out("output_implicit_constrained_devil.off"); out << mesh; out.close(); @@ -79,7 +79,7 @@ void test_implicit_constrained_devil(Mesh mesh) template void test_implicit_constrained_elephant(Mesh mesh) { -#ifdef CGAL_PMP_SMOOTHING_VERBOSE +#ifdef CGAL_PMP_SMOOTHING_DEBUG std::cout << "-- test_implicit_constrained_elephant --" << std::endl; #endif @@ -117,7 +117,7 @@ void test_implicit_constrained_elephant(Mesh mesh) ++i; } -#ifdef CGAL_PMP_SMOOTHING_VERBOSE +#ifdef CGAL_PMP_SMOOTHING_DEBUG std::ofstream out("output_implicit_constrained_elephant.off"); out << mesh; out.close(); @@ -127,14 +127,14 @@ void test_implicit_constrained_elephant(Mesh mesh) template void test_curvature_flow_time_step(Mesh mesh) { -#ifdef CGAL_PMP_SMOOTHING_VERBOSE +#ifdef CGAL_PMP_SMOOTHING_DEBUG std::cout << "-- test_curvature_flow_time_step --" << std::endl; #endif const double time_step = 1e-15; PMP::smooth_shape(mesh, time_step); -#ifdef CGAL_PMP_SMOOTHING_VERBOSE +#ifdef CGAL_PMP_SMOOTHING_DEBUG std::ofstream out("output_devil_time_step.off"); out << mesh; out.close(); @@ -144,14 +144,14 @@ void test_curvature_flow_time_step(Mesh mesh) template void test_curvature_flow(Mesh mesh) { -#ifdef CGAL_PMP_SMOOTHING_VERBOSE +#ifdef CGAL_PMP_SMOOTHING_DEBUG std::cout << "-- test_curvature_flow --" << std::endl; #endif const double time_step = 1.0; PMP::smooth_shape(mesh, time_step); -#ifdef CGAL_PMP_SMOOTHING_VERBOSE +#ifdef CGAL_PMP_SMOOTHING_DEBUG std::ofstream out("output_precision_elephant.off"); out << mesh; out.close(); diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/C3t3_io_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/C3t3_io_plugin.cpp index 4a85cb13b7e..d2232c11dd4 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/C3t3_io_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/C3t3_io_plugin.cpp @@ -406,6 +406,11 @@ struct Update_vertex typedef typename Tr2::Vertex V2; typedef typename Tr2::Point Point; + V2 operator()(const V1&) + { + return V2(); + } + bool operator()(const V1& v1, V2& v2) { v2.set_point(Point(v1.point())); @@ -418,7 +423,7 @@ struct Update_vertex const Sp_index sp_index = boost::get(index); v2.set_index((std::max)(sp_index.first, sp_index.second)); } - break; + break; default:// -1, 0, 1, 3 v2.set_index(boost::get(v1.index())); } @@ -427,7 +432,9 @@ struct Update_vertex }; // end struct Update_vertex struct Update_cell { + typedef Fake_mesh_domain::Surface_patch_index Sp_index; + template bool operator()(const C1& c1, C2& c2) { c2.set_subdomain_index(c1.subdomain_index()); @@ -442,7 +449,6 @@ struct Update_cell { } }; // end struct Update_cell -#include template struct Update_vertex_from_CDT_3 { @@ -452,24 +458,28 @@ struct Update_vertex_from_CDT_3 { typedef typename Tr2::Vertex V2; typedef typename Tr2::Point Point; - bool operator()(const V1& v1, V2& v2) + V2 operator()(const V1&) + { + return V2(); + } + void operator()(const V1& v1, V2& v2) { v2.set_point(Point(v1.point())); v2.set_dimension(2); v2.set_special(false); - return true; } }; // end struct Update_vertex struct Update_cell_from_CDT_3 { + typedef Fake_mesh_domain::Surface_patch_index Sp_index; - template - bool operator()(const C1& c1, C2& c2) { + + template + void operator()(const C1& c1, C2& c2) { c2.set_subdomain_index(1); for(int i = 0; i < 4; ++i) { c2.set_surface_patch_index(i, c1.constrained_facet[i]); } - return true; } }; // end struct Update_cell @@ -500,11 +510,10 @@ try_load_a_cdt_3(std::istream& is, C3t3& c3t3) } } if(binary) CGAL::set_binary_mode(is); - if(CGAL::file_input< + if(c3t3.triangulation().file_input< Fake_CDT_3, - C3t3::Triangulation, Update_vertex_from_CDT_3, - Update_cell_from_CDT_3>(is, c3t3.triangulation())) + Update_cell_from_CDT_3>(is)) { c3t3.rescan_after_load_of_triangulation(); std::cerr << "Try load a CDT_3... DONE"; @@ -544,11 +553,10 @@ try_load_other_binary_format(std::istream& is, C3t3& c3t3) } if(binary) CGAL::set_binary_mode(is); else CGAL::set_ascii_mode(is); - std::istream& f_is = CGAL::file_input< + std::istream& f_is = c3t3.triangulation().file_input< Fake_c3t3::Triangulation, - C3t3::Triangulation, Update_vertex, - Update_cell>(is, c3t3.triangulation()); + Update_cell>(is); c3t3.rescan_after_load_of_triangulation(); return f_is.good(); diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Repair_polyhedron_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Repair_polyhedron_plugin.cpp index c2a214b0173..59365eb0d0e 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Repair_polyhedron_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Repair_polyhedron_plugin.cpp @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include "ui_RemoveNeedlesDialog.h" @@ -220,7 +220,7 @@ void Polyhedron_demo_repair_polyhedron_plugin::on_actionRemoveSelfIntersections_ if (poly_item) { bool solved = - CGAL::Polygon_mesh_processing::remove_self_intersections( + CGAL::Polygon_mesh_processing::experimental::remove_self_intersections( *poly_item->polyhedron()); if (!solved) CGAL::Three::Three::information(tr("Some self-intersection could not be fixed")); diff --git a/Polyhedron/demo/Polyhedron/Scene.cpp b/Polyhedron/demo/Polyhedron/Scene.cpp index d664cb9d707..2fcc276a1ce 100644 --- a/Polyhedron/demo/Polyhedron/Scene.cpp +++ b/Polyhedron/demo/Polyhedron/Scene.cpp @@ -598,6 +598,10 @@ void Scene::renderWireScene(const QList &items, || item.renderingMode() == PointsPlusNormals || item.renderingMode() == GouraudPlusEdges) { + if(with_names) { + viewer->glClearDepthf(1.0); + viewer->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } viewer->setGlPointSize(2.f); item.drawEdges(viewer); } @@ -656,6 +660,10 @@ void Scene::renderPointScene(const QList &items, (item.renderingMode() == PointsPlusNormals) || (item.renderingMode() == ShadedPoints)) { + if(with_names) { + viewer->glClearDepthf(1.0); + viewer->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } viewer->setGlPointSize(3.0f); item.drawPoints(viewer); } diff --git a/Polyhedron/demo/Polyhedron/include/CGAL/Triangulation_file_input.h b/Polyhedron/demo/Polyhedron/include/CGAL/Triangulation_file_input.h deleted file mode 100644 index 9385f5b693f..00000000000 --- a/Polyhedron/demo/Polyhedron/include/CGAL/Triangulation_file_input.h +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) 1997-2010 INRIA Sophia-Antipolis (France). -// Copyright (c) 2011 GeometryFactory Sarl (France) -// All rights reserved. -// -// This file is part of CGAL (www.cgal.org); you may redistribute it under -// the terms of the Q Public License version 1.0. -// See the file LICENSE.QPL distributed with CGAL. -// -// Licensees holding a valid commercial license may use this file in -// accordance with the commercial license agreement provided with the software. -// -// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -// -// $URL$ -// $Id$ -// -// Author(s) : Laurent Rineau -// - -// Adapted from operator>>(std::istream&, Triangulation_3&) from -// - -#ifndef CGAL_TRIANGULATION_FILE_INPUT_3_H -#define CGAL_TRIANGULATION_FILE_INPUT_3_H - -#include - -namespace CGAL { - -template -std::istream& file_input(std::istream& is, Tr2 &tr, - Update_vertex update_vertex = Update_vertex(), - Update_cell update_cell = Update_cell()) - // reads - // the dimension - // the number of finite vertices - // the non combinatorial information on vertices (point, etc) - // the number of cells - // the cells by the indices of their vertices in the preceding list - // of vertices, plus the non combinatorial information on each cell - // the neighbors of each cell by their index in the preceding list of cells - // when dimension < 3 : the same with faces of maximal dimension -{ - typedef Tr2 Triangulation; - typedef typename Triangulation::Vertex_handle Vertex_handle; - typedef typename Triangulation::Cell_handle Cell_handle; - - typedef typename Tr1::Vertex Vertex1; - typedef typename Tr1::Cell Cell1; - - tr.clear(); - tr.tds().cells().clear(); - - std::size_t n; - int d; - if(is_ascii(is)) - is >> d >> n; - else { - read(is, d); - read(is, n); - } - if(!is) return is; - tr.tds().set_dimension(d); - - std::vector< Vertex_handle > V(n+1); - V[0] = tr.infinite_vertex(); - // the infinite vertex is numbered 0 - - for (std::size_t i=1; i <= n; i++) { - V[i] = tr.tds().create_vertex(); - Vertex1 v; - if(!(is >> v)) return is; - if(!update_vertex(v, *V[i])) { - is.setstate(std::ios_base::failbit); - return is; - } - } - - std::vector< Cell_handle > C; - - std::size_t m; - tr.tds().read_cells(is, V, m, C); - - for (std::size_t j=0 ; j < m; j++) { - Cell1 c; - if(!(is >> c)) return is; - if(!update_cell(c, *(C[j]))) { - is.setstate(std::ios_base::failbit); - return is; - } - } - - CGAL_triangulation_assertion( tr.is_valid(false) ); - return is; -} - -} // end namespace CGAL - -#endif // CGAL_TRIANGULATION_FILE_INPUT_3_H diff --git a/Property_map/doc/Property_map/Doxyfile.in b/Property_map/doc/Property_map/Doxyfile.in index 2167dbca4fe..a079ae055f4 100644 --- a/Property_map/doc/Property_map/Doxyfile.in +++ b/Property_map/doc/Property_map/Doxyfile.in @@ -3,4 +3,4 @@ PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - CGAL and Boost Property Maps" INPUT += ${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/property_map.h EXCLUDE += ${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/Dynamic_property_map.h -EXAMPLE_PATH = ${CGAL_Point_set_processing_3_EXAMPLE_DIR} +EXAMPLE_PATH += ${CGAL_Point_set_processing_3_EXAMPLE_DIR} diff --git a/Property_map/doc/Property_map/Property_map.txt b/Property_map/doc/Property_map/Property_map.txt index fef8999a0c6..d2ecf66d1df 100644 --- a/Property_map/doc/Property_map/Property_map.txt +++ b/Property_map/doc/Property_map/Property_map.txt @@ -6,6 +6,8 @@ namespace CGAL { \anchor chapterProperty_map +\cgalAutoToc + \authors Andreas Fabri and Laurent Saboret \section Property_mapA A Short Introduction to the Boost Property Maps Library @@ -55,6 +57,20 @@ The following example reads a point set from an input file and writes it to a fi The following example reads a point set in the `xyz` format and computes the average spacing. %Index, position and color are stored in a tuple and accessed through property maps. \cgalExample{Point_set_processing_3/average_spacing_example.cpp} +\section Property_mapCustom Writing Custom Property Maps + +Property maps are especially useful when using predefined data +structures that are not part of the \cgal library: algorithms written +with property maps can be called on these data structures provided the +user writes the required property maps, without the need to create +deep copies of potentially large data into \cgal formats. + +The following example shows how to write a readable point map and a +read-write normal map to run \cgal normal estimation and orientation +algorithm on raw `double` arrays: +\cgalExample{Property_map/custom_property_map.cpp} + + */ } /* namespace CGAL */ diff --git a/Property_map/doc/Property_map/examples.txt b/Property_map/doc/Property_map/examples.txt index fe5b9e2f71c..30870650f01 100644 --- a/Property_map/doc/Property_map/examples.txt +++ b/Property_map/doc/Property_map/examples.txt @@ -2,4 +2,5 @@ \example Point_set_processing_3/remove_outliers_example.cpp \example Point_set_processing_3/read_write_xyz_point_set_example.cpp \example Point_set_processing_3/average_spacing_example.cpp +\example Property_map/custom_property_map.cpp */ diff --git a/Property_map/examples/Property_map/CMakeLists.txt b/Property_map/examples/Property_map/CMakeLists.txt index 9b183930b64..d5717d3f5d2 100644 --- a/Property_map/examples/Property_map/CMakeLists.txt +++ b/Property_map/examples/Property_map/CMakeLists.txt @@ -32,4 +32,10 @@ endif() create_single_source_cgal_program( "dynamic_properties.cpp" ) +find_package(Eigen3 3.1.0) #(requires 3.1.0 or greater) +if (EIGEN3_FOUND) + create_single_source_cgal_program( "custom_property_map.cpp" ) + CGAL_target_use_Eigen(custom_property_map) +endif() + diff --git a/Property_map/examples/Property_map/custom_property_map.cpp b/Property_map/examples/Property_map/custom_property_map.cpp new file mode 100644 index 00000000000..540bec3cd2d --- /dev/null +++ b/Property_map/examples/Property_map/custom_property_map.cpp @@ -0,0 +1,112 @@ +#include +#include +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_3 = Kernel::Point_3; +using Vector_3 = Kernel::Vector_3; +using Generator = CGAL::Random_points_on_sphere_3; + +// Example of readable property map to get CGAL::Point_3 objects from +// 3 coordinate arrays +struct Custom_point_map +{ + using key_type = std::size_t; // The iterator's value type is an index + using value_type = Point_3; // The object manipulated by the algorithm is a Point_3 + using reference = Point_3; // The object does not exist in memory, so there's no reference + using category = boost::readable_property_map_tag; // The property map is only used for reading + + double *x, *y, *z; + + Custom_point_map (double* x = nullptr, double* y = nullptr, double* z = nullptr) + : x(x), y(y), z(z) { } + + // The get() function returns the object expected by the algorithm (here, Point_3) + friend Point_3 get (const Custom_point_map& map, std::size_t idx) + { + return Point_3 (map.x[idx], map.y[idx], map.z[idx]); + } +}; + +// Example of read-write property map to get CGAL::Vector_3 objects from +// a buffer array and put CGAL::Vector_3 values in this buffer +struct Custom_normal_map +{ + using key_type = std::size_t; // The iterator's value type is an index + using value_type = Vector_3; // The object manipulated by the algorithm is a Vector_3 + using reference = Vector_3; // The object does not exist in memory, so there's no reference + using category = boost::read_write_property_map_tag; // The property map is used both + // for reading and writing data + double *buffer; + + Custom_normal_map (double* buffer = nullptr) + : buffer (buffer) { } + + // The get() function returns the object expected by the algorithm (here, Vector_3) + friend Vector_3 get (const Custom_normal_map& map, std::size_t idx) + { + return Vector_3 (map.buffer[idx * 3 ], + map.buffer[idx * 3 + 1], + map.buffer[idx * 3 + 2]); + } + + // The put() function updated the user's data structure from the + // object handled by the algorithm (here Vector_3) + friend void put (const Custom_normal_map& map, std::size_t idx, const Vector_3& vector_3) + { + map.buffer[idx * 3 ] = vector_3.x(); + map.buffer[idx * 3 + 1] = vector_3.y(); + map.buffer[idx * 3 + 2] = vector_3.z(); + } +}; + + +int main() +{ + constexpr std::size_t nb_points = 1000; + + // in this example, points are stored as separate coordinate arrays + double x[nb_points]; + double y[nb_points]; + double z[nb_points]; + + // generate random points + Generator generator; + for (std::size_t i = 0; i < nb_points; ++ i) + { + Point_3 p = *(generator ++ ); + x[i] = p.x(); + y[i] = p.y(); + z[i] = p.z(); + } + + // normals are stored as a contiguous double array + double normals[3 *nb_points]; + + // we use a vector of indices to access arrays + std::vector indices; + indices.reserve (nb_points); + for (std::size_t i = 0; i < nb_points; ++ i) + indices.push_back(i); + + // estimate and orient normals using directly user's data structure + // instead of creating deep copies using Point_3 and Vector_3 + CGAL::jet_estimate_normals + (indices, 12, + CGAL::parameters::point_map (Custom_point_map(x,y,z)). + normal_map (Custom_normal_map(normals))); + + CGAL::mst_orient_normals + (indices, 12, + CGAL::parameters::point_map (Custom_point_map(x,y,z)). + normal_map (Custom_normal_map(normals))); + + // Display first 10 points+normals + for (std::size_t i = 0; i < 10; ++ i) + std::cerr << "Point(" << i << ") = " << x[i] << " " << y[i] << " " << z[i] + << "\tNormal(" << i << ") = " + << normals[3*i] << " " << normals[3*i+1] << " " << normals[3*i+2] << std::endl; + + return EXIT_SUCCESS; +} diff --git a/Property_map/include/CGAL/property_map.h b/Property_map/include/CGAL/property_map.h index fd1c383f0cb..f58413b9755 100644 --- a/Property_map/include/CGAL/property_map.h +++ b/Property_map/include/CGAL/property_map.h @@ -216,6 +216,22 @@ struct Identity_property_map /// @} }; + +/// \cond SKIP_IN_MANUAL +template +struct Identity_property_map_no_lvalue +{ + typedef T key_type; ///< typedef to `T` + typedef T value_type; ///< typedef to `T` + typedef T reference; ///< typedef to `T` + typedef boost::readable_property_map_tag category; ///< `boost::readable_property_map_tag` + + typedef Identity_property_map_no_lvalue Self; + + friend reference get(const Self&, const key_type& k) {return k;} +}; +/// \endcond + /// Free function to create a `Identity_property_map` property map. /// /// \relates Identity_property_map diff --git a/STL_Extension/include/CGAL/for_each.h b/STL_Extension/include/CGAL/for_each.h new file mode 100644 index 00000000000..f68bba0e80b --- /dev/null +++ b/STL_Extension/include/CGAL/for_each.h @@ -0,0 +1,134 @@ +// Copyright (c) 2020 GeometryFactory (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) : Simon Giraudot + +#ifndef CGAL_FOR_EACH_H +#define CGAL_FOR_EACH_H + +#include + +#ifdef CGAL_LINKED_WITH_TBB +#include +#include +#include +#endif // CGAL_LINKED_WITH_TBB + +/* + CGAL::for_each(Range, Function) does the following: + + - if Sequential_tag is used, apply Function to all elements of Range + - if Parallel_tag is used: + * static assert that TBB is available + * if Range has random access iterators, use a TBB parallel_for + loop to apply Function to all elements of Range + * if Range doesn't have random access iterators, first copy the + iterators in a vector and then use a TBB parallel_for loop to + apply Function to all elements of Range + + The loop is interrupted if `functor` returns false (it carries on + until the end otherwise). +*/ + +namespace CGAL { + +namespace internal { + +template +void for_each (RangeRef range, + const std::function::type>::reference)>& functor, + const Sequential_tag&, + IteratorCategory) +{ + for (typename std::iterator_traits + ::type>::reference r : range) + if (!functor(r)) + break; +} + +#ifdef CGAL_LINKED_WITH_TBB +template +void for_each (RangeRef range, + const Fct& functor, + const Parallel_tag&, + IteratorCategory) +{ + std::size_t range_size = std::distance (range.begin(), range.end()); + + std::vector::type> iterators; + iterators.reserve (range_size); + for (typename Range_iterator_type::type it = range.begin(); it != range.end(); ++ it) + iterators.push_back (it); + + tbb::parallel_for (tbb::blocked_range(0, range_size), + [&](const tbb::blocked_range& r) + { + for (std::size_t i = r.begin(); i != r.end(); ++ i) + if (!functor (*(iterators[i]))) + break; + }); +} + +template +void for_each (RangeRef range, + const Fct& functor, + const Parallel_tag&, + std::random_access_iterator_tag) +{ + std::size_t range_size = std::distance (range.begin(), range.end()); + + tbb::parallel_for (tbb::blocked_range(0, range_size), + [&](const tbb::blocked_range& r) + { + for (std::size_t i = r.begin(); i != r.end(); ++ i) + if (!functor (*(range.begin() + i))) + break; + }); +} +#endif + +} // namespace internal + +template +void for_each (const Range& range, + const std::function::reference)>& functor) +{ +#ifndef CGAL_LINKED_WITH_TBB + CGAL_static_assertion_msg (!(boost::is_convertible::value), + "Parallel_tag is enabled but TBB is unavailable."); +#endif + + internal::for_each + (range, functor, + ConcurrencyTag(), + typename std::iterator_traits::iterator_category()); +} + +template +void for_each (Range& range, + const std::function::reference)>& functor) +{ +#ifndef CGAL_LINKED_WITH_TBB + CGAL_static_assertion_msg (!(boost::is_convertible::value), + "Parallel_tag is enabled but TBB is unavailable."); +#endif + + internal::for_each + (range, functor, + ConcurrencyTag(), + typename std::iterator_traits::iterator_category()); +} + +} // namespace CGAL + + +#endif // CGAL_FOR_EACH_H diff --git a/STL_Extension/include/CGAL/iterator.h b/STL_Extension/include/CGAL/iterator.h index 7861550a61e..16733969b61 100644 --- a/STL_Extension/include/CGAL/iterator.h +++ b/STL_Extension/include/CGAL/iterator.h @@ -1471,6 +1471,17 @@ dispatch_or_drop_output(O... o) return Dispatch_or_drop_output_iterator, std::tuple >(o...); } + +// Trick to select iterator or const_iterator depending on the range constness +template +struct Range_iterator_type; +template +struct Range_iterator_type { typedef typename RangeRef::iterator type; }; +template +struct Range_iterator_type { typedef typename RangeRef::const_iterator type; }; + + + } //namespace CGAL #include diff --git a/STL_Extension/test/STL_Extension/CMakeLists.txt b/STL_Extension/test/STL_Extension/CMakeLists.txt index 902658da4d1..80563069300 100644 --- a/STL_Extension/test/STL_Extension/CMakeLists.txt +++ b/STL_Extension/test/STL_Extension/CMakeLists.txt @@ -42,6 +42,10 @@ if ( CGAL_FOUND ) create_single_source_cgal_program( "test_Uncertain.cpp" ) create_single_source_cgal_program( "test_vector.cpp" ) create_single_source_cgal_program( "test_join_iterators.cpp" ) + create_single_source_cgal_program( "test_for_each.cpp" ) + if(TBB_FOUND) + CGAL_target_use_TBB(test_for_each) + endif() else() message(STATUS "This program requires the CGAL library, and will not be compiled.") diff --git a/STL_Extension/test/STL_Extension/test_for_each.cpp b/STL_Extension/test/STL_Extension/test_for_each.cpp new file mode 100644 index 00000000000..d1185b378b0 --- /dev/null +++ b/STL_Extension/test/STL_Extension/test_for_each.cpp @@ -0,0 +1,48 @@ +#include + +#include +#include + +int main() +{ + std::vector vec { 0, 1, 2, 3, 4, 5 }; + std::list list { 0, 1, 2, 3, 4, 5 }; + + std::cerr << "Testing sequential random access" << std::endl; + CGAL::for_each + (vec, [](int& i) -> bool { i *= 2; return true; }); + + for (const int& i : vec) + std::cerr << i << " "; + std::cerr << std::endl; + +#ifdef CGAL_LINKED_WITH_TBB + std::cerr << "Testing parallel random access" << std::endl; + CGAL::for_each + (vec, [](int& i) -> bool { i *= 2; return true; }); + + for (const int& i : vec) + std::cerr << i << " "; + std::cerr << std::endl; +#endif + + std::cerr << "Testing sequential non-random access" << std::endl; + CGAL::for_each + (list, [](int& i) -> bool { i *= 2; return true; }); + + for (const int& i : list) + std::cerr << i << " "; + std::cerr << std::endl; + +#ifdef CGAL_LINKED_WITH_TBB + std::cerr << "Testing parallel non-random access" << std::endl; + CGAL::for_each + (list, [](int& i) -> bool { i *= 2; return true; }); + + for (const int& i : list) + std::cerr << i << " "; + std::cerr << std::endl; +#endif + + return 0; +} diff --git a/Scale_space_reconstruction_3/doc/Scale_space_reconstruction_3/Scale_space_reconstruction_3.txt b/Scale_space_reconstruction_3/doc/Scale_space_reconstruction_3/Scale_space_reconstruction_3.txt index db6d7281b90..333c6ef1d1a 100644 --- a/Scale_space_reconstruction_3/doc/Scale_space_reconstruction_3/Scale_space_reconstruction_3.txt +++ b/Scale_space_reconstruction_3/doc/Scale_space_reconstruction_3/Scale_space_reconstruction_3.txt @@ -17,6 +17,10 @@ Left: 5760 points on a synthetic knot data set. Right: reconstructed surface mes A triangulated surface mesh is generated by first computing the point set at a coarse scale, then constructing a mesh of the point set at this scale, and finally reverting the points of the mesh back to their original scale. +\note A \ref tuto_reconstruction "detailed tutorial on surface reconstruction" +is provided with a guide to choose the most appropriate method along +with pre- and post-processing. + \section ScaleSpaceReconstruction3secMethod Scale-Space diff --git a/Spatial_searching/include/CGAL/Euclidean_distance.h b/Spatial_searching/include/CGAL/Euclidean_distance.h index c10be1a806c..60fde16c20b 100644 --- a/Spatial_searching/include/CGAL/Euclidean_distance.h +++ b/Spatial_searching/include/CGAL/Euclidean_distance.h @@ -132,7 +132,7 @@ namespace CGAL { // Note: the concept SearchTraits specifies that Cartesian_const_iterator_d // must be a random-access iterator typename SearchTraits::Cartesian_const_iterator_d qe_minus_5 = qe; - std::advance(qe, -5); + std::advance(qe_minus_5, -5); for (;;) { FT diff = (*qit) - (*it_coord_begin); diff --git a/Spatial_searching/include/CGAL/Kd_tree_rectangle.h b/Spatial_searching/include/CGAL/Kd_tree_rectangle.h index cd02915ff3e..d0db23ceb1c 100644 --- a/Spatial_searching/include/CGAL/Kd_tree_rectangle.h +++ b/Spatial_searching/include/CGAL/Kd_tree_rectangle.h @@ -105,6 +105,7 @@ namespace CGAL { } Kd_tree_rectangle() + : max_span_coord_(-1) {} @@ -138,6 +139,7 @@ namespace CGAL { template // was PointIter Kd_tree_rectangle(int, PointPointerIter begin, PointPointerIter end,const Construct_cartesian_const_iterator_d& construct_it) + : max_span_coord_(-1) { update_from_point_pointers(begin,end,construct_it); } @@ -288,7 +290,7 @@ namespace CGAL { } Kd_tree_rectangle() - : coords_(0), dim(0) + : coords_(0), dim(0), max_span_coord_(-1) { } @@ -323,7 +325,7 @@ namespace CGAL { template // was PointIter Kd_tree_rectangle(int d, PointPointerIter begin, PointPointerIter end,const Construct_cartesian_const_iterator_d& construct_it) - : coords_(new FT[2*d]), dim(d) + : coords_(new FT[2*d]), dim(d), max_span_coord_(-1) { update_from_point_pointers(begin,end,construct_it); } diff --git a/Spatial_searching/include/CGAL/Search_traits_adapter.h b/Spatial_searching/include/CGAL/Search_traits_adapter.h index 9d17b72dc20..d3c6b49fd60 100644 --- a/Spatial_searching/include/CGAL/Search_traits_adapter.h +++ b/Spatial_searching/include/CGAL/Search_traits_adapter.h @@ -182,7 +182,11 @@ public: } Dereference_type& - dereference() const { return const_cast((*point)[idx]); } + dereference() const + { + // Point::operator[] takes an int as parameter... + return const_cast((*point)[static_cast(idx)]); + } }; diff --git a/Surface_mesh_segmentation/include/CGAL/boost/graph/Alpha_expansion_MaxFlow_tag.h b/Surface_mesh_segmentation/include/CGAL/boost/graph/Alpha_expansion_MaxFlow_tag.h new file mode 100644 index 00000000000..48a2ecd3dc5 --- /dev/null +++ b/Surface_mesh_segmentation/include/CGAL/boost/graph/Alpha_expansion_MaxFlow_tag.h @@ -0,0 +1,94 @@ +#ifndef CGAL_BOOST_GRAPH_ALPHA_EXPANSION_MAXFLOW_IMPL_H +// Copyright (c) 2014 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) : Ilker O. Yaz, Simon Giraudot + +#define CGAL_BOOST_GRAPH_ALPHA_EXPANSION_MAXFLOW_IMPL_H + +#include + +/// \cond SKIP_IN_MANUAL + +#include + +namespace MaxFlow +{ +#include +} + + +namespace CGAL +{ + +/** + * @brief Implements alpha-expansion graph cut algorithm. + * + * For underlying max-flow algorithm, it uses the MAXFLOW software implemented by Boykov & Kolmogorov. + * Also no pre-allocation is made. + */ +class Alpha_expansion_MaxFlow_impl +{ +public: + + typedef MaxFlow::Graph::node_id Vertex_descriptor; + +private: + + MaxFlow::Graph graph; + +public: + + void clear_graph() + { + graph = MaxFlow::Graph(); + } + + Vertex_descriptor add_vertex() + { + return graph.add_node(); + } + + void add_tweight (Vertex_descriptor& v, double w1, double w2) + { + graph.add_tweights(v, w1, w2); + } + + void init_vertices() + { + } + + double max_flow() + { + return graph.maxflow(); + } + + template + void update(VertexLabelMap vertex_label_map, + const std::vector& inserted_vertices, + InputVertexDescriptor vd, + std::size_t vertex_i, + std::size_t alpha) + { + if(get(vertex_label_map, vd) != alpha + && graph.what_segment(inserted_vertices[vertex_i]) == MaxFlow::Graph::SINK) + put(vertex_label_map, vd, alpha); + } + + void add_edge (Vertex_descriptor& v1, Vertex_descriptor& v2, double w1, double w2) + { + graph.add_edge(v1, v2, w1, w2); + } +}; + +}//namespace CGAL + +/// \endcond + +#endif //CGAL_BOOST_GRAPH_ALPHA_EXPANSION_MAXFLOW_IMPL_H diff --git a/Surface_mesh_segmentation/include/CGAL/internal/Surface_mesh_segmentation/Alpha_expansion_graph_cut.h b/Surface_mesh_segmentation/include/CGAL/internal/Surface_mesh_segmentation/Alpha_expansion_graph_cut.h deleted file mode 100644 index fb392f45467..00000000000 --- a/Surface_mesh_segmentation/include/CGAL/internal/Surface_mesh_segmentation/Alpha_expansion_graph_cut.h +++ /dev/null @@ -1,687 +0,0 @@ -#ifndef CGAL_SURFACE_MESH_SEGMENTATION_ALPHA_EXPANSION_GRAPH_CUT_H -// Copyright (c) 2014 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) : Ilker O. Yaz - -#define CGAL_SURFACE_MESH_SEGMENTATION_ALPHA_EXPANSION_GRAPH_CUT_H - -#include - - -/// @cond CGAL_DOCUMENT_INTERNAL - -/** - * @file Alpha_expansion_graph_cut.h - * @brief This file contains 3 graph-cut algorithms, which can be used as a template parameter for CGAL::internal::Surface_mesh_segmentation. - * - * Main differences between implementations are underlying max-flow algorithm and graph type (i.e. results are the same, performance differs). - * - * By default, we use MAXFLOW and the class Alpha_expansion_graph_cut_boykov_kolmogorov. - * For deactivating MAXFLOW software and using boost implementation instead, define CGAL_DO_NOT_USE_BOYKOV_KOLMOGOROV_MAXFLOW_SOFTWARE. - * It deactivates Alpha_expansion_graph_cut_boykov_kolmogorov, activate boost versions - * and makes CGAL::internal::Surface_mesh_segmentation using Alpha_expansion_graph_cut_boost - * as default implementation for the graph-cut. - * - * Also algorithms can be used by their-own for applying alpha-expansion graph-cut on any graph. - * - */ -#include -#ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT -#include -#endif -#include - -#include - -#include -#ifdef CGAL_DO_NOT_USE_BOYKOV_KOLMOGOROV_MAXFLOW_SOFTWARE -#include -#include -#if BOOST_VERSION >= 104400 // at this version kolmogorov_max_flow become depricated. -#include -#else -#include -#endif -#else -namespace MaxFlow -{ -#include -} -#endif - -#include - - - - -namespace CGAL -{ -namespace internal -{ - -//////////////////////////////////////////////////////////////////////////////////////// -// Comments about performance: -// -// 1) With BGL: -// * Using adjacency_list: -// ** Without pre-allocating vertex-list -// | OutEdgeList | VertexList | Performance | -// | listS | listS | 25.2 | -// | vecS | listS | 22.7 | -// | listS | vecS | 30.7 | -// | vecS | vecS | 26.1 | -// -// ** With pre-allocating vertex-list with max-node size -// (Note: exact number of vertices are not certain at the beginning) -// | OutEdgeList | VertexList | Performance | -// | listS | vecS | 25.2 | -// | vecS | vecS | 23.4 | -// -// * Didn't try adjacency_matrix since our graph is sparse -// ( Also one can check BGL book, performance section ) -// -// Decision: -// * Alpha_expansion_graph_cut_boost: use adjacency_list without -// pre-allocating vertex-list. -// -// 2) With Boykov-Kolmogorov MAXFLOW software: -// (http://pub.ist.ac.at/~vnk/software/maxflow-v2.21.src.tar.gz) -// | Performance | -// | 3.1 | -// * Alpha_expansion_graph_cut_boykov_kolmogorov provides an implementation. -// MAXFLOW does not provide any option for pre-allocation (It is possible with v_3.02 though). -// -// Typical Benchmark result provided by Ilker -// | construction of vertices | construction of edges | graph cut | Total -// ----------------------------------------------------------------------------------------------------------- -// boost with an adjacency list | 1.53 | 1.51 | 3.00 | 6.04 -// boost with CSR | 0.11 (gather in a vector) | 0.15 (gather in a vector) | 2.67 | 2.93 -// MaxFlow | 0.042 | 0.076 | 1.043 | 1.161 -// -// The main issue for now with CSR is the construction of the opposite edge map that is too costly, -// since it is done by exploring all edges to find opposite -//////////////////////////////////////////////////////////////////////////////////////// - -#ifdef CGAL_DO_NOT_USE_BOYKOV_KOLMOGOROV_MAXFLOW_SOFTWARE -/** - * @brief Implements alpha-expansion graph cut algorithm. - * - * For representing graph, it uses adjacency_list with OutEdgeList = vecS, VertexList = listS. - * Also no pre-allocation is made for vertex-list. - */ -class Alpha_expansion_graph_cut_boost -{ -private: - typedef boost::adjacency_list_traits - Adjacency_list_traits; - - typedef boost::adjacency_list - > > >, - // 3 edge properties - boost::property > - > > Graph; - - typedef boost::graph_traits Traits; - typedef boost::color_traits ColorTraits; - - typedef Traits::vertex_descriptor Vertex_descriptor; - typedef Traits::vertex_iterator Vertex_iterator; - typedef Traits::edge_descriptor Edge_descriptor; - typedef Traits::edge_iterator Edge_iterator; - - /** - * Adds two directional edges between @a v1 and @a v2 - * @param v1 first vertex - * @param v2 second vertex - * @param w1 weight for edge from v1 to v2 (v1->v2) - * @param w2 weight for edge from v2 to v1 (v2->v1) - * @param graph to be added - * @return pair of added edges, first: v1->v2 and second: v2->v1 - */ - boost::tuple - add_edge_and_reverse(Vertex_descriptor& v1, Vertex_descriptor& v2, double w1, - double w2, Graph& graph) const { - Edge_descriptor v1_v2, v2_v1; - bool v1_v2_added, v2_v1_added; - - boost::tie(v1_v2, v1_v2_added) = boost::add_edge(v1, v2, graph); - boost::tie(v2_v1, v2_v1_added) = boost::add_edge(v2, v1, graph); - - CGAL_assertion(v1_v2_added && v2_v1_added); - //put edge capacities - boost::put(boost::edge_reverse, graph, v1_v2, v2_v1); - boost::put(boost::edge_reverse, graph, v2_v1, v1_v2); - - //map reverse edges - boost::put(boost::edge_capacity, graph, v1_v2, w1); - boost::put(boost::edge_capacity, graph, v2_v1, w2); - - return boost::make_tuple(v1_v2, v2_v1); - } - -public: - /** - * Applies alpha-expansion graph-cut for energy minimization. - * @param edges contains incident vertex-id pairs for each edge (vertex-ids should be between [0, number of vertices -1]) - * @param edge_weights contains weights for each edge in @a edges (correspondence according to order) - * @param probability_matrix contains responsibility of the center on the vertex probability[center][vertex] - * @param[in, out] labels as input it contains initial labeling of vertices (i.e. a center-id between [0, number of centers -1]), - * and as output it returns final labeling of vertices (i.e. assigned cluster-id to each facet) - * @return result of energy function - */ - double operator()(const std::vector >& - edges, - const std::vector& edge_weights, - const std::vector >& probability_matrix, - std::vector& labels) const { - const double tolerance = 1e-10; - - double min_cut = (std::numeric_limits::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 inserted_vertices; - inserted_vertices.resize(labels.size()); - - Graph graph; - - bool success; - do { - success = false; - std::size_t alpha = 0; - - for(std::vector >::const_iterator it = - probability_matrix.begin(); - it != probability_matrix.end(); ++it, ++alpha) { - graph.clear(); - - Vertex_descriptor cluster_source = boost::add_vertex(graph); - Vertex_descriptor cluster_sink = boost::add_vertex(graph); - - #ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT - Timer timer; - timer.start(); - #endif - - // For E-Data - // add every facet as a vertex to the graph, put edges to source & sink vertices - for(std::size_t vertex_i = 0; vertex_i < labels.size(); ++vertex_i) { - Vertex_descriptor new_vertex = boost::add_vertex(graph); - inserted_vertices[vertex_i] = new_vertex; - double source_weight = probability_matrix[alpha][vertex_i]; - // since it is expansion move, current alpha labeled vertices will be assigned to alpha again, - // making sink_weight 'infinity' guarantee this. - double sink_weight = (labels[vertex_i] == alpha) ? - (std::numeric_limits::max)() - : probability_matrix[labels[vertex_i]][vertex_i]; - - add_edge_and_reverse(cluster_source, new_vertex, source_weight, 0.0, graph); - add_edge_and_reverse(new_vertex, cluster_sink, sink_weight, 0.0, graph); - } - #ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT - vertex_creation_time += timer.time(); - timer.reset(); - #endif - - // For E-Smooth - // add edge between every vertex, - std::vector::const_iterator weight_it = edge_weights.begin(); - for(std::vector >::const_iterator edge_it = - edges.begin(); edge_it != edges.end(); - ++edge_it, ++weight_it) { - Vertex_descriptor v1 = inserted_vertices[edge_it->first], - v2 = inserted_vertices[edge_it->second]; - std::size_t label_1 = labels[edge_it->first], label_2 = labels[edge_it->second]; - if(label_1 == label_2) { - if(label_1 != alpha) { - add_edge_and_reverse(v1, v2, *weight_it, *weight_it, graph); - } - } else { - Vertex_descriptor inbetween = boost::add_vertex(graph); - - double w1 = (label_1 == alpha) ? 0 : *weight_it; - double w2 = (label_2 == alpha) ? 0 : *weight_it; - add_edge_and_reverse(inbetween, v1, w1, w1, graph); - add_edge_and_reverse(inbetween, v2, w2, w2, graph); - add_edge_and_reverse(inbetween, cluster_sink, *weight_it, 0.0, graph); - } - } - #ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT - edge_creation_time += timer.time(); - #endif - - // initialize vertex indices, it is necessary since we are using VertexList = listS - Vertex_iterator v_begin, v_end; - Traits::vertices_size_type index = 0; - for(boost::tie(v_begin, v_end) = vertices(graph); v_begin != v_end; ++v_begin) { - boost::put(boost::vertex_index, graph, *v_begin, index++); - } - - #ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT - timer.reset(); - #endif -#if BOOST_VERSION >= 104400 - double flow = boost::boykov_kolmogorov_max_flow(graph, cluster_source, - cluster_sink); -#else - double flow = boost::kolmogorov_max_flow(graph, cluster_source, cluster_sink); -#endif - #ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT - cut_time += timer.time(); - #endif - - if(min_cut - flow <= flow * tolerance) { - continue; - } - min_cut = flow; - success = true; - //update labeling - for(std::size_t vertex_i = 0; vertex_i < inserted_vertices.size(); ++vertex_i) { - boost::default_color_type color = boost::get(boost::vertex_color, graph, - inserted_vertices[vertex_i]); - if(labels[vertex_i] != alpha - && color == ColorTraits::white()) { //new comers (expansion occurs) - labels[vertex_i] = alpha; - } - } - } - } 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; - #endif - - return min_cut; - } -}; - -// another implementation using compressed_sparse_row_graph -// for now there is a performance problem while setting reverse edges -// if that can be solved, it is faster than Alpha_expansion_graph_cut_boost -class Alpha_expansion_graph_cut_boost_CSR -{ -private: - // CSR only accepts bundled props - struct VertexP { - boost::default_color_type vertex_color; - double vertex_distance_t; - // ? do not now there is another way to take it, I think since edge_descriptor does not rely on properties - // this should be fine... - boost::compressed_sparse_row_graph::edge_descriptor - vertex_predecessor; - }; - - struct EdgeP { - double edge_capacity; - double edge_residual_capacity; - boost::compressed_sparse_row_graph::edge_descriptor - edge_reverse; - }; - - typedef boost::compressed_sparse_row_graph Graph; - - typedef boost::graph_traits Traits; - typedef boost::color_traits ColorTraits; - - typedef Traits::vertex_descriptor Vertex_descriptor; - typedef Traits::vertex_iterator Vertex_iterator; - typedef Traits::edge_descriptor Edge_descriptor; - typedef Traits::edge_iterator Edge_iterator; - - void - add_edge_and_reverse(std::size_t v1 , std::size_t v2, double w1, double w2, - std::vector >& edge_map, - std::vector& edge_weights) const { - edge_map.push_back(std::make_pair(v1, v2)); - EdgeP p1; - p1.edge_capacity = w1; - edge_weights.push_back(p1); - - edge_map.push_back(std::make_pair(v2, v1)); - EdgeP p2; - p2.edge_capacity = w2; - edge_weights.push_back(p2); - } - -public: - /** - * Applies alpha-expansion graph-cut for energy minimization. - * @param edges contains incident vertex-id pairs for each edge (vertex-ids should be between [0, number of vertices -1]) - * @param edge_weights contains weights for each edge in @a edges (correspondence according to order) - * @param probability_matrix contains responsibility of the center on the vertex probability[center][vertex] - * @param[in, out] labels as input it contains initial labeling of vertices (i.e. a center-id between [0, number of centers -1]), - * and as output it returns final labeling of vertices (i.e. assigned cluster-id to each facet) - * @return result of energy function - */ - double operator()(const std::vector >& - edges, - const std::vector& edge_weights, - const std::vector >& probability_matrix, - std::vector& labels) const { - const double tolerance = 1e-10; - - double min_cut = (std::numeric_limits::max)(); - - #ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT - double vertex_creation_time, edge_creation_time, graph_creation_time, - reverse_mapping_time, cut_time; - vertex_creation_time = edge_creation_time = graph_creation_time = - reverse_mapping_time = cut_time = 0.0; - #endif - - Graph graph; - - bool success; - do { - success = false; - std::size_t alpha = 0; - - for(std::vector >::const_iterator it = - probability_matrix.begin(); - it != probability_matrix.end(); ++it, ++alpha) { - std::vector > edge_map; - std::vector edge_map_weights; - edge_map.reserve(labels.size() * - 8); // there is no way to know exact edge count, it is a heuristic value - edge_map_weights.reserve(labels.size() * 8); - - std::size_t cluster_source = 0; - std::size_t cluster_sink = 1; - - #ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT - Timer timer; - timer.start(); - #endif - // For E-Data - // add every facet as a vertex to the graph, put edges to source & sink vertices - for(std::size_t vertex_i = 0; vertex_i < labels.size(); ++vertex_i) { - double source_weight = probability_matrix[alpha][vertex_i]; - // since it is expansion move, current alpha labeled vertices will be assigned to alpha again, - // making sink_weight 'infinity' guarantee this. - double sink_weight = (labels[vertex_i] == alpha) ? - (std::numeric_limits::max)() - : probability_matrix[labels[vertex_i]][vertex_i]; - - add_edge_and_reverse(cluster_source, vertex_i + 2, source_weight, 0.0, edge_map, - edge_map_weights); - add_edge_and_reverse(vertex_i + 2, cluster_sink, sink_weight, 0.0, edge_map, - edge_map_weights); - } - #ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT - vertex_creation_time += timer.time(); - timer.reset(); - #endif - // For E-Smooth - // add edge between every vertex, - std::size_t num_vert = labels.size() + 2; - std::vector::const_iterator weight_it = edge_weights.begin(); - for(std::vector >::const_iterator edge_it = - edges.begin(); edge_it != edges.end(); - ++edge_it, ++weight_it) { - std::size_t v1 = edge_it->first + 2, v2 = edge_it->second + 2; - std::size_t label_1 = labels[edge_it->first], label_2 = labels[edge_it->second]; - if(label_1 == label_2) { - if(label_1 != alpha) { - add_edge_and_reverse(v1, v2, *weight_it, *weight_it, edge_map, - edge_map_weights); - } - } else { - std::size_t inbetween = num_vert++; - - double w1 = (label_1 == alpha) ? 0 : *weight_it; - double w2 = (label_2 == alpha) ? 0 : *weight_it; - add_edge_and_reverse(inbetween, v1, w1, w1, edge_map, edge_map_weights); - add_edge_and_reverse(inbetween, v2, w2, w2, edge_map, edge_map_weights); - add_edge_and_reverse(inbetween, cluster_sink, *weight_it, 0.0, edge_map, - edge_map_weights); - } - } - #ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT - edge_creation_time += timer.time(); - timer.reset(); - #endif -#if BOOST_VERSION >= 104000 - Graph graph(boost::edges_are_unsorted, edge_map.begin(), edge_map.end(), - edge_map_weights.begin(), num_vert); -#else - Graph graph(edge_map.begin(), edge_map.end(), - edge_map_weights.begin(), num_vert); -#endif - #ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT - graph_creation_time += timer.time(); - timer.reset(); - #endif - - // PERFORMANCE PROBLEM - // need to set reverse edge map, I guess there is no way to do that before creating the graph - // since we do not have edge_descs - // however from our edge_map, we know that each (2i, 2i + 1) is reverse pairs, how to facilitate that ? - // will look it back - Graph::edge_iterator ei, ee; - for(boost::tie(ei, ee) = boost::edges(graph); ei != ee; ++ei) { - Graph::vertex_descriptor v1 = boost::source(*ei, graph); - Graph::vertex_descriptor v2 = boost::target(*ei, graph); - std::pair opp_edge = boost::edge(v2, v1, graph); - - CGAL_assertion(opp_edge.second); - graph[opp_edge.first].edge_reverse = - *ei; // and edge_reverse of *ei will be (or already have been) set by the opp_edge - } - #ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT - reverse_mapping_time += timer.time(); - timer.reset(); - #endif - -#if BOOST_VERSION >= 104400 - // since properties are bundled, defaults does not work need to specify them - double flow = boost::boykov_kolmogorov_max_flow(graph, - boost::get(&EdgeP::edge_capacity, graph), - boost::get(&EdgeP::edge_residual_capacity, graph), - boost::get(&EdgeP::edge_reverse, graph), - boost::get(&VertexP::vertex_predecessor, graph), - boost::get(&VertexP::vertex_color, graph), - boost::get(&VertexP::vertex_distance_t, graph), - boost::get(boost::vertex_index, - graph), // this is not bundled, get it from graph (CRS provides one) - cluster_source, - cluster_sink - ); -#else - double flow = boost::kolmogorov_max_flow(graph, - boost::get(&EdgeP::edge_capacity, graph), - boost::get(&EdgeP::edge_residual_capacity, graph), - boost::get(&EdgeP::edge_reverse, graph), - boost::get(&VertexP::vertex_predecessor, graph), - boost::get(&VertexP::vertex_color, graph), - boost::get(&VertexP::vertex_distance_t, graph), - boost::get(boost::vertex_index, - graph), // this is not bundled, get it from graph - cluster_source, - cluster_sink - ); -#endif - #ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT - cut_time += timer.time(); - #endif - - if(min_cut - flow <= flow * tolerance) { - continue; - } - min_cut = flow; - success = true; - //update labeling - for(std::size_t vertex_i = 0; vertex_i < labels.size(); ++vertex_i) { - boost::default_color_type color = graph[vertex_i + 2].vertex_color; - if(labels[vertex_i] != alpha - && color == ColorTraits::white()) { //new comers (expansion occurs) - labels[vertex_i] = alpha; - } - } - } - } 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 << "graph creation time: " << graph_creation_time << - std::endl; - CGAL_TRACE_STREAM << "reverse mapping time: " << reverse_mapping_time << - std::endl; - CGAL_TRACE_STREAM << "max flow algorithm time: " << cut_time << std::endl; - #endif - return min_cut; - } -}; -#endif - -#ifndef CGAL_DO_NOT_USE_BOYKOV_KOLMOGOROV_MAXFLOW_SOFTWARE -/** - * @brief Implements alpha-expansion graph cut algorithm. - * - * For underlying max-flow algorithm, it uses the MAXFLOW software implemented by Boykov & Kolmogorov. - * Also no pre-allocation is made. - */ -class Alpha_expansion_graph_cut_boykov_kolmogorov -{ -public: - /** - * Applies alpha-expansion graph-cut for energy minimization. - * @param edges contains incident vertex-id pairs for each edge (vertex-ids should be between [0, number of vertices -1]) - * @param edge_weights contains weights for each edge in @a edges (correspondence according to order) - * @param probability_matrix contains responsibility of the center on the vertex probability[center][vertex] - * @param[in, out] labels as input it contains initial labeling of vertices (i.e. a center-id between [0, number of centers -1]), - * and as output it returns final labeling of vertices - * @return result of energy function - */ - double operator()(const std::vector >& - edges, - const std::vector& edge_weights, - const std::vector >& probability_matrix, - std::vector& labels) const { - const double tolerance = 1e-10; - - double min_cut = (std::numeric_limits::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 inserted_vertices; - inserted_vertices.resize(labels.size()); - bool success; - do { - success = false; - std::size_t alpha = 0; - for(std::vector >::const_iterator it = - probability_matrix.begin(); - it != probability_matrix.end(); ++it, ++alpha) { - MaxFlow::Graph graph; - #ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT - Timer timer; - timer.start(); - #endif - // For E-Data - // add every facet as a vertex to graph, put edges to source-sink vertices - for(std::size_t vertex_i = 0; vertex_i < labels.size(); ++vertex_i) { - MaxFlow::Graph::node_id new_vertex = graph.add_node(); - inserted_vertices[vertex_i] = new_vertex; - - double source_weight = probability_matrix[alpha][vertex_i]; - // since it is expansion move, current alpha labeled vertices will be assigned to alpha again, - // making sink_weight 'infinity' guarantee this. - double sink_weight = (labels[vertex_i] == alpha) ? - (std::numeric_limits::max)() - : probability_matrix[labels[vertex_i]][vertex_i]; - graph.add_tweights(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, - std::vector::const_iterator weight_it = edge_weights.begin(); - for(std::vector >::const_iterator edge_it = - edges.begin(); edge_it != edges.end(); - ++edge_it, ++weight_it) { - MaxFlow::Graph::node_id v1 = inserted_vertices[edge_it->first]; - MaxFlow::Graph::node_id v2 = inserted_vertices[edge_it->second]; - std::size_t label_1 = labels[edge_it->first], label_2 = labels[edge_it->second]; - if(label_1 == label_2) { - if(label_1 != alpha) { - graph.add_edge(v1, v2, *weight_it, *weight_it); - } - } else { - MaxFlow::Graph::node_id inbetween = graph.add_node(); - - double w1 = (label_1 == alpha) ? 0 : *weight_it; - double w2 = (label_2 == alpha) ? 0 : *weight_it; - graph.add_edge(inbetween, v1, w1, w1); - graph.add_edge(inbetween, v2, w2, w2); - - graph.add_tweights(inbetween, 0.0, *weight_it); - } - } - #ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT - edge_creation_time += timer.time(); - timer.reset(); - #endif - - double flow = graph.maxflow(); - #ifdef CGAL_SEGMENTATION_BENCH_GRAPHCUT - cut_time += timer.time(); - #endif - - if(min_cut - flow <= flow * tolerance) { - continue; - } - - min_cut = flow; - success = true; - //update labeling - for(std::size_t vertex_i = 0; vertex_i < labels.size(); ++vertex_i) { - if(labels[vertex_i] != alpha - && graph.what_segment(inserted_vertices[vertex_i]) == MaxFlow::Graph::SINK) { - labels[vertex_i] = alpha; - } - } - } - } 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; - #endif - return min_cut; - } -}; -#endif //CGAL_DO_NOT_USE_BOYKOV_KOLMOGOROV_MAXFLOW_SOFTWARE -}//namespace internal -/// @endcond -}//namespace CGAL -#endif //CGAL_SURFACE_MESH_SEGMENTATION_ALPHA_EXPANSION_GRAPH_CUT_H diff --git a/Surface_mesh_segmentation/include/CGAL/internal/Surface_mesh_segmentation/Surface_mesh_segmentation.h b/Surface_mesh_segmentation/include/CGAL/internal/Surface_mesh_segmentation/Surface_mesh_segmentation.h index f4681ea9dfe..6adc33af483 100644 --- a/Surface_mesh_segmentation/include/CGAL/internal/Surface_mesh_segmentation/Surface_mesh_segmentation.h +++ b/Surface_mesh_segmentation/include/CGAL/internal/Surface_mesh_segmentation/Surface_mesh_segmentation.h @@ -18,9 +18,13 @@ #include #include -#include #include +#include +#ifndef CGAL_DO_NOT_USE_BOYKOV_KOLMOGOROV_MAXFLOW_SOFTWARE +#include +#endif + #include #include @@ -199,9 +203,9 @@ class Polyhedron, class VertexPointPmap, bool fast_bbox_intersection = true, #ifndef CGAL_DO_NOT_USE_BOYKOV_KOLMOGOROV_MAXFLOW_SOFTWARE - class GraphCut = Alpha_expansion_graph_cut_boykov_kolmogorov, + class AlphaExpansionImplementationTag = CGAL::Alpha_expansion_MaxFlow_tag, #else - class GraphCut = Alpha_expansion_graph_cut_boost, + class AlphaExpansionImplementationTag = CGAL::Alpha_expansion_boost_adjacency_list_tag, #endif class Filter = Bilateral_filtering > @@ -289,7 +293,8 @@ public: edge_weights); // apply graph cut - GraphCut()(edges, edge_weights, probability_matrix, labels); + CGAL::alpha_expansion_graphcut (edges, edge_weights, probability_matrix, labels, + AlphaExpansionImplementationTag()); std::vector::iterator label_it = labels.begin(); face_iterator facet_it, fend; for(boost::tie(facet_it,fend) = faces(mesh); diff --git a/Surface_mesh_segmentation/include/CGAL/mesh_segmentation.h b/Surface_mesh_segmentation/include/CGAL/mesh_segmentation.h index a96c08a1414..766f706d431 100644 --- a/Surface_mesh_segmentation/include/CGAL/mesh_segmentation.h +++ b/Surface_mesh_segmentation/include/CGAL/mesh_segmentation.h @@ -198,8 +198,7 @@ segmentation_from_sdf_values( const TriangleMesh& triangle_mesh, PointPropertyMap ppmap=PointPropertyMap(), GeomTraits traits=GeomTraits()) { - typedef typename boost::property_map::type VPMap; - internal::Surface_mesh_segmentation algorithm(triangle_mesh, traits, ppmap); + internal::Surface_mesh_segmentation algorithm(triangle_mesh, traits, ppmap); return algorithm.partition(number_of_clusters, smoothing_lambda, sdf_values_map, segment_ids, !output_cluster_ids); } diff --git a/TDS_3/doc/TDS_3/Concepts/TriangulationDataStructure_3.h b/TDS_3/doc/TDS_3/Concepts/TriangulationDataStructure_3.h index 8cdc8c934d2..081f9f98513 100644 --- a/TDS_3/doc/TDS_3/Concepts/TriangulationDataStructure_3.h +++ b/TDS_3/doc/TDS_3/Concepts/TriangulationDataStructure_3.h @@ -254,10 +254,10 @@ otherwise `Vertex_handle()` is returned. - A model of `ConvertVertex` must provide two operator()'s that are responsible for converting the source vertex `v_src` into the target vertex: - `Vertex operator()(const TDS_src::Vertex& v_src) const;` This operator is used to create the vertex from `v_src`. - - `void operator()(const TDS_src::Vertex& v_src, Vertex& v_tgt) const;` This operator is meant to be used in case heavy data should transferred to `v_tgt`. + - `void operator()(const TDS_src::Vertex& v_src, Vertex& v_tgt) const;` This operator is meant to be used in case heavy data should be transferred to `v_tgt`. - A model of ConvertCell must provide two operator()'s that are responsible for converting the source cell `c_src` into the target cell: - `Cell operator()(const TDS_src::Cell& c_src) const;` This operator is used to create the cell from `c_src`. - - `void operator()(const TDS_src::Cell& c_src, Cell& c_tgt) const;` This operator is meant to be used in case heavy data should transferred to `c_tgt`. + - `void operator()(const TDS_src::Cell& c_src, Cell& c_tgt) const;` This operator is meant to be used in case heavy data should be transferred to `c_tgt`. \pre The optional argument `v` is a vertex of `tds_src` or is `Vertex_handle()`. */ @@ -1024,17 +1024,52 @@ a precise indication on the kind of invalidity encountered. \cgalDebugEnd */ bool is_valid(Cell_handle c, bool verbose = false) const; +/// @} + +/// \name I/O +/// @{ /*! + \ingroup PkgIOTDS3 Reads a combinatorial triangulation from `is` and assigns it to `tds` */ istream& operator>> (istream& is, TriangulationDataStructure_3 & tds); -/*! +/*! \ingroup PkgIOTDS3 Writes `tds` into the stream `os` */ ostream& operator<< (ostream& os, const TriangulationDataStructure_3 & tds); +/*! \ingroup PkgIOTDS3 +The tds streamed in `is`, of original type `TDS_src`, is written into the triangulation data structure. As the vertex and cell + types might be different and incompatible, the creation of new cells and vertices +is made thanks to the functors `convert_vertex` and `convert_cell`, that convert +vertex and cell types. For each vertex `v_src` in `is`, the corresponding +vertex `v_tgt` in the triangulation data structure is a copy of the vertex returned by `convert_vertex(v_src)`. +The same operations are done for cells with the functor convert_cell, except cells +in the triangulation data structure are created using the default constructor, and then filled with the data +contained in the stream. + + - A model of `ConvertVertex` must provide two `operator()`s that are responsible + for converting the source vertex `v_src` into the target vertex: + - `Vertex operator()(const TDS_src::Vertex& v_src) const;` This operator is +used to create the vertex from `v_src`. + - `void operator()(const TDS_src::Vertex& v_src, Vertex& v_tgt) const;` This + operator is meant to be used in case heavy data should be transferred to `v_tgt`. + - A model of ConvertCell must provide an operator() that is responsible for +converting the source cell `c_src` into the target cell: + - `void operator()(const TDS_src::Cell& c_src, Cell& c_tgt) const;` This operator + is meant to be used in case heavy data should be transferred to `c_tgt`. + +\note The triangulation data structure contained in `is` can be obtained with the `operator>>` of a `TriangulationDataStructure_3`. +*/ +template +std::istream& file_input(std::istream& is, + ConvertVertex convert_vertex, + ConvertCell convert_cell); + /// @} }; /* end TriangulationDataStructure_3 */ diff --git a/TDS_3/doc/TDS_3/PackageDescription.txt b/TDS_3/doc/TDS_3/PackageDescription.txt index ee9b560977d..34f5d1acb1c 100644 --- a/TDS_3/doc/TDS_3/PackageDescription.txt +++ b/TDS_3/doc/TDS_3/PackageDescription.txt @@ -6,6 +6,8 @@ /// \defgroup PkgTDS3Classes Classes /// \ingroup PkgTDS3Ref +/// \defgroup PkgIOTDS3 I/O for a Triangulation_data_structure_3 +/// \ingroup PkgTDS3Ref /*! \addtogroup PkgTDS3Ref \todo check generated documentation diff --git a/TDS_3/include/CGAL/Triangulation_data_structure_3.h b/TDS_3/include/CGAL/Triangulation_data_structure_3.h index 968da3301ca..6e154098cb5 100644 --- a/TDS_3/include/CGAL/Triangulation_data_structure_3.h +++ b/TDS_3/include/CGAL/Triangulation_data_structure_3.h @@ -189,6 +189,7 @@ public: return hf ^ 419 * hs; } + }; static const int maximal_nb_of_facets_of_small_hole = 128; @@ -1521,6 +1522,73 @@ public: return s <= maximal_nb_of_facets_of_small_hole; } + //IO + template + std::istream& file_input(std::istream& is, + ConvertVertex convert_vertex = ConvertVertex(), + ConvertCell convert_cell = ConvertCell()) + { + // reads + // the dimension + // the number of finite vertices + // the non combinatorial information on vertices (point, etc) + // the number of cells + // the cells by the indices of their vertices in the preceding list + // of vertices, plus the non combinatorial information on each cell + // the neighbors of each cell by their index in the preceding list of cells + // when dimension < 3 : the same with faces of maximal dimension + + // If this is used for a TDS, the vertices are processed from 0 to n. + // Else, we make V[0] the infinite vertex and work from 1 to n+1. + + typedef typename Tds::Vertex_handle Vertex_handle; + typedef typename Tds::Cell_handle Cell_handle; + + typedef typename TDS_src::Vertex Vertex1; + typedef typename TDS_src::Cell Cell1; + clear(); + cells().clear(); + + std::size_t n; + int d; + if(is_ascii(is)) + is >> d >> n; + else { + read(is, d); + read(is, n); + } + if(!is) return is; + set_dimension(d); + + std::size_t V_size = n; + std::vector< Vertex_handle > V(V_size); + + // the infinite vertex is numbered 0 + for (std::size_t i=0 ; i < V_size; ++i) { + Vertex1 v; + if(!(is >> v)) return is; + Vertex_handle vh=create_vertex( convert_vertex(v) ); + V[i] = vh; + convert_vertex(v, *V[i]); + } + + std::vector< Cell_handle > C; + + std::size_t m; + read_cells(is, V, m, C); + + for (std::size_t j=0 ; j < m; j++) { + Cell1 c; + if(!(is >> c)) return is; + convert_cell(c, *C[j]); + } + + CGAL_triangulation_assertion(is_valid(false)); + return is; + } + private: // Change the orientation of the cell by swapping indices 0 and 1. @@ -1550,6 +1618,7 @@ private: // counts but does not check bool count_cells(size_type &i, bool verbose = false, int level = 0) const; // counts AND checks the validity + }; #ifdef CGAL_TDS_USE_RECURSIVE_CREATE_STAR_3 diff --git a/TDS_3/test/TDS_3/CMakeLists.txt b/TDS_3/test/TDS_3/CMakeLists.txt index 0253c40548c..b21488fe951 100644 --- a/TDS_3/test/TDS_3/CMakeLists.txt +++ b/TDS_3/test/TDS_3/CMakeLists.txt @@ -10,6 +10,7 @@ if ( CGAL_FOUND ) find_package( TBB QUIET ) create_single_source_cgal_program( "test_triangulation_tds_3.cpp" ) + create_single_source_cgal_program( "test_io_tds3.cpp" ) if(TBB_FOUND) CGAL_target_use_TBB(test_triangulation_tds_3) endif() diff --git a/TDS_3/test/TDS_3/test_io_tds3.cpp b/TDS_3/test/TDS_3/test_io_tds3.cpp new file mode 100644 index 00000000000..10bfcedc030 --- /dev/null +++ b/TDS_3/test/TDS_3/test_io_tds3.cpp @@ -0,0 +1,78 @@ +#include +#include +#include +#include +#include +#include +#include + +typedef CGAL::Triangulation_data_structure_3<> Tds; +typedef Tds::size_type size_type; +typedef Tds::Cell_handle Cell_handle; +typedef Tds::Vertex_handle Vertex_handle; + +template +struct Update_vertex +{ + typedef typename T1::Vertex V1; + typedef typename T2::Vertex V2; + + V2 operator()(const V1&) + { + return V2(); + } + + void operator()(const V1&, V2&) + { + + } + + +}; // end struct Update_vertex + +template +struct Update_cell +{ + typedef typename T1::Cell C1; + typedef typename T2::Cell C2; + + C2 operator()(const C1&) + { + return C2(); + } + + void operator()(const C1&, C2&) + { + + } + + +}; // end struct Update_vertex + +int main() +{ + Tds T; + std::vector PV(7); + PV[0] = T.insert_increase_dimension(); + // each of the following insertions of vertices increases the dimension + for ( int i=1; i<5; i++ ) { + PV[i] = T.insert_increase_dimension(PV[0]); + } + // we now have a simplex in dimension 4 + // cell incident to PV[0] + Cell_handle c = PV[0]->cell(); + int ind=0; + // PV[0] is the vertex of index ind in c + // insertion of a new vertex in the facet opposite to PV[0] + PV[5] = T.insert_in_facet(c, ind); + // insertion of a new vertex in c + PV[6] = T.insert_in_cell(c); + std::ofstream out("tr"); + out << T; + out.close(); + Tds T2; + std::ifstream in("tr"); + T2.file_input, Update_cell >(in); + in.close(); + return 0; +} diff --git a/Triangulation_2/test/Triangulation_2/include/CGAL/_test_cls_constrained_triangulation_2.h b/Triangulation_2/test/Triangulation_2/include/CGAL/_test_cls_constrained_triangulation_2.h index 344d9b858c9..fbe4b9ae725 100644 --- a/Triangulation_2/test/Triangulation_2/include/CGAL/_test_cls_constrained_triangulation_2.h +++ b/Triangulation_2/test/Triangulation_2/include/CGAL/_test_cls_constrained_triangulation_2.h @@ -280,7 +280,8 @@ _test_cls_constrained_triangulation(const Triang &) vha = fh->vertex(li); fh = T1_2.locate(Point(3,2),lt,li); assert( lt == Triang::VERTEX ); vhb = fh->vertex(li); - assert(T1_2.is_edge(vha,vhb, fh, ih)); + bool check = T1_2.is_edge(vha,vhb, fh, ih); + assert(check); assert(fh->is_constrained(ih)); T1_2.remove_constrained_edge(fh,ih); assert(!fh->is_constrained(ih)); @@ -313,7 +314,7 @@ _test_cls_constrained_triangulation(const Triang &) vha = fh->vertex(li); fh = T2_2.locate(lpt[m+1],lt,li); assert( lt == Triang::VERTEX ); vhb = fh->vertex(li); - bool check = T2_2.is_edge(vha,vhb, fh, ih); + check = T2_2.is_edge(vha,vhb, fh, ih); assert(check); assert(fh->is_constrained(ih)); T2_2.remove_constrained_edge(fh,ih); diff --git a/Triangulation_2/test/Triangulation_2/include/CGAL/_test_cls_delaunay_triangulation_2.h b/Triangulation_2/test/Triangulation_2/include/CGAL/_test_cls_delaunay_triangulation_2.h index 7c5907160d0..d5088cb094d 100644 --- a/Triangulation_2/test/Triangulation_2/include/CGAL/_test_cls_delaunay_triangulation_2.h +++ b/Triangulation_2/test/Triangulation_2/include/CGAL/_test_cls_delaunay_triangulation_2.h @@ -293,7 +293,8 @@ _test_cls_delaunay_triangulation_2( const Del & ) assert(TM_0.tds().is_valid()); assert(TM_0.is_valid()); assert(TM_0.dimension() == 2); - assert(TM_0.move_if_no_collision(tmv1, Point(3, 0)) != tmv1); + Vertex_handle mtmv1 = TM_0.move_if_no_collision(tmv1, Point(3, 0)); + assert(mtmv1 != tmv1); TM_0.move_if_no_collision(tmv1, Point(0, 1)); assert(TM_0.tds().is_valid()); @@ -340,7 +341,8 @@ _test_cls_delaunay_triangulation_2( const Del & ) // A simple test to see if move return the good vertex // when there is a collision - assert(TM_1.move(TM_1.finite_vertices_begin(), vTM_1->point()) == vTM_1); + Vertex_handle mvTM_1 = TM_1.move(TM_1.finite_vertices_begin(), vTM_1->point()); + assert(mvTM_1 == vTM_1); } diff --git a/Triangulation_2/test/Triangulation_2/include/CGAL/_test_cls_triangulation_2.h b/Triangulation_2/test/Triangulation_2/include/CGAL/_test_cls_triangulation_2.h index 8beb8650271..096b6bc5d7a 100644 --- a/Triangulation_2/test/Triangulation_2/include/CGAL/_test_cls_triangulation_2.h +++ b/Triangulation_2/test/Triangulation_2/include/CGAL/_test_cls_triangulation_2.h @@ -270,11 +270,13 @@ _test_cls_triangulation_2( const Triangul & ) assert( T2_3.is_valid() ); // make sure inserting on a previous point does not insert it again - assert( T2_3.insert(p10) == v2_3_10 ); + Vertex_handle vp10 = T2_3.insert(p10); + assert( vp10 == v2_3_10 ); assert( T2_3.number_of_vertices() == 11 ); // make sure push_back exists and does the same thing as insert - assert( T2_3.push_back(p10) == v2_3_10 ); + vp10 = T2_3.push_back(p10); + assert( vp10 == v2_3_10 ); assert( T2_3.number_of_vertices() == 11 ); // test generic iterator insert @@ -288,14 +290,16 @@ _test_cls_triangulation_2( const Triangul & ) // test list iterator insert Triangul T2_5; - assert( T2_5.insert(l.begin(), l.end()) == 10 ); + std::ptrdiff_t T2_5_nv = T2_5.insert(l.begin(), l.end()); + assert( T2_5_nv == 10 ); assert( T2_5.dimension() == 2 ); assert( T2_5.number_of_vertices() == 10 ); assert( T2_5.is_valid() ); // test list iterator insert Triangul T2_6; - assert( T2_6.insert(v.begin(), v.end()) == 10 ); + std::ptrdiff_t T2_6_nv = T2_6.insert(v.begin(), v.end()); + assert( T2_6_nv == 10 ); assert( T2_6.dimension() == 2 ); assert( T2_6.number_of_vertices() == 10 ); assert( T2_6.is_valid() ); @@ -451,7 +455,8 @@ _test_cls_triangulation_2( const Triangul & ) // A simple test to see if move returns the good vertex // when there is a collision - assert(TM_1.move(TM_1.finite_vertices_begin(), vTM_1->point()) == vTM_1); + Vertex_handle mvTM_1 = TM_1.move(TM_1.finite_vertices_begin(), vTM_1->point()); + assert(mvTM_1 == vTM_1); /****************************/ /***** CONSTRUCTORS (2) *****/ diff --git a/Triangulation_3/doc/Triangulation_3/CGAL/Triangulation_3.h b/Triangulation_3/doc/Triangulation_3/CGAL/Triangulation_3.h index 281a73b5e69..2392d312749 100644 --- a/Triangulation_3/doc/Triangulation_3/CGAL/Triangulation_3.h +++ b/Triangulation_3/doc/Triangulation_3/CGAL/Triangulation_3.h @@ -1759,6 +1759,7 @@ for faces of maximal dimension instead of cells. /// @{ /*! +\ingroup PkgIOTriangulation3 Reads the underlying combinatorial triangulation from `is` by calling the corresponding input operator of the triangulation data structure class (note that the infinite vertex is numbered 0), and the @@ -1771,12 +1772,44 @@ of the vertex and cell types. Assigns the resulting triangulation to istream& operator>> (istream& is, Triangulation_3 &t); /*! + * \ingroup PkgIOTriangulation3 Writes the triangulation `t` into `os`. */ ostream& operator<< (ostream& os, const Triangulation_3 &t); -/// @} +/*! +\ingroup PkgIOTriangulation3 +The triangulation streamed in `is`, of original type `Tr_src`, is written into the triangulation. As the vertex and cell + types might be different and incompatible, the creation of new cells and vertices +is made thanks to the functors `convert_vertex` and `convert_cell`, that convert +vertex and cell types. For each vertex `v_src` in `is`, the corresponding +vertex `v_tgt` in the triangulation is a copy of the vertex returned by `convert_vertex(v_src)`. +The same operations are done for cells with the functor convert_cell, except cells +in the triangulation are created using the default constructor, and then filled with the data +contained in the stream. + - A model of `ConvertVertex` must provide two `operator()`s that are responsible + for converting the source vertex `v_src` into the target vertex: + - `Vertex operator()(const Tr_src::Vertex& v_src) const;` This operator is +used to create the vertex from `v_src`. + - `void operator()(const Tr_src::Vertex& v_src, Vertex& v_tgt) const;` This + operator is meant to be used in case heavy data should be transferred to `v_tgt`. + - A model of ConvertCell must provide an operator() that is responsible for +converting the source cell `c_src` into the target cell: + - `void operator()(const Tr_src::Cell& c_src, Cell& c_tgt) const;` This operator + is meant to be used in case data should be transferred to `c_tgt`. + +\note The triangulation contained in `is` can be obtained with the `operator>>` of a `Triangulation_3`. +*/ +template +std::istream& file_input(std::istream& is, + ConvertVertex convert_vertex = ConvertVertex(), + ConvertCell convert_cell = ConvertCell()); + +/// @} +/// /// \name Concurrency /// @{ diff --git a/Triangulation_3/doc/Triangulation_3/PackageDescription.txt b/Triangulation_3/doc/Triangulation_3/PackageDescription.txt index 61103aec56d..52ca64d321e 100644 --- a/Triangulation_3/doc/Triangulation_3/PackageDescription.txt +++ b/Triangulation_3/doc/Triangulation_3/PackageDescription.txt @@ -20,6 +20,9 @@ /// \defgroup PkgDrawTriangulation3 Draw a Triangulation 3 /// \ingroup PkgTriangulation3Ref +/// \defgroup PkgIOTriangulation3 I/O for a Triangulation 3 +/// \ingroup PkgTriangulation3Ref + /*! \addtogroup PkgTriangulation3Ref \cgalPkgDescriptionBegin{3D Triangulations,PkgTriangulation3} @@ -120,5 +123,8 @@ is opposite to the vertex with the same index. See - \link PkgDrawTriangulation3 CGAL::draw() \endlink +\cgalCRPSection{I/O} + - \link PkgIOTriangulation3 CGAL:Triangulation_3::file_input()\endlink + - \link PkgIOTriangulation3 CGAL::file_input()\endlink */ diff --git a/Triangulation_3/include/CGAL/Triangulation_3.h b/Triangulation_3/include/CGAL/Triangulation_3.h index 21df723f9ba..cb0e4b20136 100644 --- a/Triangulation_3/include/CGAL/Triangulation_3.h +++ b/Triangulation_3/include/CGAL/Triangulation_3.h @@ -2271,8 +2271,80 @@ public: bool is_valid(bool verbose = false, int level = 0) const; bool is_valid(Cell_handle c, bool verbose = false, int level = 0) const; bool is_valid_finite(Cell_handle c, bool verbose = false, int level=0) const; + + //IO + template + std::istream& file_input(std::istream& is, + ConvertVertex convert_vertex = ConvertVertex(), + ConvertCell convert_cell = ConvertCell()) + { + // reads + // the dimension + // the number of finite vertices + // the non combinatorial information on vertices (point, etc) + // the number of cells + // the cells by the indices of their vertices in the preceding list + // of vertices, plus the non combinatorial information on each cell + // the neighbors of each cell by their index in the preceding list of cells + // when dimension < 3 : the same with faces of maximal dimension + + // If this is used for a TDS, the vertices are processed from 0 to n. + // Else, we make V[0] the infinite vertex and work from 1 to n+1. + + typedef Self Triangulation; + typedef typename Triangulation::Vertex_handle Vertex_handle; + typedef typename Triangulation::Cell_handle Cell_handle; + + typedef typename Tr_src::Vertex Vertex1; + typedef typename Tr_src::Cell Cell1; + + clear(); + tds().cells().clear(); + + std::size_t n; + int d; + if(is_ascii(is)) + is >> d >> n; + else { + read(is, d); + read(is, n); + } + if(!is) return is; + tds().set_dimension(d); + + std::size_t V_size = n+1; + std::vector< Vertex_handle > V(V_size); + + // the infinite vertex is numbered 0 + V[0] = infinite_vertex(); + + for (std::size_t i = 1; i < V_size; ++i) { + Vertex1 v; + if(!(is >> v)) return is; + Vertex_handle vh=tds().create_vertex( convert_vertex(v) ); + V[i] = vh; + convert_vertex(v, *V[i]); + } + + std::vector< Cell_handle > C; + + std::size_t m; + tds().read_cells(is, V, m, C); + + for (std::size_t j=0 ; j < m; j++) { + Cell1 c; + if(!(is >> c)) return is; + convert_cell(c, *C[j]); + } + + CGAL_triangulation_assertion( is_valid(false) ); + return is; + } }; + template < class GT, class Tds, class Lds > std::istream& operator>> (std::istream& is, Triangulation_3& tr) { diff --git a/Triangulation_3/test/Triangulation_3/CMakeLists.txt b/Triangulation_3/test/Triangulation_3/CMakeLists.txt index 46e2f3d2d1e..9853b51d025 100644 --- a/Triangulation_3/test/Triangulation_3/CMakeLists.txt +++ b/Triangulation_3/test/Triangulation_3/CMakeLists.txt @@ -31,6 +31,7 @@ if ( CGAL_FOUND ) create_single_source_cgal_program( "test_segment_simplex_traverser_3.cpp" ) create_single_source_cgal_program( "test_static_filters.cpp" ) create_single_source_cgal_program( "test_triangulation_3.cpp" ) + create_single_source_cgal_program( "test_io_triangulation_3.cpp" ) if(TBB_FOUND) foreach(target diff --git a/Triangulation_3/test/Triangulation_3/test_io_triangulation_3.cpp b/Triangulation_3/test/Triangulation_3/test_io_triangulation_3.cpp new file mode 100644 index 00000000000..a6e4694e5e1 --- /dev/null +++ b/Triangulation_3/test/Triangulation_3/test_io_triangulation_3.cpp @@ -0,0 +1,66 @@ + + +#include +#include +#include +#include +#include +#include + +typedef CGAL::Simple_cartesian K1; +typedef CGAL::Exact_predicates_inexact_constructions_kernel K2; +typedef CGAL::Triangulation_3 Tr1; +typedef CGAL::Triangulation_3 Tr2; + + +template +struct Update_vertex +{ + typedef typename T1::Vertex V1; + typedef typename T2::Vertex V2; + typedef typename T2::Point Point; + + V2 operator()(const V1&) + { + return V2(); + } + + void operator()(const V1& v1, V2& v2) + { + CGAL::Cartesian_converter c; + v2.set_point(Point(c(v1.point()))); + } +}; // end struct Update_vertex + +struct Update_cell { + template + void operator()(const C1&, C2&) {} +}; // end struct Update_cell +int main() +{ + // construction from a list of points : + std::list L; + L.push_back(Tr1::Point(0,0,0)); + L.push_back(Tr1::Point(1,0,0)); + L.push_back(Tr1::Point(0,1,0)); + L.push_back(Tr1::Point(0,0,1)); + Tr1 T1(L.begin(), L.end()); + std::ofstream out("tr"); + out << T1; + out.close(); + + Tr2 T2; + std::ifstream in("tr"); + T2.file_input, Update_cell>(in); + in.close(); + assert(T2.is_valid()); + Tr2::Point_iterator pit = T2.points_begin(); + assert(*(pit)++ == Tr2::Point(0,0,0)); + assert(*(pit)++ == Tr2::Point(1,0,0)); + assert(*(pit)++ == Tr2::Point(0,1,0)); + assert(*(pit)++ == Tr2::Point(0,0,1)); + + + std::cout << "done" << std::endl; + return 0; +}