// Copyright (c) 2014 INRIA Sophia-Antipolis (France) // All rights reserved. // // This file is part of CGAL (www.cgal.org). // You can redistribute it and/or modify it under the terms of the GNU // General Public License as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // // Licensees holding a valid commercial license may use this file in // accordance with the commercial license agreement provided with the software. // // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. // // $URL: $ // $Id: $ // // // Author(s) : Clement Jamin #ifndef TANGENTIAL_COMPLEX_H #define TANGENTIAL_COMPLEX_H #include const double HALF_SPARSITY = 0.5*INPUT_SPARSITY; const double SQ_HALF_SPARSITY = HALF_SPARSITY*HALF_SPARSITY; #include #include #include #include #include #include #include #include #include #include #include #ifdef CGAL_TC_PROFILING # include #endif #include // CJTODO TEMP #include #include #include #include #include #include #include #include #include #include #include #ifdef CGAL_LINKED_WITH_TBB # include //# include #endif //#define CGAL_TC_EXPORT_NORMALS // Only for 3D surfaces (k=2, d=3) namespace CGAL { using namespace Tangential_complex_; class Vertex_data { public: Vertex_data(std::size_t data = std::numeric_limits::max()) : m_data(data) {} operator std::size_t() { return m_data; } operator std::size_t() const { return m_data; } private: std::size_t m_data; }; /// The class Tangential_complex represents a tangential complex template < typename Kernel, int Intrinsic_dimension, typename Concurrency_tag = CGAL::Parallel_tag, typename Tr = Regular_triangulation < Regular_triangulation_euclidean_traits< Epick_d > >, Triangulation_data_structure < typename Regular_triangulation_euclidean_traits< Epick_d > >::Dimension, Triangulation_vertex > >, Vertex_data >, Triangulation_full_cell > > > > > > class Tangential_complex { typedef typename Kernel::FT FT; typedef typename Kernel::Point_d Point; typedef typename Kernel::Vector_d Vector; typedef Tr Triangulation; typedef typename Triangulation::Geom_traits Tr_traits; typedef typename Triangulation::Weighted_point Tr_point; typedef typename Triangulation::Bare_point Tr_bare_point; typedef typename Triangulation::Vertex_handle Tr_vertex_handle; typedef typename Triangulation::Full_cell_handle Tr_full_cell_handle; typedef typename Tr_traits::Vector_d Tr_vector; typedef std::vector Tangent_space_basis; typedef std::vector Points; typedef std::vector Weights; typedef Point_cloud_data_structure Points_ds; typedef typename Points_ds::KNS_range KNS_range; typedef typename Points_ds::KNS_iterator KNS_iterator; typedef typename Points_ds::INS_range INS_range; typedef typename Points_ds::INS_iterator INS_iterator; // Store a local triangulation and a handle to its center vertex struct Tr_and_VH { public: Tr_and_VH() : m_tr(NULL) {} Tr_and_VH(int dim) : m_tr(new Triangulation(dim)) {} ~Tr_and_VH() { destroy_triangulation(); } Triangulation & construct_triangulation(int dim) { delete m_tr; m_tr = new Triangulation(dim); return tr(); } void destroy_triangulation() { delete m_tr; m_tr = NULL; } Triangulation & tr() { return *m_tr; } Triangulation const& tr() const { return *m_tr; } Tr_vertex_handle const& center_vertex() const { return m_center_vertex; } Tr_vertex_handle & center_vertex() { return m_center_vertex; } private: Triangulation* m_tr; Tr_vertex_handle m_center_vertex; }; typedef typename std::vector TS_container; typedef typename std::vector Tr_container; typedef typename std::vector Vectors; #ifdef CGAL_LINKED_WITH_TBB // CJTODO: test other mutexes // http://www.threadingbuildingblocks.org/docs/help/reference/synchronization/mutexes/mutex_concept.htm //typedef tbb::queuing_mutex Tr_mutex; #endif // For transform_iterator static const Tr_point &vertex_handle_to_point(Tr_vertex_handle vh) { return vh->point(); } public: /// Constructor for a range of points template Tangential_complex(InputIterator first, InputIterator last, const Kernel &k = Kernel()) : m_k(k), m_points(first, last), m_points_ds(m_points), m_ambiant_dim(k.point_dimension_d_object()(*first)) {} /// Destructor ~Tangential_complex() {} std::size_t number_of_vertices() { return m_points.size(); } void compute_tangential_complex() { #ifdef CGAL_TC_PROFILING Wall_clock_timer t; #endif // We need to do that because we don't want the container to copy the // already-computed triangulations (while resizing) since it would // invalidate the vertex handles stored beside the triangulations m_triangulations.resize(m_points.size()); #ifdef CGAL_LINKED_WITH_TBB //m_tr_mutexes.resize(m_points.size()); #endif m_tangent_spaces.resize(m_points.size()); #ifdef CGAL_TC_PERTURB_WEIGHT m_weights.resize(m_points.size(), FT(0)); #endif #ifdef CGAL_TC_PERTURB_POSITION m_translations.resize(m_points.size()); #endif #ifdef CGAL_TC_EXPORT_NORMALS m_normals.resize(m_points.size()); #endif #ifdef CGAL_LINKED_WITH_TBB // Parallel if (boost::is_convertible::value) { tbb::parallel_for(tbb::blocked_range(0, m_points.size()), Compute_tangent_triangulation(*this) ); } // Sequential else #endif // CGAL_LINKED_WITH_TBB { for (std::size_t i = 0 ; i < m_points.size() ; ++i) compute_tangent_triangulation(i); } #ifdef CGAL_TC_PROFILING std::cerr << "Tangential complex computed in " << t.elapsed() << " seconds." << std::endl; #endif } void estimate_intrinsic_dimension() { // Kernel functors typename Kernel::Compute_coordinate_d coord = m_k.compute_coordinate_d_object(); std::vector sum_eigen_values(m_ambiant_dim, FT(0)); Points::const_iterator it_p = m_points.begin(); Points::const_iterator it_p_end = m_points.end(); // For each point p for ( ; it_p != it_p_end ; ++it_p) { const Point &p = *it_p; KNS_range kns_range = m_points_ds.query_ANN( p, NUM_POINTS_FOR_PCA, false); //******************************* PCA ************************************* // One row = one point Eigen::MatrixXd mat_points(NUM_POINTS_FOR_PCA, m_ambiant_dim); KNS_iterator nn_it = kns_range.begin(); for (int j = 0 ; j < NUM_POINTS_FOR_PCA && nn_it != kns_range.end() ; ++j, ++nn_it) { for (int i = 0 ; i < m_ambiant_dim ; ++i) mat_points(j, i) = CGAL::to_double(coord(m_points[nn_it->first], i)); } Eigen::MatrixXd centered = mat_points.rowwise() - mat_points.colwise().mean(); Eigen::MatrixXd cov = centered.adjoint() * centered; Eigen::SelfAdjointEigenSolver eig(cov); // The eigenvectors are sorted in increasing order of their corresponding // eigenvalues Tangent_space_basis ts; for (int i = 0 ; i < m_ambiant_dim ; ++i) sum_eigen_values[i] += eig.eigenvalues()[i]; //************************************************************************* } // CJTODO: replace this by an actual estimation for (FT v : sum_eigen_values) // CJTODO C++11 { std::cout << v << " "; } std::cout << "\n"; } void refresh_tangential_complex() { #ifdef CGAL_TC_PROFILING Wall_clock_timer t; #endif #ifdef CGAL_LINKED_WITH_TBB // Parallel if (boost::is_convertible::value) { tbb::parallel_for(tbb::blocked_range(0, m_points.size()), Compute_tangent_triangulation(*this, true) //tangent_spaces_are_already_computed ); } // Sequential else #endif // CGAL_LINKED_WITH_TBB { for (std::size_t i = 0 ; i < m_points.size() ; ++i) { compute_tangent_triangulation(i, true); // tangent_spaces_are_already_computed } } #ifdef CGAL_TC_PROFILING std::cerr << "Tangential complex refreshed in " << t.elapsed() << " seconds." << std::endl; #endif } unsigned int fix_inconsistencies() { typename Kernel::Point_drop_weight_d drop_w = m_k.point_drop_weight_d_object(); #ifdef CGAL_TC_VERBOSE std::cerr << "Fixing inconsistencies..." << std::endl; #endif #ifdef CGAL_TC_SHOW_DETAILED_STATS_FOR_INCONSISTENCIES std::pair stats_before = number_of_inconsistent_simplices(false); # ifdef CGAL_TC_VERBOSE std::cerr << "Initial number of inconsistencies: " << stats_before.second << std::endl; # endif if (stats_before.second == 0) { # ifdef CGAL_TC_VERBOSE std::cerr << "Nothing to fix." << std::endl; # endif return 0; } #endif // CGAL_TC_SHOW_DETAILED_STATS_FOR_INCONSISTENCIES bool done = false; unsigned int num_steps = 0; while (!done) { std::size_t num_inconsistent_local_tr = 0; // CJTODO: the parallel version is not working for now /*#ifdef CGAL_LINKED_WITH_TBB // Parallel if (boost::is_convertible::value) { tbb::parallel_for( tbb::blocked_range(0, m_triangulations.size()), Try_to_solve_inconsistencies_in_a_local_triangulation(*this) ); } // Sequential else #endif // CGAL_LINKED_WITH_TBB*/ { #ifdef CGAL_TC_PROFILING Wall_clock_timer t; #endif for (std::size_t i = 0 ; i < m_triangulations.size() ; ++i) { num_inconsistent_local_tr += (try_to_solve_inconsistencies_in_a_local_triangulation(i) ? 1 : 0); } #ifdef CGAL_TC_PROFILING std::cerr << "Attempt to fix inconsistencies: " << t.elapsed() << " seconds." << std::endl; #endif } #ifdef CGAL_TC_GLOBAL_REFRESH refresh_tangential_complex(); #endif #ifdef CGAL_TC_SHOW_DETAILED_STATS_FOR_INCONSISTENCIES std::pair stats_after = number_of_inconsistent_simplices(false); std::cerr << std::endl << "==========================================================" << std::endl << "Inconsistencies (detailed stats):\n" << " * Number of vertices: " << m_points.size() << std::endl << std::endl << " * BEFORE fix_inconsistencies:" << std::endl << " - Total number of simplices in stars (incl. duplicates): " << stats_before.first << std::endl << " - Num inconsistent simplices in stars (incl. duplicates): " << stats_before.second << " (" << 100. * stats_before.second / stats_before.first << "%)" << std::endl << " * Num inconsistent local triangulations: " << num_inconsistent_local_tr << " (" << 100. * num_inconsistent_local_tr / m_points.size() << "%)" << std::endl << std::endl << " * AFTER fix_inconsistencies:" << std::endl << " - Total number of simplices in stars (incl. duplicates): " << stats_after.first << std::endl << " - Num inconsistent simplices in stars (incl. duplicates): " << stats_after.second << std::endl << " - Percentage of inconsistencies: " << 100. * stats_after.second / stats_before.first << "%" << std::endl << "==========================================================" << std::endl; stats_before = stats_after; #else // CGAL_TC_SHOW_DETAILED_STATS_FOR_INCONSISTENCIES # ifdef CGAL_TC_VERBOSE std::cerr << std::endl << "==========================================================" << std::endl << "fix_inconsistencies():\n" << " * " << m_points.size() << " vertices" << std::endl << " * " << num_inconsistent_local_tr << " (" << 100. * num_inconsistent_local_tr / m_points.size() << "%)" << " inconsistent triangulations encountered" << std::endl << "==========================================================" << std::endl; # endif #endif // CGAL_TC_SHOW_DETAILED_STATS_FOR_INCONSISTENCIES ++num_steps; done = (num_inconsistent_local_tr == 0); } return num_steps; } // Return a pair std::pair number_of_inconsistent_simplices( #ifdef CGAL_TC_VERBOSE bool verbose = true #else bool verbose = false #endif ) { std::size_t num_simplices = 0; std::size_t num_inconsistent_simplices = 0; typename Tr_container::const_iterator it_tr = m_triangulations.begin(); typename Tr_container::const_iterator it_tr_end = m_triangulations.end(); // For each triangulation for ( ; it_tr != it_tr_end ; ++it_tr) { Triangulation const& tr = it_tr->tr(); Tr_vertex_handle center_vh = it_tr->center_vertex(); std::vector incident_cells; tr.incident_full_cells(center_vh, std::back_inserter(incident_cells)); typename std::vector::const_iterator it_c = incident_cells.begin(); typename std::vector::const_iterator it_c_end = incident_cells.end(); // For each cell for ( ; it_c != it_c_end ; ++it_c) { if (tr.is_infinite(*it_c)) // Don't check infinite cells continue; if (!is_simplex_consistent(*it_c, tr.current_dimension())) ++num_inconsistent_simplices; ++num_simplices; } } if (verbose) { std::cerr << std::endl << "==========================================================" << std::endl << "Inconsistencies:\n" << " * Number of vertices: " << m_points.size() << std::endl << " * Total number of simplices in stars (incl. duplicates): " << num_simplices << std::endl << " * Number of inconsistent simplices in stars (incl. duplicates): " << num_inconsistent_simplices << std::endl << " * Percentage of inconsistencies: " << 100 * num_inconsistent_simplices / num_simplices << "%" << std::endl << "==========================================================" << std::endl; } return std::make_pair(num_simplices, num_inconsistent_simplices); } std::ostream &export_to_off( std::ostream & os, bool color_inconsistencies = false, std::set > const* excluded_simplices = NULL, bool show_excluded_vertices_in_color = false) { if (m_points.empty()) return os; const int ambient_dim = m_k.point_dimension_d_object()(*m_points.begin()); if (ambient_dim < 2) { std::cerr << "Error: export_to_off => ambient dimension should be >= 2." << std::endl; os << "Error: export_to_off => ambient dimension should be >= 2." << std::endl; return os; } if (ambient_dim > 3) { std::cerr << "Warning: export_to_off => ambient dimension should be " "<= 3. Only the first 3 coordinates will be exported." << std::endl; } if (Intrinsic_dimension < 1 || Intrinsic_dimension > 3) { std::cerr << "Error: export_to_off => intrinsic dimension should be " "between 1 and 3." << std::endl; os << "Error: export_to_off => intrinsic dimension should be " "between 1 and 3." << std::endl; return os; } std::stringstream output; std::size_t num_simplices, num_vertices; export_vertices_to_off(output, num_vertices); export_simplices_to_off( output, num_simplices, color_inconsistencies, excluded_simplices, show_excluded_vertices_in_color); #ifdef CGAL_TC_EXPORT_NORMALS os << "N"; #endif os << "OFF \n" << num_vertices << " " << num_simplices << " " << "0 \n" << output.str(); return os; } bool check_if_all_simplices_are_in_the_ambient_delaunay( std::set > * incorrect_simplices = NULL) { if (m_points.empty()) return true; const int ambient_dim = m_k.point_dimension_d_object()(*m_points.begin()); typedef Delaunay_triangulation > > DT; typedef typename DT::Vertex_handle DT_VH; typedef typename DT::Finite_full_cell_const_iterator FFC_it; typedef std::set Indexed_simplex; //------------------------------------------------------------------------- // Build the ambient Delaunay triangulation // Then save its simplices into "amb_dt_simplices" //------------------------------------------------------------------------- DT ambient_dt(ambient_dim); for (std::size_t i=0; idata() = i; } std::set amb_dt_simplices; for (FFC_it cit = ambient_dt.finite_full_cells_begin() ; cit != ambient_dt.finite_full_cells_end() ; ++cit ) { CGAL::Combination_enumerator combi( Intrinsic_dimension + 1, 0, ambient_dim + 1); for ( ; !combi.finished() ; ++combi) { Indexed_simplex simplex; for (int i = 0 ; i < Intrinsic_dimension + 1 ; ++i) simplex.insert(cit.base()->vertex(combi[i])->data()); amb_dt_simplices.insert(simplex); } } //------------------------------------------------------------------------- // Parse the TC and save its simplices into "stars_simplices" //------------------------------------------------------------------------- std::set stars_simplices; typename Tr_container::const_iterator it_tr = m_triangulations.begin(); typename Tr_container::const_iterator it_tr_end = m_triangulations.end(); // For each triangulation for ( ; it_tr != it_tr_end ; ++it_tr) { Triangulation const& tr = it_tr->tr(); Tr_vertex_handle center_vh = it_tr->center_vertex(); std::vector incident_cells; tr.incident_full_cells(center_vh, std::back_inserter(incident_cells)); typename std::vector::const_iterator it_c = incident_cells.begin(); typename std::vector::const_iterator it_c_end = incident_cells.end(); // For each cell for ( ; it_c != it_c_end ; ++it_c) { if (tr.is_infinite(*it_c)) { std::cerr << "Warning: infinite cell in star" << std::endl; continue; } Indexed_simplex simplex; for (int i = 0 ; i < tr.current_dimension() + 1 ; ++i) simplex.insert((*it_c)->vertex(i)->data()); stars_simplices.insert(simplex); } } //------------------------------------------------------------------------- // Check if simplices of "stars_simplices" are all in "amb_dt_simplices" //------------------------------------------------------------------------- std::set diff; if (!incorrect_simplices) incorrect_simplices = &diff; set_difference(stars_simplices.begin(), stars_simplices.end(), amb_dt_simplices.begin(), amb_dt_simplices.end(), std::inserter(*incorrect_simplices, incorrect_simplices->begin()) ); if (!incorrect_simplices->empty()) { std::cerr << "ERROR check_if_all_simplices_are_in_the_ambient_delaunay:" << std::endl << " Number of simplices in ambient DT: " << amb_dt_simplices.size() << std::endl << " Number of unique simplices in TC stars: " << stars_simplices.size() << std::endl << " Number of wrong simplices: " << incorrect_simplices->size() << std::endl; return false; } else return true; } private: class Compare_distance_to_ref_point { public: Compare_distance_to_ref_point(Point const& ref, Kernel const& k) : m_ref(ref), m_k(k) {} bool operator()(Point const& p1, Point const& p2) { typename Kernel::Squared_distance_d sqdist = m_k.squared_distance_d_object(); return sqdist(p1, m_ref) < sqdist(p2, m_ref); } private: Point const& m_ref; Kernel const& m_k; }; struct Tr_vertex_to_global_point { typedef Tr_vertex_handle argument_type; typedef Point result_type; Tr_vertex_to_global_point(Points const& points) : m_points(points) {} result_type operator()(argument_type const& vh) const { return m_points[vh->data()]; } private: Points const& m_points; }; struct Tr_vertex_to_bare_point { typedef Tr_vertex_handle argument_type; typedef Tr_bare_point result_type; Tr_vertex_to_bare_point(Tr_traits const& traits) : m_traits(traits) {} result_type operator()(argument_type const& vh) const { typename Tr_traits::Point_drop_weight_d pdw = m_traits.point_drop_weight_d_object(); return pdw(vh->point()); } private: Tr_traits const& m_traits; }; #ifdef CGAL_LINKED_WITH_TBB // Functor for compute_tangential_complex function class Compute_tangent_triangulation { Tangential_complex & m_tc; bool m_tangent_spaces_are_already_computed; public: // Constructor Compute_tangent_triangulation( Tangential_complex &tc, bool tangent_spaces_are_already_computed = false) : m_tc(tc), m_tangent_spaces_are_already_computed(tangent_spaces_are_already_computed) {} // Constructor Compute_tangent_triangulation(const Compute_tangent_triangulation &ctt) : m_tc(ctt.m_tc) {} // operator() void operator()( const tbb::blocked_range& r ) const { for( size_t i = r.begin() ; i != r.end() ; ++i) { m_tc.compute_tangent_triangulation( i, m_tangent_spaces_are_already_computed); } } }; #endif // CGAL_LINKED_WITH_TBB void compute_tangent_triangulation( std::size_t i, bool tangent_spaces_are_already_computed = false) { //std::cerr << "***********************************************" << std::endl; Triangulation &local_tr = m_triangulations[i].construct_triangulation(Intrinsic_dimension); const Tr_traits &local_tr_traits = local_tr.geom_traits(); Tr_vertex_handle ¢er_vertex = m_triangulations[i].center_vertex(); // Kernel functor & objects typename Kernel::Difference_of_points_d k_diff_pts = m_k.difference_of_points_d_object(); typename Kernel::Squared_distance_d k_sqdist = m_k.squared_distance_d_object(); typename Kernel::Translated_point_d k_transl = m_k.translated_point_d_object(); // Triangulation's traits functor & objects typename Tr_traits::Squared_distance_d sqdist = local_tr_traits.squared_distance_d_object(); typename Tr_traits::Point_weight_d point_weight = local_tr_traits.point_weight_d_object(); typename Tr_traits::Point_drop_weight_d drop_w = local_tr_traits.point_drop_weight_d_object(); /*typename Tr_traits::Power_center_d power_center = local_tr_traits.power_center_d_object();*/ // CJTODO typename Get_functor::type power_center(local_tr_traits); #ifdef CGAL_TC_PERTURB_POSITION const Point center_pt = k_transl(m_points[i], m_translations[i]); #else const Point ¢er_pt = m_points[i]; #endif // Estimate the tangent space if (!tangent_spaces_are_already_computed) { #ifdef CGAL_TC_EXPORT_NORMALS m_tangent_spaces[i] = compute_tangent_space(center_pt, &m_normals[i]); #else m_tangent_spaces[i] = compute_tangent_space(center_pt); #endif } //*************************************************** // Build a minimal triangulation in the tangent space // (we only need the star of p) //*************************************************** // Insert p Tr_point wp = local_tr_traits.construct_weighted_point_d_object()( local_tr_traits.construct_point_d_object()(Intrinsic_dimension, ORIGIN), #ifdef CGAL_TC_PERTURB_WEIGHT m_weights[i] #else 0 #endif ); center_vertex = local_tr.insert(wp); center_vertex->data() = i; //const int NUM_NEIGHBORS = 150; //KNS_range ins_range = m_points_ds.query_ANN(center_pt, NUM_NEIGHBORS); INS_range ins_range = m_points_ds.query_incremental_ANN(center_pt); // While building the local triangulation, we keep the radius // of the sphere "star sphere" centered at "center_vertex" // and which contains all the // circumspheres of the star of "center_vertex" boost::optional star_sphere_squared_radius; // Insert points until we find a point which is outside "star shere" for (INS_iterator nn_it = ins_range.begin() ; nn_it != ins_range.end() ; ++nn_it) { std::size_t neighbor_point_idx = nn_it->first; // ith point = p, which is already inserted if (neighbor_point_idx != i) { #ifdef CGAL_TC_PERTURB_POSITION const Point neighbor_pt = k_transl( m_points[neighbor_point_idx], m_translations[neighbor_point_idx]); #else const Point &neighbor_pt = m_points[neighbor_point_idx]; #endif #ifdef CGAL_TC_PERTURB_WEIGHT FT neighbor_weight = m_weights[neighbor_point_idx]; #else FT neighbor_weight = 0; #endif if (star_sphere_squared_radius && k_sqdist(center_pt, neighbor_pt) > *star_sphere_squared_radius + 4*SQ_HALF_SPARSITY) // CJTODO: why is "4*" needed? break; Tr_point proj_pt = project_point_and_compute_weight( neighbor_pt, neighbor_weight, center_pt, m_tangent_spaces[i], local_tr_traits); Tr_vertex_handle vh = local_tr.insert_if_in_star(proj_pt, center_vertex); //Tr_vertex_handle vh = local_tr.insert(proj_pt); if (vh != Tr_vertex_handle()) { // CJTODO TEMP TEST /*if (star_sphere_squared_radius && k_sqdist(center_pt, neighbor_pt) > *star_sphere_squared_radius + 4*SQ_HALF_SPARSITY) std::cout << "ARGGGGGGGH" << std::endl;*/ vh->data() = neighbor_point_idx; // Let's recompute star_sphere_squared_radius if (local_tr.current_dimension() >= Intrinsic_dimension) { star_sphere_squared_radius = 0; // Get the incident cells and look for the biggest circumsphere std::vector incident_cells; local_tr.incident_full_cells( center_vertex, std::back_inserter(incident_cells)); for (typename std::vector::iterator cit = incident_cells.begin(); cit != incident_cells.end(); ++cit) { Tr_full_cell_handle cell = *cit; if (local_tr.is_infinite(cell)) { star_sphere_squared_radius = boost::none; break; } else { Tr_point c = power_center( boost::make_transform_iterator( cell->vertices_begin(), vertex_handle_to_point), boost::make_transform_iterator( cell->vertices_end(), vertex_handle_to_point)); FT sq_power_sphere_diam = 4*point_weight(c); if (!star_sphere_squared_radius || sq_power_sphere_diam > *star_sphere_squared_radius) { star_sphere_squared_radius = sq_power_sphere_diam; } } } } } //std::cerr << star_sphere_squared_radius << std::endl; } } // CJTODO DEBUG //std::cerr << "\nChecking topology and geometry..." // << (local_tr.is_valid(true) ? "OK.\n" : "Error.\n"); // DEBUG: output the local mesh into an OFF file //std::stringstream sstr; //sstr << "data/local_tri_" << i << ".off"; //std::ofstream off_stream_tr(sstr.str()); //CGAL::export_triangulation_to_off(off_stream_tr, local_tr); } Tangent_space_basis compute_tangent_space(const Point &p #ifdef CGAL_TC_EXPORT_NORMALS , Vector *p_normal #endif ) const { // Kernel functors typename Kernel::Construct_vector_d constr_vec = m_k.construct_vector_d_object(); typename Kernel::Compute_coordinate_d coord = m_k.compute_coordinate_d_object(); typename Kernel::Squared_length_d sqlen = m_k.squared_length_d_object(); typename Kernel::Scaled_vector_d scaled_vec = m_k.scaled_vector_d_object(); typename Kernel::Scalar_product_d inner_pdct = m_k.scalar_product_d_object(); typename Kernel::Difference_of_vectors_d diff_vec = m_k.difference_of_vectors_d_object(); KNS_range kns_range = m_points_ds.query_ANN( p, NUM_POINTS_FOR_PCA, false); //******************************* PCA ************************************* // One row = one point Eigen::MatrixXd mat_points(NUM_POINTS_FOR_PCA, m_ambiant_dim); KNS_iterator nn_it = kns_range.begin(); for (int j = 0 ; j < NUM_POINTS_FOR_PCA && nn_it != kns_range.end() ; ++j, ++nn_it) { for (int i = 0 ; i < m_ambiant_dim ; ++i) mat_points(j, i) = CGAL::to_double(coord(m_points[nn_it->first], i)); } Eigen::MatrixXd centered = mat_points.rowwise() - mat_points.colwise().mean(); Eigen::MatrixXd cov = centered.adjoint() * centered; Eigen::SelfAdjointEigenSolver eig(cov); // The eigenvectors are sorted in increasing order of their corresponding // eigenvalues Tangent_space_basis ts; for (int i = m_ambiant_dim - 1 ; i >= m_ambiant_dim - Intrinsic_dimension ; --i) { ts.push_back(constr_vec( m_ambiant_dim, eig.eigenvectors().col(i).data(), eig.eigenvectors().col(i).data() + m_ambiant_dim)); } #ifdef CGAL_TC_EXPORT_NORMALS *p_normal = constr_vec( m_ambiant_dim, eig.eigenvectors().col(m_ambiant_dim - Intrinsic_dimension - 1).data(), eig.eigenvectors().col(m_ambiant_dim - Intrinsic_dimension - 1).data() + m_ambiant_dim); #endif //************************************************************************* //Vector n = m_k.point_to_vector_d_object()(p); //n = scaled_vec(n, FT(1)/sqrt(sqlen(n))); //std::cerr << "IP = " << inner_pdct(n, ts[0]) << " & " << inner_pdct(n, ts[1]) << std::endl; return compute_gram_schmidt_basis(ts, m_k); /* // CJTODO: this is only for a sphere in R^3 Vector t1(-p[1] - p[2], p[0], p[0]); Vector t2(p[1] * t1[2] - p[2] * t1[1], p[2] * t1[0] - p[0] * t1[2], p[0] * t1[1] - p[1] * t1[0]); // Normalize t1 and t2 typename Kernel::Squared_length_d sqlen = m_k.squared_length_d_object(); typename Kernel::Scaled_vector_d scaled_vec = m_k.scaled_vector_d_object(); Tangent_space_basis ts; ts.reserve(Intrinsic_dimension); ts.push_back(scaled_vec(t1, FT(1)/CGAL::sqrt(sqlen(t1)))); ts.push_back(scaled_vec(t2, FT(1)/CGAL::sqrt(sqlen(t2)))); return ts;*/ /* // Alternative code (to be used later) //Vector n = m_k.point_to_vector_d_object()(p); //n = scaled_vec(n, FT(1)/sqrt(sqlen(n))); //Vector t1(12., 15., 65.); //Vector t2(32., 5., 85.); //Tangent_space_basis ts; //ts.reserve(Intrinsic_dimension); //ts.push_back(diff_vec(t1, scaled_vec(n, inner_pdct(t1, n)))); //ts.push_back(diff_vec(t2, scaled_vec(n, inner_pdct(t2, n)))); //return compute_gram_schmidt_basis(ts, m_k); */ } // Project the point in the tangent space // The weight will be the squared distance between p and the projection of p Tr_bare_point project_point(const Point &p, const Point &origin, const Tangent_space_basis &ts) const { typename Kernel::Scalar_product_d inner_pdct = m_k.scalar_product_d_object(); typename Kernel::Difference_of_points_d diff_points = m_k.difference_of_points_d_object(); std::vector coords; // Ambiant-space coords of the projected point coords.reserve(Intrinsic_dimension); for (std::size_t i = 0 ; i < Intrinsic_dimension ; ++i) { // Compute the inner product p * ts[i] Vector v = diff_points(p, origin); FT coord = inner_pdct(v, ts[i]); coords.push_back(coord); } return Tr_bare_point(Intrinsic_dimension, coords.begin(), coords.end()); } // Project the point in the tangent space // The weight will be the squared distance between p and the projection of p Tr_point project_point_and_compute_weight( const Point &p, FT w, const Point &origin, const Tangent_space_basis &ts, const Tr_traits &tr_traits) const { const int point_dim = m_k.point_dimension_d_object()(p); typename Kernel::Scalar_product_d inner_pdct = m_k.scalar_product_d_object(); typename Kernel::Difference_of_points_d diff_points = m_k.difference_of_points_d_object(); typename Kernel::Construct_cartesian_const_iterator_d ccci = m_k.construct_cartesian_const_iterator_d_object(); Vector v = diff_points(p, origin); std::vector coords; // Ambiant-space coords of the projected point std::vector p_proj(ccci(origin), ccci(origin, 0)); coords.reserve(Intrinsic_dimension); for (std::size_t i = 0 ; i < Intrinsic_dimension ; ++i) { // Compute the inner product p * ts[i] FT coord = inner_pdct(v, ts[i]); coords.push_back(coord); // p_proj += coord * v; for (int j = 0 ; j < point_dim ; ++j) p_proj[j] += coord * ts[i][j]; } Point projected_pt(point_dim, p_proj.begin(), p_proj.end()); return tr_traits.construct_weighted_point_d_object() ( tr_traits.construct_point_d_object()( Intrinsic_dimension, coords.begin(), coords.end()), w - m_k.squared_distance_d_object()(p, projected_pt) ); } // A simplex here is a local tri's full cell handle bool is_simplex_consistent(Tr_full_cell_handle fch, int cur_dim) { std::set c; for (int i = 0 ; i < cur_dim + 1 ; ++i) { std::size_t data = fch->vertex(i)->data(); c.insert(data); } return is_simplex_consistent(c); } // A simplex here is a list of point indices bool is_simplex_consistent(std::set const& simplex) { int cur_dim_plus_1 = static_cast(simplex.size()); // Check if the simplex is in the stars of all its vertices std::set::const_iterator it_point_idx = simplex.begin(); // For each point p of the simplex, we parse the incidents cells of p // and we check if "simplex" is among them for ( ; it_point_idx != simplex.end() ; ++it_point_idx) { std::size_t point_idx = *it_point_idx; if (point_idx == std::numeric_limits::max()) continue; Triangulation const& tr = m_triangulations[point_idx].tr(); Tr_vertex_handle center_vh = m_triangulations[point_idx].center_vertex(); std::vector incident_cells; tr.incident_full_cells(center_vh, std::back_inserter(incident_cells)); typename std::vector::const_iterator it_c = incident_cells.begin(); typename std::vector::const_iterator it_c_end= incident_cells.end(); // For each cell bool found = false; for ( ; !found && it_c != it_c_end ; ++it_c) { std::set cell; for (int i = 0 ; i < cur_dim_plus_1 ; ++i) cell.insert((*it_c)->vertex(i)->data()); if (cell == simplex) found = true; } if (!found) return false; } return true; } #ifdef CGAL_LINKED_WITH_TBB // Functor for try_to_solve_inconsistencies_in_a_local_triangulation function class Try_to_solve_inconsistencies_in_a_local_triangulation { Tangential_complex & m_tc; public: // Constructor Try_to_solve_inconsistencies_in_a_local_triangulation( Tangential_complex &tc) : m_tc(tc) {} // Constructor Try_to_solve_inconsistencies_in_a_local_triangulation( const Compute_tangent_triangulation &ctt) : m_tc(ctt.m_tc) {} // operator() void operator()( const tbb::blocked_range& r ) const { for( size_t i = r.begin() ; i != r.end() ; ++i) m_tc.try_to_solve_inconsistencies_in_a_local_triangulation(i); } }; #endif // CGAL_LINKED_WITH_TBB bool try_to_solve_inconsistencies_in_a_local_triangulation( std::size_t tr_index) { bool is_inconsistent = false; #ifdef CGAL_LINKED_WITH_TBB //Tr_mutex::scoped_lock lock(m_tr_mutexes[tr_index]); #endif #ifdef CGAL_TC_PERTURB_POSITION # ifdef CGAL_TC_PERTURB_POSITION_GLOBAL CGAL::Random_points_on_sphere_d tr_point_on_sphere_generator(m_ambiant_dim, 1); # else CGAL::Random_points_on_sphere_d tr_point_on_sphere_generator(Intrinsic_dimension, 1); # endif #endif Triangulation const& tr = m_triangulations[tr_index].tr(); Tr_vertex_handle center_vh = m_triangulations[tr_index].center_vertex(); const Tr_traits &local_tr_traits = tr.geom_traits(); int cur_dim = tr.current_dimension(); std::vector incident_cells; tr.incident_full_cells(center_vh, std::back_inserter(incident_cells)); typename std::vector::const_iterator it_c = incident_cells.begin(); typename std::vector::const_iterator it_c_end= incident_cells.end(); // For each cell for ( ; it_c != it_c_end ; ++it_c) { #ifdef CGAL_TC_ONLY_CHANGE_SIMPLEX_WEIGHTS std::set c; for (int i = 0 ; i < tr.current_dimension() + 1 ; ++i) { std::size_t data = (*it_c)->vertex(i)->data(); c.insert(data); } // Inconsistent? if (!is_simplex_consistent(c)) { is_inconsistent = true; //m_weights[tr_index] = rng.get_double(0., SQ_HALF_SPARSITY); //break; // CJTODO TEMP CGAL::Random rng; for (std::set::iterator it=c.begin(); it!=c.end(); ++it) { m_weights[*it] = rng.get_double(0., SQ_HALF_SPARSITY); } #if !defined(CGAL_TC_GLOBAL_REFRESH) refresh_tangential_complex(); #endif // We will try the other cells next time (incident_cells is not // valid anymore here) break; } #else // Inconsistent? if (!is_simplex_consistent(*it_c, cur_dim)) { is_inconsistent = true; // Get the k + 2 closest points /*int point_dim = m_k.point_dimension_d_object()(*m_points.begin()); std::vector center(point_dim, FT(0)); for (int i = 0 ; i < point_dim ; ++i) { for (int j = 0 ; j < Intrinsic_dimension + 1 ; ++j) { std::size_t data = (*it_c)->vertex(j)->data(); const Point &p = m_points[data]; center[i] += p[i]; } center[i] /= (Intrinsic_dimension + 1); } Point global_center(center.begin(), center.end());*/ std::vector simplex_pts; for (int i = 0 ; i < cur_dim + 1 ; ++i) simplex_pts.push_back((*it_c)->vertex(i)->point()); //typename Tr_traits::Power_center_d power_center = // local_tr_traits.power_center_d_object(); // CJTODO typename Get_functor::type power_center(local_tr_traits); typename Tr_traits::Compute_coordinate_d coord = local_tr_traits.compute_coordinate_d_object(); typename Tr_traits::Construct_weighted_point_d cwp = local_tr_traits.construct_weighted_point_d_object(); //typename Tr_traits::Point_to_vector_d pt_to_vec = // local_tr_traits.construct_point_to_vector_d_object(); typename Kernel::Translated_point_d k_transl = m_k.translated_point_d_object(); typename Kernel::Scaled_vector_d k_scaled_vec = m_k.scaled_vector_d_object(); typename Kernel::Construct_vector_d k_constr_vec = m_k.construct_vector_d_object(); #if defined(CGAL_TC_PERTURB_POSITION) && defined(CGAL_TC_PERTURB_POSITION_GLOBAL) typename Kernel::Point_to_vector_d k_pt_to_vec = m_k.point_to_vector_d_object(); #endif Tr_point local_center = power_center(simplex_pts.begin(), simplex_pts.end()); Point global_center = m_points[tr_index]; const Tangent_space_basis &tsb = m_tangent_spaces[tr_index]; for (int i = 0 ; i < Intrinsic_dimension ; ++i) { global_center = k_transl( global_center, k_scaled_vec(tsb[i], coord(local_center, i))); } KNS_range kns_range = m_points_ds.query_ANN( global_center, Intrinsic_dimension + 1 + CGAL_TC_NUMBER_OF_ADDITIONNAL_PERTURBED_POINTS); std::vector neighbors; for (KNS_iterator nn_it = kns_range.begin() ; nn_it != kns_range.end() ; ++nn_it) { neighbors.push_back(nn_it->first); } CGAL::Random rng; for (std::vector::iterator it = neighbors.begin(); it != neighbors.end() ; ++it) { #ifdef CGAL_TC_PERTURB_WEIGHT m_weights[*it] = rng.get_double(0., SQ_HALF_SPARSITY); #endif #ifdef CGAL_TC_PERTURB_POSITION # ifdef CGAL_TC_PERTURB_POSITION_GLOBAL m_translations[*it] = k_scaled_vec(k_pt_to_vec( *tr_point_on_sphere_generator++), HALF_SPARSITY); # else // CGAL_TC_PERTURB_POSITION_TANGENTIAL Tr_point local_random_transl = cwp(*tr_point_on_sphere_generator++, 0); Vector &global_transl = m_translations[*it]; global_transl = k_constr_vec(m_ambiant_dim); for (int i = 0 ; i < Intrinsic_dimension ; ++i) { global_transl = k_transl( global_transl, k_scaled_vec(tsb[i], HALF_SPARSITY*coord(local_random_transl, i)) ); } # endif #endif } #if !defined(CGAL_TC_GLOBAL_REFRESH) refresh_tangential_complex(); #endif // We will try the other cells next time (incident_cells is not // valid anymore here) break; } #endif } return is_inconsistent; } std::ostream &export_vertices_to_off( std::ostream & os, std::size_t &num_vertices) { if (m_points.empty()) { num_vertices = 0; return os; } // If Intrinsic_dimension = 1, we output each point two times // to be able to export each segment as a flat triangle with 3 different // indices (otherwise, Meshlab detects degenerated simplices) const int N = (Intrinsic_dimension == 1 ? 2 : 1); const int ambient_dim = m_k.point_dimension_d_object()(*m_points.begin()); // Kernel functors typename Kernel::Compute_coordinate_d coord = m_k.compute_coordinate_d_object(); int num_coords = min(ambient_dim, 3); #ifdef CGAL_TC_EXPORT_NORMALS Vectors::const_iterator it_n = m_normals.begin(); #endif typename Points::const_iterator it_p = m_points.begin(); typename Points::const_iterator it_p_end = m_points.end(); // For each point p for ( ; it_p != it_p_end ; ++it_p) { for (int ii = 0 ; ii < N ; ++ii) { int i = 0; for ( ; i < num_coords ; ++i) os << CGAL::to_double(coord(*it_p, i)) << " "; if (i == 2) os << "0"; #ifdef CGAL_TC_EXPORT_NORMALS for (i = 0 ; i < num_coords ; ++i) os << " " << CGAL::to_double(coord(*it_n, i)); #endif os << std::endl; } #ifdef CGAL_TC_EXPORT_NORMALS ++it_n; #endif } num_vertices = N*m_points.size(); return os; } std::ostream &export_simplices_to_off( std::ostream & os, std::size_t &num_simplices, bool color_inconsistencies = false, std::set > const* excluded_simplices = NULL, bool show_excluded_vertices_in_color = false) { // If Intrinsic_dimension = 1, each point is output two times // (see export_vertices_to_off) int factor = (Intrinsic_dimension == 1 ? 2 : 1); int OFF_simplices_dim = (Intrinsic_dimension == 1 ? 3 : Intrinsic_dimension + 1); num_simplices = 0; std::size_t num_inconsistent_simplices = 0; typename Tr_container::const_iterator it_tr = m_triangulations.begin(); typename Tr_container::const_iterator it_tr_end = m_triangulations.end(); // For each triangulation for ( ; it_tr != it_tr_end ; ++it_tr) { Triangulation const& tr = it_tr->tr(); Tr_vertex_handle center_vh = it_tr->center_vertex(); if (tr.current_dimension() < Intrinsic_dimension) continue; // Color for this star std::stringstream color; //color << rand()%256 << " " << 100+rand()%156 << " " << 100+rand()%156; color << 128 << " " << 128 << " " << 128; std::vector incident_cells; tr.incident_full_cells(center_vh, std::back_inserter(incident_cells)); typename std::vector::const_iterator it_c = incident_cells.begin(); typename std::vector::const_iterator it_c_end= incident_cells.end(); // For each cell for ( ; it_c != it_c_end ; ++it_c) { if (tr.is_infinite(*it_c)) // Don't export infinite cells continue; if (color_inconsistencies || excluded_simplices) { std::set c; std::stringstream sstr_c; std::size_t data; for (int i = 0 ; i < Intrinsic_dimension + 1 ; ++i) { data = (*it_c)->vertex(i)->data(); sstr_c << data*factor << " "; c.insert(data); } // See export_vertices_to_off if (Intrinsic_dimension == 1) sstr_c << (data*factor + 1) << " "; bool excluded = (excluded_simplices && excluded_simplices->find(c) != excluded_simplices->end()); if (!excluded) { os << OFF_simplices_dim << " " << sstr_c.str() << " "; if (color_inconsistencies && is_simplex_consistent(c)) os << color.str(); else { os << "255 0 0"; ++num_inconsistent_simplices; } ++num_simplices; } else if (show_excluded_vertices_in_color) { os << OFF_simplices_dim << " " << sstr_c.str() << " " << "0 0 255"; ++num_simplices; } } else { os << OFF_simplices_dim << " "; std::size_t data; for (int i = 0 ; i < Intrinsic_dimension + 1 ; ++i) { data = (*it_c)->vertex(i)->data(); os << data*factor << " "; } // See export_vertices_to_off if (Intrinsic_dimension == 1) os << (data*factor + 1) << " "; ++num_simplices; } os << std::endl; } } #ifdef CGAL_TC_VERBOSE std::cerr << std::endl << "==========================================================" << std::endl << "Export to OFF:\n" << " * Number of vertices: " << m_points.size() << std::endl << " * Total number of simplices in stars (incl. duplicates): " << num_simplices << std::endl << " * Number of inconsistent simplices in stars (incl. duplicates): " << num_inconsistent_simplices << std::endl << " * Percentage of inconsistencies: " << (num_simplices > 0 ? 100. * num_inconsistent_simplices / num_simplices : 0.) << "%" << std::endl << "==========================================================" << std::endl; #endif return os; } private: const Kernel m_k; const int m_ambiant_dim; Points m_points; #ifdef CGAL_TC_PERTURB_WEIGHT Weights m_weights; #endif #ifdef CGAL_TC_PERTURB_POSITION Vectors m_translations; #endif Points_ds m_points_ds; TS_container m_tangent_spaces; Tr_container m_triangulations; // Contains the triangulations // and their center vertex #ifdef CGAL_LINKED_WITH_TBB //std::vector m_tr_mutexes; #endif #ifdef CGAL_TC_EXPORT_NORMALS Vectors m_normals; #endif }; // /class Tangential_complex } // end namespace CGAL #endif // TANGENTIAL_COMPLEX_H