diff options
Diffstat (limited to 'src/Alpha_complex/include/gudhi/Alpha_complex_3d.h')
-rw-r--r-- | src/Alpha_complex/include/gudhi/Alpha_complex_3d.h | 171 |
1 files changed, 94 insertions, 77 deletions
diff --git a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h index 13ebb9c1..562ef139 100644 --- a/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h +++ b/src/Alpha_complex/include/gudhi/Alpha_complex_3d.h @@ -12,8 +12,10 @@ #ifndef ALPHA_COMPLEX_3D_H_ #define ALPHA_COMPLEX_3D_H_ -#include <boost/version.hpp> #include <boost/variant.hpp> +#include <boost/range/size.hpp> +#include <boost/range/combine.hpp> +#include <boost/range/adaptor/transformed.hpp> #include <gudhi/Debug_utils.h> #include <gudhi/Alpha_complex_options.h> @@ -35,15 +37,13 @@ #include <CGAL/iterator.h> #include <CGAL/version.h> // for CGAL_VERSION_NR -#include <Eigen/src/Core/util/Macros.h> // for EIGEN_VERSION_AT_LEAST - #include <boost/container/static_vector.hpp> #include <iostream> #include <vector> #include <unordered_map> #include <stdexcept> -#include <cstddef> +#include <cstddef> // for std::size_t #include <memory> // for std::unique_ptr #include <type_traits> // for std::conditional and std::enable_if #include <limits> // for numeric_limits<> @@ -53,19 +53,10 @@ # error Alpha_complex_3d is only available for CGAL >= 4.11 #endif -#if !EIGEN_VERSION_AT_LEAST(3,1,0) -# error Alpha_complex_3d is only available for Eigen3 >= 3.1.0 installed with CGAL -#endif - namespace Gudhi { namespace alpha_complex { -#ifdef GUDHI_CAN_USE_CXX11_THREAD_LOCAL -thread_local -#endif // GUDHI_CAN_USE_CXX11_THREAD_LOCAL - double RELATIVE_PRECISION_OF_TO_DOUBLE = 0.00001; - // Value_from_iterator returns the filtration value from an iterator on alpha shapes values // // FAST SAFE EXACT @@ -97,7 +88,7 @@ struct Value_from_iterator<complexity::EXACT> { * \details * The data structure is constructing a <a href="https://doc.cgal.org/latest/Alpha_shapes_3/index.html">CGAL 3D Alpha * Shapes</a> from a range of points (can be read from an OFF file, cf. Points_off_reader). - * Duplicate points are inserted once in the Alpha_complex. This is the reason why the vertices may be not contiguous. + * Duplicate points are inserted once in the Alpha_complex. * * \tparam Complexity shall be `Gudhi::alpha_complex::complexity` type. Default value is * `Gudhi::alpha_complex::complexity::SAFE`. @@ -107,7 +98,7 @@ struct Value_from_iterator<complexity::EXACT> { * \tparam Periodic Boolean used to set/unset the periodic version of Alpha_complex_3d. Default value is false. * * For the weighted version, weights values are explained on CGAL - * <a href="https://doc.cgal.org/latest/Alpha_shapes_3/index.html#title0">Alpha shapes 3d</a> and + * <a href="https://doc.cgal.org/latest/Alpha_shapes_3/index.html#Alpha_shapes_3Definitions">Alpha shapes 3d</a> and * <a href="https://doc.cgal.org/latest/Triangulation_3/index.html#Triangulation3secclassRegulartriangulation">Regular * triangulation</a> documentation. * @@ -160,8 +151,10 @@ class Alpha_complex_3d { using Kernel = CGAL::Periodic_3_regular_triangulation_traits_3<Predicates>; }; + public: using Kernel = typename Kernel_3<Predicates, Weighted, Periodic>::Kernel; + private: using TdsVb = typename std::conditional<Periodic, CGAL::Periodic_3_triangulation_ds_vertex_base_3<>, CGAL::Triangulation_ds_vertex_base_3<>>::type; @@ -225,23 +218,23 @@ class Alpha_complex_3d { * Must be compatible with double. */ using FT = typename Alpha_shape_3::FT; - /** \brief Gives public access to the Point_3 type. Here is a Point_3 constructor example: + /** \brief Gives public access to the Bare_point_3 (bare aka. unweighed) type. + * Here is a Bare_point_3 constructor example: \code{.cpp} using Alpha_complex_3d = Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::SAFE, false, false>; // x0 = 1., y0 = -1.1, z0 = -1.. -Alpha_complex_3d::Point_3 p0(1., -1.1, -1.); +Alpha_complex_3d::Bare_point_3 p0(1., -1.1, -1.); \endcode * */ - using Point_3 = typename Kernel::Point_3; + using Bare_point_3 = typename Kernel::Point_3; /** \brief Gives public access to the Weighted_point_3 type. A Weighted point can be constructed as follows: \code{.cpp} -using Weighted_alpha_complex_3d = - Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::SAFE, true, false>; +using Weighted_alpha_complex_3d = Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::SAFE, true, false>; // x0 = 1., y0 = -1.1, z0 = -1., weight = 4. -Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point_3(1., -1.1, -1.), 4.); +Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Bare_point_3(1., -1.1, -1.), 4.); \endcode * * Note: This type is defined to void if Alpha complex is not weighted. @@ -249,6 +242,11 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point * */ using Weighted_point_3 = typename Triangulation_3<Kernel, Tds, Weighted, Periodic>::Weighted_point_3; + /** \brief `Alpha_complex_3d::Point_3` type is either a `Alpha_complex_3d::Bare_point_3` (Weighted = false) or a + * `Alpha_complex_3d::Weighted_point_3` (Weighted = true). + */ + using Point_3 = typename Alpha_shape_3::Point; + private: using Dispatch = CGAL::Dispatch_output_iterator<CGAL::cpp11::tuple<CGAL::Object, FT>, @@ -264,33 +262,32 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point public: /** \brief Alpha_complex constructor from a list of points. * - * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Point_3` or - * `Alpha_complex_3d::Weighted_point_3`. + * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Point_3`. * * @pre Available if Alpha_complex_3d is not Periodic. * * The type InputPointRange must be a range for which std::begin and std::end return input iterators on a - * `Alpha_complex_3d::Point_3` or a `Alpha_complex_3d::Weighted_point_3`. + * `Alpha_complex_3d::Point_3`. */ template <typename InputPointRange> Alpha_complex_3d(const InputPointRange& points) { static_assert(!Periodic, "This constructor is not available for periodic versions of Alpha_complex_3d"); - alpha_shape_3_ptr_ = std::unique_ptr<Alpha_shape_3>( - new Alpha_shape_3(std::begin(points), std::end(points), 0, Alpha_shape_3::GENERAL)); + alpha_shape_3_ptr_ = std::make_unique<Alpha_shape_3>( + std::begin(points), std::end(points), 0, Alpha_shape_3::GENERAL); } /** \brief Alpha_complex constructor from a list of points and associated weights. * * @exception std::invalid_argument In debug mode, if points and weights do not have the same size. * - * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Point_3`. + * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Bare_point_3`. * @param[in] weights Range of weights on points. Weights shall be in double. * * @pre Available if Alpha_complex_3d is Weighted and not Periodic. * * The type InputPointRange must be a range for which std::begin and - * std::end return input iterators on a `Alpha_complex_3d::Point_3`. + * std::end return input iterators on a `Alpha_complex_3d::Bare_point_3`. * The type WeightRange must be a range for which std::begin and * std::end return an input iterator on a double. */ @@ -298,28 +295,22 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point Alpha_complex_3d(const InputPointRange& points, WeightRange weights) { static_assert(Weighted, "This constructor is not available for non-weighted versions of Alpha_complex_3d"); static_assert(!Periodic, "This constructor is not available for periodic versions of Alpha_complex_3d"); - GUDHI_CHECK((weights.size() == points.size()), + // FIXME: this test is only valid if we have a forward range + GUDHI_CHECK(boost::size(weights) == boost::size(points), std::invalid_argument("Points number in range different from weights range number")); - std::vector<Weighted_point_3> weighted_points_3; + auto weighted_points_3 = boost::range::combine(points, weights) + | boost::adaptors::transformed([](auto const&t){return Weighted_point_3(boost::get<0>(t), boost::get<1>(t));}); - std::size_t index = 0; - weighted_points_3.reserve(points.size()); - while ((index < weights.size()) && (index < points.size())) { - weighted_points_3.push_back(Weighted_point_3(points[index], weights[index])); - index++; - } - - alpha_shape_3_ptr_ = std::unique_ptr<Alpha_shape_3>( - new Alpha_shape_3(std::begin(weighted_points_3), std::end(weighted_points_3), 0, Alpha_shape_3::GENERAL)); + alpha_shape_3_ptr_ = std::make_unique<Alpha_shape_3>( + std::begin(weighted_points_3), std::end(weighted_points_3), 0, Alpha_shape_3::GENERAL); } /** \brief Alpha_complex constructor from a list of points and an iso-cuboid coordinates. * * @exception std::invalid_argument In debug mode, if the size of the cuboid in every directions is not the same. * - * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Point_3` or - * `Alpha_complex_3d::Weighted_point_3`. + * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Point_3`. * @param[in] x_min Iso-oriented cuboid x_min. * @param[in] y_min Iso-oriented cuboid y_min. * @param[in] z_min Iso-oriented cuboid z_min. @@ -330,7 +321,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point * @pre Available if Alpha_complex_3d is Periodic. * * The type InputPointRange must be a range for which std::begin and std::end return input iterators on a - * `Alpha_complex_3d::Point_3` or a `Alpha_complex_3d::Weighted_point_3`. + * `Alpha_complex_3d::Point_3`. * * @note In weighted version, please check weights are greater than zero, and lower than 1/64*cuboid length * squared. @@ -356,7 +347,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point // alpha shape construction from points. CGAL has a strange behavior in REGULARIZED mode. This is the default mode // Maybe need to set it to GENERAL mode - alpha_shape_3_ptr_ = std::unique_ptr<Alpha_shape_3>(new Alpha_shape_3(pdt, 0, Alpha_shape_3::GENERAL)); + alpha_shape_3_ptr_ = std::make_unique<Alpha_shape_3>(pdt, 0, Alpha_shape_3::GENERAL); } /** \brief Alpha_complex constructor from a list of points, associated weights and an iso-cuboid coordinates. @@ -366,7 +357,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point * @exception std::invalid_argument In debug mode, if a weight is negative, zero, or greater than 1/64*cuboid length * squared. * - * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Point_3`. + * @param[in] points Range of points to triangulate. Points must be in `Alpha_complex_3d::Bare_point_3`. * @param[in] weights Range of weights on points. Weights shall be in double. * @param[in] x_min Iso-oriented cuboid x_min. * @param[in] y_min Iso-oriented cuboid y_min. @@ -378,7 +369,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point * @pre Available if Alpha_complex_3d is Weighted and Periodic. * * The type InputPointRange must be a range for which std::begin and - * std::end return input iterators on a `Alpha_complex_3d::Point_3`. + * std::end return input iterators on a `Alpha_complex_3d::Bare_point_3`. * The type WeightRange must be a range for which std::begin and * std::end return an input iterator on a double. * The type of x_min, y_min, z_min, x_max, y_max and z_max must be a double. @@ -388,31 +379,27 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point FT z_min, FT x_max, FT y_max, FT z_max) { static_assert(Weighted, "This constructor is not available for non-weighted versions of Alpha_complex_3d"); static_assert(Periodic, "This constructor is not available for non-periodic versions of Alpha_complex_3d"); - GUDHI_CHECK((weights.size() == points.size()), + // FIXME: this test is only valid if we have a forward range + GUDHI_CHECK(boost::size(weights) == boost::size(points), std::invalid_argument("Points number in range different from weights range number")); // Checking if the cuboid is the same in x,y and z direction. If not, CGAL will not process it. GUDHI_CHECK( (x_max - x_min == y_max - y_min) && (x_max - x_min == z_max - z_min) && (z_max - z_min == y_max - y_min), std::invalid_argument("The size of the cuboid in every directions is not the same.")); - std::vector<Weighted_point_3> weighted_points_3; - - std::size_t index = 0; - weighted_points_3.reserve(points.size()); - #ifdef GUDHI_DEBUG // Defined in GUDHI_DEBUG to avoid unused variable warning for GUDHI_CHECK FT maximal_possible_weight = 0.015625 * (x_max - x_min) * (x_max - x_min); #endif - while ((index < weights.size()) && (index < points.size())) { - GUDHI_CHECK((weights[index] < maximal_possible_weight) && (weights[index] >= 0), - std::invalid_argument("Invalid weight at index " + std::to_string(index + 1) + - ". Must be positive and less than maximal possible weight = 1/64*cuboid length " - "squared, which is not an acceptable input.")); - weighted_points_3.push_back(Weighted_point_3(points[index], weights[index])); - index++; - } + auto weighted_points_3 = boost::range::combine(points, weights) + | boost::adaptors::transformed([=](auto const&t){ + auto w = boost::get<1>(t); + GUDHI_CHECK((w < maximal_possible_weight) && (w >= 0), + std::invalid_argument("Invalid weight " + std::to_string(w) + + ". Must be non-negative and less than maximal possible weight = 1/64*cuboid length squared.")); + return Weighted_point_3(boost::get<0>(t), w); + }); // Define the periodic cube Dt pdt(typename Kernel::Iso_cuboid_3(x_min, y_min, z_min, x_max, y_max, z_max)); @@ -426,7 +413,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point // alpha shape construction from points. CGAL has a strange behavior in REGULARIZED mode. This is the default mode // Maybe need to set it to GENERAL mode - alpha_shape_3_ptr_ = std::unique_ptr<Alpha_shape_3>(new Alpha_shape_3(pdt, 0, Alpha_shape_3::GENERAL)); + alpha_shape_3_ptr_ = std::make_unique<Alpha_shape_3>(pdt, 0, Alpha_shape_3::GENERAL); } /** \brief Inserts all Delaunay triangulation into the simplicial complex. @@ -452,9 +439,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point return false; // ----- >> } - // using Filtration_value = typename SimplicialComplexForAlpha3d::Filtration_value; using Complex_vertex_handle = typename SimplicialComplexForAlpha3d::Vertex_handle; - using Alpha_shape_simplex_tree_map = std::unordered_map<Alpha_vertex_handle, Complex_vertex_handle>; using Simplex_tree_vector_vertex = std::vector<Complex_vertex_handle>; #ifdef DEBUG_TRACES @@ -471,10 +456,13 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point alpha_shape_3_ptr_->filtration_with_alpha_values(dispatcher); #ifdef DEBUG_TRACES - std::cout << "filtration_with_alpha_values returns : " << objects.size() << " objects" << std::endl; + std::clog << "filtration_with_alpha_values returns : " << objects.size() << " objects" << std::endl; #endif // DEBUG_TRACES + if (objects.size() == 0) { + std::cerr << "Alpha_complex_3d create_complex - no triangulation as points are on a 2d plane\n"; + return false; // ----- >> + } - Alpha_shape_simplex_tree_map map_cgal_simplex_tree; using Alpha_value_iterator = typename std::vector<FT>::const_iterator; Alpha_value_iterator alpha_value_iterator = alpha_values.begin(); for (auto object_iterator : objects) { @@ -484,7 +472,8 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point if (const Cell_handle* cell = CGAL::object_cast<Cell_handle>(&object_iterator)) { for (auto i = 0; i < 4; i++) { #ifdef DEBUG_TRACES - std::cout << "from cell[" << i << "]=" << (*cell)->vertex(i)->point() << std::endl; + std::clog << "from cell[" << i << "] - Point coordinates (" << (*cell)->vertex(i)->point() << ")" + << std::endl; #endif // DEBUG_TRACES vertex_list.push_back((*cell)->vertex(i)); } @@ -495,7 +484,8 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point for (auto i = 0; i < 4; i++) { if ((*facet).second != i) { #ifdef DEBUG_TRACES - std::cout << "from facet=[" << i << "]" << (*facet).first->vertex(i)->point() << std::endl; + std::clog << "from facet=[" << i << "] - Point coordinates (" << (*facet).first->vertex(i)->point() << ")" + << std::endl; #endif // DEBUG_TRACES vertex_list.push_back((*facet).first->vertex(i)); } @@ -506,7 +496,8 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point } else if (const Edge* edge = CGAL::object_cast<Edge>(&object_iterator)) { for (auto i : {(*edge).second, (*edge).third}) { #ifdef DEBUG_TRACES - std::cout << "from edge[" << i << "]=" << (*edge).first->vertex(i)->point() << std::endl; + std::clog << "from edge[" << i << "] - Point coordinates (" << (*edge).first->vertex(i)->point() << ")" + << std::endl; #endif // DEBUG_TRACES vertex_list.push_back((*edge).first->vertex(i)); } @@ -516,7 +507,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point } else if (const Alpha_vertex_handle* vertex = CGAL::object_cast<Alpha_vertex_handle>(&object_iterator)) { #ifdef DEBUG_TRACES count_vertices++; - std::cout << "from vertex=" << (*vertex)->point() << std::endl; + std::clog << "from vertex - Point coordinates (" << (*vertex)->point() << ")" << std::endl; #endif // DEBUG_TRACES vertex_list.push_back((*vertex)); } @@ -528,7 +519,8 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point // alpha shape not found Complex_vertex_handle vertex = map_cgal_simplex_tree.size(); #ifdef DEBUG_TRACES - std::cout << "vertex [" << the_alpha_shape_vertex->point() << "] not found - insert " << vertex << std::endl; + std::clog << "Point (" << the_alpha_shape_vertex->point() << ") not found - insert new vertex id " << vertex + << std::endl; #endif // DEBUG_TRACES the_simplex.push_back(vertex); map_cgal_simplex_tree.emplace(the_alpha_shape_vertex, vertex); @@ -536,7 +528,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point // alpha shape found Complex_vertex_handle vertex = the_map_iterator->second; #ifdef DEBUG_TRACES - std::cout << "vertex [" << the_alpha_shape_vertex->point() << "] found in " << vertex << std::endl; + std::clog << "Point (" << the_alpha_shape_vertex->point() << ") found as vertex id " << vertex << std::endl; #endif // DEBUG_TRACES the_simplex.push_back(vertex); } @@ -545,7 +537,7 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point Filtration_value filtr = Value_from_iterator<Complexity>::perform(alpha_value_iterator); #ifdef DEBUG_TRACES - std::cout << "filtration = " << filtr << std::endl; + std::clog << "filtration = " << filtr << std::endl; #endif // DEBUG_TRACES complex.insert_simplex(the_simplex, static_cast<Filtration_value>(filtr)); GUDHI_CHECK(alpha_value_iterator != alpha_values.end(), "CGAL provided more simplices than values"); @@ -553,23 +545,48 @@ Weighted_alpha_complex_3d::Weighted_point_3 wp0(Weighted_alpha_complex_3d::Point } #ifdef DEBUG_TRACES - std::cout << "vertices \t" << count_vertices << std::endl; - std::cout << "edges \t\t" << count_edges << std::endl; - std::cout << "facets \t\t" << count_facets << std::endl; - std::cout << "cells \t\t" << count_cells << std::endl; + std::clog << "vertices \t" << count_vertices << std::endl; + std::clog << "edges \t\t" << count_edges << std::endl; + std::clog << "facets \t\t" << count_facets << std::endl; + std::clog << "cells \t\t" << count_cells << std::endl; #endif // DEBUG_TRACES // -------------------------------------------------------------------------------------------- - // As Alpha value is an approximation, we have to make filtration non decreasing while increasing the dimension - complex.make_filtration_non_decreasing(); + if (Complexity == complexity::FAST) + // As Alpha value is an approximation, we have to make filtration non decreasing while increasing the dimension + // Only in FAST version, cf. https://github.com/GUDHI/gudhi-devel/issues/57 + complex.make_filtration_non_decreasing(); // Remove all simplices that have a filtration value greater than max_alpha_square complex.prune_above_filtration(max_alpha_square); // -------------------------------------------------------------------------------------------- return true; } + /** \brief get_point returns the point corresponding to the vertex given as parameter. + * + * @param[in] vertex Vertex handle of the point to retrieve. + * @return The point found. + * @exception std::out_of_range In case vertex is not found (cf. std::vector::at). + */ + const Point_3& get_point(std::size_t vertex) { + if (map_cgal_simplex_tree.size() != cgal_vertex_iterator_vector.size()) { + cgal_vertex_iterator_vector.resize(map_cgal_simplex_tree.size()); + for (auto map_iterator : map_cgal_simplex_tree) { + cgal_vertex_iterator_vector[map_iterator.second] = map_iterator.first; + } + + } + auto cgal_vertex_iterator = cgal_vertex_iterator_vector.at(vertex); + return cgal_vertex_iterator->point(); + } + private: // use of a unique_ptr on cgal Alpha_shape_3, as copy and default constructor is not available - no need to be freed std::unique_ptr<Alpha_shape_3> alpha_shape_3_ptr_; + + // Map type to switch from CGAL vertex iterator to simplex tree vertex handle. + std::unordered_map<Alpha_vertex_handle, std::size_t> map_cgal_simplex_tree; + // Vector type to switch from simplex tree vertex handle to CGAL vertex iterator. + std::vector<Alpha_vertex_handle> cgal_vertex_iterator_vector; }; } // namespace alpha_complex |