diff --git a/Mesh_3/demo/Mesh_3/Mesh_function.h b/Mesh_3/demo/Mesh_3/Mesh_function.h index b9e8e8e7147..d6d6a5a7f01 100644 --- a/Mesh_3/demo/Mesh_3/Mesh_function.h +++ b/Mesh_3/demo/Mesh_3/Mesh_function.h @@ -204,7 +204,7 @@ launch() /*mesher_->initialize(); #ifdef CGAL_MESH_3_PROFILING - WallClockTimer t; + Wall_clock_timer t; #endif while ( ! mesher_->is_algorithm_done() && continue_ ) diff --git a/Mesh_3/include/CGAL/Mesh_3/C3T3_helpers.h b/Mesh_3/include/CGAL/Mesh_3/C3T3_helpers.h index be3288e1920..c45d067d6b4 100644 --- a/Mesh_3/include/CGAL/Mesh_3/C3T3_helpers.h +++ b/Mesh_3/include/CGAL/Mesh_3/C3T3_helpers.h @@ -2651,7 +2651,7 @@ rebuild_restricted_delaunay(OutdatedCells& outdated_cells, # ifdef CGAL_MESH_3_PROFILING std::cerr << std::endl << " Updating cells..."; - WallClockTimer t; + Wall_clock_timer t; size_t num_cells = c3t3_.number_of_cells_in_complex(); # endif diff --git a/Mesh_3/include/CGAL/Mesh_3/Mesh_global_optimizer.h b/Mesh_3/include/CGAL/Mesh_3/Mesh_global_optimizer.h index 7b4f50317a2..ebbacdc1181 100644 --- a/Mesh_3/include/CGAL/Mesh_3/Mesh_global_optimizer.h +++ b/Mesh_3/include/CGAL/Mesh_3/Mesh_global_optimizer.h @@ -309,7 +309,7 @@ private: #ifdef CGAL_MESH_3_PROFILING std::cerr << "Computing moves..."; - WallClockTimer t; + Wall_clock_timer t; #endif @@ -688,7 +688,7 @@ operator()(int nb_iterations, Visitor visitor) running_time_.start(); #ifdef CGAL_MESH_3_PROFILING - WallClockTimer t; + Wall_clock_timer t; #endif // Fill set containing moving vertices @@ -896,7 +896,7 @@ update_mesh(const Moves_vector& moves, #ifdef CGAL_MESH_3_PROFILING std::cerr << "Moving vertices..."; - WallClockTimer t; + Wall_clock_timer t; #endif #ifdef CGAL_LINKED_WITH_TBB diff --git a/Mesh_3/include/CGAL/Mesh_3/Mesher_3.h b/Mesh_3/include/CGAL/Mesh_3/Mesher_3.h index b9ef1c603c8..1aecf45e33b 100644 --- a/Mesh_3/include/CGAL/Mesh_3/Mesher_3.h +++ b/Mesh_3/include/CGAL/Mesh_3/Mesher_3.h @@ -320,7 +320,7 @@ Mesher_3::refine_mesh(std::string dump_after_refine_surface_prefix) #ifdef CGAL_MESH_3_PROFILING std::cerr << "Refining facets..." << std::endl; - WallClockTimer t; + Wall_clock_timer t; #endif facets_mesher_.refine(facets_visitor_); #ifdef CGAL_MESH_3_PROFILING @@ -476,7 +476,7 @@ initialize() { #ifdef CGAL_MESH_3_PROFILING std::cerr << "Initializing... "; - WallClockTimer t; + Wall_clock_timer t; #endif //===================================== // Bounding box estimation diff --git a/Mesh_3/include/CGAL/Mesh_3/Mesher_level.h b/Mesh_3/include/CGAL/Mesh_3/Mesher_level.h index 509be7abe19..79943874df4 100644 --- a/Mesh_3/include/CGAL/Mesh_3/Mesher_level.h +++ b/Mesh_3/include/CGAL/Mesh_3/Mesher_level.h @@ -194,7 +194,7 @@ protected: #ifdef CGAL_MESH_3_PROFILING protected: - WallClockTimer m_timer; + Wall_clock_timer m_timer; #endif public: diff --git a/Mesh_3/include/CGAL/Mesh_3/Profiling_tools.h b/Mesh_3/include/CGAL/Mesh_3/Profiling_tools.h index eb436c93af3..32c707f9d9e 100644 --- a/Mesh_3/include/CGAL/Mesh_3/Profiling_tools.h +++ b/Mesh_3/include/CGAL/Mesh_3/Profiling_tools.h @@ -28,10 +28,10 @@ // TBB timers #ifdef CGAL_LINKED_WITH_TBB #include - struct WallClockTimer + struct Wall_clock_timer { tbb::tick_count t; - WallClockTimer() + Wall_clock_timer() { t = tbb::tick_count::now(); } @@ -48,10 +48,10 @@ #else #include - struct WallClockTimer + struct Wall_clock_timer { CGAL::Real_timer t; - WallClockTimer() + Wall_clock_timer() { t.start(); } diff --git a/Mesh_3/include/CGAL/Mesh_3/Refine_cells_3.h b/Mesh_3/include/CGAL/Mesh_3/Refine_cells_3.h index 776cd4f4796..8b6e17cb96b 100644 --- a/Mesh_3/include/CGAL/Mesh_3/Refine_cells_3.h +++ b/Mesh_3/include/CGAL/Mesh_3/Refine_cells_3.h @@ -632,7 +632,7 @@ scan_triangulation_impl() typedef typename Tr::Finite_cells_iterator Finite_cell_iterator; #ifdef CGAL_MESH_3_PROFILING - WallClockTimer t; + Wall_clock_timer t; #endif diff --git a/Mesh_3/include/CGAL/Mesh_3/Refine_facets_3.h b/Mesh_3/include/CGAL/Mesh_3/Refine_facets_3.h index 698645557d5..2d5f71200ea 100644 --- a/Mesh_3/include/CGAL/Mesh_3/Refine_facets_3.h +++ b/Mesh_3/include/CGAL/Mesh_3/Refine_facets_3.h @@ -882,7 +882,7 @@ scan_triangulation_impl() typedef typename Tr::Finite_facets_iterator Finite_facet_iterator; #ifdef CGAL_MESH_3_PROFILING - WallClockTimer t; + Wall_clock_timer t; #endif #ifdef CGAL_MESH_3_VERY_VERBOSE diff --git a/Mesh_3/include/CGAL/Mesh_3/Sliver_perturber.h b/Mesh_3/include/CGAL/Mesh_3/Sliver_perturber.h index 5bc4a79e5a6..4d9a9a8640b 100644 --- a/Mesh_3/include/CGAL/Mesh_3/Sliver_perturber.h +++ b/Mesh_3/include/CGAL/Mesh_3/Sliver_perturber.h @@ -797,7 +797,7 @@ operator()(Visitor visitor) running_time_.start(); #ifdef CGAL_MESH_3_PROFILING - WallClockTimer t; + Wall_clock_timer t; #endif // Build priority queue (we use one queue for all steps) diff --git a/Mesh_3/include/CGAL/Mesh_3/Slivers_exuder.h b/Mesh_3/include/CGAL/Mesh_3/Slivers_exuder.h index df65de8a507..07a279e25f3 100644 --- a/Mesh_3/include/CGAL/Mesh_3/Slivers_exuder.h +++ b/Mesh_3/include/CGAL/Mesh_3/Slivers_exuder.h @@ -453,7 +453,7 @@ public: // methods operator()(Visitor visitor = Visitor()) { #ifdef CGAL_MESH_3_PROFILING - WallClockTimer t; + Wall_clock_timer t; #endif Mesh_optimization_return_code ret = @@ -964,7 +964,7 @@ pump_vertices(double sliver_criterion_limit, Visitor& visitor) { #ifdef CGAL_MESH_3_PROFILING - WallClockTimer t; + Wall_clock_timer t; #endif init(sliver_criterion_limit); diff --git a/NewKernel_d/include/CGAL/Epeck_d.h b/NewKernel_d/include/CGAL/Epeck_d.h new file mode 100644 index 00000000000..52bce84ca3f --- /dev/null +++ b/NewKernel_d/include/CGAL/Epeck_d.h @@ -0,0 +1,53 @@ +// Copyright (c) 2014 +// INRIA Saclay-Ile de France (France) +// +// This file is part of CGAL (www.cgal.org); you can redistribute it and/or +// modify it under the terms of the GNU Lesser 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) : Marc Glisse + +#ifndef CGAL_EPECK_D_H +#define CGAL_EPECK_D_H +#include +#include +#include +#include + + +namespace CGAL { +#define CGAL_BASE \ + Cartesian_base_d::Type, Dim> +template +struct Epeck_d_help1 +: CGAL_BASE +{ + CGAL_CONSTEXPR Epeck_d_help1(){} + CGAL_CONSTEXPR Epeck_d_help1(int d):CGAL_BASE(d){} +}; +#undef CGAL_BASE +#define CGAL_BASE \ + Kernel_d_interface< \ + Cartesian_wrap< \ + Epeck_d_help1, \ + Epeck_d > > +template +struct Epeck_d +: CGAL_BASE +{ + CGAL_CONSTEXPR Epeck_d(){} + CGAL_CONSTEXPR Epeck_d(int d):CGAL_BASE(d){} +}; +#undef CGAL_BASE +} +#endif diff --git a/NewKernel_d/include/CGAL/NewKernel_d/Cartesian_filter_K.h b/NewKernel_d/include/CGAL/NewKernel_d/Cartesian_filter_K.h index 403a0e4d68c..179e97bf016 100644 --- a/NewKernel_d/include/CGAL/NewKernel_d/Cartesian_filter_K.h +++ b/NewKernel_d/include/CGAL/NewKernel_d/Cartesian_filter_K.h @@ -30,7 +30,7 @@ namespace CGAL { template < typename Base_, typename AK_, typename EK_ > struct Cartesian_filter_K : public Base_, - private Store_kernel, private Store_kernel2 + private Store_kernel, private Store_kernel2 { CGAL_CONSTEXPR Cartesian_filter_K(){} CGAL_CONSTEXPR Cartesian_filter_K(int d):Base_(d){} @@ -61,11 +61,11 @@ struct Cartesian_filter_K : public Base_, template struct Type : Get_type {}; template::type> struct Functor : - Inherit_functor {}; + Inherit_functor {}; template struct Functor { - typedef typename Get_functor::type AP; - typedef typename Get_functor::type EP; - typedef Filtered_predicate2 type; + typedef typename Get_functor::type AP; + typedef typename Get_functor::type EP; + typedef Filtered_predicate2 type; }; // TODO: // template struct Functor : diff --git a/NewKernel_d/include/CGAL/NewKernel_d/Kernel_d_interface.h b/NewKernel_d/include/CGAL/NewKernel_d/Kernel_d_interface.h index d7fdd545000..0b021b505b9 100644 --- a/NewKernel_d/include/CGAL/NewKernel_d/Kernel_d_interface.h +++ b/NewKernel_d/include/CGAL/NewKernel_d/Kernel_d_interface.h @@ -66,6 +66,8 @@ template struct Kernel_d_interface : public Base_ { typedef typename Get_functor::type Point_dimension_d; typedef typename Get_functor::type Side_of_oriented_sphere_d; typedef typename Get_functor::type Power_test_d; + typedef typename Get_functor::type Power_center_d; + typedef typename Get_functor::type Power_distance_d; typedef typename Get_functor::type Contained_in_affine_hull_d; typedef typename Get_functor::type Construct_flat_orientation_d; typedef typename Get_functor::type In_flat_orientation_d; @@ -73,6 +75,7 @@ template struct Kernel_d_interface : public Base_ { typedef typename Get_functor::type In_flat_power_test_d; typedef typename Get_functor::type Point_to_vector_d; typedef typename Get_functor::type Vector_to_point_d; + typedef typename Get_functor::type Translated_point_d; typedef typename Get_functor::type Scaled_vector_d; typedef typename Get_functor::type Difference_of_vectors_d; typedef typename Get_functor::type Difference_of_points_d; @@ -188,6 +191,8 @@ template struct Kernel_d_interface : public Base_ { Point_of_sphere_d point_of_sphere_d_object()const{ return Point_of_sphere_d(*this); } Side_of_oriented_sphere_d side_of_oriented_sphere_d_object()const{ return Side_of_oriented_sphere_d(*this); } Power_test_d power_test_d_object()const{ return Power_test_d(*this); } + Power_center_d power_center_d_object()const{ return Power_center_d(*this); } + Power_distance_d power_distance_d_object()const{ return Power_distance_d(*this); } Side_of_bounded_sphere_d side_of_bounded_sphere_d_object()const{ return Side_of_bounded_sphere_d(*this); } Contained_in_affine_hull_d contained_in_affine_hull_d_object()const{ return Contained_in_affine_hull_d(*this); } Contained_in_linear_hull_d contained_in_linear_hull_d_object()const{ return Contained_in_linear_hull_d(*this); } @@ -198,6 +203,7 @@ template struct Kernel_d_interface : public Base_ { In_flat_power_test_d in_flat_power_test_d_object()const{ return In_flat_power_test_d(*this); } Point_to_vector_d point_to_vector_d_object()const{ return Point_to_vector_d(*this); } Vector_to_point_d vector_to_point_d_object()const{ return Vector_to_point_d(*this); } + Translated_point_d translated_point_d_object()const{ return Translated_point_d(*this); } Scaled_vector_d scaled_vector_d_object()const{ return Scaled_vector_d(*this); } Difference_of_vectors_d difference_of_vectors_d_object()const{ return Difference_of_vectors_d(*this); } Difference_of_points_d difference_of_points_d_object()const{ return Difference_of_points_d(*this); } diff --git a/NewKernel_d/include/CGAL/NewKernel_d/Types/Sphere.h b/NewKernel_d/include/CGAL/NewKernel_d/Types/Sphere.h index ec565f2cc8c..114410b4e76 100644 --- a/NewKernel_d/include/CGAL/NewKernel_d/Types/Sphere.h +++ b/NewKernel_d/include/CGAL/NewKernel_d/Types/Sphere.h @@ -52,7 +52,6 @@ template struct Construct_sphere : Store_kernel { } template result_type operator()(Iter f, Iter e)const{ - typedef typename Get_type::type Point; typename Get_functor::type cc(this->kernel()); typename Get_functor::type sd(this->kernel()); diff --git a/NewKernel_d/include/CGAL/NewKernel_d/Types/Weighted_point.h b/NewKernel_d/include/CGAL/NewKernel_d/Types/Weighted_point.h index 32e8b586b58..4c76c32d579 100644 --- a/NewKernel_d/include/CGAL/NewKernel_d/Types/Weighted_point.h +++ b/NewKernel_d/include/CGAL/NewKernel_d/Types/Weighted_point.h @@ -75,6 +75,33 @@ template struct Point_weight { } }; +template struct Power_distance : private Store_kernel { + CGAL_FUNCTOR_INIT_STORE(Power_distance) + typedef typename Get_type::type first_argument_type; + typedef first_argument_type second_argument_type; + typedef typename Get_type::type result_type; + + result_type operator()(first_argument_type const&a, second_argument_type const&b)const{ + typename Get_functor::type pdw(this->kernel()); + typename Get_functor::type pw(this->kernel()); + typename Get_functor::type sd(this->kernel()); + return sd(pdw(a),pdw(b))-pw(a)-pw(b); + } +}; +template struct Power_distance_to_point : private Store_kernel { + CGAL_FUNCTOR_INIT_STORE(Power_distance_to_point) + typedef typename Get_type::type first_argument_type; + typedef typename Get_type::type second_argument_type; + typedef typename Get_type::type result_type; + + result_type operator()(first_argument_type const&a, second_argument_type const&b)const{ + typename Get_functor::type pdw(this->kernel()); + typename Get_functor::type pw(this->kernel()); + typename Get_functor::type sd(this->kernel()); + return sd(pdw(a),b)-pw(a); + } +}; + template struct Power_test : private Store_kernel { CGAL_FUNCTOR_INIT_STORE(Power_test) typedef R_ R; @@ -114,6 +141,56 @@ template struct In_flat_power_test : private Store_kernel { } }; +// Construct a point at (weighted) distance 0 from all the input +template struct Power_center : Store_kernel { + CGAL_FUNCTOR_INIT_STORE(Power_center) + typedef typename Get_type::type WPoint; + typedef WPoint result_type; + typedef typename Get_type::type Point; + typedef typename Get_type::type FT; + template + result_type operator()(Iter f, Iter e)const{ + // 2*(x-y).c == (x^2-wx^2)-(y^2-wy^2) + typedef typename R_::LA LA; + typedef typename LA::Square_matrix Matrix; + typedef typename LA::Vector Vec; + typedef typename LA::Construct_vector CVec; + typename Get_functor::type c(this->kernel()); + typename Get_functor >::type cp(this->kernel()); + typename Get_functor::type pd(this->kernel()); + typename Get_functor::type sdo(this->kernel()); + typename Get_functor::type pdp(this->kernel()); + typename Get_functor::type pdw(this->kernel()); + typename Get_functor::type pw(this->kernel()); + typename Get_functor >::type cwp(this->kernel()); + + WPoint const& wp0 = *f; + Point const& p0 = pdw(wp0); + int d = pd(p0); + FT const& n0 = sdo(p0) - pw(wp0); + Matrix m(d,d); + Vec b = typename CVec::Dimension()(d); + // Write the point coordinates in lines. + int i; + for(i=0; ++f!=e; ++i) { + WPoint const& wp=*f; + Point const& p=pdw(wp); + FT const& np = sdo(p) - pw(wp); + for(int j=0;j),(Point_tag),()); CGAL_KD_DEFAULT_FUNCTOR(Construct_ttag,(CartesianDKernelFunctors::Construct_weighted_point),(Weighted_point_tag,Point_tag),()); @@ -121,5 +198,8 @@ CGAL_KD_DEFAULT_FUNCTOR(Point_drop_weight_tag,(CartesianDKernelFunctors::Point_d CGAL_KD_DEFAULT_FUNCTOR(Point_weight_tag,(CartesianDKernelFunctors::Point_weight),(Weighted_point_tag,Point_tag),()); CGAL_KD_DEFAULT_FUNCTOR(Power_test_tag,(CartesianDKernelFunctors::Power_test),(Weighted_point_tag),(Power_test_raw_tag,Point_drop_weight_tag,Point_weight_tag)); CGAL_KD_DEFAULT_FUNCTOR(In_flat_power_test_tag,(CartesianDKernelFunctors::In_flat_power_test),(Weighted_point_tag),(In_flat_power_test_raw_tag,Point_drop_weight_tag,Point_weight_tag)); +CGAL_KD_DEFAULT_FUNCTOR(Power_distance_tag,(CartesianDKernelFunctors::Power_distance),(Weighted_point_tag,Point_tag),(Squared_distance_tag,Point_drop_weight_tag,Point_weight_tag)); +CGAL_KD_DEFAULT_FUNCTOR(Power_distance_to_point_tag,(CartesianDKernelFunctors::Power_distance_to_point),(Weighted_point_tag,Point_tag),(Squared_distance_tag,Point_drop_weight_tag,Point_weight_tag)); +CGAL_KD_DEFAULT_FUNCTOR(Power_center_tag,(CartesianDKernelFunctors::Power_center),(Weighted_point_tag,Point_tag),(Compute_point_cartesian_coordinate_tag,Construct_ttag,Construct_ttag,Point_dimension_tag,Squared_distance_to_origin_tag,Point_drop_weight_tag,Point_weight_tag,Power_distance_to_point_tag)); } // namespace CGAL #endif diff --git a/NewKernel_d/include/CGAL/NewKernel_d/functor_tags.h b/NewKernel_d/include/CGAL/NewKernel_d/functor_tags.h index f23239ce7b0..2dbedf4f8a6 100644 --- a/NewKernel_d/include/CGAL/NewKernel_d/functor_tags.h +++ b/NewKernel_d/include/CGAL/NewKernel_d/functor_tags.h @@ -216,6 +216,8 @@ namespace CGAL { CGAL_DECL_COMPUTE(Hyperplane_translation); CGAL_DECL_COMPUTE(Value_at); CGAL_DECL_COMPUTE(Point_weight); + CGAL_DECL_COMPUTE(Power_distance); + CGAL_DECL_COMPUTE(Power_distance_to_point); #undef CGAL_DECL_COMPUTE #define CGAL_DECL_ITER_OBJ(X,Y,Z,C) struct X##_tag {}; \ @@ -269,6 +271,7 @@ namespace CGAL { CGAL_DECL_CONSTRUCT(Construct_max_vertex,Point); CGAL_DECL_CONSTRUCT(Construct_circumcenter,Point); CGAL_DECL_CONSTRUCT(Point_drop_weight,Point); + CGAL_DECL_CONSTRUCT(Power_center,Weighted_point); #undef CGAL_DECL_CONSTRUCT #if 0 #define CGAL_DECL_ITER_CONSTRUCT(X,Y) struct X##_tag {}; \ diff --git a/NewKernel_d/test/NewKernel_d/Epick_d.cpp b/NewKernel_d/test/NewKernel_d/Epick_d.cpp index 0b53c527f9f..55f2220fcd8 100644 --- a/NewKernel_d/test/NewKernel_d/Epick_d.cpp +++ b/NewKernel_d/test/NewKernel_d/Epick_d.cpp @@ -14,6 +14,7 @@ #include #include #include +#include //typedef CGAL::Cartesian_base_d > K0; //typedef CGAL::Cartesian_base_d > KA; @@ -74,6 +75,7 @@ void test2(){ typedef typename K1::Ray_d R; typedef typename K1::Iso_box_d IB; typedef typename K1::Flat_orientation_d FO; + typedef typename K1::Weighted_point_d WP; //typedef K1::Construct_point CP; typedef typename K1::Construct_point_d CP; @@ -125,6 +127,13 @@ void test2(){ typedef typename K1::Construct_min_vertex_d CmV; typedef typename K1::Construct_max_vertex_d CMV; typedef typename K1::Compute_squared_radius_d SR; + typedef typename K1::Translated_point_d TP; + typedef typename K1::Power_center_d PC; + typedef typename K1::Power_distance_d PoD; + typedef typename K1::Weighted_point_d WP; + typedef typename K1::Construct_weighted_point_d CWP; + typedef typename K1::Point_drop_weight_d PDW; + typedef typename K1::Point_weight_d PW; CGAL_USE_TYPE(AT); CGAL_USE_TYPE(D); @@ -186,6 +195,12 @@ void test2(){ CmV cmv Kinit(construct_min_vertex_d_object); CMV cMv Kinit(construct_max_vertex_d_object); SR sr Kinit(compute_squared_radius_d_object); + TP tp Kinit(translated_point_d_object); + PC pc Kinit(power_center_d_object); + CWP cwp Kinit(construct_weighted_point_d_object); + PDW pdw Kinit(point_drop_weight_d_object); + PW pw Kinit(point_weight_d_object); + PoD pod Kinit(power_distance_d_object); CGAL_USE(bc); CGAL_USE(pol); @@ -193,11 +208,12 @@ void test2(){ CGAL_USE(cd); CGAL_USE(cli); CGAL_USE(cr); + using std::abs; P a=cp(3,4); assert(pd(a)==2); assert(pv(a)[1]==4); P b=vp(cv(5,6,7)); - assert(fabs(b[0]-5./7)<.0001); + assert(abs(b[0]-5./7)<.0001); assert(lc(b,a,1)); assert(!lc(a,b,0)); int rr[]={3,5,2}; @@ -211,8 +227,8 @@ void test2(){ assert(cl(a,c)==CGAL::SMALLER); assert(ll(b,c)); assert(cl(c,b)==CGAL::LARGER); - assert(fabs(m(a,c)[0]-3)<.0001); - assert(fabs(m(a,c)[1]-4.5)<.0001); + assert(abs(m(a,c)[0]-3)<.0001); + assert(abs(m(a,c)[1]-4.5)<.0001); P d=cp(r,r+3,CGAL::Homogeneous_tag()); S s=cs(c,d); std::cout << cc(a,1) << std::endl; @@ -267,9 +283,9 @@ void test2(){ assert(v.size()==1); assert(lr(tab3+0,tab3+2)==1); H h=ch(tab2+1,tab2+3,tab2[0]); - assert(fabs(va(h,x2)-1)<.0001); - assert(fabs(va(h,x3)-1)<.0001); - assert(fabs(va(h,x1)+1)<.0001); + assert(abs(va(h,x2)-1)<.0001); + assert(abs(va(h,x3)-1)<.0001); + assert(abs(va(h,x1)+1)<.0001); H h2=ch(tab2+1,tab2+3,x1,CGAL::ON_POSITIVE_SIDE); assert(hops(h2,x1)); assert(os(h2,x1)==CGAL::ON_POSITIVE_SIDE); @@ -330,20 +346,35 @@ void test2(){ Sp sp = csp(tabz+0,tabz+3); P cent0=cos(sp); P cent1=cos(tabz+0,tabz+3); - assert(fabs(cent0[0]-2)<.0001); - assert(fabs(cent0[1]+3)<.0001); - assert(fabs(cent1[0]-2)<.0001); - assert(fabs(cent1[1]+3)<.0001); - assert(fabs(sp.squared_radius()-25)<.0001); + assert(abs(cent0[0]-2)<.0001); + assert(abs(cent0[1]+3)<.0001); + assert(abs(cent1[0]-2)<.0001); + assert(abs(cent1[1]+3)<.0001); + assert(abs(sp.squared_radius()-25)<.0001); +#if 1 + // Fails for an exact kernel P psp0=ps(sp,0); P psp1=ps(sp,1); P psp2=ps(sp,2); assert(!ed(psp0,psp1)); assert(!ed(psp0,psp2)); assert(!ed(psp2,psp1)); - assert(fabs(sd(cent0,psp0)-25)<.0001); - assert(fabs(sd(cent0,psp1)-25)<.0001); - assert(fabs(sd(cent0,psp2)-25)<.0001); + assert(abs(sd(cent0,psp0)-25)<.0001); + assert(abs(sd(cent0,psp1)-25)<.0001); + assert(abs(sd(cent0,psp2)-25)<.0001); +#endif + P x2py1 = tp(x2,y1); + assert(x2py1[1]==-2); + WP tw[]={cwp(cp(5,0),1.5),cwp(cp(2,std::sqrt(3)),1),cwp(cp(2,-std::sqrt(3)),1)}; + WP xw=pc(tw+0,tw+3); + assert(abs(pod(xw,tw[0]))<.0001); + assert(abs(pod(xw,tw[1]))<.0001); + assert(abs(pod(xw,tw[2]))<.0001); + assert(pdw(xw)[0]<2.95); + assert(pdw(xw)[0]>2.5); + assert(pw(xw)<2.95); + assert(pw(xw)>2.5); + P tl=cp(2,5); P br=cp(4,-1); @@ -449,6 +480,7 @@ void test3(){ PD pd Kinit(point_dimension_d_object); AI ai Kinit(affinely_independent_d_object); SBDS sbds Kinit(side_of_bounded_sphere_d_object); + using std::abs; P a; // Triangulation needs this :-( a=cp(2,3,4); assert(pd(a)==3); @@ -472,7 +504,7 @@ void test3(){ std::cout << *i << ' '; std::cout << '\n'; P e=cp(-2,3,0); - assert(fabs(sd(e,a)-32)<.0001); + assert(abs(sd(e,a)-32)<.0001); P tab[]={a,b,c,d,e}; std::cout << po (&tab[0],tab+4) << ' '; std::cout << sos(&tab[0],tab+5) << ' '; diff --git a/Spatial_searching/include/CGAL/Search_traits_adapter.h b/Spatial_searching/include/CGAL/Search_traits_adapter.h index 3a0448888ad..4e1c18a80e8 100644 --- a/Spatial_searching/include/CGAL/Search_traits_adapter.h +++ b/Spatial_searching/include/CGAL/Search_traits_adapter.h @@ -91,16 +91,26 @@ public: struct Construct_cartesian_const_iterator_d: public Base_traits::Construct_cartesian_const_iterator_d{ PointPropertyMap ppmap; - using Base_traits::Construct_cartesian_const_iterator_d::operator(); - + Construct_cartesian_const_iterator_d(const typename Base_traits::Construct_cartesian_const_iterator_d& base, const PointPropertyMap& ppmap_) :Base_traits::Construct_cartesian_const_iterator_d(base), ppmap(ppmap_){} typename Base_traits::Cartesian_const_iterator_d operator()(const Point_with_info& p) const - { return this->operator() (get(ppmap,p)); } + { return Base_traits::Construct_cartesian_const_iterator_d::operator() (get(ppmap,p)); } typename Base_traits::Cartesian_const_iterator_d operator()(const Point_with_info& p, int) const - { return this->operator() (get(ppmap,p),0); } + { return Base_traits::Construct_cartesian_const_iterator_d::operator() (get(ppmap,p),0); } + + // These 2 additional operators forward the call to Base_traits. + // This is needed because of an undocumented requirement of + // Orthogonal_k_neighbor_search and Orthogonal_incremental_neighbor_search: + // Traits::Construct_cartesian_const_iterator should be callable + // on the query point type + typename Base_traits::Cartesian_const_iterator_d operator()(const typename Base_traits::Point_d& p) const + { return Base_traits::Construct_cartesian_const_iterator_d::operator() (p); } + + typename Base_traits::Cartesian_const_iterator_d operator()(const typename Base_traits::Point_d& p, int) const + { return Base_traits::Construct_cartesian_const_iterator_d::operator() (p,0); } }; struct Construct_iso_box_d: public Base::Construct_iso_box_d{ diff --git a/Tangential_complex/benchmark/Tangential_complex/CMakeLists.txt b/Tangential_complex/benchmark/Tangential_complex/CMakeLists.txt new file mode 100644 index 00000000000..7155f246c2b --- /dev/null +++ b/Tangential_complex/benchmark/Tangential_complex/CMakeLists.txt @@ -0,0 +1,77 @@ +# Created by the script cgal_create_cmake_script +# This is the CMake script for compiling a CGAL application. + + +project( Tangential_complex_benchmark ) + +cmake_minimum_required(VERSION 2.6.2) +if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER 2.6) + if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}" VERSION_GREATER 2.8.3) + cmake_policy(VERSION 2.8.4) + else() + cmake_policy(VERSION 2.6) + endif() +endif() + +# Creates a new CMake option, turned ON by default +option(ACTIVATE_MSVC_PRECOMPILED_HEADERS + "Activate precompiled headers in MSVC" + OFF) + +# Macro to add precompiled headers for MSVC +# This function does two things: +# 1. Enable precompiled headers on each file which is listed in "SourcesVar". +# 2. Add the content of "PrecompiledSource" (e.g. "StdAfx.cpp") to "SourcesVar". +MACRO(ADD_MSVC_PRECOMPILED_HEADER PrecompiledHeader PrecompiledSource SourcesVar) + IF(MSVC AND ACTIVATE_MSVC_PRECOMPILED_HEADERS) + GET_FILENAME_COMPONENT(PrecompiledBasename ${PrecompiledHeader} NAME_WE) + SET(Sources ${${SourcesVar}}) + + SET_SOURCE_FILES_PROPERTIES(${PrecompiledSource} + PROPERTIES COMPILE_FLAGS "/Yc\"${PrecompiledHeader}\"") + SET_SOURCE_FILES_PROPERTIES(${Sources} + PROPERTIES COMPILE_FLAGS "/Yu\"${PrecompiledHeaders}\" /FI\"${PrecompiledHeader}\"") + # Add precompiled header to SourcesVar + LIST(APPEND ${SourcesVar} ${PrecompiledSource}) + ENDIF(MSVC AND ACTIVATE_MSVC_PRECOMPILED_HEADERS) +ENDMACRO(ADD_MSVC_PRECOMPILED_HEADER) + +# The compiler might need more memory because of precompiled headers +if(MSVC AND ACTIVATE_MSVC_PRECOMPILED_HEADERS AND NOT(MSVC_VERSION LESS 1310)) + set(CGAL_C_FLAGS "${CGAL_C_FLAGS} /Zm1000") + set(CGAL_CXX_FLAGS "${CGAL_CXX_FLAGS} /Zm1000") +endif() + +find_package(CGAL QUIET COMPONENTS Core ) + +if ( CGAL_FOUND ) + include( ${CGAL_USE_FILE} ) + + find_package( TBB QUIET ) + + if( TBB_FOUND ) + include(${TBB_USE_FILE}) + list(APPEND CGAL_3RD_PARTY_LIBRARIES ${TBB_LIBRARIES}) + endif() + + + include( CGAL_CreateSingleSourceCGALProgram ) + + find_package(Eigen3 3.1.0) + if (EIGEN3_FOUND) + include( ${EIGEN3_USE_FILE} ) + include_directories (BEFORE "../../include") + include_directories (BEFORE "include") + + set (SOURCE_FILES "benchmark_tc.cpp") + ADD_MSVC_PRECOMPILED_HEADER("StdAfx.h" "StdAfx.cpp" SOURCE_FILES) + create_single_source_cgal_program( ${SOURCE_FILES} ) + + else() + message(STATUS "NOTICE: Some of the executables in this directory need Eigen 3.1 (or greater) and will not be compiled.") + endif() + +else() + message(STATUS "This program requires the CGAL library, and will not be compiled.") +endif() + diff --git a/Tangential_complex/benchmark/Tangential_complex/StdAfx.cpp b/Tangential_complex/benchmark/Tangential_complex/StdAfx.cpp new file mode 100644 index 00000000000..15668dcadef --- /dev/null +++ b/Tangential_complex/benchmark/Tangential_complex/StdAfx.cpp @@ -0,0 +1,2 @@ +// Build the precompiled headers. +#include "StdAfx.h" \ No newline at end of file diff --git a/Tangential_complex/benchmark/Tangential_complex/StdAfx.h b/Tangential_complex/benchmark/Tangential_complex/StdAfx.h new file mode 100644 index 00000000000..573345d0022 --- /dev/null +++ b/Tangential_complex/benchmark/Tangential_complex/StdAfx.h @@ -0,0 +1,14 @@ +#ifndef STDAFX_H +#define STDAFX_H + +// CGAL +#include +#include +#include +#include +#include +#include +#include +#include + +#endif //STDAFX_H \ No newline at end of file diff --git a/Tangential_complex/benchmark/Tangential_complex/XML_exporter.h b/Tangential_complex/benchmark/Tangential_complex/XML_exporter.h new file mode 100644 index 00000000000..158d3d648d4 --- /dev/null +++ b/Tangential_complex/benchmark/Tangential_complex/XML_exporter.h @@ -0,0 +1,236 @@ +// Copyright (c) 2012 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 +// + +#include +#include +#include +#include +#include + +template +class Simple_XML_exporter +{ +public: + typedef value_type Value_type; + typedef std::vector Element; + typedef std::map Element_with_map; + typedef std::vector List_of_elements; + + Simple_XML_exporter( + const std::string &list_name, + const std::string &element_name, + const std::vector &subelement_names, + bool add_timestamp = true) + : m_list_name(list_name), + m_element_name(element_name), + m_subelement_names(subelement_names), + m_add_timestamp(add_timestamp) + {} + + bool add_element(const Element &element) + { + if (element.size() == m_subelement_names.size()) + { + m_list_of_elements.push_back(element); + return true; + } + else + { + std::cerr << "ERROR: element.size() == m_subelement_names.size()" << std::endl; + return false; + } + } + + bool add_element(Element_with_map &element) + { + Element elt; + + std::vector::const_iterator + it_subelement_name = m_subelement_names.begin(); + std::vector::const_iterator + it_subelement_name_end = m_subelement_names.end(); + for ( ; it_subelement_name != it_subelement_name_end ; ++it_subelement_name) + { + elt.push_back(element[*it_subelement_name]); + } + + return add_element(elt); + } + + bool export_to_xml(const std::string &filename) const + { + std::ofstream xmlfile; + xmlfile.open(filename.c_str()); + xmlfile << "" << std::endl; + xmlfile << "<" << m_list_name << ">" << std::endl; + + typename List_of_elements::const_iterator it_element = m_list_of_elements.begin(); + typename List_of_elements::const_iterator it_element_end = m_list_of_elements.end(); + for (int id = 1 ; it_element != it_element_end ; ++it_element, ++id) + { + xmlfile << " <" << m_element_name << ">" << std::endl; + std::vector::const_iterator + it_subelement_name = m_subelement_names.begin(); + std::vector::const_iterator + it_subelement_name_end = m_subelement_names.end(); + + if (m_add_timestamp) + xmlfile << " " << time(NULL) << " " << std::endl; + + for (int i = 0 ; + it_subelement_name != it_subelement_name_end ; + ++it_subelement_name, ++i) + { + xmlfile + << " <" << *it_subelement_name << "> " + << (*it_element)[i] + << " " << std::endl; + } + xmlfile << " " << std::endl; + } + + xmlfile << "" << std::endl; + xmlfile.close(); + return 0; + + } + +protected: + std::string m_list_name; + std::string m_element_name; + std::vector m_subelement_names; + List_of_elements m_list_of_elements; + bool m_add_timestamp; +}; + + + + + +template +class Streaming_XML_exporter +{ +public: + typedef value_type Value_type; + typedef std::vector Element; + typedef std::map Element_with_map; + typedef std::vector List_of_elements; + + Streaming_XML_exporter( + const std::string &filename, + const std::string &list_name, + const std::string &element_name, + const std::vector &subelement_names, + bool add_timestamp = true) + : m_list_name(list_name), + m_element_name(element_name), + m_subelement_names(subelement_names), + m_add_timestamp(add_timestamp) + { + m_xml_fstream.open(filename.c_str()); + if (m_xml_fstream.good()) + { + m_xml_fstream << "" << std::endl; + m_xml_fstream << "<" << m_list_name << ">" << std::endl; + } + else + { + std::cerr << "Could not open file '" << filename << "'." << std::endl; + } + } + + virtual ~Streaming_XML_exporter() + { + close_file(); + } + + void close_file() + { + m_xml_fstream.close(); + } + + bool add_element(const Element &element) + { + if (element.size() == m_subelement_names.size()) + { + m_xml_fstream << " <" << m_element_name << ">" << std::endl; + std::vector::const_iterator + it_subelement_name = m_subelement_names.begin(); + std::vector::const_iterator + it_subelement_name_end = m_subelement_names.end(); + + if (m_add_timestamp) + { + m_xml_fstream << " " << time(NULL) << " " << std::endl; + } + + for (int i = 0 ; + it_subelement_name != it_subelement_name_end ; + ++it_subelement_name, ++i) + { + m_xml_fstream + << " <" << *it_subelement_name << "> " + << element[i] + << " " << std::endl; + } + m_xml_fstream << " " << std::endl; + + // Save current pointer position + std::ofstream::streampos pos = m_xml_fstream.tellp(); + // Close the XML file (temporarily) so that the XML file is always correct + m_xml_fstream << "" << std::endl; + // Restore the pointer position so that the next "add_element" will overwrite + // the end of the file + m_xml_fstream.seekp(pos); + + m_xml_fstream.flush(); + return true; + } + else + { + std::cerr << "ERROR: element.size() == m_subelement_names.size()" << std::endl; + return false; + } + } + + bool add_element(Element_with_map &element) + { + Element elt; + + std::vector::const_iterator + it_subelement_name = m_subelement_names.begin(); + std::vector::const_iterator + it_subelement_name_end = m_subelement_names.end(); + for ( ; it_subelement_name != it_subelement_name_end ; ++it_subelement_name) + { + elt.push_back(element[*it_subelement_name]); + } + + return add_element(elt); + } + +protected: + std::ofstream m_xml_fstream; + std::string m_list_name; + std::string m_element_name; + std::vector m_subelement_names; + bool m_add_timestamp; +}; diff --git a/Tangential_complex/benchmark/Tangential_complex/benchmark_script.txt b/Tangential_complex/benchmark/Tangential_complex/benchmark_script.txt new file mode 100644 index 00000000000..7e243e29c03 --- /dev/null +++ b/Tangential_complex/benchmark/Tangential_complex/benchmark_script.txt @@ -0,0 +1,109 @@ +#--------------------------------------------------------------------------------------------------------------------------------------------------------- +# Input PARAM1 PARAM2 PARAM3 NUM_P AMB INTR SPARSITY PERTURB ADD_HDIM COLLAPSE FIX_TIME_LIMIT NUM_ITERATIONS +#--------------------------------------------------------------------------------------------------------------------------------------------------------- + +#---------------------------------------------------------------- Alpha TC tests ------------------------------------------------------------------------ +#generate_sphere_d 1 1 - 50 2 1 0.005 N Y N 3 1 #OK +#generate_torus_d N - - 15 2 1 0.05 N Y N 10 1 +#generate_sphere_d 0.302 3 - 50 3 2 0.005 N Y N 60 1 #OK +#generate_sphere_d 0.302 2 - 300 2 1 0.005 N Y N 60 1 +#generate_torus_3D 2 1 N 200 3 2 0.05 N Y N 600 1 #OK => tetraedra only +#data/fandisk.txt - - - 0 3 2 0.005 Y Y N 5 1 +generate_torus_d N 1 - 50 4 2 0.05 N Y N 3 1 #NOT OK: various errors +#generate_torus_d Y 1 - 2500 4 2 0.05 N Y N 3 1 #NOT OK +#generate_torus_d N - - 30 6 3 0.05 N Y N 300 1 +#generate_moment_curve 0 1 - 10 3 1 0.005 N Y N 60 1 +#generate_two_spheres_d 3 4 - 500 3 2 0.05 N Y N 10 1 #OK +#generate_klein_bottle_4D 40 15 - 500 4 2 0.2 N Y N 60 1 + +#---------------------------------------------------------------- Fixed-alpha TC tests ------------------------------------------------------------------------ +#generate_sphere_d 0.302 - - 70 2 1 0.005 N N N 60 1 +#generate_sphere_d 0.304 0 - 100 3 2 0.05 N N N 60 1 +#generate_sphere_d 3 0 - 100 4 3 0.05 N N N 60 1 +#generate_sphere_d 3 0 - 1000 5 4 0.05 N N N 60 1 +#generate_two_spheres_d 3 4 - 1000 3 2 0.05 N N N 10 1 +#generate_two_spheres_d 3 10 - 1000 4 3 0.05 N N N 10 1 +#generate_two_spheres_d 3 10 - 2000 5 4 0.05 N N N 10 1 +#generate_3sphere_and_circle_d 3 - - 100000 5 3 0.05 N N N 7000 1 +#generate_plane - - - 100 4 3 0.005 N N N 3000 1 +#generate_plane - - - 10 5 4 0.005 N N N 3000 1 +#generate_klein_bottle_4D 40 15 - 500 4 2 0.2 N N N 60 1 +#generate_klein_bottle_4D 40 15 - 2000 4 2 0.2 N N N 60 1 +#generate_klein_bottle_4D 8 5 - 5000 4 2 0.2 N N N 60 1 #Takes forever +#generate_torus_3D 2 1 Y 150 3 2 0.05 N N N 600 1 +#generate_torus_3D 2 1 N 100000 3 2 0.55 N N N 600 1 +#generate_torus_d N - - 18 2 1 0.01 N N N 30 1 +#generate_torus_d N - - 50000 2 1 0.7 N N N 30 1 +#generate_torus_d Y - - 2 2 1 0 N N N 30 1 +#generate_torus_d Y - - 15 4 2 0.05 N N N 30 1 +#generate_torus_d N 0 - 150 4 2 0.05 N N N 10 1 +#generate_torus_d N - - 20000 4 2 1.0 N N N 30 1 +#generate_torus_d Y 0 - 5000 6 3 0.05 N N N 300 1 +#generate_torus_d N - - 20000 6 3 0.05 N N N 60 1 +#generate_torus_d N - - 100000 6 3 0.05 N N N 3000 1 +#generate_torus_d N - - 1000000 6 3 0.3 N N N 3000 1 +#generate_torus_d Y - - 1000 8 4 0.05 N N N 60 1 +#data/SO3_10000.txt - - - 0 9 3 0.1 N N N 60 1 + +#---------------------------------------------------------- Spatial search benchmarking -------------------------------------------------------------- +#generate_torus_3D 2 1 Y 10000 3 2 0 Y N N 600 1 +#data/buddha_100kv.txt - - - 0 3 2 0 N Y N 120 1 +#generate_torus_d N - - 10000 30 15 0 Y N N 3600 1 +#generate_torus_d N - - 100000 12 6 0 Y N N 3600 1 +#data/SO3_50000.txt - - - 0 9 3 0 Y N N 60 1 +#data/Cy8.txt - - - 0 24 2 0 N Y N 60 1 +#generate_sphere_d 0.5 - - 10000 2 1 0 N N Y 60 1 +#generate_sphere_d 0.5 - - 10000 3 2 0 N N Y 60 1 +#generate_sphere_d 0.5 - - 10000 4 3 0 N N Y 60 1 +#generate_sphere_d 0.5 - - 10000 5 4 0 N N Y 60 1 +#generate_sphere_d 0.5 - - 10000 6 5 0 N N Y 60 1 +#generate_sphere_d 0.5 - - 10000 7 6 0 N N Y 60 1 + +#---------------------------------------------------------- Very small cases for Debug mode -------------------------------------------------------------- +#generate_sphere_d 4 - - 20 3 2 0.05 N Y N 60 1 +#generate_sphere_d 3 - - 70 3 2 0.05 N Y N 60 1 +#generate_sphere_d 3 - - 1000 3 2 0.05 N Y N 60 1 +#generate_sphere_d 3 - - 70 4 3 0.05 N Y N 60 1 +#generate_sphere_d 3 - - 70 5 4 0.05 N Y N 60 1 +#generate_klein_bottle_variant_5D 4 3 - 70 5 2 0.05 N Y N 60 1 +#data/SO3_10000.txt - - - 0 9 3 0.7 N Y N 60 1 +#generate_moment_curve 0 1 - 30 3 1 0.005 N Y N 60 1 + +#------------------------------------------------------------------ From files -------------------------------------------------------------------------- +#data/SO3_50000.txt - - - 0 9 3 0.05 Y Y N 60 1 +#data/SO3_10000.txt - - - 0 9 3 0.05 Y N N 60 1 +#data/cube3D_eps_0.1.txt - - - 0 3 2 0.05 N Y N 3000 1 +#data/cube4D_eps_0.1.txt - - - 0 4 3 0.05 N Y N 3000 1 +#data/cube5D_eps_0.1.txt - - - 0 5 4 0.05 N Y N 3000 1 +#data/Cy8.txt - - - 0 24 2 0.1 N Y N 60 1 +#data/Kl.txt - - - 0 5 2 0.05 N Y N 60 1 +#data/S3.txt - - - 0 4 3 0.05 N Y N 60 1 + +#---------------------------------------------------------------------- 3D meshes ----------------------------------------------------------------------- +#data/buddha_100kv.txt - - - 0 3 2 0.005 N Y N 120 1 +#data/fandisk.txt - - - 0 3 2 0.01 N Y N 60 1 +#data/fertility.txt - - - 0 3 2 0.4 N Y N 60 1 +#data/bunny.txt - - - 0 3 2 0.5 N Y N 60 1 +#data/blob.txt - - - 0 3 2 0.01 N Y N 60 1 +#data/3holes.txt - - - 0 3 2 0.01 N Y N 60 1 +#data/785_hand_2500v.txt - - - 0 3 2 0.01 N Y N 60 1 +#data/785_hand_50kv.txt - - - 0 3 2 0.01 N Y N 60 1 +#data/bumpy_sphere.txt - - - 0 3 2 0.01 N Y N 60 1 + +#----------------------------------------------------------- Generated point sets ----------------------------------------------------------------------- +#generate_sphere_d 3 - - 4 3 2 0.05 N Y N 3000 1 +#generate_sphere_d 3 - - 30000 2 1 0.005 N Y N 3000 1 +#generate_sphere_d 3 - - 30000 3 2 0.005 N Y N 3000 1 +#generate_sphere_d 3 - - 30000 4 3 0.05 N Y N 3000 1 +#generate_sphere_d 3 0 - 3000 3 2 0.005 N Y N 60 1 +#generate_sphere_d 3 4 - 3000 3 2 0.005 N Y N 60 1 +#generate_sphere_d 3 7 - 3000 3 2 0.005 N Y N 60 1 +#generate_plane - - - 30000 3 2 0.005 N Y N 3000 1 +#generate_moment_curve 0 1 - 30000 6 1 0.005 N Y N 60 1 +#generate_klein_bottle_4D 4 3 - 1000 4 2 0.1 N Y N 60 1 +#generate_klein_bottle_variant_5D 4 3 - 30000 5 2 0.05 N Y N 60 1 +#generate_klein_bottle_4D 8 5 - 5000 4 2 0.2 N Y N 60 1 #Takes forever + +#----------------------------------------------------------- Performance testing ------------------------------------------------------------------------ +# TC: 5.55 / 1st fix step : 0.2 +#data/fertility.txt - - - 0 3 2 0.1 N Y N 100 1 \ No newline at end of file diff --git a/Tangential_complex/benchmark/Tangential_complex/benchmark_tc.cpp b/Tangential_complex/benchmark/Tangential_complex/benchmark_tc.cpp new file mode 100644 index 00000000000..299d1a0458a --- /dev/null +++ b/Tangential_complex/benchmark/Tangential_complex/benchmark_tc.cpp @@ -0,0 +1,781 @@ +//#undef CGAL_LINKED_WITH_TBB // CJTODO TEMP + +// Without TBB_USE_THREADING_TOOL Intel Inspector XE will report false positives in Intel TBB +// (http://software.intel.com/en-us/articles/compiler-settings-for-threading-error-analysis-in-intel-inspector-xe/) +#ifdef _DEBUG +# define TBB_USE_THREADING_TOOL +#endif + +#include +#include +#include +#include +#include + +#include "../../test/Tangential_complex/testing_utilities.h" + +#include +#include +#include + +#include +#include +#include +#include + +#ifdef CGAL_LINKED_WITH_TBB +# include +#endif +#include "XML_exporter.h" +#define CGAL_TC_EXPORT_PERFORMANCE_DATA +#define CGAL_TC_SET_PERFORMANCE_DATA(value_name, value) \ + XML_perf_data::set(value_name, value); + +const char * const BENCHMARK_SCRIPT_FILENAME = "benchmark_script.txt"; + +typedef CGAL::Epick_d Kernel; +typedef Kernel::FT FT; +typedef Kernel::Point_d Point; +typedef CGAL::Tangential_complex< + Kernel, CGAL::Dynamic_dimension_tag, + CGAL::Parallel_tag> TC; + +//#define JUST_BENCHMARK_SPATIAL_SEARCH // CJTODO: test +//#define CHECK_IF_ALL_SIMPLICES_ARE_IN_THE_AMBIENT_DELAUNAY +#define TC_INPUT_STRIDES 3 // only take one point every TC_INPUT_STRIDES points +#define TC_NO_EXPORT + +#ifdef JUST_BENCHMARK_SPATIAL_SEARCH +std::ofstream spatial_search_csv_file("benchmark_spatial_search.csv"); +#endif + +class XML_perf_data +{ +public: + typedef Streaming_XML_exporter XML_exporter; + + XML_perf_data(const std::string &filename) + : m_xml(filename, "ContainerPerformance", "Perf", + construct_subelements_names()) + {} + + virtual ~XML_perf_data() + { + } + + static XML_perf_data &get() + { + static XML_perf_data singleton(build_filename()); + return singleton; + } + + template + static void set(const std::string &name, Value_type value) + { + get().set_data(name, value); + } + + static void commit() + { + get().commit_current_element(); + } + +protected: + static std::string build_filename() + { + std::stringstream sstr; + sstr << "perf_logs/Performance_log_" << time(0) << ".xml"; + return sstr.str(); + } + + static std::vector construct_subelements_names() + { + std::vector subelements; + subelements.push_back("Input"); + subelements.push_back("Intrinsic_dim"); + subelements.push_back("Ambient_dim"); + subelements.push_back("Sparsity"); + subelements.push_back("Num_points_in_input"); + subelements.push_back("Num_points"); + subelements.push_back("Initial_num_inconsistent_local_tr"); + subelements.push_back("Best_num_inconsistent_local_tr"); + subelements.push_back("Final_num_inconsistent_local_tr"); + subelements.push_back("Init_time"); + subelements.push_back("Comput_time"); + subelements.push_back("Perturb_successful"); + subelements.push_back("Perturb_time"); + subelements.push_back("Perturb_steps"); + subelements.push_back("Add_higher_dim_simpl_time"); + subelements.push_back("Result_pure_pseudomanifold"); + subelements.push_back("Result_num_wrong_dim_simplices"); + subelements.push_back("Result_num_wrong_number_of_cofaces"); + subelements.push_back("Result_num_unconnected_stars"); + subelements.push_back("Info"); + + return subelements; + } + + void set_data(const std::string &name, const std::string &value) + { + m_current_element[name] = value; + } + + template + void set_data(const std::string &name, Value_type value) + { + std::stringstream sstr; + sstr << value; + set_data(name, sstr.str()); + } + + void commit_current_element() + { + m_xml.add_element(m_current_element); + m_current_element.clear(); + } + + XML_exporter m_xml; + XML_exporter::Element_with_map m_current_element; +}; + +class Test_dim +{ +public: + Test_dim( + int min_allowed_dim = 0, + int max_allowed_dim = std::numeric_limits::max()) + : m_min_allowed_dim(min_allowed_dim), m_max_allowed_dim(max_allowed_dim) + {} + + template + bool operator()(Simplex const& s) + { + return s.size() - 1 >= m_min_allowed_dim + && s.size() - 1 <= m_max_allowed_dim; + } + +private: + int m_min_allowed_dim; + int m_max_allowed_dim; +}; + +template +bool export_to_off( + TC const& tc, + std::string const& input_name_stripped, + std::string const& suffix, + bool color_inconsistencies = false, + typename TC::Simplicial_complex const* p_complex = NULL, + std::set > const *p_simpl_to_color_in_red = NULL, + std::set > const *p_simpl_to_color_in_green = NULL, + std::set > const *p_simpl_to_color_in_blue = NULL) +{ +#ifdef TC_NO_EXPORT + return true; +#endif + if (tc.intrinsic_dimension() <= 3) + { + std::stringstream output_filename; + output_filename << "output/" << input_name_stripped << "_" + << tc.intrinsic_dimension() << "_in_R" + << tc.ambient_dimension() << "_" + << tc.number_of_vertices() << "v" + << suffix << ".off"; + std::ofstream off_stream(output_filename.str().c_str()); + + if (p_complex) + { +#ifndef TC_NO_EXPORT + tc.export_to_off( + *p_complex, off_stream, + p_simpl_to_color_in_red, + p_simpl_to_color_in_green, + p_simpl_to_color_in_blue); +#endif + } + else + { +/*#ifdef CGAL_ALPHA_TC + TC::Simplicial_complex complex; + tc.export_TC(complex, false); + tc.export_to_off( + complex, off_stream, + p_simpl_to_color_in_red, + p_simpl_to_color_in_green, + p_simpl_to_color_in_blue); +#else*/ + tc.export_to_off( + off_stream, color_inconsistencies, + p_simpl_to_color_in_red, + p_simpl_to_color_in_green, + p_simpl_to_color_in_blue); +//#endif + } + return true; + } + return false; +} + +void make_tc(std::vector &points, + int intrinsic_dim, + double sparsity = 0., + bool perturb = true, + bool add_high_dim_simpl = false, + bool collapse = false, + double time_limit_for_perturb = 0., + const char *input_name = "tc") +{ + Kernel k; + + // CJTODO TEMP TEST + //TC::Simplicial_complex compl; + //{std::size_t ss[] = {0, 1, 2}; compl.add_simplex(std::set(ss, ss + 3)); } + //{std::size_t ss[] = {0, 2, 3}; compl.add_simplex(std::set(ss, ss + 3)); } + //{std::size_t ss[] = {0, 3, 4}; compl.add_simplex(std::set(ss, ss + 3)); } + //{std::size_t ss[] = {0, 4, 1}; compl.add_simplex(std::set(ss, ss + 3)); } + //{std::size_t ss[] = {0, 5, 6}; compl.add_simplex(std::set(ss, ss + 3)); } + //compl.is_pure_pseudomanifold(2, 7, false, 10); + + //TC::Simplicial_complex compl; + //{std::size_t ss[] = {0, 1, 2, 5}; compl.add_simplex(std::set(ss, ss + 4)); } + //{std::size_t ss[] = {0, 2, 3, 5}; compl.add_simplex(std::set(ss, ss + 4)); } + //{std::size_t ss[] = {0, 3, 4, 5}; compl.add_simplex(std::set(ss, ss + 4)); } + //{std::size_t ss[] = {0, 4, 1, 5}; compl.add_simplex(std::set(ss, ss + 4)); } + //{std::size_t ss[] = {0, 1, 2, 6}; compl.add_simplex(std::set(ss, ss + 4)); } + //{std::size_t ss[] = {0, 2, 3, 6}; compl.add_simplex(std::set(ss, ss + 4)); } + //{std::size_t ss[] = {0, 3, 4, 6}; compl.add_simplex(std::set(ss, ss + 4)); } + //{std::size_t ss[] = {0, 4, 1, 6}; compl.add_simplex(std::set(ss, ss + 4)); } + //{std::size_t ss[] = {0, 4, 7, 8}; compl.add_simplex(std::set(ss, ss + 4)); } + //compl.is_pure_pseudomanifold(3, 9, false, 10); + // /CJTODO TEMP TEST + +#ifdef JUST_BENCHMARK_SPATIAL_SEARCH + benchmark_spatial_search(points, k, spatial_search_csv_file); + return; +#endif + + //=========================================================================== + // Init + //=========================================================================== + Wall_clock_timer t; + + // Get input_name_stripped + std::string input_name_stripped(input_name); + size_t slash_index = input_name_stripped.find_last_of('/'); + if (slash_index == std::string::npos) + slash_index = input_name_stripped.find_last_of('\\'); + if (slash_index == std::string::npos) + slash_index = 0; + else + ++slash_index; + input_name_stripped = input_name_stripped.substr( + slash_index, input_name_stripped.find_last_of('.') - slash_index); + + int ambient_dim = k.point_dimension_d_object()(*points.begin()); + + CGAL_TC_SET_PERFORMANCE_DATA("Num_points_in_input", points.size()); + +#ifdef USE_ANOTHER_POINT_SET_FOR_TANGENT_SPACE_ESTIM + std::vector points_not_sparse = points; +#endif + + //=========================================================================== + // Sparsify point set if requested + //=========================================================================== + if (sparsity != 0.) + { + std::size_t num_points_before = points.size(); + points = sparsify_point_set(k, points, sparsity*sparsity); + std::cerr << "Number of points before/after sparsification: " + << num_points_before << " / " << points.size() << std::endl; + } + + CGAL_TC_SET_PERFORMANCE_DATA("Sparsity", sparsity); + CGAL_TC_SET_PERFORMANCE_DATA("Num_points", points.size()); + + //=========================================================================== + // Compute Tangential Complex + //=========================================================================== + +#ifdef USE_ANOTHER_POINT_SET_FOR_TANGENT_SPACE_ESTIM + TC tc(points.begin(), points.end(), sparsity, intrinsic_dim, + points_not_sparse.begin(), points_not_sparse.end(), k); +#else + TC tc(points.begin(), points.end(), sparsity, intrinsic_dim, k); +#endif + + double init_time = t.elapsed(); t.reset(); + + tc.compute_tangential_complex(); + double computation_time = t.elapsed(); t.reset(); + + //=========================================================================== + // CJTODO TEMP + //=========================================================================== + /*{ + TC::Simplicial_complex complex; + int max_dim = tc.export_TC(complex, false); + complex.display_stats(); + + std::stringstream output_filename; + output_filename << "output/" << input_name_stripped << "_" << intrinsic_dim + << "_in_R" << ambient_dim << "_ALPHA_COMPLEX.off"; + std::ofstream off_stream(output_filename.str().c_str()); + tc.export_to_off(complex, off_stream); + + // Collapse + complex.collapse(max_dim); + { + std::stringstream output_filename; + output_filename << "output/" << input_name_stripped << "_" << intrinsic_dim + << "_in_R" << ambient_dim << "_AFTER_COLLAPSE.off"; + std::ofstream off_stream(output_filename.str().c_str()); + tc.export_to_off(complex, off_stream); + } + std::size_t num_wrong_dim_simplices, + num_wrong_number_of_cofaces, + num_unconnected_stars; + bool pure_manifold = complex.is_pure_pseudomanifold( + intrinsic_dim, tc.number_of_vertices(), false, 1, + &num_wrong_dim_simplices, &num_wrong_number_of_cofaces, + &num_unconnected_stars); + complex.display_stats(); + } + return;*/ + // CJTODO TEMP =========================== + +#ifdef CHECK_IF_ALL_SIMPLICES_ARE_IN_THE_AMBIENT_DELAUNAY + if (ambient_dim <= 4) + tc.check_if_all_simplices_are_in_the_ambient_delaunay(); +#endif + + //tc.check_correlation_between_inconsistencies_and_fatness(); + + //=========================================================================== + // Export to OFF + //=========================================================================== + t.reset(); + double export_before_time = + (export_to_off(tc, input_name_stripped, "_BEFORE_FIX") ? t.elapsed() : -1); + t.reset(); + + unsigned int num_perturb_steps = 0; + double perturb_time = -1; + double export_after_perturb_time = -1.; + CGAL::Fix_inconsistencies_status perturb_ret = CGAL::FIX_NOT_PERFORMED; + if (perturb) + { + //========================================================================= + // Try to fix inconsistencies by perturbing points + //========================================================================= + t.reset(); + std::size_t initial_num_inconsistent_local_tr; + std::size_t best_num_inconsistent_local_tr; + std::size_t final_num_inconsistent_local_tr; + perturb_ret = tc.fix_inconsistencies_using_perturbation( + num_perturb_steps, initial_num_inconsistent_local_tr, + best_num_inconsistent_local_tr, final_num_inconsistent_local_tr, + time_limit_for_perturb); + perturb_time = t.elapsed(); t.reset(); + + CGAL_TC_SET_PERFORMANCE_DATA("Initial_num_inconsistent_local_tr", + initial_num_inconsistent_local_tr); + CGAL_TC_SET_PERFORMANCE_DATA("Best_num_inconsistent_local_tr", + best_num_inconsistent_local_tr); + CGAL_TC_SET_PERFORMANCE_DATA("Final_num_inconsistent_local_tr", + final_num_inconsistent_local_tr); + + //tc.check_correlation_between_inconsistencies_and_fatness(); + + //========================================================================= + // Export to OFF + //========================================================================= + t.reset(); + bool exported = export_to_off(tc, input_name_stripped, "_AFTER_FIX", true); + double export_after_perturb_time = (exported ? t.elapsed() : -1); + t.reset(); + + //std::string fn = "output/inc_stars/"; + //fn += input_name_stripped; + //tc.export_inconsistent_stars_to_OFF_files(fn); + } + else + { + CGAL_TC_SET_PERFORMANCE_DATA("Initial_num_inconsistent_local_tr", "N/A"); + CGAL_TC_SET_PERFORMANCE_DATA("Best_num_inconsistent_local_tr", "N/A"); + CGAL_TC_SET_PERFORMANCE_DATA("Final_num_inconsistent_local_tr", "N/A"); + } + + int max_dim = -1; + double fix2_time = -1; + double export_after_fix2_time = -1.; + TC::Simplicial_complex complex; + if (add_high_dim_simpl) + { + //========================================================================= + // Try to fix inconsistencies by adding higher-dimension simplices + //========================================================================= + t.reset(); + // Try to solve the remaining inconstencies +#ifdef CGAL_ALPHA_TC + tc.solve_inconsistencies_using_alpha_TC(); +#else + tc.check_and_solve_inconsistencies_by_adding_higher_dim_simplices(); +#endif + fix2_time = t.elapsed(); t.reset(); + max_dim = tc.export_TC(complex, false); + /*std::set > not_delaunay_simplices; + if (ambient_dim <= 4) + { + tc.check_if_all_simplices_are_in_the_ambient_delaunay( + &complex, true, ¬_delaunay_simplices); + }*/ + + //========================================================================= + // Export to OFF + //========================================================================= + t.reset(); + bool exported = export_to_off( + tc, input_name_stripped, "_AFTER_FIX2", false, &complex); + double export_after_fix2_time = (exported ? t.elapsed() : -1); + t.reset(); + } + else + { + max_dim = tc.export_TC(complex, false); + } + + complex.display_stats(); + + // Export to OFF with higher-dim simplices colored + std::set > higher_dim_simplices; + complex.get_simplices_matching_test( + Test_dim(intrinsic_dim + 1), + std::inserter(higher_dim_simplices, higher_dim_simplices.begin())); + export_to_off( + tc, input_name_stripped, "_BEFORE_COLLAPSE", false, &complex, + &higher_dim_simplices); + + //=========================================================================== + // Collapse + //=========================================================================== + if (collapse) + complex.collapse(max_dim); + + //=========================================================================== + // Is the result a pure pseudomanifold? + //=========================================================================== + std::size_t num_wrong_dim_simplices, + num_wrong_number_of_cofaces, + num_unconnected_stars; + std::set > wrong_dim_simplices; + std::set > wrong_number_of_cofaces_simplices; + std::set > unconnected_stars_simplices; + bool is_pure_pseudomanifold = complex.is_pure_pseudomanifold( + intrinsic_dim, tc.number_of_vertices(), false, 1, + &num_wrong_dim_simplices, &num_wrong_number_of_cofaces, + &num_unconnected_stars, + &wrong_dim_simplices, &wrong_number_of_cofaces_simplices, + &unconnected_stars_simplices); + + // Stats about the simplices + complex.display_stats(); + + //=========================================================================== + // Export to OFF + //=========================================================================== + t.reset(); + bool exported = export_to_off( + tc, input_name_stripped, "_AFTER_COLLAPSE", false, &complex, + &wrong_dim_simplices, &wrong_number_of_cofaces_simplices, + &unconnected_stars_simplices); + std::cerr + << " OFF colors:" << std::endl + << " * Red: wrong dim simplices" << std::endl + << " * Green: wrong number of cofaces simplices" << std::endl + << " * Blue: not-connected stars" << std::endl; + double export_after_collapse_time = (exported ? t.elapsed() : -1); + t.reset(); + + //=========================================================================== + // Display info + //=========================================================================== + + std::cerr << std::endl + << "================================================" << std::endl + << "Number of vertices: " << tc.number_of_vertices() << std::endl + << "Pure pseudomanifold: " << (is_pure_pseudomanifold ? "YES" : "NO") << std::endl + << "Computation times (seconds): " << std::endl + << " * Tangential complex: " << init_time + computation_time << std::endl + << " - Init + kd-tree = " << init_time << std::endl + << " - TC computation = " << computation_time << std::endl + << " * Export to OFF (before perturb): " << export_before_time << std::endl + << " * Fix inconsistencies 1: " << perturb_time + << " (" << num_perturb_steps << " steps) ==> " + << (perturb_ret == CGAL::TC_FIXED ? "FIXED" : "NOT fixed") << std::endl + << " * Fix inconsistencies 2: " << fix2_time << std::endl + << " * Export to OFF (after perturb): " << export_after_perturb_time << std::endl + << " * Export to OFF (after fix2): "<< export_after_fix2_time << std::endl + << " * Export to OFF (after collapse): " + << export_after_collapse_time << std::endl + << "================================================" << std::endl + << std::endl; + + //=========================================================================== + // Export info + //=========================================================================== + CGAL_TC_SET_PERFORMANCE_DATA("Init_time", init_time); + CGAL_TC_SET_PERFORMANCE_DATA("Comput_time", computation_time); + CGAL_TC_SET_PERFORMANCE_DATA("Perturb_successful", + (perturb_ret == CGAL::TC_FIXED ? "Y" : "N")); + CGAL_TC_SET_PERFORMANCE_DATA("Perturb_time", perturb_time); + CGAL_TC_SET_PERFORMANCE_DATA("Perturb_steps", num_perturb_steps); + CGAL_TC_SET_PERFORMANCE_DATA("Add_higher_dim_simpl_time", fix2_time); + CGAL_TC_SET_PERFORMANCE_DATA("Result_pure_pseudomanifold", + (is_pure_pseudomanifold ? "Y" : "N")); + CGAL_TC_SET_PERFORMANCE_DATA("Result_num_wrong_dim_simplices", + num_wrong_dim_simplices); + CGAL_TC_SET_PERFORMANCE_DATA("Result_num_wrong_number_of_cofaces", + num_wrong_number_of_cofaces); + CGAL_TC_SET_PERFORMANCE_DATA("Result_num_unconnected_stars", + num_unconnected_stars); + CGAL_TC_SET_PERFORMANCE_DATA("Info", ""); +} + +int main() +{ + CGAL::set_error_behaviour(CGAL::ABORT); + +#ifdef CGAL_LINKED_WITH_TBB +# ifdef _DEBUG + int num_threads = 1; +# else + int num_threads = 10; +# endif +#endif + + unsigned int seed = static_cast(time(NULL)); + CGAL::default_random = CGAL::Random(seed); + std::cerr << "Random seed = " << seed << std::endl; + + std::ifstream script_file; + script_file.open(BENCHMARK_SCRIPT_FILENAME); + // Script? + // Script file format: each line gives + // - Filename (point set) or "generate_XXX" (point set generation) + // - Ambient dim + // - Intrinsic dim + // - Number of iterations with these parameters + if (script_file.is_open()) + { + int i = 1; +#ifdef CGAL_LINKED_WITH_TBB +# ifdef BENCHMARK_WITH_1_TO_MAX_THREADS + for(num_threads = 1 ; + num_threads <= tbb::task_scheduler_init::default_num_threads() ; + ++num_threads) +# endif +#endif + /*for (Concurrent_mesher_config::get().num_work_items_per_batch = 5 ; + Concurrent_mesher_config::get().num_work_items_per_batch < 100 ; + Concurrent_mesher_config::get().num_work_items_per_batch += 5)*/ + { +#ifdef CGAL_LINKED_WITH_TBB + tbb::task_scheduler_init init( + num_threads > 0 ? num_threads : tbb::task_scheduler_init::automatic); +#endif + + std::cerr << "Script file '" << BENCHMARK_SCRIPT_FILENAME << "' found." << std::endl; + script_file.seekg(0); + while (script_file.good()) + { + std::string line; + std::getline(script_file, line); + if (line.size() > 1 && line[0] != '#') + { + boost::replace_all(line, "\t", " "); + boost::trim_all(line); + std::cerr << std::endl << std::endl; + std::cerr << "*****************************************" << std::endl; + std::cerr << "******* " << line << std::endl; + std::cerr << "*****************************************" << std::endl; + std::stringstream sstr(line); + + std::string input; + std::string param1; + std::string param2; + std::string param3; + std::size_t num_points; + int ambient_dim; + int intrinsic_dim; + double sparsity; + char perturb, add_high_dim_simpl, collapse; + double time_limit_for_perturb; + int num_iteration; + sstr >> input; + sstr >> param1; + sstr >> param2; + sstr >> param3; + sstr >> num_points; + sstr >> ambient_dim; + sstr >> intrinsic_dim; + sstr >> sparsity; + sstr >> perturb; + sstr >> add_high_dim_simpl; + sstr >> collapse; + sstr >> time_limit_for_perturb; + sstr >> num_iteration; + + for (int j = 0 ; j < num_iteration ; ++j) + { + std::string input_stripped = input; + size_t slash_index = input_stripped.find_last_of('/'); + if (slash_index == std::string::npos) + slash_index = input_stripped.find_last_of('\\'); + if (slash_index == std::string::npos) + slash_index = 0; + else + ++slash_index; + input_stripped = input_stripped.substr( + slash_index, input_stripped.find_last_of('.') - slash_index); + + CGAL_TC_SET_PERFORMANCE_DATA("Input", input_stripped); + CGAL_TC_SET_PERFORMANCE_DATA("Ambient_dim", ambient_dim); + CGAL_TC_SET_PERFORMANCE_DATA("Intrinsic_dim", intrinsic_dim); +#ifdef CGAL_LINKED_WITH_TBB + CGAL_TC_SET_PERFORMANCE_DATA( + "Num_threads", + (num_threads == -1 ? tbb::task_scheduler_init::default_num_threads() : num_threads)); +#else + CGAL_TC_SET_PERFORMANCE_DATA("Num_threads", "N/A"); +#endif + + std::cerr << std::endl << "TC #" << i << "..." << std::endl; + +#ifdef CGAL_TC_PROFILING + Wall_clock_timer t_gen; +#endif + + std::vector points; + + if (input == "generate_moment_curve") + { + points = generate_points_on_moment_curve( + num_points, ambient_dim, + std::atof(param1.c_str()), std::atof(param2.c_str())); + } + else if (input == "generate_plane") + { + points = generate_points_on_plane( + num_points, intrinsic_dim, ambient_dim); + } + else if (input == "generate_sphere_d") + { + points = generate_points_on_sphere_d( + num_points, ambient_dim, + std::atof(param1.c_str()), // radius + std::atof(param2.c_str())); // radius_noise_percentage + } + else if (input == "generate_two_spheres_d") + { + points = generate_points_on_two_spheres_d( + num_points, ambient_dim, + std::atof(param1.c_str()), + std::atof(param2.c_str()), + std::atof(param3.c_str())); + } + else if (input == "generate_3sphere_and_circle_d") + { + CGAL_assertion(intrinsic_dim == 3); + CGAL_assertion(ambient_dim == 5); + points = generate_points_on_3sphere_and_circle( + num_points, + std::atof(param1.c_str())); + } + else if (input == "generate_torus_3D") + { + points = generate_points_on_torus_3D( + num_points, + std::atof(param1.c_str()), + std::atof(param2.c_str()), + param3 == "Y"); + } + else if (input == "generate_torus_d") + { + points = generate_points_on_torus_d( + num_points, + intrinsic_dim, + param1 == "Y", // uniform + std::atof(param2.c_str())); // radius_noise_percentage + } + else if (input == "generate_klein_bottle_3D") + { + points = generate_points_on_klein_bottle_3D( + num_points, + std::atof(param1.c_str()), std::atof(param2.c_str())); + } + else if (input == "generate_klein_bottle_4D") + { + points = generate_points_on_klein_bottle_4D( + num_points, + std::atof(param1.c_str()), std::atof(param2.c_str())); + } + else if (input == "generate_klein_bottle_variant_5D") + { + points = generate_points_on_klein_bottle_variant_5D( + num_points, + std::atof(param1.c_str()), std::atof(param2.c_str())); + } + else + { + load_points_from_file( + input, std::back_inserter(points)/*, 600*/); + } + +#ifdef CGAL_TC_PROFILING + std::cerr << "Point set generated/loaded in " << t_gen.elapsed() + << " seconds." << std::endl; +#endif + + if (!points.empty()) + { +#if defined(TC_INPUT_STRIDES) && TC_INPUT_STRIDES > 1 + auto p = points | boost::adaptors::strided(TC_INPUT_STRIDES); // CJTODO C++11 (auto) + std::vector points(p.begin(), p.end()); + std::cerr << "****************************************\n" + << "WARNING: taking 1 point every " << TC_INPUT_STRIDES + << " points.\n" + << "****************************************\n"; +#endif + make_tc(points, intrinsic_dim, sparsity, // strided: CJTODO TEMP + perturb=='Y', add_high_dim_simpl=='Y', collapse=='Y', + time_limit_for_perturb, input.c_str()); + + std::cerr << "TC #" << i++ << " done." << std::endl; + std::cerr << std::endl << "---------------------------------" + << std::endl << std::endl; + } + else + { + std::cerr << "TC #" << i++ << ": no points loaded." << std::endl; + } + + XML_perf_data::commit(); + } + } + } + script_file.seekg(0); + script_file.clear(); + } + + script_file.close(); + } + // Or not script? + else + { + std::cerr << "Script file '" << BENCHMARK_SCRIPT_FILENAME << "' NOT found." << std::endl; + } + + system("pause"); + return 0; +} diff --git a/Tangential_complex/dont_submit b/Tangential_complex/dont_submit new file mode 100644 index 00000000000..e69de29bb2d diff --git a/Tangential_complex/include/CGAL/Tangential_complex.h b/Tangential_complex/include/CGAL/Tangential_complex.h new file mode 100644 index 00000000000..d430d3d1c3a --- /dev/null +++ b/Tangential_complex/include/CGAL/Tangential_complex.h @@ -0,0 +1,4105 @@ +// 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 +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +# include + +#include // CJTODO TEMP + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CGAL_LINKED_WITH_TBB +# include +# include +# include +#endif + +//#define CGAL_TC_EXPORT_NORMALS // Only for 3D surfaces (k=2, d=3) + +//static std::ofstream csv_stream("output/stats.csv"); // CJTODO TEMP + +//CJTODO: debug +//#define CGAL_TC_COMPUTE_TANGENT_PLANES_FOR_SPHERE_2 +//#define CGAL_TC_COMPUTE_TANGENT_PLANES_FOR_SPHERE_3 +//#define CGAL_TC_COMPUTE_TANGENT_PLANES_FOR_TORUS_D +//#define CGAL_TC_ADD_NOISE_TO_TANGENT_SPACE +//#define BETTER_EXPORT_FOR_FLAT_TORUS + +namespace CGAL { + +using namespace Tangential_complex_; + +enum Fix_inconsistencies_status { + TC_FIXED = 0, TIME_LIMIT_REACHED, FIX_NOT_PERFORMED }; + +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, // ambiant dimension + typename DimensionTag, // intrinsic dimension + typename Concurrency_tag = CGAL::Parallel_tag, +#ifdef CGAL_ALPHA_TC + // For the alpha-TC, the dimension of the RT is variable + // => we need to force + 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 > > + > + > +#else + 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 > > + > + > +#endif +> +class Tangential_complex +{ + typedef typename Kernel::FT FT; + typedef typename Kernel::Point_d Point; + typedef typename Kernel::Weighted_point_d Weighted_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 Basis Tangent_space_basis; + typedef Basis Orthogonal_space_basis; + + typedef std::vector Points; +#if defined(CGAL_LINKED_WITH_TBB) && defined(CGAL_TC_GLOBAL_REFRESH) + typedef tbb::mutex Mutex_for_perturb; + typedef Vector Translation_for_perturb; + typedef std::vector > Weights; + #ifdef CGAL_TC_PERTURB_WEIGHT + typedef std::vector > Weights_memory; + #endif +#else + typedef Vector Translation_for_perturb; + typedef std::vector Weights; + #ifdef CGAL_TC_PERTURB_WEIGHT + typedef std::vector Weights_memory; + #endif +#endif + typedef std::vector Translations_for_perturb; + + 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; + }; + +public: + typedef typename std::vector TS_container; + typedef typename std::vector OS_container; +private: + typedef typename std::vector Tr_container; + typedef typename std::vector Vectors; + + // An Incident_simplex is the list of the vertex indices + // except the center vertex + typedef std::set Incident_simplex; + typedef std::set Indexed_simplex; + typedef std::vector Star; + typedef std::vector Stars_container; + + // For the priority queues of solve_inconsistencies_using_alpha_TC + struct Simplex_and_alpha + { + Simplex_and_alpha() {} + Simplex_and_alpha( + std::size_t center_point_index, + Incident_simplex const& simplex, // NOT including "center_point_index" + FT squared_alpha, + Vector const& thickening_vector) + : m_center_point_index(center_point_index), + m_simplex(simplex), + m_squared_alpha(squared_alpha), + m_thickening_vector(thickening_vector) + {} + + // For the priority queue + bool operator>(Simplex_and_alpha const& other) const + { + return m_squared_alpha > other.m_squared_alpha; + } + + std::size_t m_center_point_index; // P + Incident_simplex m_simplex; // Missing simplex (does NOT includes P) + FT m_squared_alpha; + Vector m_thickening_vector; // (P, Cq) + }; + + // For transform_iterator + static const Tr_point &vertex_handle_to_point(Tr_vertex_handle vh) + { + return vh->point(); + } + template + static const P &vertex_handle_to_point(VH vh) + { + return vh->point(); + } + +public: + typedef Tangential_complex_::Simplicial_complex Simplicial_complex; + + /// Constructor for a range of points + template + Tangential_complex(InputIterator first, InputIterator last, + double sparsity, int intrinsic_dimension, +#ifdef USE_ANOTHER_POINT_SET_FOR_TANGENT_SPACE_ESTIM + InputIterator first_for_tse, InputIterator last_for_tse, +#endif + const Kernel &k = Kernel() + ) + : m_k(k), + m_intrinsic_dim(intrinsic_dimension), + m_half_sparsity(0.5*sparsity), + m_sq_half_sparsity(m_half_sparsity*m_half_sparsity), + m_ambient_dim(k.point_dimension_d_object()(*first)), + m_points(first, last), + m_weights(m_points.size(), FT(0)) +#ifdef CGAL_TC_PERTURB_WEIGHT + , m_weights_memory() +#endif +#if defined(CGAL_LINKED_WITH_TBB) && defined(CGAL_TC_PERTURB_POSITION) \ + && defined(CGAL_TC_GLOBAL_REFRESH) + , m_p_perturb_mutexes(NULL) +#endif + , m_points_ds(m_points) + , m_are_tangent_spaces_computed(m_points.size(), false) + , m_tangent_spaces(m_points.size(), Tangent_space_basis()) +#ifdef CGAL_TC_EXPORT_NORMALS + , m_orth_spaces(m_points.size(), Orthogonal_space_basis()) +#endif +#ifdef USE_ANOTHER_POINT_SET_FOR_TANGENT_SPACE_ESTIM + , m_points_for_tse(first_for_tse, last_for_tse) + , m_points_ds_for_tse(m_points_for_tse) +#endif + { + if (sparsity <= 0.) + std::cerr << "!Warning! Sparsity should be > 0\n"; + } + + /// Destructor + ~Tangential_complex() + { +#if defined(CGAL_LINKED_WITH_TBB) && defined(CGAL_TC_PERTURB_POSITION) \ + && defined(CGAL_TC_GLOBAL_REFRESH) + delete [] m_p_perturb_mutexes; +#endif + } + + int intrinsic_dimension() const + { + return m_intrinsic_dim; + } + int ambient_dimension() const + { + return m_ambient_dim; + } + + std::size_t number_of_vertices() const + { + return m_points.size(); + } + + void set_weights(const Weights& weights) + { + m_weights = weights; +#ifdef CGAL_TC_PERTURB_WEIGHT + m_weights_memory = weights; +#endif + } + + void set_tangent_planes(const TS_container& tangent_spaces +#ifdef CGAL_TC_EXPORT_NORMALS + , const OS_container& orthogonal_spaces +#endif + ) + { +#ifdef CGAL_TC_PERTURB_TANGENT_SPACE + std::cerr << "Cannot use CGAL_TC_PERTURB_TANGENT_SPACE and set " + << " tangent spaces manually at the same time" << std::endl; + std::exit(EXIT_FAILURE); +#endif +#ifdef CGAL_TC_EXPORT_NORMALS + CGAL_assertion(m_points.size() == tangent_spaces.size() + && m_points.size() == orthogonal_spaces.size()); +#else + CGAL_assertion(m_points.size() == tangent_spaces.size()); +#endif + m_tangent_spaces = tangent_spaces; +#ifdef CGAL_TC_EXPORT_NORMALS + m_orth_spaces = orthogonal_spaces; +#endif + for(std::size_t i=0; i::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); + } + +#if defined(CGAL_TC_PROFILING) && defined(CGAL_LINKED_WITH_TBB) + std::cerr << "Tangential complex computed in " << t.elapsed() + << " seconds." << std::endl; +#endif + } + + void estimate_intrinsic_dimension() const + { + // Kernel functors + typename Kernel::Compute_coordinate_d coord = + m_k.compute_coordinate_d_object(); + + std::vector sum_eigen_values(m_ambient_dim, FT(0)); + std::size_t num_points_for_pca = static_cast( + std::pow(BASE_VALUE_FOR_PCA, m_intrinsic_dim)); + + 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) + { + 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_ambient_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_ambient_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 + for (int i = 0 ; i < m_ambient_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) + ); + } + // 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 refreshed in " << t.elapsed() + << " seconds." << std::endl; +#endif + } + + // time_limit in seconds: 0 = no fix to do, < 0 = no time limit + Fix_inconsistencies_status fix_inconsistencies_using_perturbation( + unsigned int &num_steps, + std::size_t &initial_num_inconsistent_local_tr, + std::size_t &best_num_inconsistent_local_tr, + std::size_t &final_num_inconsistent_local_tr, + double time_limit = -1.) + { + if (time_limit == 0.) + return TIME_LIMIT_REACHED; + + Wall_clock_timer t; + +#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 TC_FIXED; + } +#endif // CGAL_TC_SHOW_DETAILED_STATS_FOR_INCONSISTENCIES + + bool done = false; + best_num_inconsistent_local_tr = m_triangulations.size(); + num_steps = 0; + while (!done) + { + std::size_t num_inconsistent_local_tr = 0; + +#ifdef CGAL_TC_PROFILING + Wall_clock_timer t_fix_step; +#endif + + // Parallel +#if defined(CGAL_LINKED_WITH_TBB) && defined(CGAL_TC_GLOBAL_REFRESH) + if (boost::is_convertible::value) + { + tbb::combinable num_inconsistencies; + tbb::parallel_for( + tbb::blocked_range(0, m_triangulations.size()), + Try_to_solve_inconsistencies_in_a_local_triangulation( + *this, num_inconsistencies) + ); + num_inconsistent_local_tr = + num_inconsistencies.combine(std::plus()); + } + // Sequential + else +#endif // CGAL_LINKED_WITH_TBB + { + 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_fix_step.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_using_perturbation:" << 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 stars: " + << num_inconsistent_local_tr + << " (" << 100. * num_inconsistent_local_tr / m_points.size() << "%)" + << std::endl + << std::endl + << " * AFTER fix_inconsistencies_using_perturbation:" << 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_after.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_using_perturbation():\n" + << " * " << m_points.size() << " vertices" << std::endl + << " * " << num_inconsistent_local_tr + << " (" << 100. * num_inconsistent_local_tr / m_points.size() << "%)" + << " inconsistent stars encountered" << std::endl + << "==========================================================" + << std::endl; +# endif +#endif // CGAL_TC_SHOW_DETAILED_STATS_FOR_INCONSISTENCIES + + if (num_steps == 0) + initial_num_inconsistent_local_tr = num_inconsistent_local_tr; + + if (num_inconsistent_local_tr < best_num_inconsistent_local_tr) + best_num_inconsistent_local_tr = num_inconsistent_local_tr; + + final_num_inconsistent_local_tr = num_inconsistent_local_tr; + + ++num_steps; + done = (num_inconsistent_local_tr == 0); + if (!done && time_limit > 0. && t.elapsed() > time_limit) + { +#ifdef CGAL_TC_VERBOSE + std::cerr << "Time limit reached." << std::endl; +#endif + return TIME_LIMIT_REACHED; + } + } + + return TC_FIXED; + } + + // Return a pair + std::pair number_of_inconsistent_simplices( +#ifdef CGAL_TC_VERBOSE + bool verbose = true +#else + bool verbose = false +#endif + ) const + { + std::size_t num_simplices = 0; + std::size_t num_inconsistent_simplices = 0; + // For each triangulation + for (std::size_t idx = 0 ; idx < m_points.size() ; ++idx) + { + // For each cell + Star::const_iterator it_inc_simplex = m_stars[idx].begin(); + Star::const_iterator it_inc_simplex_end = m_stars[idx].end(); + for ( ; it_inc_simplex != it_inc_simplex_end ; ++it_inc_simplex) + { + // Don't export infinite cells + if (is_infinite(*it_inc_simplex)) + continue; + + Indexed_simplex c = *it_inc_simplex; + c.insert(idx); // Add the missing index + + if (!is_simplex_consistent(c)) + ++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); + } + + // Return the max dimension of the simplices + int export_TC(Simplicial_complex &complex, + bool export_infinite_simplices = false) const + { + int max_dim = -1; + + // For each triangulation + for (std::size_t idx = 0 ; idx < m_points.size() ; ++idx) + { + // For each cell of the star + Star::const_iterator it_inc_simplex = m_stars[idx].begin(); + Star::const_iterator it_inc_simplex_end = m_stars[idx].end(); + for ( ; it_inc_simplex != it_inc_simplex_end ; ++it_inc_simplex) + { + // Don't export infinite cells + if (!export_infinite_simplices && is_infinite(*it_inc_simplex)) + continue; + + Indexed_simplex c = *it_inc_simplex; + if (static_cast(c.size()) > max_dim) + max_dim = static_cast(c.size()); + // Add the missing center vertex + c.insert(idx); + complex.add_simplex(c); + } + } + return max_dim; + } + + void check_and_solve_inconsistencies_by_adding_higher_dim_simplices() + { + // CJTODO: parallel_for??? + for (std::size_t idx = 0 ; idx < m_triangulations.size() ; ++idx) + { + bool inconsistencies_found = false; + do + { + Star::const_iterator it_inc_simplex = m_stars[idx].begin(); + Star::const_iterator it_inc_simplex_end = m_stars[idx].end(); + for ( ; it_inc_simplex != it_inc_simplex_end ; ++it_inc_simplex) + { + inconsistencies_found = + check_and_solve_inconsistencies_by_adding_higher_dim_simplices( + idx, *it_inc_simplex); + + // m_stars[idx] has been modified, let's start again + // CJTODO: optimize? + if (inconsistencies_found) + break; + } + } while (inconsistencies_found); + } + + // CJTODO TEMP + std::pair stats_after = + number_of_inconsistent_simplices(false); + std::cerr << "AFTER check_and_solve_inconsistencies_by_adding_higher_dim_simplices():\n" + << " - 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_after.first << "%" + << std::endl; + } + + +#ifdef CGAL_ALPHA_TC +private: + + // Look in the star of point "i" for inconsistent simplices, compute + // an approximation of alpha for each one and push it into the priority + // queues. + // Returns the number of inconsistent simplices found + template + std::size_t fill_pqueues_for_alpha_tc(std::size_t i, PQueues &pqueues) + { + // Kernel/traits functors + typename Kernel::Difference_of_points_d k_diff_points = + m_k.difference_of_points_d_object(); + typename Kernel::Squared_length_d k_sqlen = + m_k.squared_length_d_object(); + typename Tr_traits::Construct_weighted_point_d constr_wp = + m_triangulations[0].tr().geom_traits().construct_weighted_point_d_object(); + + std::size_t num_inconsistent_simplices = 0; + + // For each cell + Star::const_iterator it_inc_simplex = m_stars[i].begin(); + Star::const_iterator it_inc_simplex_end = m_stars[i].end(); + for ( ; it_inc_simplex != it_inc_simplex_end ; ++it_inc_simplex) + { + Incident_simplex const& s = *it_inc_simplex; + + // Don't check infinite cells + if (is_infinite(s)) + continue; + + int simplex_dim = static_cast(s.size()); + + // P: points whose star does not contain "s" + std::vector P; + is_simplex_consistent(i, s, std::back_inserter(P), true); + + if (!P.empty()) + { + ++num_inconsistent_simplices; + + Triangulation const& q_tr = m_triangulations[i].tr(); + Indexed_simplex full_simplex = s; + full_simplex.insert(i); + for (std::vector::const_iterator it_p = P.begin(), + it_p_end = P.end() ; it_p != it_p_end ; ++it_p) + { + // star(p) does not contain "s" + std::size_t p = *it_p; + + // Compute the intersection between aff(Voronoi_cell(s)) and Tq + boost::optional intersection = + compute_aff_of_voronoi_face_and_tangent_subspace_intersection( + q_tr.current_dimension(), + project_points_and_compute_weights( + full_simplex, m_tangent_spaces[i], q_tr.geom_traits()), + m_tangent_spaces[i], + q_tr.geom_traits()); + + // CJTODO: replace with an assertion? + if (!intersection) + { + std::cerr << "ERROR fill_pqueues_for_alpha_tc: " + "aff(Voronoi_cell(s)) and Tq do not intersect.\n"; + continue; + } + + // The following computations are done in the Euclidian space + Point inters_global = unproject_point( + constr_wp(*intersection, 0), m_tangent_spaces[i], + q_tr.geom_traits()); + Vector thickening_v = k_diff_points( + inters_global, compute_perturbed_point(p)); + FT squared_alpha = k_sqlen(thickening_v); + + // We insert full_simplex \ p + Incident_simplex is = full_simplex; + is.erase(p); + pqueues[simplex_dim - m_intrinsic_dim].push( + Simplex_and_alpha(p, is, squared_alpha, thickening_v)); + + // CJTODO TEMP + /*std::cerr + << "Just inserted the simplex "; + std::copy(full_simplex.begin(), full_simplex.end(), + std::ostream_iterator(std::cerr, ", ")); + std::cerr << "into pqueue (i = " << i << ")\n";*/ + } + } + } + + return num_inconsistent_simplices; + } + +public: + void solve_inconsistencies_using_alpha_TC() + { +#ifdef CGAL_TC_PROFILING + Wall_clock_timer t; +#endif + +#ifdef CGAL_TC_VERBOSE + std::cerr << "Fixing inconsistencies using alpha TC..." << std::endl; +#endif + + //------------------------------------------------------------------------- + // 1. Fill priority queues + //------------------------------------------------------------------------- + typedef std::priority_queue, + std::greater > AATC_pq; + typedef std::vector PQueues; + +#ifdef CGAL_TC_PROFILING + Wall_clock_timer t_pq; +#endif + + // One queue per dimension, from intrinsic dim (index = 0) to + // ambiant dim (index = ambiant - intrinsic dim) + PQueues pqueues; + pqueues.resize(m_ambient_dim - m_intrinsic_dim + 1); + + std::size_t num_inconsistent_simplices = 0; + // For each triangulation + for (std::size_t i = 0 ; i < m_points.size() ; ++i) + num_inconsistent_simplices += fill_pqueues_for_alpha_tc(i, pqueues); + +#ifdef CGAL_TC_VERBOSE + std::cerr + << "Num inconsistent simplices found when filling the priority queues: " + << num_inconsistent_simplices; +# ifdef CGAL_TC_PROFILING + std::cerr << " (" << t_pq.elapsed() << " s)" << std::endl; +# endif + std::cerr << std::endl; +#endif + + //------------------------------------------------------------------------- + // 2. Thicken tangent spaces to solve inconsistencies + //------------------------------------------------------------------------- + + // While there's elements to treat in the queues + for(;;) + { +#ifdef CGAL_TC_PROFILING + Wall_clock_timer t_one_fix; +#endif + + // Pick the simplex with the lowest dimension and the lowest alpha + Simplex_and_alpha saa; + bool found_saa = false; + for (PQueues::iterator it_pq = pqueues.begin(), it_pq_end = pqueues.end(); + !found_saa && it_pq != it_pq_end ; ++it_pq) + { + while (!found_saa && !it_pq->empty()) + { + saa = it_pq->top(); + it_pq->pop(); + + // Check if the simplex is still missing in the star + if (!is_simplex_in_star(saa.m_center_point_index, saa.m_simplex)) + found_saa = true; + } + } + + // If all the queues are empty, we're done! + if (!found_saa) + break; + + Tangent_space_basis &tangent_basis = + m_tangent_spaces[saa.m_center_point_index]; + + // If we're already in the ambiant dim, we just need to thicken the + // tangent subspace a bit more (see below) + if (tangent_basis.dimension() < m_ambient_dim) + { + // Otherwise, let's thicken the tangent space + bool vec_added = add_vector_to_orthonormal_basis( + tangent_basis, + saa.m_thickening_vector, + m_k, + FT(0), /* sqlen_threshold: default value */ + true /* add_to_thickening_vectors */); + + // CJTODO TEMP + if (!vec_added) + { + std::cerr << "FYI: the thickening vector was not added " + "to the basis since it was linearly dependent to it.\n"; + } + } + + // Update the alpha+/- values + tangent_basis.update_alpha_values_of_thickening_vectors( + saa.m_thickening_vector, m_k); + + // Recompute triangulation & star + compute_tangent_triangulation(saa.m_center_point_index); + +#ifdef CGAL_TC_PERFORM_EXTRA_CHECKS + if (!is_simplex_in_star(saa.m_center_point_index, saa.m_simplex)) + { + std::cerr + << "FAILED in solve_inconsistencies_using_alpha_TC(): " + << "simplex " << saa.m_center_point_index << ", "; + std::copy(saa.m_simplex.begin(), saa.m_simplex.end(), + std::ostream_iterator(std::cerr, ", ")); + std::cerr << " not added in star #" + << saa.m_center_point_index + << " (basis dim = " << tangent_basis.dimension() +# ifdef CGAL_TC_PROFILING + << " - " << t_one_fix.elapsed() << " s" +# endif + << ")\n"; + + Indexed_simplex full_s = saa.m_simplex; + full_s.insert(saa.m_center_point_index); + + // CJTODO TEMP + bool is_this_simplex_somewhere = false; + for(auto ii : saa.m_simplex) // CJTODO C++11 + { + Indexed_simplex z = full_s; + z.erase(ii); + if (is_simplex_in_star(ii, z)) + { + is_this_simplex_somewhere = true; + std::cerr << "The simplex is in star #" << ii << std::endl; + break; + } + } + if (!is_this_simplex_somewhere) + std::cerr << "WOW The simplex is NOWHERE!\n"; + + // CJTODO TEMP + if (m_ambient_dim <= 3) + { + if (is_simplex_in_the_ambient_delaunay(full_s)) + std::cerr << "The simplex is in the ambiant Delaunay." << std::endl; + else + std::cerr << "The simplex is NOT in the ambiant Delaunay." << std::endl; + + std::cerr << "Checking simplices of the star #" + << saa.m_center_point_index << std::endl; + Star const& star = m_stars[saa.m_center_point_index]; + for (Star::const_iterator is = star.begin(), is_end = star.end() ; + is != is_end ; ++is) + { + if (is_simplex_in_the_ambient_delaunay(*is)) + std::cerr << "The simplex is in the ambiant Delaunay." << std::endl; + else + { + std::cerr << "The simplex is NOT in the ambiant Delaunay." << std::endl; + for(auto ii : *is) // CJTODO C++11 + perturb(ii); + } + } + } + + std::cerr << "Perturbing the points..." << std::endl; + perturb(saa.m_center_point_index); + for(auto ii : saa.m_simplex) // CJTODO C++11 + perturb(ii); + refresh_tangential_complex(); + pqueues.clear(); + pqueues.resize(m_ambient_dim - m_intrinsic_dim + 1); + + std::size_t num_inconsistent_simplices = 0; + // For each triangulation + for (std::size_t i = 0 ; i < m_points.size() ; ++i) + num_inconsistent_simplices += fill_pqueues_for_alpha_tc(i, pqueues); + +#ifdef CGAL_TC_VERBOSE + std::cerr + << "Num inconsistent simplices found when filling the priority queues: " + << num_inconsistent_simplices << std::endl; +#endif + } + // CJTODO TEMP + else + { + std::cerr << "SUCCESS: " + << saa.m_center_point_index << ", "; + std::copy(saa.m_simplex.begin(), saa.m_simplex.end(), + std::ostream_iterator(std::cerr, ", ")); + std::cerr << " added in star #" + << saa.m_center_point_index + << " (basis dim = " << tangent_basis.dimension() +# ifdef CGAL_TC_PROFILING + << " - " << t_one_fix.elapsed() << " s" +# endif + << ")\n"; + //check_if_all_simplices_are_in_the_ambient_delaunay(); + } +#endif + + // It's not a problem if entries are duplicated in the pqueues + // since there's a check when we pop elements + fill_pqueues_for_alpha_tc(saa.m_center_point_index, pqueues); + } + +#ifdef CGAL_TC_PROFILING + std::cerr << "Tangential complex fixed in " << t.elapsed() + << " seconds." << std::endl; +#endif + } +#endif // CGAL_ALPHA_TC + + std::ostream &export_to_off( + const Simplicial_complex &complex, std::ostream & os, + std::set const *p_simpl_to_color_in_red = NULL, + std::set const *p_simpl_to_color_in_green = NULL, + std::set const *p_simpl_to_color_in_blue = NULL) + const + { + return export_to_off( + os, false, p_simpl_to_color_in_red, p_simpl_to_color_in_green, + p_simpl_to_color_in_blue, &complex); + } + + std::ostream &export_to_off( + std::ostream & os, bool color_inconsistencies = false, + std::set const *p_simpl_to_color_in_red = NULL, + std::set const *p_simpl_to_color_in_green = NULL, + std::set const *p_simpl_to_color_in_blue = NULL, + const Simplicial_complex *p_complex = NULL) const + { + if (m_points.empty()) + return os; + + if (m_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 (m_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 (m_intrinsic_dim < 1 || m_intrinsic_dim > 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); + if (p_complex) + { + export_simplices_to_off( + *p_complex, output, num_simplices, p_simpl_to_color_in_red, + p_simpl_to_color_in_green, p_simpl_to_color_in_blue); + } + else + { + export_simplices_to_off( + output, num_simplices, color_inconsistencies, p_simpl_to_color_in_red, + p_simpl_to_color_in_green, p_simpl_to_color_in_blue); + } + +#ifdef CGAL_TC_EXPORT_NORMALS + os << "N"; +#endif + + os << "OFF \n" + << num_vertices << " " + << num_simplices << " " + << "0 \n" + << output.str(); + + return os; + } + + // Return a pair + void export_inconsistent_stars_to_OFF_files( + std::string const& filename_base) const + { + std::size_t num_simplices = 0; + std::size_t num_inconsistent_simplices = 0; + // For each triangulation + for (std::size_t idx = 0 ; idx < m_points.size() ; ++idx) + { + // We build a SC along the way in case it's inconsistent + Simplicial_complex sc; + // For each cell + bool is_inconsistent = false; + Star::const_iterator it_inc_simplex = m_stars[idx].begin(); + Star::const_iterator it_inc_simplex_end = m_stars[idx].end(); + for ( ; it_inc_simplex != it_inc_simplex_end ; + ++it_inc_simplex) + { + // Skip infinite cells + if (is_infinite(*it_inc_simplex)) + continue; + + Indexed_simplex c = *it_inc_simplex; + c.insert(idx); // Add the missing index + + sc.add_simplex(c); + + // If we do not already know this star is inconsistent, test it + if (!is_inconsistent && !is_simplex_consistent(c)) + is_inconsistent = true; + } + + if (is_inconsistent) + { + // Export star to OFF file + std::stringstream output_filename; + output_filename << filename_base << "_" << idx << ".off"; + std::ofstream off_stream(output_filename.str().c_str()); + export_to_off(sc, off_stream); + } + } + } + + // Expensive! + bool is_simplex_in_the_ambient_delaunay( + Indexed_simplex const& s) const + { + //------------------------------------------------------------------------- + // Build the ambient Delaunay triangulation + // Then save its simplices into "amb_dt_simplices" + //------------------------------------------------------------------------- + + typedef Regular_triangulation_euclidean_traits RT_Traits; + typedef Regular_triangulation< + RT_Traits, + Triangulation_data_structure< + typename RT_Traits::Dimension, + Triangulation_vertex + > > RT; + typedef typename RT::Vertex_handle RT_VH; + typedef typename RT::Finite_full_cell_const_iterator FFC_it; + + RT ambient_dt(m_ambient_dim); + for (std::size_t i=0; idata() = i; + } + + for (FFC_it cit = ambient_dt.finite_full_cells_begin() ; + cit != ambient_dt.finite_full_cells_end() ; ++cit ) + { + Indexed_simplex simplex; + for (int i = 0 ; i < m_ambient_dim + 1 ; ++i) + simplex.insert(cit->vertex(i)->data()); + + if (std::includes(simplex.begin(), simplex.end(), + s.begin(), s.end())) + return true; + } + + return false; + } + + bool check_if_all_simplices_are_in_the_ambient_delaunay( + const Simplicial_complex *p_complex = NULL, + bool check_for_any_dimension_simplices = true, + std::set * incorrect_simplices = NULL) const + { + typedef Simplicial_complex::Simplex Simplex; + typedef Simplicial_complex::Simplex_range Simplex_range; + + if (m_points.empty()) + return true; + + typedef Regular_triangulation_euclidean_traits RT_Traits; + typedef Regular_triangulation< + RT_Traits, + Triangulation_data_structure< + typename RT_Traits::Dimension, + Triangulation_vertex + > > RT; + typedef typename RT::Vertex_handle RT_VH; + typedef typename RT::Finite_full_cell_const_iterator FFC_it; + + //------------------------------------------------------------------------- + // Build the ambient Delaunay triangulation + // Then save its simplices into "amb_dt_simplices" + //------------------------------------------------------------------------- + + RT ambient_dt(m_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 ) + { + int lowest_dim = + (check_for_any_dimension_simplices ? 1 : m_intrinsic_dim); + int highest_dim = + (check_for_any_dimension_simplices ? m_ambient_dim : m_intrinsic_dim); + + for (int dim = lowest_dim ; dim <= highest_dim ; ++dim) + { + CGAL::Combination_enumerator combi(dim + 1, 0, m_ambient_dim + 1); + + for ( ; !combi.finished() ; ++combi) + { + Simplex simplex; + for (int i = 0 ; i < dim + 1 ; ++i) + simplex.insert(cit->vertex(combi[i])->data()); + + amb_dt_simplices.insert(simplex); + } + } + } + + //------------------------------------------------------------------------- + // If p_complex is NULL, parse the TC and + // save its simplices into "stars_simplices" + //------------------------------------------------------------------------- + + Simplex_range const *p_simplices; + + std::size_t num_infinite_cells = 0; + Simplex_range stars_simplices; + if (!p_complex) + { + Stars_container::const_iterator it_star = m_stars.begin(); + Stars_container::const_iterator it_star_end = m_stars.end(); + // For each star: get the finite simplices + for ( ; it_star != it_star_end ; ++it_star) + { + for (Star::const_iterator it_s = it_star->begin(), + it_s_end = it_star->end() ; it_s != it_s_end ; ++it_s) + { + if (!is_infinite(*it_s)) + stars_simplices.insert(*it_s); + } + } + /*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)) + { + ++num_infinite_cells; + continue; + } + Simplex simplex; + for (int i = 0 ; i < tr.current_dimension() + 1 ; ++i) + simplex.insert((*it_c)->vertex(i)->data()); + + stars_simplices.insert(simplex); + } + }*/ + + p_simplices = &stars_simplices; + } + else + { + p_simplices = &p_complex->simplex_range(); + } + + //------------------------------------------------------------------------- + // Check if simplices of "*p_complex" are all in "amb_dt_simplices" + //------------------------------------------------------------------------- + + std::set diff; + if (!incorrect_simplices) + incorrect_simplices = &diff; + std::set_difference(p_simplices->begin(), p_simplices->end(), + amb_dt_simplices.begin(), amb_dt_simplices.end(), + std::inserter(*incorrect_simplices, + incorrect_simplices->begin()) ); + +#ifdef CGAL_TC_VERBOSE + std::cerr + << (incorrect_simplices->empty() ? "OK " : "ERROR ") + << "check_if_all_simplices_are_in_the_ambient_delaunay:" + << std::endl + << " Number of simplices in ambient RT: " << amb_dt_simplices.size() + << std::endl + << " Number of unique simplices in TC stars: " << p_simplices->size() + << std::endl + << " Number of infinite full cells in TC stars: " << num_infinite_cells + << std::endl + << " Number of wrong simplices: " << incorrect_simplices->size() + << std::endl; +#endif + return incorrect_simplices->empty(); + } + +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; + }; + +#ifdef CGAL_LINKED_WITH_TBB + // Functor for compute_tangential_complex function + class Compute_tangent_triangulation + { + Tangential_complex & m_tc; + + public: + // Constructor + Compute_tangent_triangulation( + Tangential_complex &tc) + : m_tc(tc) + { } + + // 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); + } + }; +#endif // CGAL_LINKED_WITH_TBB + + bool is_infinite(Indexed_simplex const& s) const + { + return *s.rbegin() == std::numeric_limits::max(); + } + + void compute_tangent_triangulation(std::size_t i, bool verbose = false) + { + if (verbose) + std::cerr << "** Computing tangent tri #" << i << " **" << std::endl; + //std::cerr << "***********************************************" << std::endl; + + // No need to lock the mutex here since this will not be called while + // other threads are perturbing the positions + const Point center_pt = compute_perturbed_point(i); + Tangent_space_basis &tsb = m_tangent_spaces[i]; + +#if defined(CGAL_TC_VERY_VERBOSE) && defined(CGAL_ALPHA_TC) + std::cerr << "Base dimension, incl. thickening vectors: " + << tsb.dimension() << std::endl; +#endif + // Estimate the tangent space + if (!m_are_tangent_spaces_computed[i]) + { +#ifdef CGAL_TC_EXPORT_NORMALS + tsb = compute_tangent_space(center_pt, i, true/*normalize*/, &m_orth_spaces[i]); +#else + tsb = compute_tangent_space(center_pt, i); +#endif + } +#ifdef CGAL_TC_PERTURB_TANGENT_SPACE + else if (m_perturb_tangent_space[i]) + { +#ifdef CGAL_TC_EXPORT_NORMALS + tsb = compute_tangent_space(center_pt, i, + true /*normalize_basis*/, + &m_orth_spaces[i], + true /*perturb*/); +#else + tsb = compute_tangent_space(center_pt, i, + true /*normalize_basis*/, + NULL /*ortho basis*/, + true /*perturb*/); +#endif + m_perturb_tangent_space[i] = false; + } +#endif + + int tangent_space_dim = tangent_basis_dim(i); + Triangulation &local_tr = + m_triangulations[i].construct_triangulation(tangent_space_dim); + 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::Squared_distance_d k_sqdist = + m_k.squared_distance_d_object(); + + // Triangulation's traits functor & objects + typename Tr_traits::Point_weight_d point_weight = + local_tr_traits.point_weight_d_object(); + typename Tr_traits::Power_center_d power_center = + local_tr_traits.power_center_d_object(); + typename Tr_traits::Compute_coordinate_d coord = + local_tr_traits.compute_coordinate_d_object(); + + + //*************************************************** + // Build a minimal triangulation in the tangent space + // (we only need the star of p) + //*************************************************** + + // Insert p + Tr_point proj_wp; + if(i == tsb.origin()) + { + proj_wp = local_tr_traits.construct_weighted_point_d_object()( + local_tr_traits.construct_point_d_object()(tangent_space_dim, ORIGIN), + m_weights[i]); + } + else + { + const Weighted_point& wp = compute_perturbed_weighted_point(i); + proj_wp = project_point_and_compute_weight(wp, tsb, local_tr_traits); + } + + center_vertex = local_tr.insert(proj_wp); + center_vertex->data() = i; + if (verbose) + std::cerr << "* Inserted point #" << i << std::endl; + +#ifdef CGAL_TC_VERY_VERBOSE + std::size_t num_attempts_to_insert_points = 1; + std::size_t num_inserted_points = 1; +#endif + //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 squared_star_sphere_radius_plus_margin; + +#ifdef CGAL_ALPHA_TC + /*FT max_absolute_alpha = tsb.max_absolute_alpha(); + // "2*m_half_sparsity" because both points can be perturbed + FT max_sqdist_to_tangent_space = (max_absolute_alpha == FT(0) ? + FT(0) : CGAL::square(2*max_absolute_alpha + 2*m_half_sparsity));*/ + std::size_t number_of_attempts_to_insert_points = 0; + const std::size_t MAX_NUM_INSERTED_POINTS = + tsb.num_thickening_vectors() > 0 ? + static_cast(std::pow(4, /*tsb.dimension()*/m_intrinsic_dim)) + : std::numeric_limits::max(); +#endif + + // 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) + { +#ifdef CGAL_ALPHA_TC + ++number_of_attempts_to_insert_points; + if (number_of_attempts_to_insert_points > MAX_NUM_INSERTED_POINTS) + break; +#endif + + std::size_t neighbor_point_idx = nn_it->first; + + // ith point = p, which is already inserted + if (neighbor_point_idx != i) + { + // No need to lock the Mutex_for_perturb here since this will not be + // called while other threads are perturbing the positions + Point neighbor_pt; + FT neighbor_weight; + compute_perturbed_weighted_point( + neighbor_point_idx, neighbor_pt, neighbor_weight); + + if (squared_star_sphere_radius_plus_margin + && k_sqdist(center_pt, neighbor_pt) + > *squared_star_sphere_radius_plus_margin) + break; + + Tr_point proj_pt = project_point_and_compute_weight( + neighbor_pt, neighbor_weight, tsb, + local_tr_traits); + +#ifdef CGAL_TC_VERY_VERBOSE + ++num_attempts_to_insert_points; +#endif + + + 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()) + { +#ifdef CGAL_TC_VERY_VERBOSE + ++num_inserted_points; +#endif + if (verbose) + std::cerr << "* Inserted point #" << neighbor_point_idx << std::endl; + + vh->data() = neighbor_point_idx; + + // Let's recompute squared_star_sphere_radius_plus_margin + if (local_tr.current_dimension() >= tangent_space_dim) + { + squared_star_sphere_radius_plus_margin = boost::none; + // 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)) + { + squared_star_sphere_radius_plus_margin = 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 (!squared_star_sphere_radius_plus_margin + || sq_power_sphere_diam > + *squared_star_sphere_radius_plus_margin) + { + squared_star_sphere_radius_plus_margin = sq_power_sphere_diam; + } + } + } + + // Let's add the margin, now + // The value depends on whether we perturb weight or position + if (squared_star_sphere_radius_plus_margin) + { +#ifdef CGAL_TC_PERTURB_WEIGHT + // "4*m_sq_half_sparsity" because both points can be perturbed + squared_star_sphere_radius_plus_margin = + *squared_star_sphere_radius_plus_margin + 4*m_sq_half_sparsity; +#else + // "2*m_half_sparsity" because both points can be perturbed + squared_star_sphere_radius_plus_margin = CGAL::square( + CGAL::sqrt(*squared_star_sphere_radius_plus_margin) + + 2*m_half_sparsity); +#endif + } + } + } + } + } + +#ifdef CGAL_TC_VERY_VERBOSE + std::cerr << "Inserted " << num_inserted_points << " points / " + << num_attempts_to_insert_points << " attemps to compute the star\n"; +#endif +#ifdef CGAL_ALPHA_TC + if (tsb.num_thickening_vectors() == 0) + update_star__no_thickening_vectors(i); + else + { + update_star__with_thickening_vector(i); + //check_if_all_simplices_are_in_the_ambient_delaunay(); // CJTODO TEMP + } +#else + update_star__no_thickening_vectors(i); +#endif + } + + // Updates m_stars[i] directly from m_triangulations[i] + void update_star__no_thickening_vectors(std::size_t i) + { + //*************************************************** + // Update the associated star (in m_stars) + //*************************************************** + Star &star = m_stars[i]; + star.clear(); + Triangulation &local_tr = m_triangulations[i].tr(); + Tr_vertex_handle center_vertex = m_triangulations[i].center_vertex(); + int cur_dim_plus_1 = local_tr.current_dimension() + 1; + + std::vector incident_cells; + local_tr.incident_full_cells( + center_vertex, 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) + { + // Will contain all indices except center_vertex + Incident_simplex incident_simplex; + for (int j = 0 ; j < cur_dim_plus_1 ; ++j) + { + std::size_t index = (*it_c)->vertex(j)->data(); + if (index != i) + incident_simplex.insert(index); + } + star.push_back(incident_simplex); + } + + // 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); + } + +#ifdef CGAL_ALPHA_TC + void update_star__with_thickening_vector(std::size_t i) + { + //*************************************************** + // Parse the faces of the star and add the ones that are in the + // restriction to alpha-Tp + // Update the associated star (in m_stars) + //*************************************************** + + Triangulation &local_tr = m_triangulations[i].tr(); + int triangulation_dim = local_tr.current_dimension(); + Tr_traits const& local_tr_traits = local_tr.geom_traits(); + Tr_vertex_handle center_vertex = m_triangulations[i].center_vertex(); + Tangent_space_basis const& tsb = m_tangent_spaces[i]; + +#ifdef CGAL_TC_PERFORM_EXTRA_CHECKS + if (triangulation_dim != tangent_basis_dim(i)) + std::cerr << "WARNING in update_star__with_thickening_vector: the " + "dimension of the local triangulation is different from " + "the dimension of the tangent space.\n"; +#endif + + Star &star = m_stars[i]; + star.clear(); + int cur_dim_plus_1 = triangulation_dim + 1; + + std::vector incident_cells; + local_tr.incident_full_cells( + center_vertex, std::back_inserter(incident_cells)); + + typedef std::set DT_face; // DT face without center vertex (i) + typedef std::set Neighbor_vertices; + typedef std::map DT_faces_and_neighbors; + + // Maps that associate a m-face F and the points of its m+1-cofaces + // (except the points of F). Those points are called its "neighbors". + // N.B.: each m-face contains 'i', so 'i' is not stored in the faces + // N.B.2: faces_and_neighbors[0] => dim 1, faces_and_neighbors[1] => dim 2 + std::vector faces_and_neighbors; + faces_and_neighbors.resize(triangulation_dim); + + // Fill faces_and_neighbors + // Let's first take care of the maximal simplices (dim = triangulation_dim) + 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) + { + DT_face face; + for (int j = 0 ; j < cur_dim_plus_1 ; ++j) + { + Tr_vertex_handle vh = (*it_c)->vertex(j); + // Skip infinite simplices + if (vh == local_tr.infinite_vertex()) + goto next_face; + if (vh->data() != i) + face.insert(vh); + } + // No co-faces => no neighbors + faces_and_neighbors[triangulation_dim-1][face] = Neighbor_vertices(); +next_face: + ; + } + // Then the D-m-faces... + int current_dim = triangulation_dim - 1; + while (current_dim > 0) + { + // Let's fill faces_and_neighbors[current_dim-1] + // (stores the current_dim-faces) + DT_faces_and_neighbors& cur_faces_and_nghb = + faces_and_neighbors[current_dim-1]; + + typedef DT_faces_and_neighbors::const_iterator FaN_it; + // Parse m+1-faces + for (FaN_it it_k_p1_face = faces_and_neighbors[current_dim].begin(), + it_k_p1_face_end = faces_and_neighbors[current_dim].end() ; + it_k_p1_face != it_k_p1_face_end ; ++it_k_p1_face) + { + DT_face const& k_p1_face = it_k_p1_face->first; + + // Add each m-face to cur_faces_and_nghb + std::size_t n = current_dim + 1; // Not +2 since 'i' is not stored + std::vector booleans(n, false); + std::fill(booleans.begin() + 1, booleans.end(), true); + do + { + DT_face k_face; + Tr_vertex_handle remaining_vertex; + DT_face::const_iterator it_v = k_p1_face.begin(); + for (std::size_t i = 0 ; i < n ; ++i, ++it_v) + { + if (booleans[i]) + k_face.insert(*it_v); + else + remaining_vertex = *it_v; + } + + cur_faces_and_nghb[k_face].insert(remaining_vertex); + + } while (std::next_permutation(booleans.begin(), booleans.end())); + } + --current_dim; + } + + // For each face V of Voronoi_cell(P[i]) - dim 0 to dim D-1 + // I.e. For each DT face F of the star - dim D to dim 1 + // Test if V intersects the thickened tangent space + current_dim = triangulation_dim; + while (current_dim > 0) + { + // Remember: faces_and_neighbors[current_dim-1] stores + // the current_dim-faces + DT_faces_and_neighbors const& cur_faces_and_nghb = + faces_and_neighbors[current_dim-1]; + + for (DT_faces_and_neighbors::const_iterator + it_f = cur_faces_and_nghb.begin(), + it_f_end = cur_faces_and_nghb.end() ; + it_f != it_f_end ; ++it_f) + { + Neighbor_vertices const& curr_neighbors = it_f->second; + + DT_face const& current_DT_face = it_f->first; + CGAL_assertion(static_cast(current_DT_face.size()) + == current_dim); + + // P: list of current_DT_face points (including 'i') + std::vector P; + P.reserve(current_DT_face.size() + 1); + for (DT_face::const_iterator it = current_DT_face.begin(), + it_end = current_DT_face.end() ; it != it_end ; ++it) + { + P.push_back((*it)->point()); + } + P.push_back(center_vertex->point()); + + // Q: vertices which are common neighbors of all vertices of P + std::vector Q; + P.reserve(curr_neighbors.size()); + for (Neighbor_vertices::const_iterator it = curr_neighbors.begin(), + it_end = curr_neighbors.end() ; it != it_end ; ++it) + { + Q.push_back((*it)->point()); + } + + bool does_intersect = + does_voronoi_face_and_tangent_subspace_intersect( + triangulation_dim, + P, + Q, + tsb, + local_tr_traits); + if (does_intersect) + { + // Get the indices of the face's points + Incident_simplex face; + DT_face::const_iterator it_vh = current_DT_face.begin(); + DT_face::const_iterator it_vh_end = current_DT_face.end(); + for ( ; it_vh != it_vh_end ; ++it_vh) + face.insert((*it_vh)->data()); + + star.push_back(face); + + // Clear all subfaces of current_DT_face from the maps + for (int dim = current_dim - 1 ; dim > 0 ; --dim) + { + std::size_t n = current_DT_face.size(); + std::vector booleans(n, false); + std::fill(booleans.begin() + n - dim, booleans.end(), true); + do + { + DT_face dim_face; + DT_face::const_iterator it_v = current_DT_face.begin(); + for (std::size_t i = 0 ; i < n ; ++i, ++it_v) + { + if (booleans[i]) + dim_face.insert(*it_v); + } + + faces_and_neighbors[dim-1].erase(dim_face); + + } while (std::next_permutation(booleans.begin(), booleans.end())); + } + } + } + + --current_dim; + } + } +#endif //CGAL_ALPHA_TC + + + Tangent_space_basis compute_tangent_space( + const Point &p + , const std::size_t i + , bool normalize_basis = true + , Orthogonal_space_basis *p_orth_space_basis = NULL +#ifdef CGAL_TC_PERTURB_TANGENT_SPACE + , bool perturb = false +#endif + ) + { +#ifdef CGAL_TC_COMPUTE_TANGENT_PLANES_FOR_SPHERE_2 + + double tt[2] = {p[1], -p[0]}; + Vector t(2, &tt[0], &tt[2]); + + // 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(i); + ts.reserve(m_intrinsic_dim); + ts.push_back(scaled_vec(t, FT(1)/CGAL::sqrt(sqlen(t)))); + m_are_tangent_spaces_computed[i] = true; + + return ts; + +#elif defined(CGAL_TC_COMPUTE_TANGENT_PLANES_FOR_SPHERE_3) + + double tt1[3] = {-p[1] - p[2], p[0], p[0]}; + double tt2[3] = {p[1] * tt1[2] - p[2] * tt1[1], + p[2] * tt1[0] - p[0] * tt1[2], + p[0] * tt1[1] - p[1] * tt1[0]}; + Vector t1(3, &tt1[0], &tt1[3]); + Vector t2(3, &tt2[0], &tt2[3]); + + // 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(i); + ts.reserve(m_intrinsic_dim); + ts.push_back(scaled_vec(t1, FT(1)/CGAL::sqrt(sqlen(t1)))); + ts.push_back(scaled_vec(t2, FT(1)/CGAL::sqrt(sqlen(t2)))); + + m_are_tangent_spaces_computed[i] = true; + + return ts; + +#elif defined(CGAL_TC_COMPUTE_TANGENT_PLANES_FOR_TORUS_D) + + Tangent_space_basis ts(i); + ts.reserve(m_intrinsic_dim); + for (int dim = 0 ; dim < m_intrinsic_dim ; ++dim) + { + std::vector tt(m_ambient_dim, 0.); + tt[2*dim] = -p[2*dim + 1]; + tt[2*dim + 1] = p[2*dim]; + Vector t(2*m_intrinsic_dim, tt.begin(), tt.end()); + ts.push_back(t); + } + + m_are_tangent_spaces_computed[i] = true; + + //return compute_gram_schmidt_basis(ts, m_k); + return ts; + //******************************* PCA ************************************* + +#else + + unsigned int num_points_for_pca = static_cast( + std::pow(BASE_VALUE_FOR_PCA, m_intrinsic_dim)); + + // 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 scalar_pdct = + m_k.scalar_product_d_object(); + typename Kernel::Difference_of_vectors_d diff_vec = + m_k.difference_of_vectors_d_object(); + //typename Kernel::Translated_point_d transl = + // m_k.translated_point_d_object(); + +#ifdef USE_ANOTHER_POINT_SET_FOR_TANGENT_SPACE_ESTIM + KNS_range kns_range = m_points_ds_for_tse.query_ANN( + p, num_points_for_pca, false); + const Points &points_for_pca = m_points_for_tse; +#else + KNS_range kns_range = m_points_ds.query_ANN(p, num_points_for_pca, false); + const Points &points_for_pca = m_points; +#endif + + // One row = one point + Eigen::MatrixXd mat_points(num_points_for_pca, m_ambient_dim); + KNS_iterator nn_it = kns_range.begin(); + for (unsigned int j = 0 ; + j < num_points_for_pca && nn_it != kns_range.end() ; + ++j, ++nn_it) + { + for (int i = 0 ; i < m_ambient_dim ; ++i) + { + //const Point p = transl( + // m_points[nn_it->first], m_translations[nn_it->first]); + mat_points(j, i) = CGAL::to_double(coord(m_points[nn_it->first], i)); +#ifdef CGAL_TC_ADD_NOISE_TO_TANGENT_SPACE + mat_points(j, i) += m_random_generator.get_double( + -0.5*m_half_sparsity, 0.5*m_half_sparsity); +#endif +#ifdef CGAL_TC_PERTURB_TANGENT_SPACE + if (perturb) + mat_points(j, i) += m_random_generator.get_double( + -0.5*m_half_sparsity, 0.5*m_half_sparsity); +#endif + } + } + Eigen::MatrixXd centered = mat_points.rowwise() - mat_points.colwise().mean(); + Eigen::MatrixXd cov = centered.adjoint() * centered; + Eigen::SelfAdjointEigenSolver eig(cov); + + Tangent_space_basis tsb(i); // p = compute_perturbed_point(i) here + + // The eigenvectors are sorted in increasing order of their corresponding + // eigenvalues + for (int j = m_ambient_dim - 1 ; + j >= m_ambient_dim - m_intrinsic_dim ; + --j) + { + if (normalize_basis) + { + Vector v = constr_vec(m_ambient_dim, + eig.eigenvectors().col(j).data(), + eig.eigenvectors().col(j).data() + m_ambient_dim); + tsb.push_back(normalize_vector(v, m_k)); + } + else + { + tsb.push_back(constr_vec( + m_ambient_dim, + eig.eigenvectors().col(j).data(), + eig.eigenvectors().col(j).data() + m_ambient_dim)); + } + } + + if (p_orth_space_basis) + { + p_orth_space_basis->set_origin(i); + for (int j = m_ambient_dim - m_intrinsic_dim - 1 ; + j >= 0 ; + --j) + { + if (normalize_basis) + { + Vector v = constr_vec(m_ambient_dim, + eig.eigenvectors().col(j).data(), + eig.eigenvectors().col(j).data() + m_ambient_dim); + p_orth_space_basis->push_back(normalize_vector(v, m_k)); + } + else + { + p_orth_space_basis->push_back(constr_vec( + m_ambient_dim, + eig.eigenvectors().col(j).data(), + eig.eigenvectors().col(j).data() + m_ambient_dim)); + } + } + } +#if defined(CGAL_ALPHA_TC) && defined(CGAL_USE_A_FIXED_ALPHA) + // Add the orthogonal vectors as "thickening vectors" + for (int j = m_ambient_dim - m_intrinsic_dim - 1 ; + j >= 0 ; + --j) + { + Vector v = constr_vec(m_ambient_dim, + eig.eigenvectors().col(j).data(), + eig.eigenvectors().col(j).data() + m_ambient_dim); + tsb.add_thickening_vector( + normalize_vector(v, m_k), -CGAL_TC_ALPHA_VALUE, CGAL_TC_ALPHA_VALUE); + } +#endif + + m_are_tangent_spaces_computed[i] = true; + + //************************************************************************* + + //Vector n = m_k.point_to_vector_d_object()(p); + //n = scaled_vec(n, FT(1)/sqrt(sqlen(n))); + //std::cerr << "IP = " << scalar_pdct(n, ts[0]) << " & " << scalar_pdct(n, ts[1]) << std::endl; + + return tsb; + +#endif + + /* + // 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(m_intrinsic_dim); + //ts.push_back(diff_vec(t1, scaled_vec(n, scalar_pdct(t1, n)))); + //ts.push_back(diff_vec(t2, scaled_vec(n, scalar_pdct(t2, n)))); + //ts = compute_gram_schmidt_basis(ts, m_k); + //return ts; + */ + } + + // Returns the dimension of the ith local triangulation + // This is particularly useful for the alpha-TC + int tangent_basis_dim(std::size_t i) const + { + return m_tangent_spaces[i].dimension(); + } + + Point compute_perturbed_point(std::size_t pt_idx) const + { +#ifdef CGAL_TC_PERTURB_POSITION + return m_k.translated_point_d_object()( + m_points[pt_idx], m_translations[pt_idx]); +#else + return m_points[pt_idx]; +#endif + } + + void compute_perturbed_weighted_point(std::size_t pt_idx, Point &p, FT &w) const + { +#ifdef CGAL_TC_PERTURB_POSITION + p = m_k.translated_point_d_object()( + m_points[pt_idx], m_translations[pt_idx]); +#else + p = m_points[pt_idx]; +#endif + w = m_weights[pt_idx]; + } + + Weighted_point compute_perturbed_weighted_point(std::size_t pt_idx) const + { + typename Kernel::Construct_weighted_point_d k_constr_wp = + m_k.construct_weighted_point_d_object(); + + Weighted_point wp = k_constr_wp( +#ifdef CGAL_TC_PERTURB_POSITION + m_k.translated_point_d_object()(m_points[pt_idx], m_translations[pt_idx]), +#else + m_points[pt_idx], +#endif + m_weights[pt_idx]); + + return wp; + } + + Point unproject_point(const Tr_point &p, + const Tangent_space_basis &tsb, + const Tr_traits &tr_traits) const + { + 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 Tr_traits::Compute_coordinate_d coord = + tr_traits.compute_coordinate_d_object(); + + Point global_point = compute_perturbed_point(tsb.origin()); + for (int i = 0 ; i < m_intrinsic_dim ; ++i) + global_point = k_transl(global_point, + k_scaled_vec(tsb[i], coord(p, i))); + +#ifdef CGAL_ALPHA_TC + Tangent_space_basis::Thickening_vectors const& tv = tsb.thickening_vectors(); + for (int i = 0 ; i < tv.size() ; ++i) + { + global_point = k_transl( + global_point, + k_scaled_vec(tv[i].vec, coord(p, m_intrinsic_dim + i))); + } +#endif + return global_point; + } + + // Project the point in the tangent space + Tr_bare_point project_point(const Point &p, + const Tangent_space_basis &tsb) const + { + typename Kernel::Scalar_product_d scalar_pdct = + m_k.scalar_product_d_object(); + typename Kernel::Difference_of_points_d diff_points = + m_k.difference_of_points_d_object(); + + Vector v = diff_points(p, compute_perturbed_point(tsb.origin())); + + std::vector coords; + // Ambiant-space coords of the projected point + coords.reserve(tsb.dimension()); + for (std::size_t i = 0 ; i < m_intrinsic_dim ; ++i) + { + // Local coords are given by the scalar product with the vectors of tsb + FT coord = scalar_pdct(v, tsb[i]); + coords.push_back(coord); + } + +#ifdef CGAL_ALPHA_TC + Tangent_space_basis::Thickening_vectors const& tv = tsb.thickening_vectors(); + for (int i = 0 ; i < tv.size() ; ++i) + { + FT coord = scalar_pdct(v, tv[i].vec); + coords.push_back(coord); + } +#endif + return Tr_bare_point(static_cast( + coords.size()), 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 Weighted_point &wp, + const Tangent_space_basis &tsb, + const Tr_traits &tr_traits) const + { + typename Kernel::Point_drop_weight_d k_drop_w = + m_k.point_drop_weight_d_object(); + typename Kernel::Point_weight_d k_point_weight = + m_k.point_weight_d_object(); + return project_point_and_compute_weight( + k_drop_w(wp), k_point_weight(wp), tsb, tr_traits); + } + + Tr_point project_point_and_compute_weight(const Point &p, const FT w, + const Tangent_space_basis &tsb, + const Tr_traits &tr_traits) const + { + const int point_dim = m_k.point_dimension_d_object()(p); + typename Kernel::Scalar_product_d scalar_pdct = + m_k.scalar_product_d_object(); + typename Kernel::Difference_of_points_d diff_points = + m_k.difference_of_points_d_object(); + typename Kernel::Compute_coordinate_d coord = + m_k.compute_coordinate_d_object(); + typename Kernel::Construct_cartesian_const_iterator_d ccci = + m_k.construct_cartesian_const_iterator_d_object(); + + Point origin = compute_perturbed_point(tsb.origin()); + Vector v = diff_points(p, origin); + + // Same dimension? Then the weight is 0 + bool same_dim = (point_dim == tsb.dimension()); + + std::vector coords; + // Ambiant-space coords of the projected point + std::vector p_proj(ccci(origin), ccci(origin, 0)); + coords.reserve(tsb.dimension()); + for (std::size_t i = 0 ; i < m_intrinsic_dim ; ++i) + { + // Local coords are given by the scalar product with the vectors of tsb + FT c = scalar_pdct(v, tsb[i]); + coords.push_back(c); + + // p_proj += c * tsb[i] + if (!same_dim) + for (int j = 0 ; j < point_dim ; ++j) + p_proj[j] += c * coord(tsb[i], j); + } + +#ifdef CGAL_ALPHA_TC + Tangent_space_basis::Thickening_vectors const& tv = tsb.thickening_vectors(); + for (int i = 0 ; i < tv.size() ; ++i) + { + FT c = scalar_pdct(v, tv[i].vec); + coords.push_back(c); + + // p_proj += c * tv[i].vec + if (!same_dim) + for (int j = 0 ; j < point_dim ; ++j) + p_proj[j] += c * coord(tv[i].vec, j); + } +#endif + + // Same dimension? Then the weight is 0 + FT sq_dist_to_proj_pt = 0; + if (!same_dim) + { + Point projected_pt(point_dim, p_proj.begin(), p_proj.end()); + sq_dist_to_proj_pt = m_k.squared_distance_d_object()(p, projected_pt); + } + + return tr_traits.construct_weighted_point_d_object() + ( + tr_traits.construct_point_d_object()( + static_cast(coords.size()), coords.begin(), coords.end()), + w - sq_dist_to_proj_pt + ); + } + + // Project all the points in the tangent space + template + std::vector project_points_and_compute_weights( + const Indexed_point_range &point_indices, + const Tangent_space_basis &tsb, + const Tr_traits &tr_traits) const + { + std::vector ret; + for (typename Indexed_point_range::const_iterator + it = point_indices.begin(), it_end = point_indices.end(); + it != it_end ; ++it) + { + ret.push_back(project_point_and_compute_weight( + compute_perturbed_weighted_point(*it), tsb, tr_traits)); + } + return ret; + } + + // A simplex here is a local tri's full cell handle + bool is_simplex_consistent(Tr_full_cell_handle fch, int cur_dim) const + { + Indexed_simplex 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 + template + double compute_simplex_fatness(IndexRange const& simplex) const + { + // Kernel functors + typename Kernel::Compute_coordinate_d coord = + m_k.compute_coordinate_d_object(); + typename Kernel::Squared_distance_d sqdist = + m_k.squared_distance_d_object(); + typename Kernel::Difference_of_points_d diff_pts = + m_k.difference_of_points_d_object(); + + typename Tr_traits::Difference_of_points_d tr_diff_pts = + m_triangulations[0].tr().geom_traits().difference_of_points_d_object(); + + std::vector s(simplex.begin(), simplex.end()); + std::size_t simplex_dim = s.size() - 1; + + // Compute basis + Tangent_space_basis basis(s[0]); + for (int j = 0 ; j < simplex_dim ; ++j) + { + Vector e = diff_pts( + compute_perturbed_point(s[j+1]), compute_perturbed_point(s[0])); + basis.push_back(e); + } + basis = compute_gram_schmidt_basis(basis, m_k); + + // Compute the volume of the simplex: determinant + Eigen::MatrixXd m(simplex_dim, simplex_dim); + for (int j = 0 ; j < simplex_dim ; ++j) + { + Tr_vector v_j = tr_diff_pts( + project_point(compute_perturbed_point(s[j+1]), basis), + project_point(compute_perturbed_point(s[0]), basis)); + for (int i = 0 ; i < simplex_dim ; ++i) + m(j, i) = CGAL::to_double(coord(v_j, i)); + } + double volume = + std::abs(m.determinant()) + / boost::math::factorial(simplex_dim); + + // Compute the longest edge of the simplex + CGAL::Combination_enumerator combi(2, 0, simplex_dim+1); + FT max_sq_length = FT(0); + for ( ; !combi.finished() ; ++combi) + { + FT sq_length = sqdist( + compute_perturbed_point(s[combi[0]]), + compute_perturbed_point(s[combi[1]])); + if (sq_length > max_sq_length) + max_sq_length = sq_length; + } + + return volume / std::pow(CGAL::sqrt(max_sq_length), simplex_dim); + } + + // A simplex here is a list of point indices + // CJTODO: improve it like the other "is_simplex_consistent" below + bool is_simplex_consistent(Indexed_simplex const& simplex) const + { + // Check if the simplex is in the stars of all its vertices + Indexed_simplex::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; + // Don't check infinite simplices + if (point_idx == std::numeric_limits::max()) + continue; + + Star const& star = m_stars[point_idx]; + + // What we're looking for is "simplex" \ point_idx + Incident_simplex is_to_find = simplex; + is_to_find.erase(point_idx); + + // For each cell + if (std::find(star.begin(), star.end(), is_to_find) == star.end()) + return false; + } + + return true; + } + + // A simplex here is a list of point indices + // "s" contains all the points of the simplex except "center_point" + // This function returns the points whose star doesn't contain the simplex + // N.B.: the function assumes that the simplex is contained in + // star(center_point) + template // value_type = std::size_t + bool is_simplex_consistent( + std::size_t center_point, + Incident_simplex const& s, // without "center_point" + OutputIterator points_whose_star_does_not_contain_s, + bool check_also_in_non_maximal_faces = false) const + { + Indexed_simplex full_simplex = s; + full_simplex.insert(center_point); + + // Check if the simplex is in the stars of all its vertices + Incident_simplex::const_iterator it_point_idx = s.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 != s.end() ; ++it_point_idx) + { + std::size_t point_idx = *it_point_idx; + // Don't check infinite simplices + if (point_idx == std::numeric_limits::max()) + continue; + + Star const& star = m_stars[point_idx]; + + // What we're looking for is full_simplex \ point_idx + Incident_simplex is_to_find = full_simplex; + is_to_find.erase(point_idx); + + if (check_also_in_non_maximal_faces) + { + // For each simplex "is" of the star, check if ic_to_simplex is + // included in "is" + bool found = false; + for (Star::const_iterator is = star.begin(), is_end = star.end() ; + !found && is != is_end ; ++is) + { + if (std::includes(is->begin(), is->end(), + is_to_find.begin(), is_to_find.end())) + found = true; + } + + if (!found) + *points_whose_star_does_not_contain_s++ = point_idx; + } + else + { + // Does the star contain is_to_find? + if (std::find(star.begin(), star.end(), is_to_find) == star.end()) + *points_whose_star_does_not_contain_s++ = point_idx; + } + } + + return true; + } + + // A simplex here is a list of point indices + // It looks for s in star(p). + // "s" contains all the points of the simplex except p. + bool is_simplex_in_star( + std::size_t p, + Incident_simplex const& s, + bool check_also_in_non_maximal_faces = true) const + { + Star const& star = m_stars[p]; + + if (check_also_in_non_maximal_faces) + { + // For each simplex "is" of the star, check if ic_to_simplex is + // included in "is" + bool found = false; + for (Star::const_iterator is = star.begin(), is_end = star.end() ; + !found && is != is_end ; ++is) + { + if (std::includes(is->begin(), is->end(), s.begin(), s.end())) + found = true; + } + + return found; + } + else + { + return !(std::find(star.begin(), star.end(), s) == star.end()); + } + } + +#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; + tbb::combinable &m_num_inconsistencies; + + public: + // Constructor + Try_to_solve_inconsistencies_in_a_local_triangulation( + Tangential_complex &tc, tbb::combinable &num_inconsistencies) + : m_tc(tc), m_num_inconsistencies(num_inconsistencies) + {} + + // Constructor + Try_to_solve_inconsistencies_in_a_local_triangulation( + const Compute_tangent_triangulation &ctt) + : m_tc(ctt.m_tc), m_num_inconsistencies(ctt.m_num_inc) + {} + + // operator() + void operator()( const tbb::blocked_range& r ) const + { + for( size_t i = r.begin() ; i != r.end() ; ++i) + { + m_num_inconsistencies.local() += + m_tc.try_to_solve_inconsistencies_in_a_local_triangulation(i); + } + } + }; +#endif // CGAL_LINKED_WITH_TBB + + void perturb(std::size_t point_idx) + { + // Perturb the weight? +#ifdef CGAL_TC_PERTURB_WEIGHT + m_weights[point_idx] = m_random_generator.get_double(0., m_sq_half_sparsity); + if(m_weights_memory.size() > 0) // external weights were initially set + m_weights[point_idx] = m_weights[point_idx] + m_weights_memory[point_idx]; +#endif + +#ifdef CGAL_TC_PERTURB_TANGENT_SPACE + m_perturb_tangent_space[point_idx] = true; +#endif + + // Perturb the position? +#ifdef CGAL_TC_PERTURB_POSITION +# ifdef CGAL_TC_PERTURB_POSITION_GLOBAL + typename Kernel::Point_to_vector_d k_pt_to_vec = + m_k.point_to_vector_d_object(); + CGAL::Random_points_in_ball_d + tr_point_in_ball_generator( + m_ambient_dim, m_half_sparsity); + // Parallel +# if defined(CGAL_LINKED_WITH_TBB) && defined(CGAL_TC_GLOBAL_REFRESH) + Vector transl = k_pt_to_vec(*tr_point_in_ball_generator++); + m_p_perturb_mutexes[point_idx].lock(); + m_translations[point_idx] = transl; + m_p_perturb_mutexes[point_idx].unlock(); + // Sequential +# else + m_translations[point_idx] = k_pt_to_vec(*tr_point_in_ball_generator++); +# endif + +# else // CGAL_TC_PERTURB_POSITION_TANGENTIAL + const Tr_traits &local_tr_traits = + m_triangulations[point_idx].tr().geom_traits(); + typename Tr_traits::Compute_coordinate_d coord = + local_tr_traits.compute_coordinate_d_object(); + typename Kernel::Translated_point_d k_transl = + m_k.translated_point_d_object(); + typename Kernel::Construct_vector_d k_constr_vec = + m_k.construct_vector_d_object(); + typename Kernel::Scaled_vector_d k_scaled_vec = + m_k.scaled_vector_d_object(); + + CGAL::Random_points_in_ball_d + tr_point_in_ball_generator( + m_intrinsic_dim, + m_random_generator.get_double(0., m_half_sparsity)); + + Tr_point local_random_transl = + local_tr_traits.construct_weighted_point_d_object()( + *tr_point_in_ball_generator++, 0); + Translation_for_perturb global_transl = k_constr_vec(m_ambient_dim); + const Tangent_space_basis &tsb = m_tangent_spaces[point_idx]; + for (int i = 0 ; i < m_intrinsic_dim ; ++i) + { + global_transl = k_transl( + global_transl, + k_scaled_vec(tsb[i], coord(local_random_transl, i)) + ); + } + // Parallel +# if defined(CGAL_LINKED_WITH_TBB) && defined(CGAL_TC_GLOBAL_REFRESH) + m_p_perturb_mutexes[point_idx].lock(); + m_translations[point_idx] = global_transl; + m_p_perturb_mutexes[point_idx].unlock(); + // Sequential +# else + m_translations[point_idx] = global_transl; +# endif + +# endif // CGAL_TC_PERTURB_POSITION_TANGENTIAL +#endif // CGAL_TC_PERTURB_POSITION + } + + 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 + + Star const& star = m_stars[tr_index]; + 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(); + + // For each incident simplex + Star::const_iterator it_inc_simplex = star.begin(); + Star::const_iterator it_inc_simplex_end = star.end(); + for ( ; it_inc_simplex != it_inc_simplex_end ; ++it_inc_simplex) + { + const Incident_simplex &incident_simplex = *it_inc_simplex; + + // Don't check infinite cells + if (is_infinite(incident_simplex)) + continue; + + Indexed_simplex c = incident_simplex; + c.insert(tr_index); // Add the missing index + +//***************************************************************************** +// STRATEGY 1: perturb all the points of the first inconsistent simplex +//***************************************************************************** +#ifdef CGAL_TC_PERTURB_THE_SIMPLEX_ONLY + // Inconsistent? + if (!is_simplex_consistent(c)) + { + is_inconsistent = true; + + for (Indexed_simplex::const_iterator it = c.begin(); + it != c.end() ; ++it) + { + perturb(*it); + } + +# if !defined(CGAL_TC_GLOBAL_REFRESH) + refresh_tangential_complex(); +# endif + + // We will try the other cells next time + break; + } + +//***************************************************************************** +// STRATEGY 2: perturb the center point only +//***************************************************************************** +#elif defined(CGAL_TC_PERTURB_THE_CENTER_VERTEX_ONLY) + if (!is_simplex_consistent(c)) + { + is_inconsistent = true; + + std::size_t idx = tr_index; + /*int k; + do + { + k = rand() % tr.current_dimension(); + } while ((*it_c)->vertex(k) == center_vh); + std::size_t idx = (*it_c)->vertex(k)->data();*/ + + perturb(idx); + +# if !defined(CGAL_TC_GLOBAL_REFRESH) + refresh_tangential_complex(); +# endif + + // We will try the other cells next time + break; + } + +//***************************************************************************** +// STRATEGY 3: perturb all the points of the 1-star +//***************************************************************************** +#elif defined(CGAL_TC_PERTURB_THE_1_STAR) + + // Inconsistent? + if (!is_simplex_consistent(c)) + { + is_inconsistent = true; + + std::set the_1_star; + + Star::const_iterator it_inc_simplex = star.begin(); + Star::const_iterator it_inc_simplex_end = star.end(); + for ( ; it_inc_simplex != it_inc_simplex_end ; ++it_inc_simplex) + { + the_1_star.insert(it_inc_simplex->begin(), it_inc_simplex ->end()); + } + + for (std::set::iterator it = the_1_star.begin() ; + it != the_1_star.end() ; ++it) + { + perturb(*it); + } + +# if !defined(CGAL_TC_GLOBAL_REFRESH) + refresh_tangential_complex(); +# endif + + // We will try the other cells next time + break; + } + +//***************************************************************************** +// STRATEGY 4: perturb the k + 1 + CGAL_TC_NUMBER_OF_ADDITIONNAL_PERTURBED_POINTS +// closest points (to the power center of first the inconsistent cell) +//***************************************************************************** +#elif defined(CGAL_TC_PERTURB_N_CLOSEST_POINTS) + + // Inconsistent? + if (!is_simplex_consistent(c)) + { + is_inconsistent = true; + + // Get the k + 1 + CGAL_TC_NUMBER_OF_ADDITIONNAL_PERTURBED_POINTS + // closest points + + std::vector simplex_pts; + simplex_pts.reserve(c.size()); + + Incident_simplex::const_iterator it_point_idx = c.begin(); + Incident_simplex::const_iterator it_point_idx_end = c.end(); + // For each point p of the simplex, we reproject it onto the tangent + // space. Could be optimized since it's already been computed before. + for ( ; it_point_idx != it_point_idx_end ; ++it_point_idx) + { + simplex_pts.push_back(project_point_and_compute_weight( + m_points[*it_point_idx], m_weights[*it_point_idx], + m_tangent_spaces[tr_index], local_tr_traits)); + } + + typename Tr_traits::Power_center_d power_center = + local_tr_traits.power_center_d_object(); + typename Tr_traits::Compute_coordinate_d coord = + local_tr_traits.compute_coordinate_d_object(); + + Point global_center = unproject_point( + power_center(simplex_pts.begin(), simplex_pts.end()), + m_tangent_spaces[tr_index], + local_tr_traits); + + KNS_range kns_range = m_points_ds.query_ANN( + global_center, + CGAL_TC_NUMBER_OF_PERTURBED_POINTS(m_intrinsic_dim)); + std::vector neighbors; + for (KNS_iterator nn_it = kns_range.begin() ; + nn_it != kns_range.end() ; + ++nn_it) + { + neighbors.push_back(nn_it->first); + } + + for (std::vector::iterator it = neighbors.begin(); + it != neighbors.end() ; + ++it) + { + perturb(*it); + } + +# if !defined(CGAL_TC_GLOBAL_REFRESH) + refresh_tangential_complex(); +# endif + + // We will try the other cells next time + break; + } +//***************************************************************************** +// STRATEGY 5: perturb one random point of the simplex +//***************************************************************************** +#else + // Inconsistent? + if (!is_simplex_consistent(c)) + { + is_inconsistent = true; + int rnd = m_random_generator.get_int(0, static_cast(c.size())); + if (rnd == 0) + perturb(tr_index); + else + { + Indexed_simplex::const_iterator it_idx = c.begin(); + std::advance(it_idx, rnd - 1); + perturb(*it_idx); + } + +# if !defined(CGAL_TC_GLOBAL_REFRESH) + refresh_tangential_complex(); +# endif + + // We will try the other cells next time + break; + } + +#endif // CGAL_TC_PERTURB_THE_SIMPLEX_ONLY + } + + return is_inconsistent; + } + + std::ostream &export_vertices_to_off( + std::ostream & os, std::size_t &num_vertices, + bool use_perturbed_points = false) const + { + if (m_points.empty()) + { + num_vertices = 0; + return os; + } + + // If m_intrinsic_dim = 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 = (m_intrinsic_dim == 1 ? 2 : 1); + + // Kernel functors + typename Kernel::Compute_coordinate_d coord = + m_k.compute_coordinate_d_object(); + + int num_coords = min(m_ambient_dim, 3); +#ifdef CGAL_TC_EXPORT_NORMALS + OS_container::const_iterator it_os = m_orth_spaces.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 (std::size_t i = 0 ; it_p != it_p_end ; ++it_p, ++i) + { + Point p = (use_perturbed_points ? compute_perturbed_point(i) : *it_p); + for (int ii = 0 ; ii < N ; ++ii) + { + int i = 0; +#if BETTER_EXPORT_FOR_FLAT_TORUS + // For flat torus + os << (2 + 1 * CGAL::to_double(coord(p, 0))) * CGAL::to_double(coord(p, 2)) << " " + << (2 + 1 * CGAL::to_double(coord(p, 0))) * CGAL::to_double(coord(p, 3)) << " " + << 1 * CGAL::to_double(coord(p, 1)); +#else + for ( ; i < num_coords ; ++i) + os << CGAL::to_double(coord(p, i)) << " "; +#endif + if (i == 2) + os << "0"; + +#ifdef CGAL_TC_EXPORT_NORMALS + for (i = 0 ; i < num_coords ; ++i) + os << " " << CGAL::to_double(coord(*it_os->begin(), i)); +#endif + os << std::endl; + } +#ifdef CGAL_TC_EXPORT_NORMALS + ++it_os; +#endif + } + + num_vertices = N*m_points.size(); + return os; + } + + void insert_higher_dim_simplex_into_star( + std::size_t index, + const Indexed_simplex &simplex) + { + Incident_simplex incident_simplex = simplex; + incident_simplex.erase(index); // Remove the center index + + Star &star = m_stars[index]; + + Indexed_simplex::const_iterator it_point_idx = simplex.begin(); + Indexed_simplex::const_iterator it_point_idx_end = simplex.end(); + for ( ; it_point_idx != it_point_idx_end ; ++it_point_idx) + { + // Skip center index + if (*it_point_idx == index) + continue; + + // Temporarily remove this index + incident_simplex.erase(*it_point_idx); + // Erase incident_simplex from star + star.erase(std::remove(star.begin(), star.end(), incident_simplex), + star.end()); + incident_simplex.insert(*it_point_idx); + } + + star.push_back(incident_simplex); + } + + // Solves one inconsistency + // "inconsistent_simplex" must contain p_idx and q_idx + // "inconsistent_simplex" must be in star(p) but not in star(q) + void solve_inconsistency_by_adding_higher_dimensional_simplices( + std::size_t p_idx, std::size_t q_idx, + const Indexed_simplex &inconsistent_simplex) + { + CGAL_assertion_code( + Indexed_simplex inc_s_minus_p = inconsistent_simplex; + inc_s_minus_p.erase(p_idx); + Indexed_simplex inc_s_minus_q = inconsistent_simplex; + inc_s_minus_q.erase(q_idx); + ); + CGAL_assertion(std::find(m_stars[p_idx].begin(), m_stars[p_idx].end(), + inc_s_minus_p) != m_stars[p_idx].end()); + CGAL_assertion(std::find(m_stars[q_idx].begin(), m_stars[q_idx].end(), + inc_s_minus_q) == m_stars[q_idx].end()); + + typename Kernel::Point_drop_weight_d k_drop_w = + m_k.point_drop_weight_d_object(); + typename Kernel::Translated_point_d k_transl = + m_k.translated_point_d_object(); + typename Kernel::Squared_distance_d k_sqdist = + m_k.squared_distance_d_object(); + typename Kernel::Difference_of_points_d k_diff_pts = + m_k.difference_of_points_d_object(); + typename Kernel::Scalar_product_d k_scalar_pdct = + m_k.scalar_product_d_object(); + typename Kernel::Construct_weighted_point_d k_constr_wp = + m_k.construct_weighted_point_d_object(); + typename Kernel::Power_distance_d k_power_dist = + m_k.power_distance_d_object(); + + const Tr_traits &q_tr_traits = m_triangulations[q_idx].tr().geom_traits(); + typename Tr_traits::Power_center_d tr_power_center = + q_tr_traits.power_center_d_object(); + typename Tr_traits::Point_weight_d tr_point_weight = + q_tr_traits.point_weight_d_object(); + + //------------------------------------------------------------------------- + //1. Compute power_center(p'q'r1'r2'..ri') in Tp => Cp + //2. Compute power_center(inconsistent_simplex projected in Tq) + // => gives Cq and radius Rq + // Rq is also the radius of the ambient sphere S whose center is Cq and + // which goes through all the ambient points of "inconsistent_simplex" + //------------------------------------------------------------------------ + std::vector simplex_pts_in_Tp, simplex_pts_in_Tq; + simplex_pts_in_Tp.reserve(inconsistent_simplex.size()); + simplex_pts_in_Tq.reserve(inconsistent_simplex.size()); + + // No need to lock the mutex here since this will not be called while + // other threads are perturbing the positions + const Point pt_p = compute_perturbed_point(p_idx); + + Indexed_simplex::const_iterator it_point_idx = + inconsistent_simplex.begin(); + Indexed_simplex::const_iterator it_point_idx_end = + inconsistent_simplex.end(); + // For each point of the simplex, we reproject it onto the tangent + // space. Could be optimized since it's already been computed before. + for ( ; it_point_idx != it_point_idx_end ; ++it_point_idx) + { + const Weighted_point wp = compute_perturbed_weighted_point(*it_point_idx); + // No need to lock the Mutex_for_perturb here since this will not be + // called while other threads are perturbing the positions + simplex_pts_in_Tp.push_back(project_point_and_compute_weight( + wp, m_tangent_spaces[p_idx], q_tr_traits)); + simplex_pts_in_Tq.push_back(project_point_and_compute_weight( + wp, m_tangent_spaces[q_idx], q_tr_traits)); + } + + Tr_point Cp = tr_power_center( + simplex_pts_in_Tp.begin(), simplex_pts_in_Tp.end()); + Tr_point Cq = tr_power_center( + simplex_pts_in_Tq.begin(), simplex_pts_in_Tq.end()); + + FT circumsphere_sqradius_p = tr_point_weight(Cp); + FT circumsphere_sqradius_q = tr_point_weight(Cq); +#ifdef CGAL_TC_PERTURB_WEIGHT + FT squared_circumsphere_radius_q_plus_margin = + circumsphere_sqradius_q + 4*m_sq_half_sparsity; +#else + FT squared_circumsphere_radius_q_plus_margin = CGAL::square( + CGAL::sqrt(circumsphere_sqradius_q) + 2*m_half_sparsity); +#endif + + Weighted_point global_Cp = k_constr_wp( + unproject_point(Cp, m_tangent_spaces[p_idx], q_tr_traits), + circumsphere_sqradius_p); + + Weighted_point global_Cq = k_constr_wp( + unproject_point(Cq, m_tangent_spaces[q_idx], q_tr_traits), + circumsphere_sqradius_q); + + // CJTODO TEMP ==================== + /*{ + INS_range ins_range = m_points_ds.query_incremental_ANN(k_drop_w(global_Cp)); + for (INS_iterator nn_it = ins_range.begin() ; + nn_it != ins_range.end() ; + ++nn_it) + { + FT neighbor_sqdist = nn_it->second; + + //std::cerr << nn_it->first << " : " << neighbor_sqdist << " / "; // CJTODO TEMP + + // When we're sure we got all the potential points, break + //if (neighbor_sqdist > circumsphere_sqradius_p + m_sq_half_sparsity) + // break; + + std::size_t neighbor_point_idx = nn_it->first; + FT point_to_Cp_power_sqdist = k_power_dist( + global_Cp, compute_perturbed_weighted_point(neighbor_point_idx)); + //std::cerr << point_to_Cp_power_sqdist << std::endl; // CJTODO TEMP + // If the point is ACTUALLY "inside" S + if (point_to_Cp_power_sqdist <= FT(0) + && inconsistent_simplex.find(neighbor_point_idx) == + inconsistent_simplex.end()) + { + std::cerr << "Warning: " << neighbor_point_idx << " is inside Cp with power dist " << point_to_Cp_power_sqdist << "\n"; + } + } + }*/ + // /CJTODO ==================== + + //------------------------------------------------------------------------- + //3. Find points t1, t2... (in ambient space) which are inside S + //------------------------------------------------------------------------- + std::vector inside_pt_indices; + INS_range ins_range = m_points_ds.query_incremental_ANN(k_drop_w(global_Cq)); + for (INS_iterator nn_it = ins_range.begin() ; + nn_it != ins_range.end() ; + ++nn_it) + { + FT neighbor_sqdist = nn_it->second; + + // When we're sure we got all the potential points, break + if (neighbor_sqdist > squared_circumsphere_radius_q_plus_margin) + break; + + std::size_t neighbor_point_idx = nn_it->first; + FT point_to_Cq_power_sqdist = k_power_dist( + global_Cq, compute_perturbed_weighted_point(neighbor_point_idx)); + // If the point is ACTUALLY "inside" S + if (point_to_Cq_power_sqdist <= FT(0) + && inconsistent_simplex.find(neighbor_point_idx) == + inconsistent_simplex.end()) + { + inside_pt_indices.push_back(neighbor_point_idx); + } + // CJTODO: use this instead of point_to_Cq_power_sqdist? + /*{ + typename Tr_traits::Power_test_d side = q_tr_traits.power_test_d_object(); + typename Tr_traits::Orientation_d orient = q_tr_traits.orientation_d_object(); + Orientation o = orient(simplex_pts_in_Tq.begin(), simplex_pts_in_Tq.end()); + auto p = project_point_and_compute_weight( + compute_perturbed_weighted_point(neighbor_point_idx), + m_tangent_spaces[q_idx], q_tr_traits); + auto sid = (o == NEGATIVE ? + side(simplex_pts_in_Tq.rbegin(), simplex_pts_in_Tq.rend(), p) + : side(simplex_pts_in_Tq.begin(), simplex_pts_in_Tq.end(), p)); + switch(sid) + { + case ON_NEGATIVE_SIDE: + std::cerr << "ON_NEGATIVE_SIDE" << std::endl; // CJTODO TEMP + break; + case ON_POSITIVE_SIDE: + std::cerr << "ON_POSITIVE_SIDE" << std::endl; // CJTODO TEMP + break; + case ON_ORIENTED_BOUNDARY: + std::cerr << "ON_ORIENTED_BOUNDARY" << std::endl; // CJTODO TEMP + break; + } + }*/ + } + CGAL_assertion_msg(!inside_pt_indices.empty(), + "There should be at least one vertex inside the sphere"); + + // CJTODO TEMP DEBUG + /*if (inside_pt_indices.empty()) + { + //compute_tangent_triangulation(q_idx, true); + std::cerr << "Error: inside_pt_indices.empty()\n"; + std::cerr << "Stars:\n"; + for (auto s : m_stars[q_idx]) + { + std::cerr << q_idx << " "; + std::copy(s.begin(), s.end(), + std::ostream_iterator(std::cerr, " ")); + std::cerr << std::endl; + } + std::cerr << std::endl; + }*/ + + // CJTODO TEMP DEBUG + // If co-intrinsic dimension = 1, let's compare normals + /*if (m_ambient_dim - m_intrinsic_dim == 1) + { + typename Kernel::Scaled_vector_d k_scaled_vec = + m_k.scaled_vector_d_object(); + typename Kernel::Squared_length_d k_sqlen = + m_k.squared_length_d_object(); + Vector pq = k_diff_pts( + compute_perturbed_point(q_idx), compute_perturbed_point(p_idx)); + pq = k_scaled_vec(pq, FT(1)/sqrt(k_sqlen(pq))); + FT dot_product_1 = std::abs( + k_scalar_pdct(m_orth_spaces[p_idx][0], pq)); + FT dot_product_2 = std::abs( + k_scalar_pdct(m_orth_spaces[q_idx][0], pq)); + csv_stream << inside_pt_indices.size() << " ; "; + csv_stream << dot_product_1 << " ; " << dot_product_2; + csv_stream << std::endl; + }*/ + + // CJTODO TEMP DEBUG + if (inside_pt_indices.size() > 1) + { + std::cerr << "Warning: " << inside_pt_indices.size() << " insiders in " + << inconsistent_simplex.size() - 1 << " simplex\n"; + + // If co-intrinsic dimension = 1, let's compare normals + /*if (m_ambient_dim - m_intrinsic_dim == 1) + { + std::cerr << "(dot product between normals = "; + Indexed_simplex::const_iterator it_v = + inconsistent_simplex.begin(); + std::size_t i1 = *it_v; + ++it_v; + for ( ; it_v != inconsistent_simplex.end() ; ++it_v) + { + FT dot_products_between_normals = + k_scalar_pdct(m_tangent_spaces[i1][0], m_tangent_spaces[*it_v][0]); + std::cerr << dot_products_between_normals << ", "; + //csv_stream << " ; " < 1) + { + //----------------------------------------------------------------------- + //5. For each ti, compute the sphere that goes through + // p, q, r1, r2..., ri and ti whose center is on (cp, cq) + // We're looking for a point on (Cp, Cq) at equal distance from p and + // ti. + // The center of the sphere is then: Cp + a(Cq - Cp) + // where a = (sqdist(Cp,ti) - sqdist(Cp,p)) / (2*(Cq-Cp).(ti-p)) + //6. Keep point ti such as dist(cp, ci) is the smallest + //----------------------------------------------------------------------- + + FT min_a = std::numeric_limits::max(); + for (std::size_t i = 0 ; i < inside_pt_indices.size() ; ++i) + { + std::size_t idx = inside_pt_indices[i]; + const Point ti = compute_perturbed_point(idx); + const Point &cp = k_drop_w(global_Cp); + const Point &cq = k_drop_w(global_Cq); + +#ifdef CGAL_TC_PERTURB_WEIGHT + const Weighted_point ti_w = compute_perturbed_weighted_point(idx); + const Weighted_point p_w = compute_perturbed_weighted_point(p_idx); + const Weighted_point cp_w0 = k_constr_wp(k_drop_w(global_Cp), FT(0)); + const Weighted_point wp_w0 = k_constr_wp(k_drop_w(global_Cq), FT(0)); + FT a = + (k_power_dist(cp_w0, ti_w) - k_power_dist(cp_w0, p_w)) / + (FT(2)*k_scalar_pdct(k_diff_pts(cq, cp), k_diff_pts(ti, pt_p))); +#else + FT a = + (k_sqdist(cp, ti) - k_sqdist(cp, pt_p)) / + (FT(2)*k_scalar_pdct(k_diff_pts(cq, cp), k_diff_pts(ti, pt_p))); +#endif + + if (a < min_a) + { + min_a = a; + inside_point_idx = idx; + } + } + + // CJTODO TEMP ==================== + /*{ + typename Kernel::Scaled_vector_d scaled_vec = m_k.scaled_vector_d_object(); + typename Kernel::Point_weight_d k_weight = m_k.point_weight_d_object(); + Weighted_point C = k_constr_wp( + k_transl(k_drop_w(global_Cp), scaled_vec(k_diff_pts(k_drop_w(global_Cq), k_drop_w(global_Cp)), min_a)), + k_sqdist(k_transl(k_drop_w(global_Cp), scaled_vec(k_diff_pts(k_drop_w(global_Cq), k_drop_w(global_Cp)), min_a)), pt_p)); + INS_range ins_range = m_points_ds.query_incremental_ANN(k_drop_w(C)); + for (INS_iterator nn_it = ins_range.begin() ; + nn_it != ins_range.end() ; + ++nn_it) + { + FT neighbor_sqdist = nn_it->second; + + //std::cerr << nn_it->first << " : " << neighbor_sqdist << " / "; // CJTODO TEMP + + // When we're sure we got all the potential points, break + if (neighbor_sqdist > k_weight(C) + m_sq_half_sparsity) + break; + + std::size_t neighbor_point_idx = nn_it->first; + FT point_to_C_power_sqdist = + k_power_dist(C, compute_perturbed_weighted_point(neighbor_point_idx)); + //std::cerr << point_to_Cp_power_sqdist << std::endl; // CJTODO TEMP + // If the point is ACTUALLY "inside" S + if (point_to_C_power_sqdist <= FT(-0.000001) + && inconsistent_simplex.find(neighbor_point_idx) == + inconsistent_simplex.end()) + { + std::cerr << "Warning: " << neighbor_point_idx << " is inside C with power dist " << point_to_C_power_sqdist << "\n"; + } + } + }*/ + // /CJTODO ==================== + } + else + { + inside_point_idx = *inside_pt_indices.begin(); + } + + //------------------------------------------------------------------------- + //7. Create a k+1-simplex (inconsistent_simplex, ti) + //------------------------------------------------------------------------- + Indexed_simplex new_simplex = inconsistent_simplex; + new_simplex.insert(inside_point_idx); + + it_point_idx = new_simplex.begin(); + it_point_idx_end = new_simplex.end(); + for ( ; it_point_idx != it_point_idx_end ; ++it_point_idx) + { + insert_higher_dim_simplex_into_star(*it_point_idx, new_simplex); + } + // CJTODO: call + // check_and_solve_inconsistencies_by_adding_higher_dim_simplices + // recursively? Not sure, since the star will be parsed again from + // the beginning + } + + // Test and solve inconsistencies of a simplex. + // Returns true if some inconsistencies were found. + // Precondition: incident_simplex is in the star of m_points[tr_index] + bool check_and_solve_inconsistencies_by_adding_higher_dim_simplices( + std::size_t tr_index, const Incident_simplex &incident_simplex) + { + bool inconsistencies_found = false; + + // Don't check infinite simplices + if (is_infinite(incident_simplex)) + return false; + + Indexed_simplex simplex = incident_simplex; + simplex.insert(tr_index); + + // Check if the simplex is in the stars of all its vertices + Incident_simplex::const_iterator it_point_idx = incident_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 != incident_simplex.end() ; ++it_point_idx) + { + std::size_t point_idx = *it_point_idx; + + Star const& star = m_stars[point_idx]; + + // What we're looking for is "simplex" \ point_idx + Incident_simplex is_to_find = simplex; + is_to_find.erase(point_idx); + + if (std::find(star.begin(), star.end(), is_to_find) == star.end()) + { + solve_inconsistency_by_adding_higher_dimensional_simplices( + tr_index, *it_point_idx, simplex); + inconsistencies_found = true; + break; + } + // CJTODO TEMP + /*else if (m_ambient_dim - m_intrinsic_dim == 1) + { + typename Kernel::Difference_of_points_d k_diff_pts = + m_k.difference_of_points_d_object(); + typename Kernel::Scaled_vector_d k_scaled_vec = + m_k.scaled_vector_d_object(); + typename Kernel::Squared_length_d k_sqlen = + m_k.squared_length_d_object(); + typename Kernel::Scalar_product_d k_scalar_pdct = + m_k.scalar_product_d_object(); + Vector pq = k_diff_pts( + compute_perturbed_point(*it_point_idx), compute_perturbed_point(tr_index)); + pq = k_scaled_vec(pq, FT(1)/sqrt(k_sqlen(pq))); + FT dot_product_1 = std::abs( + k_scalar_pdct(m_orth_spaces[tr_index][0], pq)); + FT dot_product_2 = std::abs( + k_scalar_pdct(m_orth_spaces[*it_point_idx][0], pq)); + csv_stream << "0 ; "; + csv_stream << dot_product_1 << " ; " << dot_product_2; + csv_stream << std::endl; + }*/ + } + + return inconsistencies_found; + } + + // P: dual face in Delaunay triangulation (p0, p1, ..., pn) + // Q: vertices which are common neighbors of all vertices of P + // Note the computation is made in local coordinates. "tsb"'s vectors are not + // used because these vectors become (0..., 1..., 0) in local coordinates. + template + CGAL::Quadratic_program_solution + compute_voronoi_face_and_tangent_subspace_LP_problem( + int points_dim, + Weighted_point_range_a const& P, + Weighted_point_range_b const& Q, + Tangent_space_basis const& tsb, + const Tr_traits &tr_traits) const + { + // Notations: + // Fv: Voronoi k-face + // Fd: dual, (D-k)-face of Delaunay (p0, p1, ..., pn) + + typename Tr_traits::Point_drop_weight_d drop_w = + tr_traits.point_drop_weight_d_object(); + typename Tr_traits::Point_weight_d point_weight = + tr_traits.point_weight_d_object(); + typename Tr_traits::Scalar_product_d scalar_pdct = + tr_traits.scalar_product_d_object(); + typename Tr_traits::Point_to_vector_d pt_to_vec = + tr_traits.point_to_vector_d_object(); + typename Tr_traits::Compute_coordinate_d coord = + tr_traits.compute_coordinate_d_object(); + + typename Kernel::Compute_coordinate_d k_coord = + m_k.compute_coordinate_d_object(); + + std::size_t card_P = P.size(); + std::size_t card_Q = Q.size(); + + // Linear solver + typedef CGAL::Quadratic_program Linear_program; + typedef CGAL::Quadratic_program_solution LP_solution; + + Linear_program lp(CGAL::SMALLER, false); + int current_row = 0; + + //=========== First set of equations =========== + // For points pi in P + // 2(p0 - pi).x = p0^2 - wght(p0) - pi^2 + wght(pi) + typename Weighted_point_range_a::const_iterator it_p = P.begin(); + Tr_point const& p0 = *it_p; + FT const w0 = point_weight(p0); + FT p0_dot_p0 = scalar_pdct(pt_to_vec(drop_w(p0)), pt_to_vec(drop_w(p0))); + ++it_p; + for (typename Weighted_point_range_a::const_iterator it_p_end = P.end() ; + it_p != it_p_end ; ++it_p) + { + Tr_point const& pi = *it_p; + FT const wi = point_weight(pi); + + for (int k = 0 ; k < points_dim ; ++k) + lp.set_a(k, current_row, 2*(coord(p0, k) - coord(pi, k))); + + FT pi_dot_pi = scalar_pdct(pt_to_vec(drop_w(pi)), pt_to_vec(drop_w(pi))); + lp.set_b(current_row, p0_dot_p0 - pi_dot_pi - w0 + wi); + lp.set_r(current_row, CGAL::EQUAL); + + ++current_row; + } + + //=========== Second set of equations =========== + // For each point qi in Q + // 2(qi - p0).x <= qi^2 - wght(pi) - p0^2 + wght(p0) + for (typename Weighted_point_range_b::const_iterator it_q = Q.begin(), + it_q_end = Q.end() ; + it_q != it_q_end ; ++it_q) + { + Tr_point const& qi = *it_q; + FT const wi = point_weight(qi); + + for (int k = 0 ; k < points_dim ; ++k) + lp.set_a(k, current_row, 2*(coord(qi, k) - coord(p0, k))); + + FT qi_dot_qi = scalar_pdct(pt_to_vec(drop_w(qi)), pt_to_vec(drop_w(qi))); + lp.set_b(current_row, qi_dot_qi - wi - p0_dot_p0 + w0); + + ++current_row; + } + + //=========== Third set of equations =========== + // For each thickening vector bj of TSB, + // x.bj <= alpha_plus and >= alpha_minus + // where bj is in the TSB => bj = (0..., 1..., 0) (1 is at the jth position) + // x.bj <= alpha_plus + // -x.bj <= -alpha_minus + std::size_t j = points_dim - tsb.num_thickening_vectors(); + for (Tangent_space_basis::Thickening_vectors::const_iterator + it_tv = tsb.thickening_vectors().begin(), + it_tv_end = tsb.thickening_vectors().end() ; + it_tv != it_tv_end ; ++it_tv) + { + Tangent_space_basis::Thickening_vector const& bj = *it_tv; + + for (int k = 0 ; k < points_dim ; ++k) + { + lp.set_a(k, current_row , (j == k ? 1. : 0.)); + lp.set_a(k, current_row + 1, (j == k ? -1. : 0.)); + } + + lp.set_b(current_row , bj.alpha_plus); + lp.set_b(current_row + 1, -bj.alpha_minus); + + current_row += 2; + ++j; + } + + //=========== Other LP parameters =========== + lp.set_c(0, 1); // Minimize x[0] + + //=========== Solve ========================= + LP_solution solution = CGAL::solve_linear_program(lp, ET()); + return solution; + } + + // P: dual face in Delaunay triangulation (p0, p1, ..., pn) + // Q: vertices which are common neighbors of all vertices of P + template + bool does_voronoi_face_and_tangent_subspace_intersect( + int points_dim, + Weighted_point_range_a const& P, + Weighted_point_range_b const& Q, + Tangent_space_basis const& tsb, + const Tr_traits &tr_traits) const + { + return compute_voronoi_face_and_tangent_subspace_LP_problem( + points_dim, P, Q, tsb, tr_traits).status() == CGAL::QP_OPTIMAL; + } + + // Returns any point of the intersection between aff(voronoi_cell) and a + // tangent space. + // P: dual face in Delaunay triangulation (p0, p1, ..., pn) + // Return value: the point coordinates are expressed in the tsb base + template + boost::optional + compute_aff_of_voronoi_face_and_tangent_subspace_intersection( + int points_dim, + Weighted_point_range const& P, + Tangent_space_basis const& tsb, + const Tr_traits &tr_traits) const + { + // As we're only interested by aff(v), Q is empty + return compute_voronoi_face_and_tangent_subspace_intersection( + points_dim, P, std::vector(), + tsb, tr_traits); + } + + // Returns any point of the intersection between a Voronoi cell and a + // tangent space. + // P: dual face in Delaunay triangulation (p0, p1, ..., pn) + // Q: vertices which are common neighbors of all vertices of P + // Return value: the point coordinates are expressed in the tsb base + template + boost::optional + compute_voronoi_face_and_tangent_subspace_intersection( + int points_dim, + Weighted_point_range_a const& P, + Weighted_point_range_b const& Q, + Tangent_space_basis const& tsb, + const Tr_traits &tr_traits) const + { + typedef CGAL::Quadratic_program_solution LP_solution; + + LP_solution sol = compute_voronoi_face_and_tangent_subspace_LP_problem( + points_dim, P, Q, tsb, tr_traits); + + boost::optional ret; + if (sol.status() == CGAL::QP_OPTIMAL) + { + std::vector p; + p.reserve(points_dim); + for (LP_solution::Variable_value_iterator + it_v = sol.variable_values_begin(), + it_v_end = sol.variable_values_end() ; + it_v != it_v_end ; ++it_v) + { + p.push_back(to_double(*it_v)); + } + ret = tr_traits.construct_point_d_object()(points_dim, p.begin(), p.end()); + } + else + { + ret = boost::none; + } + + return ret; + } + + // P: dual face in Delaunay triangulation (p0, p1, ..., pn) + // Q: vertices which are common neighbors of all vertices of P + template + bool does_voronoi_face_and_fixed_alpha_tangent_subspace_intersect( + std::size_t center_pt_index, + Indexed_point_range_a const& P, + Indexed_point_range_b const& Q, + Orthogonal_space_basis const& orthogonal_subspace_basis, + FT alpha) const + { + // Notations: + // Fv: Voronoi k-face + // Fd: dual, (D-k)-face of Delaunay (p0, p1, ..., pn) + + typename Kernel::Scalar_product_d scalar_pdct = m_k.scalar_product_d_object(); + typename Kernel::Point_to_vector_d pt_to_vec = m_k.point_to_vector_d_object(); + typename Kernel::Compute_coordinate_d coord = m_k.compute_coordinate_d_object(); + + Point center_pt = compute_perturbed_point(center_pt_index); + int const ambient_dim = m_k.point_dimension_d_object()(center_pt); + + std::size_t card_P = P.size(); + std::size_t card_Q = Q.size(); + + // Linear solver + typedef CGAL::Quadratic_program Linear_program; + typedef CGAL::Quadratic_program_solution LP_solution; + + Linear_program lp(CGAL::SMALLER, false); + int current_row = 0; + + //=========== First set of equations =========== + // For points pi in P + // 2(p0 - pi).x = p0^2 - w0 - pi^2 + wi + Point const& p0 = center_pt; + FT const w0 = m_weights[center_pt_index]; + FT p0_dot_p0 = scalar_pdct(pt_to_vec(p0), pt_to_vec(p0)); + + for (typename Indexed_point_range_a::const_iterator it_p = P.begin(), + it_p_end = P.end() ; + it_p != it_p_end ; ++it_p) + { + Point pi; + FT wi; + compute_perturbed_weighted_point(*it_p, pi, wi); + + for (int k = 0 ; k < ambient_dim ; ++k) + lp.set_a(k, current_row, 2*(coord(p0, k) - coord(pi, k))); + + FT pi_dot_pi = scalar_pdct(pt_to_vec(pi), pt_to_vec(pi)); + lp.set_b(current_row, p0_dot_p0 - pi_dot_pi - w0 + wi); + lp.set_r(current_row, CGAL::EQUAL); + + ++current_row; + } + + // CJTODO: this code might be useful for Option 1 + /*CGAL::Combination_enumerator pi_pj(2, 0, static_cast(card_P)); + for ( ; !pi_pj.finished() ; ++pi_pj) + { + Point const& pi = P[pi_pj[0]]; + FT wi = all_weights[pi_pj[0]]; + Point const& pj = P[pi_pj[1]]; + FT wj = all_weights[pi_pj[1]]; + + for (int k = 0 ; k < ambient_dim ; ++k) + { + FT a = 2*(coord(pi, k) + coord(pj, k)); + lp.set_a(k, current_row , -a); + lp.set_a(k, current_row + 1, a); + } + + FT b = scalar_pdct(pi, pi) - wi - scalar_pdct(pj, pj) + wj; + lp.set_b(current_row , -b); + lp.set_b(current_row + 1, b); + + current_row += 2; + }*/ + + //=========== Second set of equations =========== + // For each point qi in Q + // 2(qi - p0).x <= qi^2 - wi - p0^2 + w0 + for (typename Indexed_point_range_b::const_iterator it_q = Q.begin(), + it_q_end = Q.end() ; + it_q != it_q_end ; ++it_q) + { + Point qi; + FT wi; + compute_perturbed_weighted_point(*it_q, qi, wi); + + for (int k = 0 ; k < ambient_dim ; ++k) + lp.set_a(k, current_row, 2*(coord(qi, k) - coord(p0, k))); + + FT qi_dot_qi = scalar_pdct(pt_to_vec(qi), pt_to_vec(qi)); + lp.set_b(current_row, qi_dot_qi - wi - p0_dot_p0 + w0); + + ++current_row; + } + + //=========== Third set of equations =========== + // For each vector bi of OSB, (x-p).bi <= alpha and >= -alpha + // p is the origin of the basis + // bi.x <= bi.p + alpha + // -bi.x <= -bi.p + alpha + for (Orthogonal_space_basis::const_iterator it_osb = + orthogonal_subspace_basis.begin(), + it_osb_end = orthogonal_subspace_basis.end() ; + it_osb != it_osb_end ; ++it_osb) + { + Vector const& bi = *it_osb; + + for (int k = 0 ; k < ambient_dim ; ++k) + { + lp.set_a(k, current_row , coord(bi, k)); + lp.set_a(k, current_row + 1, -coord(bi, k)); + } + + FT bi_dot_p = scalar_pdct(bi, + pt_to_vec(compute_perturbed_point(orthogonal_subspace_basis.origin()))); + lp.set_b(current_row , bi_dot_p + alpha); + lp.set_b(current_row + 1, -bi_dot_p + alpha); + + current_row += 2; + } + + //=========== Other LP parameters =========== + lp.set_c(0, 1); // Minimize x[0] + + //=========== Solve ========================= + LP_solution solution = CGAL::solve_linear_program(lp, ET()); + bool ret = (solution.status() == CGAL::QP_OPTIMAL); + + return ret; + } + + std::ostream &export_simplices_to_off( + std::ostream & os, std::size_t &num_OFF_simplices, + bool color_inconsistencies = false, + std::set const *p_simpl_to_color_in_red = NULL, + std::set const *p_simpl_to_color_in_green = NULL, + std::set const *p_simpl_to_color_in_blue = NULL) + const + { + // If m_intrinsic_dim = 1, each point is output two times + // (see export_vertices_to_off) + num_OFF_simplices = 0; + std::size_t num_maximal_simplices = 0; + std::size_t num_inconsistent_maximal_simplices = 0; + std::size_t num_inconsistent_stars = 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 (std::size_t idx = 0 ; it_tr != it_tr_end ; ++it_tr, ++idx) + { + bool is_star_inconsistent = false; + + Triangulation const& tr = it_tr->tr(); + Tr_vertex_handle center_vh = it_tr->center_vertex(); + + if (&tr == NULL || tr.current_dimension() < m_intrinsic_dim) + continue; + + // Color for this star + std::stringstream color; + //color << rand()%256 << " " << 100+rand()%156 << " " << 100+rand()%156; + color << 128 << " " << 128 << " " << 128; + + // Gather the triangles here, with an int telling its color + typedef std::vector > + Star_using_triangles; + Star_using_triangles star_using_triangles; + + // For each cell of the star + Star::const_iterator it_inc_simplex = m_stars[idx].begin(); + Star::const_iterator it_inc_simplex_end = m_stars[idx].end(); + for ( ; it_inc_simplex != it_inc_simplex_end ; ++it_inc_simplex) + { + Indexed_simplex c = *it_inc_simplex; + c.insert(idx); + std::size_t num_vertices = c.size(); + ++num_maximal_simplices; + + int color_simplex = -1;// -1=no color, 0=yellow, 1=red, 2=green, 3=blue + if (color_inconsistencies && !is_simplex_consistent(c)) + { + ++num_inconsistent_maximal_simplices; + color_simplex = 0; + is_star_inconsistent = true; + } + else + { + if (p_simpl_to_color_in_red && + std::find( + p_simpl_to_color_in_red->begin(), + p_simpl_to_color_in_red->end(), + c) != p_simpl_to_color_in_red->end()) + { + color_simplex = 1; + } + else if (p_simpl_to_color_in_green && + std::find( + p_simpl_to_color_in_green->begin(), + p_simpl_to_color_in_green->end(), + c) != p_simpl_to_color_in_green->end()) + { + color_simplex = 2; + } + else if (p_simpl_to_color_in_blue && + std::find( + p_simpl_to_color_in_blue->begin(), + p_simpl_to_color_in_blue->end(), + c) != p_simpl_to_color_in_blue->end()) + { + color_simplex = 3; + } + } + + // If m_intrinsic_dim = 1, each point is output two times, + // so we need to multiply each index by 2 + // And if only 2 vertices, add a third one (each vertex is duplicated in + // the file when m_intrinsic dim = 2) + if (m_intrinsic_dim == 1) + { + Indexed_simplex tmp_c; + Indexed_simplex::iterator it = c.begin(); + for ( ; it != c.end() ; ++it) + tmp_c.insert(*it * 2); + if (num_vertices == 2) + tmp_c.insert(*tmp_c.rbegin() + 1); + + c = tmp_c; + } + + if (num_vertices <= 3) + { + star_using_triangles.push_back(std::make_pair(c, color_simplex)); + } + else + { + // num_vertices >= 4: decompose the simplex in triangles + std::vector booleans(num_vertices, false); + std::fill(booleans.begin() + num_vertices - 3, booleans.end(), true); + do + { + Indexed_simplex triangle; + Indexed_simplex::iterator it = c.begin(); + for (int i = 0; it != c.end() ; ++i, ++it) + { + if (booleans[i]) + triangle.insert(*it); + } + star_using_triangles.push_back( + std::make_pair(triangle, color_simplex)); + } while (std::next_permutation(booleans.begin(), booleans.end())); + } + } + + // For each cell + Star_using_triangles::const_iterator it_simplex = + star_using_triangles.begin(); + Star_using_triangles::const_iterator it_simplex_end = + star_using_triangles.end(); + for ( ; it_simplex != it_simplex_end ; ++it_simplex) + { + // Don't export infinite cells + if (is_infinite(it_simplex->first)) + continue; + + const Indexed_simplex &c = it_simplex->first; + int color_simplex = it_simplex->second; + + std::stringstream sstr_c; + + Indexed_simplex::const_iterator it_point_idx = c.begin(); + for ( ; it_point_idx != c.end() ; ++it_point_idx) + { + sstr_c << *it_point_idx << " "; + } + + // In order to have only one time each simplex, we only keep it + // if the lowest index is the index of the center vertex + // CJTODO: uncomment? but it only works if there's no inconsistencies + /*if (*c.begin() != (m_intrinsic_dim == 1 ? 2*idx : idx) + && color_simplex == -1) + continue;*/ + + os << 3 << " " << sstr_c.str(); + if (color_inconsistencies || p_simpl_to_color_in_red + || p_simpl_to_color_in_green || p_simpl_to_color_in_blue) + { + switch (color_simplex) + { + case 0: os << " 255 255 0"; break; + case 1: os << " 255 0 0"; break; + case 2: os << " 0 255 0"; break; + case 3: os << " 0 0 255"; break; + default: os << " " << color.str(); break; + } + } + ++num_OFF_simplices; + os << std::endl; + } + if (is_star_inconsistent) + ++num_inconsistent_stars; + } + +#ifdef CGAL_TC_VERBOSE + std::cerr << std::endl + << "==========================================================" + << std::endl + << "Export from list of stars to OFF:\n" + << " * Number of vertices: " << m_points.size() << std::endl + << " * Total number of maximal simplices: " << num_maximal_simplices + << std::endl; + if (color_inconsistencies) + { + std::cerr + << " * Number of inconsistent stars: " + << num_inconsistent_stars << " (" + << (m_points.size() > 0 ? + 100. * num_inconsistent_stars / m_points.size() : 0.) << "%)" + << std::endl + << " * Number of inconsistent maximal simplices: " + << num_inconsistent_maximal_simplices << " (" + << (num_maximal_simplices > 0 ? + 100. * num_inconsistent_maximal_simplices / num_maximal_simplices + : 0.) << "%)" + << std::endl; + } + std::cerr << "==========================================================" + << std::endl; +#endif + + return os; + } + +public: + std::ostream &export_simplices_to_off( + const Simplicial_complex &complex, + std::ostream & os, std::size_t &num_OFF_simplices, + std::set const *p_simpl_to_color_in_red = NULL, + std::set const *p_simpl_to_color_in_green = NULL, + std::set const *p_simpl_to_color_in_blue = NULL) + const + { + typedef Simplicial_complex::Simplex Simplex; + typedef Simplicial_complex::Simplex_range Simplex_range; + + // If m_intrinsic_dim = 1, each point is output two times + // (see export_vertices_to_off) + num_OFF_simplices = 0; + std::size_t num_maximal_simplices = 0; + + typename Simplex_range::const_iterator it_s = + complex.simplex_range().begin(); + typename Simplex_range::const_iterator it_s_end = + complex.simplex_range().end(); + // For each simplex + for ( ; it_s != it_s_end ; ++it_s) + { + Simplex c = *it_s; + ++num_maximal_simplices; + + int color_simplex = -1;// -1=no color, 0=yellow, 1=red, 2=green, 3=blue + if (p_simpl_to_color_in_red && + std::find( + p_simpl_to_color_in_red->begin(), + p_simpl_to_color_in_red->end(), + c) != p_simpl_to_color_in_red->end()) + { + color_simplex = 1; + } + else if (p_simpl_to_color_in_green && + std::find( + p_simpl_to_color_in_green->begin(), + p_simpl_to_color_in_green->end(), + c) != p_simpl_to_color_in_green->end()) + { + color_simplex = 2; + } + else if (p_simpl_to_color_in_blue && + std::find( + p_simpl_to_color_in_blue->begin(), + p_simpl_to_color_in_blue->end(), + c) != p_simpl_to_color_in_blue->end()) + { + color_simplex = 3; + } + + // Gather the triangles here + typedef std::vector Triangles; + Triangles triangles; + + std::size_t num_vertices = c.size(); + // Do not export smaller dimension simplices + if (num_vertices < m_intrinsic_dim + 1) + continue; + + // If m_intrinsic_dim = 1, each point is output two times, + // so we need to multiply each index by 2 + // And if only 2 vertices, add a third one (each vertex is duplicated in + // the file when m_intrinsic dim = 2) + if (m_intrinsic_dim == 1) + { + Indexed_simplex tmp_c; + Indexed_simplex::iterator it = c.begin(); + for ( ; it != c.end() ; ++it) + tmp_c.insert(*it * 2); + if (num_vertices == 2) + tmp_c.insert(*tmp_c.rbegin() + 1); + + c = tmp_c; + } + + if (num_vertices <= 3) + { + triangles.push_back(c); + } + else + { + // num_vertices >= 4: decompose the simplex in triangles + std::vector booleans(num_vertices, false); + std::fill(booleans.begin() + num_vertices - 3, booleans.end(), true); + do + { + Indexed_simplex triangle; + Indexed_simplex::iterator it = c.begin(); + for (int i = 0; it != c.end() ; ++i, ++it) + { + if (booleans[i]) + triangle.insert(*it); + } + triangles.push_back(triangle); + } + while (std::next_permutation(booleans.begin(), booleans.end())); + } + + // For each cell + Triangles::const_iterator it_tri = triangles.begin(); + Triangles::const_iterator it_tri_end = triangles.end(); + for ( ; it_tri != it_tri_end ; ++it_tri) + { + // Don't export infinite cells + if (is_infinite(*it_tri)) + continue; + + os << 3 << " "; + Indexed_simplex::const_iterator it_point_idx = it_tri->begin(); + for ( ; it_point_idx != it_tri->end() ; ++it_point_idx) + { + os << *it_point_idx << " "; + } + + if (p_simpl_to_color_in_red || p_simpl_to_color_in_green + || p_simpl_to_color_in_blue) + { + switch (color_simplex) + { + case 0: os << " 255 255 0"; break; + case 1: os << " 255 0 0"; break; + case 2: os << " 0 255 0"; break; + case 3: os << " 0 0 255"; break; + default: os << " 128 128 128"; break; + } + } + + ++num_OFF_simplices; + os << std::endl; + } + } + +#ifdef CGAL_TC_VERBOSE + std::cerr << std::endl + << "==========================================================" + << std::endl + << "Export from complex to OFF:\n" + << " * Number of vertices: " << m_points.size() << std::endl + << " * Total number of maximal simplices: " << num_maximal_simplices + << std::endl + << "==========================================================" + << std::endl; +#endif + + return os; + } + + // Return a pair + void check_correlation_between_inconsistencies_and_fatness() const + { + std::ofstream csv_consistent("output/correlation_consistent.csv"); // CJTODO TEMP + std::ofstream csv_inconsistent("output/correlation_inconsistent.csv"); // CJTODO TEMP + if (m_intrinsic_dim < 3) + { + std::cerr << std::endl + << "==========================================================" << std::endl + << "check_correlation_between_inconsistencies_and_fatness():" << std::endl + << "Intrinsic dimension should be >= 3." << std::endl + << "==========================================================" << std::endl + << std::endl; + } + + std::size_t num_consistent_simplices = 0; + double sum_vol_edge_ratio_consistent = 0.; + std::size_t num_inconsistent_simplices = 0; + double sum_vol_edge_ratio_inconsistent = 0.; + // For each triangulation + for (std::size_t idx = 0 ; idx < m_points.size() ; ++idx) + { + // For each cell + Star::const_iterator it_inc_simplex = m_stars[idx].begin(); + Star::const_iterator it_inc_simplex_end = m_stars[idx].end(); + for ( ; it_inc_simplex != it_inc_simplex_end ; ++it_inc_simplex) + { + // Don't check infinite cells + if (is_infinite(*it_inc_simplex)) + continue; + + Indexed_simplex c = *it_inc_simplex; + c.insert(idx); // Add the missing index + + double fatness = compute_simplex_fatness(c); + + if (!is_simplex_consistent(c)) + { + ++num_inconsistent_simplices; + sum_vol_edge_ratio_inconsistent += fatness; + csv_inconsistent << fatness << std::endl; + } + else + { + ++num_consistent_simplices; + sum_vol_edge_ratio_consistent += fatness; + csv_consistent << fatness << std::endl; + } + } + } + + double avg_vol_edge_ratio_inconsistent = + sum_vol_edge_ratio_inconsistent / num_inconsistent_simplices; + double avg_vol_edge_ratio_consistent = + sum_vol_edge_ratio_consistent / num_consistent_simplices; + + std::cerr << std::endl + << "==========================================================" + << std::endl + << "check_correlation_between_inconsistencies_and_fatness()\n" + << " * Avg. volume/longest_edge^d ratio of consistent simplices: " + << avg_vol_edge_ratio_consistent + << " (" << num_consistent_simplices << " simplices)" << std::endl + << " * Avg. volume/longest_edge^d ratio of inconsistent simplices: " + << avg_vol_edge_ratio_inconsistent + << " (" << num_inconsistent_simplices << " simplices)" << std::endl + << "==========================================================" + << std::endl; + } + +private: + const Kernel m_k; + const int m_intrinsic_dim; + const double m_half_sparsity; + const double m_sq_half_sparsity; + const int m_ambient_dim; + + Points m_points; + Weights m_weights; +#ifdef CGAL_TC_PERTURB_WEIGHT + Weights_memory m_weights_memory; +#endif +#ifdef CGAL_TC_PERTURB_POSITION + Translations_for_perturb m_translations; +# if defined(CGAL_LINKED_WITH_TBB) && defined(CGAL_TC_GLOBAL_REFRESH) + Mutex_for_perturb *m_p_perturb_mutexes; +# endif +#endif +#ifdef CGAL_TC_PERTURB_TANGENT_SPACE + std::vector > m_perturb_tangent_space; +#endif + + Points_ds m_points_ds; + std::vector m_are_tangent_spaces_computed; + TS_container m_tangent_spaces; +#ifdef CGAL_TC_EXPORT_NORMALS + OS_container m_orth_spaces; +#endif + Tr_container m_triangulations; // Contains the triangulations + // and their center vertex + Stars_container m_stars; +#ifdef CGAL_LINKED_WITH_TBB + //std::vector m_tr_mutexes; +#endif + +#ifdef USE_ANOTHER_POINT_SET_FOR_TANGENT_SPACE_ESTIM + Points m_points_for_tse; + Points_ds m_points_ds_for_tse; +#endif + + mutable CGAL::Random m_random_generator; + +}; // /class Tangential_complex + +} // end namespace CGAL + +#endif // TANGENTIAL_COMPLEX_H diff --git a/Tangential_complex/include/CGAL/Tangential_complex/Point_cloud.h b/Tangential_complex/include/CGAL/Tangential_complex/Point_cloud.h new file mode 100644 index 00000000000..6a273ce28fe --- /dev/null +++ b/Tangential_complex/include/CGAL/Tangential_complex/Point_cloud.h @@ -0,0 +1,391 @@ +// 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 POINT_CLOUD_H +#define POINT_CLOUD_H + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#ifdef CGAL_TC_ANN_IS_AVAILABLE +# include +#endif + +#ifdef CGAL_TC_NANOFLANN_IS_AVAILABLE +# include "nanoflann.hpp" +#endif + +namespace CGAL { +namespace Tangential_complex_ { + +template +class Point_cloud_data_structure +{ +public: + typedef typename Point_container_::value_type Point; + typedef K Kernel; + typedef typename Kernel::FT FT; + + typedef CGAL::Search_traits< + FT, Point, + typename Kernel::Cartesian_const_iterator_d, + typename Kernel::Construct_cartesian_const_iterator_d> Traits_base; + // using a pointer as a special property map type + typedef CGAL::Search_traits_adapter< + std::ptrdiff_t, Point*, Traits_base> STraits; + + typedef CGAL::Orthogonal_k_neighbor_search K_neighbor_search; + typedef typename K_neighbor_search::Tree Tree; + typedef typename K_neighbor_search::Distance Distance; + typedef typename K_neighbor_search::iterator KNS_iterator; + typedef K_neighbor_search KNS_range; + + typedef CGAL::Orthogonal_incremental_neighbor_search< + STraits, Distance, CGAL::Sliding_midpoint, Tree> + Incremental_neighbor_search; + typedef typename Incremental_neighbor_search::iterator INS_iterator; + typedef Incremental_neighbor_search INS_range; + + /// Constructor + Point_cloud_data_structure(Point_container_ const& points) + : m_points(points), + m_tree(boost::counting_iterator(0), + boost::counting_iterator(points.size()), + typename Tree::Splitter(), + STraits((Point*)&(points[0])) ) + { + // Build the tree now (we don't want to wait for the first query) + m_tree.build(); + } + + /// Constructor + Point_cloud_data_structure( + Point_container_ const& points, + std::size_t begin_idx, std::size_t past_the_end_idx) + : m_points(points), + m_tree( + boost::counting_iterator(begin_idx), + boost::counting_iterator(past_the_end_idx), + typename Tree::Splitter(), + STraits((Point*)&(points[0])) ) + { + // Build the tree now (we don't want to wait for the first query) + m_tree.build(); + } + + /*Point_container_ &points() + { + return m_points; + } + + const Point_container_ &points() const + { + return m_points; + }*/ + + // Be careful, this function invalidates the tree, + // which will be recomputed at the next query + void insert(std::ptrdiff_t point_idx) + { + m_tree.insert(point_idx); + } + + KNS_range query_ANN(const + Point &sp, + unsigned int k, + bool sorted = true) const + { + // Initialize the search structure, and search all N points + // Note that we need to pass the Distance explicitly since it needs to + // know the property map + K_neighbor_search search( + m_tree, + sp, + k, + FT(0), + true, + Distance_adapter >( + (Point*)&(m_points[0])), + sorted); + + return search; + } + + INS_range query_incremental_ANN(const Point &sp) const + { + // Initialize the search structure, and search all N points + // Note that we need to pass the Distance explicitly since it needs to + // know the property map + Incremental_neighbor_search search( + m_tree, + sp, + FT(0), + true, + Distance_adapter >( + (Point*)&(m_points[0])) ); + + return search; + } + +protected: + Point_container_ const& m_points; + Tree m_tree; +}; + +//***************************************************************************** +//***************************************************************************** +//***************************************************************************** + +#ifdef CGAL_TC_NANOFLANN_IS_AVAILABLE + +// "dataset to kd-tree" adaptor class +template +class Point_cloud_adaptator__nanoflann +{ +public: + typedef typename Point_container_::value_type Point; + typedef typename CGAL::Kernel_traits::type Kernel; + typedef typename Kernel::FT FT; + + /// The constructor that sets the data set source + Point_cloud_adaptator__nanoflann(Point_container_ const& points, Kernel const& k) + : m_points(points), m_k(k) + {} + + /// CRTP helper method + inline Point_container_ const& points() const + { + return m_points; + } + inline Point_container_& points() + { + return m_points; + } + + // Must return the number of data points + inline size_t kdtree_get_point_count() const + { + return m_points.size(); + } + + // Returns the distance between the vector "p1[0:size-1]" + // and the data point with index "idx_p2" stored in the class: + inline FT kdtree_distance( + const FT *p1, const size_t idx_p2, size_t size) const + { + Point sp(p1, p1 + size); + return m_k.squared_distance_d_object()(sp, points()[idx_p2]); + } + + // Returns the dim'th component of the idx'th point in the class: + // Since this is inlined and the "dim" argument is typically an + // immediate value, the "if/else's" are actually solved at compile time. + inline FT kdtree_get_pt(const size_t idx, int dim) const + { + return m_k.compute_coordinate_d_object()(points()[idx], dim); + } + + // Optional bounding-box computation: return false to default to a standard + // bbox computation loop. + // Return true if the BBOX was already computed by the class and returned + // in "bb" so it can be avoided to redo it again. + // Look at bb.size() to find out the expected dimensionality + // (e.g. 2 or 3 for point clouds) + template + bool kdtree_get_bbox(Bbox &bb) const + { + return false; + } + + Kernel const& kernel() const + { + return m_k; + } + +protected: + Point_container_ const& m_points; //!< A const ref to the data set origin + Kernel const& m_k; //!< A const ref to the kernel + +}; + +template +class Point_cloud_data_structure__nanoflann +{ +public: + typedef typename Point_container_::value_type Point; + typedef typename K Kernel; + typedef typename Kernel::FT FT; + + /// Constructor + /// "points" must not be empty + Point_cloud_data_structure__nanoflann( + Point_container_ const& points, Kernel const& k) + : m_adaptor(points, k), + m_kd_tree(k.point_dimension_d_object()(*points.begin()), + m_adaptor, + nanoflann::KDTreeSingleIndexAdaptorParams(10 /* max leaf */) ) + { + m_kd_tree.buildIndex(); + } + + /*Point_container_ &points() + { + return m_adaptor.points(); + } + + const Point_container_ &points() const + { + return m_adaptor.points(); + }*/ + + void query_ANN(const Point &sp, + std::size_t k, + size_t *neighbor_indices, + FT *squared_distance) const + { + std::vector sp_vec( + m_adaptor.kernel().construct_cartesian_const_iterator_d_object()(sp), + m_adaptor.kernel().construct_cartesian_const_iterator_d_object()(sp, 0)); + nanoflann::KNNResultSet result_set(k); + result_set.init(neighbor_indices, squared_distance); + m_kd_tree.findNeighbors(result_set, + &sp_vec[0], + nanoflann::SearchParams()); + + /*std::cout << "knnSearch(nn="<< num_results <<"): \n"; + for (int i = 0 ; i < num_results ; ++i) + { + std::cout << " * neighbor_indices = " << neighbor_indices [i] + << " (out_dist_sqr = " << squared_distance[i] << ")" + << std::endl; + }*/ + } + + void query_ball(const Point &sp, + const FT radius, + std::vector > &neighbors, + bool sort_output = true) + { + std::vector sp_vec( + m_adaptor.kernel().construct_cartesian_const_iterator_d_object()(sp), + m_adaptor.kernel().construct_cartesian_const_iterator_d_object()(sp, 0)); + m_kd_tree.radiusSearch(&sp_vec[0], + radius, + neighbors, + nanoflann::SearchParams(32, 0.f, sort_output)); + + /*std::cout << "radiusSearch(num="<< neighbors.size() <<"): \n"; + for (const auto idx_and_dist : neighbors) + { + std::cout << " * neighbor_indices = " << idx_and_dist.first + << " (out_dist_sqr = " << idx_and_dist.second << ")" + << std::endl; + }*/ + } + +protected: + typedef Point_cloud_adaptator__nanoflann Adaptor; + typedef nanoflann::KDTreeSingleIndexAdaptor< + nanoflann::L2_Simple_Adaptor , + Adaptor, + -1 // dim + > Kd_tree; + + Adaptor m_adaptor; + Kd_tree m_kd_tree; +}; + +#endif //CGAL_TC_NANOFLANN_IS_AVAILABLE + +//***************************************************************************** +//***************************************************************************** +//***************************************************************************** + +#ifdef CGAL_TC_ANN_IS_AVAILABLE + +template +class Point_cloud_data_structure__ANN +{ +public: + typedef typename Point_container_::value_type Point; + typedef K Kernel; + typedef typename Kernel::FT FT; + + /// Constructor + Point_cloud_data_structure__ANN( + Point_container_ const& points, Kernel const& k) + : m_dim(k.point_dimension_d_object()(*points.begin())), + m_k(k), + m_points(annAllocPts(points.size(), m_dim)), + m_tree(m_points, points.size(), m_dim) + { + for (int i = 0 ; i < points.size() ; ++i) + { + for (int j = 0 ; j < m_dim ; ++j) + m_points[i][j] = m_k.compute_coordinate_d_object()(points[i], j); + } + } + + void query_ANN( + Point const& p, + unsigned int k, + ANNidxArray neighbors_indices, + ANNdistArray neighbors_sq_distances) + { + // Create an ANN query point + ANNpoint query_pt = annAllocPt(m_dim); + for (int j = 0 ; j < m_dim ; ++j) + query_pt[j] = m_k.compute_coordinate_d_object()(p, j); + + m_tree.annkSearch( // search + query_pt, // query point + k, // number of near neighbors + neighbors_indices, // nearest neighbors (returned) + neighbors_sq_distances, // distance (returned) + 0); // error bound + + } + +protected: + int m_dim; + Kernel const& m_k; + ANNpointArray m_points; + ANNkd_tree m_tree; +}; + +#endif // CGAL_TC_ANN_IS_AVAILABLE + +} // namespace Tangential_complex_ +} //namespace CGAL + +#endif // POINT_CLOUD_H diff --git a/Tangential_complex/include/CGAL/Tangential_complex/Simplicial_complex.h b/Tangential_complex/include/CGAL/Tangential_complex/Simplicial_complex.h new file mode 100644 index 00000000000..ecdea36ce3a --- /dev/null +++ b/Tangential_complex/include/CGAL/Tangential_complex/Simplicial_complex.h @@ -0,0 +1,526 @@ +// 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 SIMPLICIAL_COMPLEX_H +#define SIMPLICIAL_COMPLEX_H + +#include +#include + +#include +#include + +// For is_pure_pseudomanifold +#include +#include +#include +#include + +#include + +namespace CGAL { +namespace Tangential_complex_ { + +class Simplicial_complex +{ +public: + typedef std::set Simplex; + typedef std::set Simplex_range; + + void add_simplex( + const std::set &s, bool perform_checks = true) + { + if (perform_checks) + { + unsigned int num_pts = static_cast(s.size()); + std::vector to_erase; + bool check_higher_dim_simpl = true; + for (Complex::iterator it_simplex = m_complex.begin(), + it_simplex_end = m_complex.end() ; + it_simplex != it_simplex_end ; + ++it_simplex) + { + // Check if the simplex is not already in a higher dim simplex + if (check_higher_dim_simpl + && it_simplex->size() > num_pts + && std::includes(it_simplex->begin(), it_simplex->end(), + s.begin(), s.end())) + { + // No need to insert it, then + return; + } + // Check if the simplex includes some lower-dim simplices + if (it_simplex->size() < num_pts + && std::includes(s.begin(), s.end(), + it_simplex->begin(), it_simplex->end())) + { + to_erase.push_back(it_simplex); + // We don't need to check higher-sim simplices any more + check_higher_dim_simpl = false; + } + } + for (std::vector::const_iterator it= to_erase.begin(); + it != to_erase.end() ; ++it) + { + m_complex.erase(*it); + } + } + m_complex.insert(s); + } + + const Simplex_range &simplex_range() const + { + return m_complex; + } + + void clear() + { + m_complex.clear(); + } + + template + void get_simplices_matching_test(Test test, Output_it out) + { + for (Complex::const_iterator it_simplex = m_complex.begin(), + it_simplex_end = m_complex.end() ; + it_simplex != it_simplex_end ; + ++it_simplex) + { + if (test(*it_simplex)) + *out++ = *it_simplex; + } + } + + // When a simplex S has only one co-face C, we can remove S and C + // without changing the topology + void collapse(int max_simplex_dim, bool quiet = false) + { +#ifdef CGAL_TC_VERBOSE + if (!quiet) + std::cerr << "Collapsing... "; +#endif + // We note k = max_simplex_dim - 1 + int k = max_simplex_dim - 1; + + typedef Complex::iterator Simplex_iterator; + typedef std::vector Simplex_iterator_list; + typedef std::map Cofaces_map; + + std::size_t num_collapsed_maximal_simplices = 0; + do + { + num_collapsed_maximal_simplices = 0; + // Create a map associating each non-maximal k-faces to the list of its + // maximal cofaces + Cofaces_map cofaces_map; + for (Complex::const_iterator it_simplex = m_complex.begin(), + it_simplex_end = m_complex.end() ; + it_simplex != it_simplex_end ; + ++it_simplex) + { + if (it_simplex->size() > k + 1) + { + std::vector k_faces; + // Get the k-faces composing the simplex + combinations(*it_simplex, k + 1, std::back_inserter(k_faces)); + for (const auto &comb : k_faces) // CJTODO C++1 + cofaces_map[comb].push_back(it_simplex); + } + } + + // For each non-maximal k-face F, if F has only one maximal coface Cf: + // - Look for the other k-faces F2, F3... of Cf in the map and: + // * if the list contains only Cf, clear the list (we don't remove the + // list since it creates troubles with the iterators) and add the F2, + // F3... to the complex + // * otherwise, remove Cf from the associated list + // - Remove Cf from the complex + for (Cofaces_map::const_iterator it_map_elt = cofaces_map.begin(), + it_map_end = cofaces_map.end() ; + it_map_elt != it_map_end ; + ++it_map_elt) + { + if (it_map_elt->second.size() == 1) + { + std::vector k_faces; + const Simplex_iterator_list::value_type &it_Cf = + *it_map_elt->second.begin(); + CGAL_assertion(it_Cf->size() == max_simplex_dim + 1); + // Get the k-faces composing the simplex + combinations(*it_Cf, k + 1, std::back_inserter(k_faces)); + for (const auto &f2 : k_faces) // CJTODO C++1 + { + // Skip F + if (f2 != it_map_elt->first) + { + Cofaces_map::iterator it_comb_in_map = cofaces_map.find(f2); + if (it_comb_in_map->second.size() == 1) + { + it_comb_in_map->second.clear(); + m_complex.insert(f2); + } + else // it_comb_in_map->second.size() > 1 + { + Simplex_iterator_list::iterator it = std::find( + it_comb_in_map->second.begin(), + it_comb_in_map->second.end(), + it_Cf); + CGAL_assertion(it != it_comb_in_map->second.end()); + it_comb_in_map->second.erase(it); + } + } + } + m_complex.erase(it_Cf); + ++num_collapsed_maximal_simplices; + } + } + // Repeat until no maximal simplex got removed + } while (num_collapsed_maximal_simplices > 0); + + // Collapse the lower dimension simplices + if (k > 0) + collapse(max_simplex_dim - 1, true); + +#ifdef CGAL_TC_VERBOSE + if (!quiet) + std::cerr << "done." << std::endl; +#endif + } + + void display_stats() const + { + std::cerr << "==========================================================\n"; + std::cerr << "Complex stats:\n"; + + if (m_complex.empty()) + { + std::cerr << "No simplices.\n"; + } + else + { + // Number of simplex for each dimension + std::map simplex_stats; + + for (Complex::const_iterator it_simplex = m_complex.begin(), + it_simplex_end = m_complex.end() ; + it_simplex != it_simplex_end ; + ++it_simplex) + { + ++simplex_stats[static_cast(it_simplex->size()) - 1]; + } + + for (std::map::const_iterator it_map = simplex_stats.begin() ; + it_map != simplex_stats.end() ; ++it_map) + { + std::cerr << " * " << it_map->first << "-simplices: " + << it_map->second << std::endl; + } + } + + std::cerr << "==========================================================\n"; + } + + // verbose_level = 0, 1 or 2 + bool is_pure_pseudomanifold__do_not_check_if_stars_are_connected( + int simplex_dim, + bool exit_at_the_first_problem = false, + int verbose_level = 0, + std::size_t *p_num_wrong_dim_simplices = NULL, + std::size_t *p_num_wrong_number_of_cofaces = NULL) + { + typedef std::set K_1_face; + typedef std::map Cofaces_map; + + std::size_t num_wrong_dim_simplices = 0; + std::size_t num_wrong_number_of_cofaces = 0; + + // Counts the number of cofaces of each K_1_face + + // Create a map associating each non-maximal k-faces to the list of its + // maximal cofaces + Cofaces_map cofaces_map; + for (Complex::const_iterator it_simplex = m_complex.begin(), + it_simplex_end = m_complex.end() ; + it_simplex != it_simplex_end ; + ++it_simplex) + { + if (it_simplex->size() != simplex_dim + 1) + { + if (verbose_level >= 2) + std::cerr << "Found a simplex with dim = " + << it_simplex->size() - 1 << std::endl; + ++num_wrong_dim_simplices; + } + else + { + std::vector k_1_faces; + // Get the facets composing the simplex + combinations( + *it_simplex, simplex_dim, std::back_inserter(k_1_faces)); + for (const auto &k_1_face : k_1_faces) // CJTODO C++1 + { + ++cofaces_map[k_1_face]; + } + } + } + + for (Cofaces_map::const_iterator it_map_elt = cofaces_map.begin(), + it_map_end = cofaces_map.end() ; + it_map_elt != it_map_end ; + ++it_map_elt) + { + if (it_map_elt->second != 2) + { + if (verbose_level >= 2) + std::cerr << "Found a k-1-face with " + << it_map_elt->second << " cofaces\n"; + + if (exit_at_the_first_problem) + return false; + else + ++num_wrong_number_of_cofaces; + } + } + + bool ret = num_wrong_dim_simplices == 0 && num_wrong_number_of_cofaces == 0; + + if (verbose_level >= 1) + { + std::cerr << "is_pure_manifold: " << (ret ? "YES" : "NO") << std::endl; + if (!ret) + { + std::cerr << " * Number of wrong dimension simplices: " + << num_wrong_dim_simplices << std::endl + << " * Number of wrong number of cofaces: " + << num_wrong_number_of_cofaces << std::endl; + } + } + + if (p_num_wrong_dim_simplices) + *p_num_wrong_dim_simplices = num_wrong_dim_simplices; + if (p_num_wrong_number_of_cofaces) + *p_num_wrong_number_of_cofaces = num_wrong_number_of_cofaces; + + return ret; + } + + // CJTODO: ADD COMMENTS + bool is_pure_pseudomanifold( + int simplex_dim, + std::size_t num_vertices, + bool exit_at_the_first_problem = false, + int verbose_level = 0, + std::size_t *p_num_wrong_dim_simplices = NULL, + std::size_t *p_num_wrong_number_of_cofaces = NULL, + std::size_t *p_num_unconnected_stars = NULL, + Simplex_range *p_wrong_dim_simplices = NULL, + Simplex_range *p_wrong_number_of_cofaces_simplices = NULL, + Simplex_range *p_unconnected_stars_simplices = NULL) + { + // If simplex_dim == 1, we do not need to check if stars are connected + if (simplex_dim == 1) + { + if (p_num_unconnected_stars) + *p_num_unconnected_stars = 0; + return is_pure_pseudomanifold__do_not_check_if_stars_are_connected( + simplex_dim, + exit_at_the_first_problem, + verbose_level, + p_num_wrong_dim_simplices, + p_num_wrong_number_of_cofaces); + } + // Associates each vertex (= the index in the vector) + // to its star (list of simplices) + typedef std::vector > Stars; + std::size_t num_wrong_dim_simplices = 0; + std::size_t num_wrong_number_of_cofaces = 0; + std::size_t num_unconnected_stars = 0; + + // Fills a Stars data structure + Stars stars; + stars.resize(num_vertices); + for (Complex::const_iterator it_simplex = m_complex.begin(), + it_simplex_end = m_complex.end() ; + it_simplex != it_simplex_end ; + ++it_simplex) + { + if (it_simplex->size() != simplex_dim + 1) + { + if (verbose_level >= 2) + std::cerr << "Found a simplex with dim = " + << it_simplex->size() - 1 << std::endl; + ++num_wrong_dim_simplices; + if (p_wrong_dim_simplices) + p_wrong_dim_simplices->insert(*it_simplex); + } + else + { + for (Simplex::const_iterator it_point_idx = it_simplex->begin() ; + it_point_idx != it_simplex->end() ; + ++it_point_idx) + { + stars[*it_point_idx].push_back(it_simplex); + } + } + } + + // Now, for each star, with have a vector of its d-simplices + // i.e. one index for each d-simplex + // Boost Graph only deals with indexes, so we also need indexes for the + // (d-1)-simplices + std::size_t center_vertex_index = 0; + for (Stars::const_iterator it_star = stars.begin() ; + it_star != stars.end() ; + ++it_star, ++center_vertex_index) + { + typedef std::map > + Dm1_faces_to_adj_D_faces; + Dm1_faces_to_adj_D_faces dm1_faces_to_adj_d_faces; + + for (int i_dsimpl = 0 ; i_dsimpl < it_star->size() ; ++i_dsimpl) + { + Simplex dm1_simpl_of_link = *((*it_star)[i_dsimpl]); + dm1_simpl_of_link.erase(center_vertex_index); + // Copy it to a vector so that we can use operator[] on it + std::vector dm1_simpl_of_link_vec( + dm1_simpl_of_link.begin(), dm1_simpl_of_link.end()); + + CGAL::Combination_enumerator dm2_simplices( + simplex_dim - 1, 0, simplex_dim); + for ( ; !dm2_simplices.finished() ; ++dm2_simplices) + { + Simplex dm2_simpl; + for (int j = 0 ; j < simplex_dim - 1 ; ++j) + dm2_simpl.insert(dm1_simpl_of_link_vec[dm2_simplices[j]]); + dm1_faces_to_adj_d_faces[dm2_simpl].push_back(i_dsimpl); + } + } + + Adj_graph adj_graph; + std::vector d_faces_descriptors; + d_faces_descriptors.resize(it_star->size()); + for (int j = 0 ; j < it_star->size() ; ++j) + d_faces_descriptors[j] = boost::add_vertex(adj_graph); + + Dm1_faces_to_adj_D_faces::const_iterator dm1_to_d_it = + dm1_faces_to_adj_d_faces.begin(); + Dm1_faces_to_adj_D_faces::const_iterator dm1_to_d_it_end = + dm1_faces_to_adj_d_faces.end(); + for (std::size_t i_km1_face = 0 ; + dm1_to_d_it != dm1_to_d_it_end ; + ++dm1_to_d_it, ++i_km1_face) + { + Graph_vertex km1_gv = boost::add_vertex(adj_graph); + + for (std::vector::const_iterator kface_it = + dm1_to_d_it->second.begin() ; + kface_it != dm1_to_d_it->second.end() ; + ++kface_it) + { + boost::add_edge(km1_gv, *kface_it, adj_graph); + } + + if (dm1_to_d_it->second.size() != 2) + { + ++num_wrong_number_of_cofaces; + if (p_wrong_number_of_cofaces_simplices) + { + for (auto idx : dm1_to_d_it->second) + p_wrong_number_of_cofaces_simplices->insert(*((*it_star)[idx])); + } + } + } + + // What is left is to check the connexity + std::vector components(boost::num_vertices(adj_graph)); + bool is_connected = + (boost::connected_components(adj_graph, &components[0]) == 1); + + if (!is_connected) + { + if (verbose_level >= 2) + std::cerr << "Error: star #" << center_vertex_index + << " is not connected" << std::endl; + ++num_unconnected_stars; + if (p_unconnected_stars_simplices) + { + for (std::vector::const_iterator + it_simpl = it_star->begin(), + it_simpl_end = it_star->end(); + it_simpl != it_simpl_end ; + ++it_simpl) + { + p_unconnected_stars_simplices->insert(**it_simpl); + } + } + } + } + + // Each one has been counted several times ("simplex_dim" times) + num_wrong_number_of_cofaces /= simplex_dim; + + bool ret = + num_wrong_dim_simplices == 0 + && num_wrong_number_of_cofaces == 0 + && num_unconnected_stars == 0; + + if (verbose_level >= 1) + { + std::cerr << "is_pure_pseudo_manifold: " + << (ret ? "YES" : "NO") << std::endl; + if (!ret) + { + std::cerr << " * Number of wrong dimension simplices: " + << num_wrong_dim_simplices << std::endl + << " * Number of wrong number of cofaces: " + << num_wrong_number_of_cofaces << std::endl + << " * Number of not-connected stars: " + << num_unconnected_stars << std::endl; + } + } + + if (p_num_wrong_dim_simplices) + *p_num_wrong_dim_simplices = num_wrong_dim_simplices; + if (p_num_wrong_number_of_cofaces) + *p_num_wrong_number_of_cofaces = num_wrong_number_of_cofaces; + if (p_num_unconnected_stars) + *p_num_unconnected_stars = num_unconnected_stars; + + return ret; + } + +private: + typedef Simplex_range Complex; + + // graph is an adjacency list + typedef boost::adjacency_list Adj_graph; + // map that gives to a certain simplex its node in graph and its dimension + typedef boost::graph_traits::vertex_descriptor Graph_vertex; + typedef boost::graph_traits::edge_descriptor Graph_edge; + + Complex m_complex; + +}; // /class Simplicial_complex + +} // namespace Tangential_complex_ +} //namespace CGAL + +#endif // SIMPLICIAL_COMPLEX_H diff --git a/Tangential_complex/include/CGAL/Tangential_complex/config.h b/Tangential_complex/include/CGAL/Tangential_complex/config.h new file mode 100644 index 00000000000..3f01cde07a4 --- /dev/null +++ b/Tangential_complex/include/CGAL/Tangential_complex/config.h @@ -0,0 +1,80 @@ +// 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 CGAL_TC_CONFIG_H +#define CGAL_TC_CONFIG_H + +#include + +// Without TBB_USE_THREADING_TOOL Intel Inspector XE will report false +// positives in Intel TBB +// (http://software.intel.com/en-us/articles/compiler-settings-for-threading-error-analysis-in-intel-inspector-xe/) +#ifdef _DEBUG +# define TBB_USE_THREADING_TOOL +#endif + +//=========================== Alpha-TC or not? ================================ + +#define CGAL_ALPHA_TC + //#define CGAL_USE_A_FIXED_ALPHA + const double CGAL_TC_ALPHA_VALUE = 0.3; + +//========================= Debugging & profiling ============================= +#define CGAL_TC_PROFILING +#define CGAL_TC_VERBOSE +#define CGAL_TC_VERY_VERBOSE +#define CGAL_TC_PERFORM_EXTRA_CHECKS +//#define CGAL_TC_SHOW_DETAILED_STATS_FOR_INCONSISTENCIES +//#define USE_ANOTHER_POINT_SET_FOR_TANGENT_SPACE_ESTIM + +// Solving inconsistencies: only perturb the vertex, the simplex or more? +//#define CGAL_TC_PERTURB_THE_CENTER_VERTEX_ONLY +//#define CGAL_TC_PERTURB_THE_SIMPLEX_ONLY +//#define CGAL_TC_PERTURB_THE_1_STAR +#define CGAL_TC_PERTURB_N_CLOSEST_POINTS // perturb the CGAL_TC_NUMBER_OF_PERTURBED_POINTS closest points +// Otherwise, perturb one random point of the simplex + +// Only used if CGAL_TC_PERTURB_N_CLOSEST_POINTS is defined +#define CGAL_TC_NUMBER_OF_PERTURBED_POINTS(intr_dim) (1) +//#define CGAL_TC_NUMBER_OF_PERTURBED_POINTS(intr_dim) (intr_dim + 2) + + +//========================= Strategy ========================================== +#define CGAL_TC_NANOFLANN_IS_AVAILABLE +//#define CGAL_TC_ANN_IS_AVAILABLE +//#define CGAL_TC_USE_SLOW_BUT_ACCURATE_SPARSIFIER +#define CGAL_TC_GLOBAL_REFRESH +//#define CGAL_TC_ON_DEMAND_REFRESH // CJTODO: not implemented yet + // The idea is to perform a global refresh + some local refreshes, just + // for local tri where there are some inconsistencies + // But be careful: refreshing the TC may invalidate cells, so the + // incident cells have to be recomputed again +#define CGAL_TC_PERTURB_POSITION +# define CGAL_TC_PERTURB_POSITION_TANGENTIAL // default +//# define CGAL_TC_PERTURB_POSITION_GLOBAL +//#define CGAL_TC_PERTURB_WEIGHT +//#define CGAL_TC_PERTURB_TANGENT_SPACE + +//========================= Parameters ======================================== + +// PCA will use BASE_VALUE_FOR_PCA^intrinsic_dim points +const std::size_t BASE_VALUE_FOR_PCA = 5; + +#endif // CGAL_TC_CONFIG_H diff --git a/Tangential_complex/include/CGAL/Tangential_complex/nanoflann.hpp b/Tangential_complex/include/CGAL/Tangential_complex/nanoflann.hpp new file mode 100644 index 00000000000..8bedc45a2d3 --- /dev/null +++ b/Tangential_complex/include/CGAL/Tangential_complex/nanoflann.hpp @@ -0,0 +1,1449 @@ +/*********************************************************************** + * Software License Agreement (BSD License) + * + * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. + * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. + * Copyright 2011-2013 Jose Luis Blanco (joseluisblancoc@gmail.com). + * All rights reserved. + * + * THE BSD LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *************************************************************************/ + +#ifndef NANOFLANN_HPP_ +#define NANOFLANN_HPP_ + +#include +#include +#include +#include +#include // for fwrite() +#include // for fabs(),... +#include + +// Avoid conflicting declaration of min/max macros in windows headers +#if !defined(NOMINMAX) && (defined(_WIN32) || defined(_WIN32_) || defined(WIN32) || defined(_WIN64)) +# define NOMINMAX +# ifdef max +# undef max +# undef min +# endif +#endif + +namespace nanoflann +{ +/** @addtogroup nanoflann_grp nanoflann C++ library for ANN + * @{ */ + + /** Library version: 0xMmP (M=Major,m=minor,P=path) */ + #define NANOFLANN_VERSION 0x117 + + /** @addtogroup result_sets_grp Result set classes + * @{ */ + template + class KNNResultSet + { + IndexType * indices; + DistanceType* dists; + CountType capacity; + CountType count; + + public: + inline KNNResultSet(CountType capacity_) : capacity(capacity_), count(0) + { + } + + inline void init(IndexType* indices_, DistanceType* dists_) + { + indices = indices_; + dists = dists_; + count = 0; + dists[capacity-1] = (std::numeric_limits::max)(); + } + + inline CountType size() const + { + return count; + } + + inline bool full() const + { + return count == capacity; + } + + + inline void addPoint(DistanceType dist, IndexType index) + { + CountType i; + for (i=count; i>0; --i) { +#ifdef NANOFLANN_FIRST_MATCH // If defined and two poins have the same distance, the one with the lowest-index will be returned first. + if ( (dists[i-1]>dist) || ((dist==dists[i-1])&&(indices[i-1]>index)) ) { +#else + if (dists[i-1]>dist) { +#endif + if (i + class RadiusResultSet + { + public: + const DistanceType radius; + + std::vector >& m_indices_dists; + + inline RadiusResultSet(DistanceType radius_, std::vector >& indices_dists) : radius(radius_), m_indices_dists(indices_dists) + { + init(); + } + + inline ~RadiusResultSet() { } + + inline void init() { clear(); } + inline void clear() { m_indices_dists.clear(); } + + inline size_t size() const { return m_indices_dists.size(); } + + inline bool full() const { return true; } + + inline void addPoint(DistanceType dist, IndexType index) + { + if (dist 0 + */ + std::pair worst_item() const + { + if (m_indices_dists.empty()) throw std::runtime_error("Cannot invoke RadiusResultSet::worst_item() on an empty list of results."); + typedef typename std::vector >::const_iterator DistIt; + DistIt it = std::max_element(m_indices_dists.begin(), m_indices_dists.end()); + return *it; + } + }; + + /** operator "<" for std::sort() */ + struct IndexDist_Sorter + { + /** PairType will be typically: std::pair */ + template + inline bool operator()(const PairType &p1, const PairType &p2) const { + return p1.second < p2.second; + } + }; + + /** @} */ + + + /** @addtogroup loadsave_grp Load/save auxiliary functions + * @{ */ + template + void save_value(FILE* stream, const T& value, size_t count = 1) + { + fwrite(&value, sizeof(value),count, stream); + } + + template + void save_value(FILE* stream, const std::vector& value) + { + size_t size = value.size(); + fwrite(&size, sizeof(size_t), 1, stream); + fwrite(&value[0], sizeof(T), size, stream); + } + + template + void load_value(FILE* stream, T& value, size_t count = 1) + { + size_t read_cnt = fread(&value, sizeof(value), count, stream); + if (read_cnt != count) { + throw std::runtime_error("Cannot read from file"); + } + } + + + template + void load_value(FILE* stream, std::vector& value) + { + size_t size; + size_t read_cnt = fread(&size, sizeof(size_t), 1, stream); + if (read_cnt!=1) { + throw std::runtime_error("Cannot read from file"); + } + value.resize(size); + read_cnt = fread(&value[0], sizeof(T), size, stream); + if (read_cnt!=size) { + throw std::runtime_error("Cannot read from file"); + } + } + /** @} */ + + + /** @addtogroup metric_grp Metric (distance) classes + * @{ */ + + template inline T abs(T x) { return (x<0) ? -x : x; } + template<> inline int abs(int x) { return ::abs(x); } + template<> inline float abs(float x) { return fabsf(x); } + template<> inline double abs(double x) { return fabs(x); } + template<> inline long double abs(long double x) { return fabsl(x); } + + /** Manhattan distance functor (generic version, optimized for high-dimensionality data sets). + * Corresponding distance traits: nanoflann::metric_L1 + * \tparam T Type of the elements (e.g. double, float, uint8_t) + * \tparam DistanceType Type of distance variables (must be signed) (e.g. float, double, int64_t) + */ + template + struct L1_Adaptor + { + typedef T ElementType; + typedef _DistanceType DistanceType; + + const DataSource &data_source; + + L1_Adaptor(const DataSource &_data_source) : data_source(_data_source) { } + + inline DistanceType operator()(const T* a, const size_t b_idx, size_t size, DistanceType worst_dist = -1) const + { + DistanceType result = DistanceType(); + const T* last = a + size; + const T* lastgroup = last - 3; + size_t d = 0; + + /* Process 4 items with each loop for efficiency. */ + while (a < lastgroup) { + const DistanceType diff0 = nanoflann::abs(a[0] - data_source.kdtree_get_pt(b_idx,d++)); + const DistanceType diff1 = nanoflann::abs(a[1] - data_source.kdtree_get_pt(b_idx,d++)); + const DistanceType diff2 = nanoflann::abs(a[2] - data_source.kdtree_get_pt(b_idx,d++)); + const DistanceType diff3 = nanoflann::abs(a[3] - data_source.kdtree_get_pt(b_idx,d++)); + result += diff0 + diff1 + diff2 + diff3; + a += 4; + if ((worst_dist>0)&&(result>worst_dist)) { + return result; + } + } + /* Process last 0-3 components. Not needed for standard vector lengths. */ + while (a < last) { + result += nanoflann::abs( *a++ - data_source.kdtree_get_pt(b_idx,d++) ); + } + return result; + } + + template + inline DistanceType accum_dist(const U a, const V b, int ) const + { + return nanoflann::abs(a-b); + } + }; + + /** Squared Euclidean distance functor (generic version, optimized for high-dimensionality data sets). + * Corresponding distance traits: nanoflann::metric_L2 + * \tparam T Type of the elements (e.g. double, float, uint8_t) + * \tparam DistanceType Type of distance variables (must be signed) (e.g. float, double, int64_t) + */ + template + struct L2_Adaptor + { + typedef T ElementType; + typedef _DistanceType DistanceType; + + const DataSource &data_source; + + L2_Adaptor(const DataSource &_data_source) : data_source(_data_source) { } + + inline DistanceType operator()(const T* a, const size_t b_idx, size_t size, DistanceType worst_dist = -1) const + { + DistanceType result = DistanceType(); + const T* last = a + size; + const T* lastgroup = last - 3; + size_t d = 0; + + /* Process 4 items with each loop for efficiency. */ + while (a < lastgroup) { + const DistanceType diff0 = a[0] - data_source.kdtree_get_pt(b_idx,d++); + const DistanceType diff1 = a[1] - data_source.kdtree_get_pt(b_idx,d++); + const DistanceType diff2 = a[2] - data_source.kdtree_get_pt(b_idx,d++); + const DistanceType diff3 = a[3] - data_source.kdtree_get_pt(b_idx,d++); + result += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3; + a += 4; + if ((worst_dist>0)&&(result>worst_dist)) { + return result; + } + } + /* Process last 0-3 components. Not needed for standard vector lengths. */ + while (a < last) { + const DistanceType diff0 = *a++ - data_source.kdtree_get_pt(b_idx,d++); + result += diff0 * diff0; + } + return result; + } + + template + inline DistanceType accum_dist(const U a, const V b, int ) const + { + return (a-b)*(a-b); + } + }; + + /** Squared Euclidean distance functor (suitable for low-dimensionality datasets, like 2D or 3D point clouds) + * Corresponding distance traits: nanoflann::metric_L2_Simple + * \tparam T Type of the elements (e.g. double, float, uint8_t) + * \tparam DistanceType Type of distance variables (must be signed) (e.g. float, double, int64_t) + */ + template + struct L2_Simple_Adaptor + { + typedef T ElementType; + typedef _DistanceType DistanceType; + + const DataSource &data_source; + + L2_Simple_Adaptor(const DataSource &_data_source) : data_source(_data_source) { } + + inline DistanceType operator()(const T* a, const size_t b_idx, size_t size) const { + return data_source.kdtree_distance(a,b_idx,size); + } + + template + inline DistanceType accum_dist(const U a, const V b, int ) const + { + return (a-b)*(a-b); + } + }; + + /** Metaprogramming helper traits class for the L1 (Manhattan) metric */ + struct metric_L1 { + template + struct traits { + typedef L1_Adaptor distance_t; + }; + }; + /** Metaprogramming helper traits class for the L2 (Euclidean) metric */ + struct metric_L2 { + template + struct traits { + typedef L2_Adaptor distance_t; + }; + }; + /** Metaprogramming helper traits class for the L2_simple (Euclidean) metric */ + struct metric_L2_Simple { + template + struct traits { + typedef L2_Simple_Adaptor distance_t; + }; + }; + + /** @} */ + + + + /** @addtogroup param_grp Parameter structs + * @{ */ + + /** Parameters (see http://code.google.com/p/nanoflann/ for help choosing the parameters) + */ + struct KDTreeSingleIndexAdaptorParams + { + KDTreeSingleIndexAdaptorParams(size_t _leaf_max_size = 10, int dim_ = -1) : + leaf_max_size(_leaf_max_size), dim(dim_) + {} + + size_t leaf_max_size; + int dim; + }; + + /** Search options for KDTreeSingleIndexAdaptor::findNeighbors() */ + struct SearchParams + { + /** Note: The first argument (checks_IGNORED_) is ignored, but kept for compatibility with the FLANN interface */ + SearchParams(int checks_IGNORED_ = 32, float eps_ = 0, bool sorted_ = true ) : + checks(checks_IGNORED_), eps(eps_), sorted(sorted_) {} + + int checks; //!< Ignored parameter (Kept for compatibility with the FLANN interface). + float eps; //!< search for eps-approximate neighbours (default: 0) + bool sorted; //!< only for radius search, require neighbours sorted by distance (default: true) + }; + /** @} */ + + + /** @addtogroup memalloc_grp Memory allocation + * @{ */ + + /** + * Allocates (using C's malloc) a generic type T. + * + * Params: + * count = number of instances to allocate. + * Returns: pointer (of type T*) to memory buffer + */ + template + inline T* allocate(size_t count = 1) + { + T* mem = (T*) ::malloc(sizeof(T)*count); + return mem; + } + + + /** + * Pooled storage allocator + * + * The following routines allow for the efficient allocation of storage in + * small chunks from a specified pool. Rather than allowing each structure + * to be freed individually, an entire pool of storage is freed at once. + * This method has two advantages over just using malloc() and free(). First, + * it is far more efficient for allocating small objects, as there is + * no overhead for remembering all the information needed to free each + * object or consolidating fragmented memory. Second, the decision about + * how long to keep an object is made at the time of allocation, and there + * is no need to track down all the objects to free them. + * + */ + + const size_t WORDSIZE=16; + const size_t BLOCKSIZE=8192; + + class PooledAllocator + { + /* We maintain memory alignment to word boundaries by requiring that all + allocations be in multiples of the machine wordsize. */ + /* Size of machine word in bytes. Must be power of 2. */ + /* Minimum number of bytes requested at a time from the system. Must be multiple of WORDSIZE. */ + + + size_t remaining; /* Number of bytes left in current block of storage. */ + void* base; /* Pointer to base of current block of storage. */ + void* loc; /* Current location in block to next allocate memory. */ + size_t blocksize; + + void internal_init() + { + remaining = 0; + base = NULL; + usedMemory = 0; + wastedMemory = 0; + } + + public: + size_t usedMemory; + size_t wastedMemory; + + /** + Default constructor. Initializes a new pool. + */ + PooledAllocator(const size_t blocksize_ = BLOCKSIZE) : blocksize(blocksize_) { + internal_init(); + } + + /** + * Destructor. Frees all the memory allocated in this pool. + */ + ~PooledAllocator() { + free_all(); + } + + /** Frees all allocated memory chunks */ + void free_all() + { + while (base != NULL) { + void *prev = *((void**) base); /* Get pointer to prev block. */ + ::free(base); + base = prev; + } + internal_init(); + } + + /** + * Returns a pointer to a piece of new memory of the given size in bytes + * allocated from the pool. + */ + void* malloc(const size_t req_size) + { + /* Round size up to a multiple of wordsize. The following expression + only works for WORDSIZE that is a power of 2, by masking last bits of + incremented size to zero. + */ + const size_t size = (req_size + (WORDSIZE - 1)) & ~(WORDSIZE - 1); + + /* Check whether a new block must be allocated. Note that the first word + of a block is reserved for a pointer to the previous block. + */ + if (size > remaining) { + + wastedMemory += remaining; + + /* Allocate new storage. */ + const size_t blocksize = (size + sizeof(void*) + (WORDSIZE-1) > BLOCKSIZE) ? + size + sizeof(void*) + (WORDSIZE-1) : BLOCKSIZE; + + // use the standard C malloc to allocate memory + void* m = ::malloc(blocksize); + if (!m) { + fprintf(stderr,"Failed to allocate memory.\n"); + return NULL; + } + + /* Fill first word of new block with pointer to previous block. */ + ((void**) m)[0] = base; + base = m; + + size_t shift = 0; + //int size_t = (WORDSIZE - ( (((size_t)m) + sizeof(void*)) & (WORDSIZE-1))) & (WORDSIZE-1); + + remaining = blocksize - sizeof(void*) - shift; + loc = ((char*)m + sizeof(void*) + shift); + } + void* rloc = loc; + loc = (char*)loc + size; + remaining -= size; + + usedMemory += size; + + return rloc; + } + + /** + * Allocates (using this pool) a generic type T. + * + * Params: + * count = number of instances to allocate. + * Returns: pointer (of type T*) to memory buffer + */ + template + T* allocate(const size_t count = 1) + { + T* mem = (T*) this->malloc(sizeof(T)*count); + return mem; + } + + }; + /** @} */ + + /** @addtogroup nanoflann_metaprog_grp Auxiliary metaprogramming stuff + * @{ */ + + // ---------------- CArray ------------------------- + /** A STL container (as wrapper) for arrays of constant size defined at compile time (class imported from the MRPT project) + * This code is an adapted version from Boost, modifed for its integration + * within MRPT (JLBC, Dec/2009) (Renamed array -> CArray to avoid possible potential conflicts). + * See + * http://www.josuttis.com/cppcode + * for details and the latest version. + * See + * http://www.boost.org/libs/array for Documentation. + * for documentation. + * + * (C) Copyright Nicolai M. Josuttis 2001. + * Permission to copy, use, modify, sell and distribute this software + * is granted provided this copyright notice appears in all copies. + * This software is provided "as is" without express or implied + * warranty, and with no claim as to its suitability for any purpose. + * + * 29 Jan 2004 - minor fixes (Nico Josuttis) + * 04 Dec 2003 - update to synch with library TR1 (Alisdair Meredith) + * 23 Aug 2002 - fix for Non-MSVC compilers combined with MSVC libraries. + * 05 Aug 2001 - minor update (Nico Josuttis) + * 20 Jan 2001 - STLport fix (Beman Dawes) + * 29 Sep 2000 - Initial Revision (Nico Josuttis) + * + * Jan 30, 2004 + */ + template + class CArray { + public: + T elems[N]; // fixed-size array of elements of type T + + public: + // type definitions + typedef T value_type; + typedef T* iterator; + typedef const T* const_iterator; + typedef T& reference; + typedef const T& const_reference; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + // iterator support + inline iterator begin() { return elems; } + inline const_iterator begin() const { return elems; } + inline iterator end() { return elems+N; } + inline const_iterator end() const { return elems+N; } + + // reverse iterator support +#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) && !defined(BOOST_MSVC_STD_ITERATOR) && !defined(BOOST_NO_STD_ITERATOR_TRAITS) + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; +#elif defined(_MSC_VER) && (_MSC_VER == 1300) && defined(BOOST_DINKUMWARE_STDLIB) && (BOOST_DINKUMWARE_STDLIB == 310) + // workaround for broken reverse_iterator in VC7 + typedef std::reverse_iterator > reverse_iterator; + typedef std::reverse_iterator > const_reverse_iterator; +#else + // workaround for broken reverse_iterator implementations + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; +#endif + + reverse_iterator rbegin() { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } + // operator[] + inline reference operator[](size_type i) { return elems[i]; } + inline const_reference operator[](size_type i) const { return elems[i]; } + // at() with range check + reference at(size_type i) { rangecheck(i); return elems[i]; } + const_reference at(size_type i) const { rangecheck(i); return elems[i]; } + // front() and back() + reference front() { return elems[0]; } + const_reference front() const { return elems[0]; } + reference back() { return elems[N-1]; } + const_reference back() const { return elems[N-1]; } + // size is constant + static inline size_type size() { return N; } + static bool empty() { return false; } + static size_type max_size() { return N; } + enum { static_size = N }; + /** This method has no effects in this class, but raises an exception if the expected size does not match */ + inline void resize(const size_t nElements) { if (nElements!=N) throw std::logic_error("Try to change the size of a CArray."); } + // swap (note: linear complexity in N, constant for given instantiation) + void swap (CArray& y) { std::swap_ranges(begin(),end(),y.begin()); } + // direct access to data (read-only) + const T* data() const { return elems; } + // use array as C array (direct read/write access to data) + T* data() { return elems; } + // assignment with type conversion + template CArray& operator= (const CArray& rhs) { + std::copy(rhs.begin(),rhs.end(), begin()); + return *this; + } + // assign one value to all elements + inline void assign (const T& value) { for (size_t i=0;i= size()) { throw std::out_of_range("CArray<>: index out of range"); } } + }; // end of CArray + + /** Used to declare fixed-size arrays when DIM>0, dynamically-allocated vectors when DIM=-1. + * Fixed size version for a generic DIM: + */ + template + struct array_or_vector_selector + { + typedef CArray container_t; + }; + /** Dynamic size version */ + template + struct array_or_vector_selector<-1,T> { + typedef std::vector container_t; + }; + /** @} */ + + /** @addtogroup kdtrees_grp KD-tree classes and adaptors + * @{ */ + + /** kd-tree index + * + * Contains the k-d trees and other information for indexing a set of points + * for nearest-neighbor matching. + * + * The class "DatasetAdaptor" must provide the following interface (can be non-virtual, inlined methods): + * + * \code + * // Must return the number of data points + * inline size_t kdtree_get_point_count() const { ... } + * + * // Must return the Euclidean (L2) distance between the vector "p1[0:size-1]" and the data point with index "idx_p2" stored in the class: + * inline DistanceType kdtree_distance(const T *p1, const size_t idx_p2,size_t size) const { ... } + * + * // Must return the dim'th component of the idx'th point in the class: + * inline T kdtree_get_pt(const size_t idx, int dim) const { ... } + * + * // Optional bounding-box computation: return false to default to a standard bbox computation loop. + * // Return true if the BBOX was already computed by the class and returned in "bb" so it can be avoided to redo it again. + * // Look at bb.size() to find out the expected dimensionality (e.g. 2 or 3 for point clouds) + * template + * bool kdtree_get_bbox(BBOX &bb) const + * { + * bb[0].low = ...; bb[0].high = ...; // 0th dimension limits + * bb[1].low = ...; bb[1].high = ...; // 1st dimension limits + * ... + * return true; + * } + * + * \endcode + * + * \tparam IndexType Will be typically size_t or int + */ + template + class KDTreeSingleIndexAdaptor + { + public: + typedef typename Distance::ElementType ElementType; + typedef typename Distance::DistanceType DistanceType; + protected: + + /** + * Array of indices to vectors in the dataset. + */ + std::vector vind; + + size_t m_leaf_max_size; + + + /** + * The dataset used by this index + */ + const DatasetAdaptor &dataset; //!< The source of our data + + const KDTreeSingleIndexAdaptorParams index_params; + + size_t m_size; + int dim; //!< Dimensionality of each data point + + + /*--------------------- Internal Data Structures --------------------------*/ + struct Node + { + union { + struct + { + /** + * Indices of points in leaf node + */ + IndexType left, right; + } lr; + struct + { + /** + * Dimension used for subdivision. + */ + int divfeat; + /** + * The values used for subdivision. + */ + DistanceType divlow, divhigh; + } sub; + }; + /** + * The child nodes. + */ + Node* child1, * child2; + }; + typedef Node* NodePtr; + + + struct Interval + { + ElementType low, high; + }; + + /** Define "BoundingBox" as a fixed-size or variable-size container depending on "DIM" */ + typedef typename array_or_vector_selector::container_t BoundingBox; + + /** Define "distance_vector_t" as a fixed-size or variable-size container depending on "DIM" */ + typedef typename array_or_vector_selector::container_t distance_vector_t; + + /** This record represents a branch point when finding neighbors in + the tree. It contains a record of the minimum distance to the query + point, as well as the node at which the search resumes. + */ + template + struct BranchStruct + { + T node; /* Tree node at which search resumes */ + DistanceType mindist; /* Minimum distance to query for all nodes below. */ + + BranchStruct() {} + BranchStruct(const T& aNode, DistanceType dist) : node(aNode), mindist(dist) {} + + inline bool operator<(const BranchStruct& rhs) const + { + return mindist BranchSt; + typedef BranchSt* Branch; + + BoundingBox root_bbox; + + /** + * Pooled memory allocator. + * + * Using a pooled memory allocator is more efficient + * than allocating memory directly when there is a large + * number small of memory allocations. + */ + PooledAllocator pool; + + public: + + Distance distance; + + /** + * KDTree constructor + * + * Params: + * inputData = dataset with the input features + * params = parameters passed to the kdtree algorithm (see http://code.google.com/p/nanoflann/ for help choosing the parameters) + */ + KDTreeSingleIndexAdaptor(const int dimensionality, const DatasetAdaptor& inputData, const KDTreeSingleIndexAdaptorParams& params = KDTreeSingleIndexAdaptorParams() ) : + dataset(inputData), index_params(params), root_node(NULL), distance(inputData) + { + m_size = dataset.kdtree_get_point_count(); + dim = dimensionality; + if (DIM>0) dim=DIM; + else { + if (params.dim>0) dim = params.dim; + } + m_leaf_max_size = params.leaf_max_size; + + // Create a permutable array of indices to the input vectors. + init_vind(); + } + + /** + * Standard destructor + */ + ~KDTreeSingleIndexAdaptor() + { + } + + /** Frees the previously-built index. Automatically called within buildIndex(). */ + void freeIndex() + { + pool.free_all(); + root_node=NULL; + } + + /** + * Builds the index + */ + void buildIndex() + { + init_vind(); + computeBoundingBox(root_bbox); + freeIndex(); + root_node = divideTree(0, m_size, root_bbox ); // construct the tree + } + + /** + * Returns size of index. + */ + size_t size() const + { + return m_size; + } + + /** + * Returns the length of an index feature. + */ + size_t veclen() const + { + return static_cast(DIM>0 ? DIM : dim); + } + + /** + * Computes the inde memory usage + * Returns: memory used by the index + */ + size_t usedMemory() const + { + return pool.usedMemory+pool.wastedMemory+dataset.kdtree_get_point_count()*sizeof(IndexType); // pool memory and vind array memory + } + + /** \name Query methods + * @{ */ + + /** + * Find set of nearest neighbors to vec[0:dim-1]. Their indices are stored inside + * the result object. + * + * Params: + * result = the result object in which the indices of the nearest-neighbors are stored + * vec = the vector for which to search the nearest neighbors + * + * \tparam RESULTSET Should be any ResultSet + * \sa knnSearch, radiusSearch + */ + template + void findNeighbors(RESULTSET& result, const ElementType* vec, const SearchParams& searchParams) const + { + assert(vec); + if (!root_node) throw std::runtime_error("[nanoflann] findNeighbors() called before building the index."); + float epsError = 1+searchParams.eps; + + distance_vector_t dists; // fixed or variable-sized container (depending on DIM) + dists.assign((DIM>0 ? DIM : dim) ,0); // Fill it with zeros. + DistanceType distsq = computeInitialDistances(vec, dists); + searchLevel(result, vec, root_node, distsq, dists, epsError); // "count_leaf" parameter removed since was neither used nor returned to the user. + } + + /** + * Find the "num_closest" nearest neighbors to the \a query_point[0:dim-1]. Their indices are stored inside + * the result object. + * \sa radiusSearch, findNeighbors + * \note nChecks_IGNORED is ignored but kept for compatibility with the original FLANN interface. + */ + inline void knnSearch(const ElementType *query_point, const size_t num_closest, IndexType *out_indices, DistanceType *out_distances_sq, const int nChecks_IGNORED = 10) const + { + nanoflann::KNNResultSet resultSet(num_closest); + resultSet.init(out_indices, out_distances_sq); + this->findNeighbors(resultSet, query_point, nanoflann::SearchParams()); + } + + /** + * Find all the neighbors to \a query_point[0:dim-1] within a maximum radius. + * The output is given as a vector of pairs, of which the first element is a point index and the second the corresponding distance. + * Previous contents of \a IndicesDists are cleared. + * + * If searchParams.sorted==true, the output list is sorted by ascending distances. + * + * For a better performance, it is advisable to do a .reserve() on the vector if you have any wild guess about the number of expected matches. + * + * \sa knnSearch, findNeighbors + * \return The number of points within the given radius (i.e. indices.size() or dists.size() ) + */ + size_t radiusSearch(const ElementType *query_point,const DistanceType radius, std::vector >& IndicesDists, const SearchParams& searchParams) const + { + RadiusResultSet resultSet(radius,IndicesDists); + this->findNeighbors(resultSet, query_point, searchParams); + + if (searchParams.sorted) + std::sort(IndicesDists.begin(),IndicesDists.end(), IndexDist_Sorter() ); + + return resultSet.size(); + } + + /** @} */ + + private: + /** Make sure the auxiliary list \a vind has the same size than the current dataset, and re-generate if size has changed. */ + void init_vind() + { + // Create a permutable array of indices to the input vectors. + m_size = dataset.kdtree_get_point_count(); + if (vind.size()!=m_size) vind.resize(m_size); + for (size_t i = 0; i < m_size; i++) vind[i] = i; + } + + /// Helper accessor to the dataset points: + inline ElementType dataset_get(size_t idx, int component) const { + return dataset.kdtree_get_pt(idx,component); + } + + + void save_tree(FILE* stream, NodePtr tree) + { + save_value(stream, *tree); + if (tree->child1!=NULL) { + save_tree(stream, tree->child1); + } + if (tree->child2!=NULL) { + save_tree(stream, tree->child2); + } + } + + + void load_tree(FILE* stream, NodePtr& tree) + { + tree = pool.allocate(); + load_value(stream, *tree); + if (tree->child1!=NULL) { + load_tree(stream, tree->child1); + } + if (tree->child2!=NULL) { + load_tree(stream, tree->child2); + } + } + + + void computeBoundingBox(BoundingBox& bbox) + { + bbox.resize((DIM>0 ? DIM : dim)); + if (dataset.kdtree_get_bbox(bbox)) + { + // Done! It was implemented in derived class + } + else + { + for (int i=0; i<(DIM>0 ? DIM : dim); ++i) { + bbox[i].low = + bbox[i].high = dataset_get(0,i); + } + const size_t N = dataset.kdtree_get_point_count(); + for (size_t k=1; k0 ? DIM : dim); ++i) { + if (dataset_get(k,i)bbox[i].high) bbox[i].high = dataset_get(k,i); + } + } + } + } + + + /** + * Create a tree node that subdivides the list of vecs from vind[first] + * to vind[last]. The routine is called recursively on each sublist. + * Place a pointer to this new tree node in the location pTree. + * + * Params: pTree = the new node to create + * first = index of the first vector + * last = index of the last vector + */ + NodePtr divideTree(const IndexType left, const IndexType right, BoundingBox& bbox) + { + NodePtr node = pool.allocate(); // allocate memory + + /* If too few exemplars remain, then make this a leaf node. */ + if ( (right-left) <= m_leaf_max_size) { + node->child1 = node->child2 = NULL; /* Mark as leaf node. */ + node->lr.left = left; + node->lr.right = right; + + // compute bounding-box of leaf points + for (int i=0; i<(DIM>0 ? DIM : dim); ++i) { + bbox[i].low = dataset_get(vind[left],i); + bbox[i].high = dataset_get(vind[left],i); + } + for (IndexType k=left+1; k0 ? DIM : dim); ++i) { + if (bbox[i].low>dataset_get(vind[k],i)) bbox[i].low=dataset_get(vind[k],i); + if (bbox[i].highsub.divfeat = cutfeat; + + BoundingBox left_bbox(bbox); + left_bbox[cutfeat].high = cutval; + node->child1 = divideTree(left, left+idx, left_bbox); + + BoundingBox right_bbox(bbox); + right_bbox[cutfeat].low = cutval; + node->child2 = divideTree(left+idx, right, right_bbox); + + node->sub.divlow = left_bbox[cutfeat].high; + node->sub.divhigh = right_bbox[cutfeat].low; + + for (int i=0; i<(DIM>0 ? DIM : dim); ++i) { + bbox[i].low = std::min(left_bbox[i].low, right_bbox[i].low); + bbox[i].high = std::max(left_bbox[i].high, right_bbox[i].high); + } + } + + return node; + } + + void computeMinMax(IndexType* ind, IndexType count, int element, ElementType& min_elem, ElementType& max_elem) + { + min_elem = dataset_get(ind[0],element); + max_elem = dataset_get(ind[0],element); + for (IndexType i=1; imax_elem) max_elem = val; + } + } + + void middleSplit(IndexType* ind, IndexType count, IndexType& index, int& cutfeat, DistanceType& cutval, const BoundingBox& bbox) + { + // find the largest span from the approximate bounding box + ElementType max_span = bbox[0].high-bbox[0].low; + cutfeat = 0; + cutval = (bbox[0].high+bbox[0].low)/2; + for (int i=1; i<(DIM>0 ? DIM : dim); ++i) { + ElementType span = bbox[i].low-bbox[i].low; + if (span>max_span) { + max_span = span; + cutfeat = i; + cutval = (bbox[i].high+bbox[i].low)/2; + } + } + + // compute exact span on the found dimension + ElementType min_elem, max_elem; + computeMinMax(ind, count, cutfeat, min_elem, max_elem); + cutval = (min_elem+max_elem)/2; + max_span = max_elem - min_elem; + + // check if a dimension of a largest span exists + size_t k = cutfeat; + for (size_t i=0; i<(DIM>0 ? DIM : dim); ++i) { + if (i==k) continue; + ElementType span = bbox[i].high-bbox[i].low; + if (span>max_span) { + computeMinMax(ind, count, i, min_elem, max_elem); + span = max_elem - min_elem; + if (span>max_span) { + max_span = span; + cutfeat = i; + cutval = (min_elem+max_elem)/2; + } + } + } + IndexType lim1, lim2; + planeSplit(ind, count, cutfeat, cutval, lim1, lim2); + + if (lim1>count/2) index = lim1; + else if (lim2(0.00001); + ElementType max_span = bbox[0].high-bbox[0].low; + for (int i=1; i<(DIM>0 ? DIM : dim); ++i) { + ElementType span = bbox[i].high-bbox[i].low; + if (span>max_span) { + max_span = span; + } + } + ElementType max_spread = -1; + cutfeat = 0; + for (int i=0; i<(DIM>0 ? DIM : dim); ++i) { + ElementType span = bbox[i].high-bbox[i].low; + if (span>(1-EPS)*max_span) { + ElementType min_elem, max_elem; + computeMinMax(ind, count, cutfeat, min_elem, max_elem); + ElementType spread = max_elem-min_elem;; + if (spread>max_spread) { + cutfeat = i; + max_spread = spread; + } + } + } + // split in the middle + DistanceType split_val = (bbox[cutfeat].low+bbox[cutfeat].high)/2; + ElementType min_elem, max_elem; + computeMinMax(ind, count, cutfeat, min_elem, max_elem); + + if (split_valmax_elem) cutval = max_elem; + else cutval = split_val; + + IndexType lim1, lim2; + planeSplit(ind, count, cutfeat, cutval, lim1, lim2); + + if (lim1>count/2) index = lim1; + else if (lim2cutval + */ + void planeSplit(IndexType* ind, const IndexType count, int cutfeat, DistanceType cutval, IndexType& lim1, IndexType& lim2) + { + /* Move vector indices for left subtree to front of list. */ + IndexType left = 0; + IndexType right = count-1; + for (;; ) { + while (left<=right && dataset_get(ind[left],cutfeat)=cutval) --right; + if (left>right || !right) break; // "!right" was added to support unsigned Index types + std::swap(ind[left], ind[right]); + ++left; + --right; + } + /* If either list is empty, it means that all remaining features + * are identical. Split in the middle to maintain a balanced tree. + */ + lim1 = left; + right = count-1; + for (;; ) { + while (left<=right && dataset_get(ind[left],cutfeat)<=cutval) ++left; + while (right && left<=right && dataset_get(ind[right],cutfeat)>cutval) --right; + if (left>right || !right) break; // "!right" was added to support unsigned Index types + std::swap(ind[left], ind[right]); + ++left; + --right; + } + lim2 = left; + } + + DistanceType computeInitialDistances(const ElementType* vec, distance_vector_t& dists) const + { + assert(vec); + DistanceType distsq = 0.0; + + for (int i = 0; i < (DIM>0 ? DIM : dim); ++i) { + if (vec[i] < root_bbox[i].low) { + dists[i] = distance.accum_dist(vec[i], root_bbox[i].low, i); + distsq += dists[i]; + } + if (vec[i] > root_bbox[i].high) { + dists[i] = distance.accum_dist(vec[i], root_bbox[i].high, i); + distsq += dists[i]; + } + } + + return distsq; + } + + /** + * Performs an exact search in the tree starting from a node. + * \tparam RESULTSET Should be any ResultSet + */ + template + void searchLevel(RESULTSET& result_set, const ElementType* vec, const NodePtr node, DistanceType mindistsq, + distance_vector_t& dists, const float epsError) const + { + /* If this is a leaf node, then do check and return. */ + if ((node->child1 == NULL)&&(node->child2 == NULL)) { + //count_leaf += (node->lr.right-node->lr.left); // Removed since was neither used nor returned to the user. + DistanceType worst_dist = result_set.worstDist(); + for (IndexType i=node->lr.left; ilr.right; ++i) { + const IndexType index = vind[i];// reorder... : i; + DistanceType dist = distance(vec, index, (DIM>0 ? DIM : dim)); + if (distsub.divfeat; + ElementType val = vec[idx]; + DistanceType diff1 = val - node->sub.divlow; + DistanceType diff2 = val - node->sub.divhigh; + + NodePtr bestChild; + NodePtr otherChild; + DistanceType cut_dist; + if ((diff1+diff2)<0) { + bestChild = node->child1; + otherChild = node->child2; + cut_dist = distance.accum_dist(val, node->sub.divhigh, idx); + } + else { + bestChild = node->child2; + otherChild = node->child1; + cut_dist = distance.accum_dist( val, node->sub.divlow, idx); + } + + /* Call recursively to search next level down. */ + searchLevel(result_set, vec, bestChild, mindistsq, dists, epsError); + + DistanceType dst = dists[idx]; + mindistsq = mindistsq + cut_dist - dst; + dists[idx] = cut_dist; + if (mindistsq*epsError<=result_set.worstDist()) { + searchLevel(result_set, vec, otherChild, mindistsq, dists, epsError); + } + dists[idx] = dst; + } + + public: + /** Stores the index in a binary file. + * IMPORTANT NOTE: The set of data points is NOT stored in the file, so when loading the index object it must be constructed associated to the same source of data points used while building it. + * See the example: examples/saveload_example.cpp + * \sa loadIndex */ + void saveIndex(FILE* stream) + { + save_value(stream, m_size); + save_value(stream, dim); + save_value(stream, root_bbox); + save_value(stream, m_leaf_max_size); + save_value(stream, vind); + save_tree(stream, root_node); + } + + /** Loads a previous index from a binary file. + * IMPORTANT NOTE: The set of data points is NOT stored in the file, so the index object must be constructed associated to the same source of data points used while building the index. + * See the example: examples/saveload_example.cpp + * \sa loadIndex */ + void loadIndex(FILE* stream) + { + load_value(stream, m_size); + load_value(stream, dim); + load_value(stream, root_bbox); + load_value(stream, m_leaf_max_size); + load_value(stream, vind); + load_tree(stream, root_node); + } + + }; // class KDTree + + + /** A simple KD-tree adaptor for working with data directly stored in an Eigen Matrix, without duplicating the data storage. + * Each row in the matrix represents a point in the state space. + * + * Example of usage: + * \code + * Eigen::Matrix mat; + * // Fill out "mat"... + * + * typedef KDTreeEigenMatrixAdaptor< Eigen::Matrix > my_kd_tree_t; + * const int max_leaf = 10; + * my_kd_tree_t mat_index(dimdim, mat, max_leaf ); + * mat_index.index->buildIndex(); + * mat_index.index->... + * \endcode + * + * \tparam DIM If set to >0, it specifies a compile-time fixed dimensionality for the points in the data set, allowing more compiler optimizations. + * \tparam Distance The distance metric to use: nanoflann::metric_L1, nanoflann::metric_L2, nanoflann::metric_L2_Simple, etc. + * \tparam IndexType The type for indices in the KD-tree index (typically, size_t of int) + */ + template + struct KDTreeEigenMatrixAdaptor + { + typedef KDTreeEigenMatrixAdaptor self_t; + typedef typename MatrixType::Scalar num_t; + typedef typename Distance::template traits::distance_t metric_t; + typedef KDTreeSingleIndexAdaptor< metric_t,self_t,DIM,IndexType> index_t; + + index_t* index; //! The kd-tree index for the user to call its methods as usual with any other FLANN index. + + /// Constructor: takes a const ref to the matrix object with the data points + KDTreeEigenMatrixAdaptor(const int dimensionality, const MatrixType &mat, const int leaf_max_size = 10) : m_data_matrix(mat) + { + const size_t dims = mat.cols(); + if (DIM>0 && static_cast(dims)!=DIM) + throw std::runtime_error("Data set dimensionality does not match the 'DIM' template argument"); + index = new index_t( dims, *this /* adaptor */, nanoflann::KDTreeSingleIndexAdaptorParams(leaf_max_size, dims ) ); + index->buildIndex(); + } + + ~KDTreeEigenMatrixAdaptor() { + delete index; + } + + const MatrixType &m_data_matrix; + + /** Query for the \a num_closest closest points to a given point (entered as query_point[0:dim-1]). + * Note that this is a short-cut method for index->findNeighbors(). + * The user can also call index->... methods as desired. + * \note nChecks_IGNORED is ignored but kept for compatibility with the original FLANN interface. + */ + inline void query(const num_t *query_point, const size_t num_closest, IndexType *out_indices, num_t *out_distances_sq, const int nChecks_IGNORED = 10) const + { + nanoflann::KNNResultSet resultSet(num_closest); + resultSet.init(out_indices, out_distances_sq); + index->findNeighbors(resultSet, query_point, nanoflann::SearchParams()); + } + + /** @name Interface expected by KDTreeSingleIndexAdaptor + * @{ */ + + const self_t & derived() const { + return *this; + } + self_t & derived() { + return *this; + } + + // Must return the number of data points + inline size_t kdtree_get_point_count() const { + return m_data_matrix.rows(); + } + + // Returns the distance between the vector "p1[0:size-1]" and the data point with index "idx_p2" stored in the class: + inline num_t kdtree_distance(const num_t *p1, const size_t idx_p2,size_t size) const + { + num_t s=0; + for (size_t i=0; i + bool kdtree_get_bbox(BBOX &bb) const { + return false; + } + + /** @} */ + + }; // end of KDTreeEigenMatrixAdaptor + /** @} */ + +/** @} */ // end of grouping +} // end of NS + + +#endif /* NANOFLANN_HPP_ */ diff --git a/Tangential_complex/include/CGAL/Tangential_complex/utilities.h b/Tangential_complex/include/CGAL/Tangential_complex/utilities.h new file mode 100644 index 00000000000..ec95a318317 --- /dev/null +++ b/Tangential_complex/include/CGAL/Tangential_complex/utilities.h @@ -0,0 +1,410 @@ +// 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 CGAL_TC_UTILITIES_H +#define CGAL_TC_UTILITIES_H + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include // CJTODO: this is C++11 => use boost.Atomic (but it's too recent) + // or tbb::atomic (works for doubles, but not officially) + +// choose exact integral type for QP solver +// (Gmpzf is not thread-safe) +#include +typedef CGAL::MP_Float ET; +//#define CGAL_QP_NO_ASSERTIONS // CJTODO: NECESSARY? http://doc.cgal.org/latest/QP_solver/group__PkgQPSolverFunctions.html#ga1fefbd0436aca0e281f88e8e6cd8eb74 + + + +namespace CGAL { +namespace Tangential_complex_ { + + // Provides copy constructors to std::atomic so that + // it can be used in a vector + template + struct Atomic_wrapper + : public std::atomic + { + typedef std::atomic Base; + + Atomic_wrapper() {} + Atomic_wrapper(const T &t) : Base(t) {} + Atomic_wrapper(const std::atomic &a) : Base(a.load()) {} + Atomic_wrapper(const Atomic_wrapper &other) : Base(other.load()) + {} + + Atomic_wrapper &operator=(const T &other) + { + Base::store(other); + return *this; + } + Atomic_wrapper &operator=(const std::atomic &other) + { + Base::store(other.load()); + return *this; + } + Atomic_wrapper &operator=(const Atomic_wrapper &other) + { + Base::store(other.load()); + return *this; + } + }; + + /*template + struct Atomic_wrapper + { + std::atomic _a; + + Atomic_wrapper() + :_a() + {} + + Atomic_wrapper(const std::atomic &other) + :_a(other.load()) + {} + + Atomic_wrapper(const Atomic_wrapper &other) + :_a(other._a.load()) + {} + + Atomic_wrapper(const T &other) + :_a(other) + {} + + Atomic_wrapper &operator=(const std::atomic &other) + { + _a.store(other._a.load()); + return *this; + } + + Atomic_wrapper &operator=(const Atomic_wrapper &other) + { + _a.store(other._a.load()); + return *this; + } + + Atomic_wrapper &operator=(const T &other) + { + _a.store(other); + return *this; + } + + operator T() const + { + return _a.load(); + } + + operator std::atomic() const + { + return _a; + } + };*/ + + // Modifies v in-place + template + typename K::Vector_d& normalize_vector(typename K::Vector_d& v, + K const& k) + { + v = k.scaled_vector_d_object()( + v, typename K::FT(1)/CGAL::sqrt(k.squared_length_d_object()(v))); + return v; + } + + template + struct Basis + { + typedef typename Kernel::FT FT; + typedef typename Kernel::Point_d Point; + typedef typename Kernel::Vector_d Vector; + typedef typename std::vector::const_iterator const_iterator; + + std::size_t m_origin; + std::vector m_vectors; + + std::size_t origin() const { return m_origin; } + void set_origin(std::size_t o) { m_origin = o; } + const_iterator begin() const { return m_vectors.begin(); } + const_iterator end() const { return m_vectors.end(); } + std::size_t size() const { return m_vectors.size(); } + + Vector& operator[](const std::size_t i) + { + return m_vectors[i]; + } + const Vector& operator[](const std::size_t i) const + { + return m_vectors[i]; + } + void push_back(const Vector& v) + { + m_vectors.push_back(v); + } + void reserve(const std::size_t s) + { + m_vectors.reserve(s); + } + + Basis() { } + Basis(std::size_t origin) : m_origin(origin) { } + Basis(std::size_t origin, const std::vector& vectors) + : m_origin(origin), m_vectors(vectors) + { } + +#ifdef CGAL_ALPHA_TC + // Thickening vectors... + + struct Thickening_vector + { + Thickening_vector() + : alpha_minus(FT(0)), alpha_plus(FT(0)) {} + Thickening_vector(Vector const& v, FT am, FT ap) + : vec(v), alpha_minus(am), alpha_plus(ap) {} + + Vector vec; + FT alpha_minus; + FT alpha_plus; + }; + typedef std::vector Thickening_vectors; + + Thickening_vectors m_thickening_vectors; + + + std::size_t num_thickening_vectors() const + { + return m_thickening_vectors.size(); + } + Thickening_vectors const& thickening_vectors() const + { + return m_thickening_vectors; + } + void add_thickening_vector( + Vector const& vec, FT alpha_minus = FT(0), FT alpha_plus = FT(0)) + { + m_thickening_vectors.push_back( + Thickening_vector(vec, alpha_minus, alpha_plus)); + } + + void update_alpha_values_of_thickening_vectors( + Vector const& vec, Kernel const& k) + { + typename Kernel::Scalar_product_d k_scalar_pdct = + k.scalar_product_d_object(); + + for (Thickening_vectors::iterator it_v = m_thickening_vectors.begin(), + it_v_end = m_thickening_vectors.end() ; + it_v != it_v_end ; ++it_v) + { + const FT MARGIN_RATIO = 1.001; // CJTODO TEMP + FT alpha_i = k_scalar_pdct(it_v->vec, vec); + if (alpha_i * MARGIN_RATIO > it_v->alpha_plus) + { +#ifdef CGAL_TC_VERY_VERBOSE + std::cerr << "OLD alpha+ = " << it_v->alpha_plus << std::endl; +#endif + it_v->alpha_plus = alpha_i * MARGIN_RATIO; +#ifdef CGAL_TC_VERY_VERBOSE + std::cerr << "NEW alpha+ = " << it_v->alpha_plus << std::endl; + std::cerr << "NOT MODIFIED alpha- = " << it_v->alpha_minus << std::endl; +#endif + } + else if (alpha_i * MARGIN_RATIO < it_v->alpha_minus) + { +#ifdef CGAL_TC_VERY_VERBOSE + std::cerr << "OLD alpha- = " << it_v->alpha_minus << std::endl; +#endif + it_v->alpha_minus = alpha_i * MARGIN_RATIO; +#ifdef CGAL_TC_VERY_VERBOSE + std::cerr << "NEW alpha- = " << it_v->alpha_minus << std::endl; + std::cerr << "NOT MODIFIED alpha+ = " << it_v->alpha_plus << std::endl; +#endif + } + } + } + + FT alpha_minus(std::size_t i) const + { + return m_thickening_vectors[i].alpha_minus; + } + FT alpha_plus(std::size_t i) const + { + return m_thickening_vectors[i].alpha_plus; + } + + // Returns 0 if no thickening vectors + FT max_absolute_alpha() const + { + FT max = FT(0); + + for (Thickening_vectors::const_iterator + it_v = m_thickening_vectors.begin(), + it_v_end = m_thickening_vectors.end() ; + it_v != it_v_end ; + ++it_v) + { + if (it_v->alpha_plus > max) + max = it_v->alpha_plus; + if (-it_v->alpha_minus > max) + max = -it_v->alpha_minus; + } + + return max; + } + + int dimension() const + { + return static_cast(m_vectors.size() + m_thickening_vectors.size()); + } +#else + int dimension() const + { + return static_cast(m_vectors.size()); + } +#endif + }; + + // Using Gram-Schmidt + // * If the resulting vector after G-S algorithm is below "sqlen_threshold", + // the vector considered linearly dependend to the existing vectors + // and is not added to the basis + // * Returns true if the vector was added to the basis + template + bool add_vector_to_orthonormal_basis( + Basis & basis, typename K::Vector_d const& v, K const& k, + typename K::FT sqlen_threshold = typename K::FT(1e-13) +#ifdef CGAL_ALPHA_TC + , bool add_to_thickening_vectors = false +#endif + ) + { + typedef Basis Basis; + typedef typename K::FT FT; + typedef typename K::Vector_d Vector; + + // Kernel functors + typename K::Scaled_vector_d scaled_vec = k.scaled_vector_d_object(); + typename K::Scalar_product_d scalar_pdct = k.scalar_product_d_object(); + typename K::Difference_of_vectors_d diff_vec = k.difference_of_vectors_d_object(); + + Vector u = v; + for (int j = 0 ; j < basis.size() ; ++j) + { + Vector const& ej = basis[j]; + Vector u_proj = scaled_vec(ej, scalar_pdct(u, ej) / scalar_pdct(ej, ej)); + u = diff_vec(u, u_proj); + } + for (int j = 0 ; j < basis.num_thickening_vectors() ; ++j) + { + Vector const& ej = basis.thickening_vectors()[j].vec; + Vector u_proj = scaled_vec(ej, scalar_pdct(u, ej) / scalar_pdct(ej, ej)); + u = diff_vec(u, u_proj); + } + + FT sqlen_new_v = k.squared_length_d_object()(u); + bool add_it = (sqlen_new_v > sqlen_threshold); + if (add_it) + { + Vector new_v = scaled_vec(u, FT(1)/CGAL::sqrt(sqlen_new_v)); + + // If new_v is small, run the Gram-Schmidt once more to + // re-orthogonalize it + if (sqlen_new_v < 0.01) + { + for (int j = 0 ; j < basis.size() ; ++j) + { + Vector const& ej = basis[j]; + Vector new_v_proj = scaled_vec( + ej, scalar_pdct(new_v, ej) / scalar_pdct(ej, ej)); + new_v = diff_vec(new_v, new_v_proj); + } + for (int j = 0 ; j < basis.num_thickening_vectors() ; ++j) + { + Vector const& ej = basis.thickening_vectors()[j].vec; + Vector new_v_proj = scaled_vec( + ej, scalar_pdct(new_v, ej) / scalar_pdct(ej, ej)); + new_v = diff_vec(new_v, new_v_proj); + } + sqlen_new_v = k.squared_length_d_object()(new_v); + new_v = scaled_vec(new_v, FT(1)/CGAL::sqrt(sqlen_new_v)); + } + +#ifdef CGAL_ALPHA_TC + if (add_to_thickening_vectors) + basis.add_thickening_vector(new_v); + else +#endif + basis.push_back(new_v); + } + return add_it; + } + + template + Basis compute_gram_schmidt_basis(Basis const& input_basis, K const& k) + { + typedef Basis Basis; + + Basis output_basis(input_basis.origin()); + + // Add vector one by one + typename Basis::const_iterator inb_it = input_basis.begin(); + typename Basis::const_iterator inb_it_end = input_basis.end(); + for (int i = 0 ; inb_it != inb_it_end ; ++inb_it, ++i) + add_vector_to_orthonormal_basis(output_basis, *inb_it, k); + + return output_basis; + } + + // CJTODO: use CGAL::Combination_enumerator (cf. Tangential_complex.h) + // Compute all the k-combinations of elements + // Output_iterator::value_type must be std::set > + template + void combinations(const Elements_container elements, int k, + Output_iterator combinations) + { + std::size_t n = elements.size(); + std::vector booleans(n, false); + std::fill(booleans.begin() + n - k, booleans.end(), true); + do + { + std::set combination; + typename Elements_container::const_iterator it_elt = elements.begin(); + for (std::size_t i = 0 ; i < n ; ++i, ++it_elt) + { + if (booleans[i]) + combination.insert(*it_elt); + } + *combinations++ = combination; + + } while (std::next_permutation(booleans.begin(), booleans.end())); + } + +} // namespace Tangential_complex_ +} //namespace CGAL + +#endif // CGAL_TC_UTILITIES_H diff --git a/Tangential_complex/package_info/Tangential_complex/copyright b/Tangential_complex/package_info/Tangential_complex/copyright new file mode 100644 index 00000000000..8932b3233d2 --- /dev/null +++ b/Tangential_complex/package_info/Tangential_complex/copyright @@ -0,0 +1,2 @@ +INRIA Sophia-Antipolis (France) + diff --git a/Tangential_complex/package_info/Tangential_complex/copyright.txt b/Tangential_complex/package_info/Tangential_complex/copyright.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/Tangential_complex/package_info/Tangential_complex/description.txt b/Tangential_complex/package_info/Tangential_complex/description.txt new file mode 100644 index 00000000000..b8969f7fc3e --- /dev/null +++ b/Tangential_complex/package_info/Tangential_complex/description.txt @@ -0,0 +1,3 @@ +Tangential complex + +This CGAL component provides an implementation of the tangential complex. diff --git a/Tangential_complex/package_info/Tangential_complex/license.txt b/Tangential_complex/package_info/Tangential_complex/license.txt new file mode 100644 index 00000000000..8bb8efcb72b --- /dev/null +++ b/Tangential_complex/package_info/Tangential_complex/license.txt @@ -0,0 +1 @@ +GPL (v3 or later) diff --git a/Tangential_complex/package_info/Tangential_complex/maintainer b/Tangential_complex/package_info/Tangential_complex/maintainer new file mode 100644 index 00000000000..769c1668e20 --- /dev/null +++ b/Tangential_complex/package_info/Tangential_complex/maintainer @@ -0,0 +1 @@ +Clément Jamin \ No newline at end of file diff --git a/Tangential_complex/test/Tangential_complex/CMakeLists.txt b/Tangential_complex/test/Tangential_complex/CMakeLists.txt new file mode 100644 index 00000000000..b0cf36ef8ee --- /dev/null +++ b/Tangential_complex/test/Tangential_complex/CMakeLists.txt @@ -0,0 +1,81 @@ +# Created by the script cgal_create_cmake_script +# This is the CMake script for compiling a CGAL application. + + +project( Tangential_complex_test ) + +cmake_minimum_required(VERSION 2.6.2) +if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER 2.6) + if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}" VERSION_GREATER 2.8.3) + cmake_policy(VERSION 2.8.4) + else() + cmake_policy(VERSION 2.6) + endif() +endif() + +# Creates a new CMake option, turned ON by default +option(ACTIVATE_MSVC_PRECOMPILED_HEADERS + "Activate precompiled headers in MSVC" + OFF) + +# Macro to add precompiled headers for MSVC +# This function does two things: +# 1. Enable precompiled headers on each file which is listed in "SourcesVar". +# 2. Add the content of "PrecompiledSource" (e.g. "StdAfx.cpp") to "SourcesVar". +MACRO(ADD_MSVC_PRECOMPILED_HEADER PrecompiledHeader PrecompiledSource SourcesVar) + IF(MSVC AND ACTIVATE_MSVC_PRECOMPILED_HEADERS) + GET_FILENAME_COMPONENT(PrecompiledBasename ${PrecompiledHeader} NAME_WE) + SET(Sources ${${SourcesVar}}) + + SET_SOURCE_FILES_PROPERTIES(${PrecompiledSource} + PROPERTIES COMPILE_FLAGS "/Yc\"${PrecompiledHeader}\"") + SET_SOURCE_FILES_PROPERTIES(${Sources} + PROPERTIES COMPILE_FLAGS "/Yu\"${PrecompiledHeaders}\" /FI\"${PrecompiledHeader}\"") + # Add precompiled header to SourcesVar + LIST(APPEND ${SourcesVar} ${PrecompiledSource}) + ENDIF(MSVC AND ACTIVATE_MSVC_PRECOMPILED_HEADERS) +ENDMACRO(ADD_MSVC_PRECOMPILED_HEADER) + +# The compiler might need more memory because of precompiled headers +if(MSVC AND ACTIVATE_MSVC_PRECOMPILED_HEADERS AND NOT(MSVC_VERSION LESS 1310)) + set(CGAL_C_FLAGS "${CGAL_C_FLAGS} /Zm1000") + set(CGAL_CXX_FLAGS "${CGAL_CXX_FLAGS} /Zm1000") +endif() + +find_package(CGAL QUIET COMPONENTS Core ) + +if ( CGAL_FOUND ) + include( ${CGAL_USE_FILE} ) + + find_package( TBB QUIET ) + + if( TBB_FOUND ) + include(${TBB_USE_FILE}) + list(APPEND CGAL_3RD_PARTY_LIBRARIES ${TBB_LIBRARIES}) + endif() + + + include( CGAL_CreateSingleSourceCGALProgram ) + + find_package(Eigen3 3.1.0) + if (EIGEN3_FOUND) + include( ${EIGEN3_USE_FILE} ) + include_directories (BEFORE "../../include") + include_directories (BEFORE "include") + include_directories(../../../Anisotropic_mesh_TC/include) + + set (SOURCE_FILES "test_tangential_complex.cpp") + ADD_MSVC_PRECOMPILED_HEADER("StdAfx.h" "StdAfx.cpp" SOURCE_FILES) + create_single_source_cgal_program( ${SOURCE_FILES} ) + + create_single_source_cgal_program( "aniso_TC.cpp" ) + create_single_source_cgal_program( "test_utilities.cpp" ) + + else() + message(STATUS "NOTICE: Some of the executables in this directory need Eigen 3.1 (or greater) and will not be compiled.") + endif() + +else() + message(STATUS "This program requires the CGAL library, and will not be compiled.") +endif() + diff --git a/Tangential_complex/test/Tangential_complex/StdAfx.cpp b/Tangential_complex/test/Tangential_complex/StdAfx.cpp new file mode 100644 index 00000000000..15668dcadef --- /dev/null +++ b/Tangential_complex/test/Tangential_complex/StdAfx.cpp @@ -0,0 +1,2 @@ +// Build the precompiled headers. +#include "StdAfx.h" \ No newline at end of file diff --git a/Tangential_complex/test/Tangential_complex/StdAfx.h b/Tangential_complex/test/Tangential_complex/StdAfx.h new file mode 100644 index 00000000000..573345d0022 --- /dev/null +++ b/Tangential_complex/test/Tangential_complex/StdAfx.h @@ -0,0 +1,14 @@ +#ifndef STDAFX_H +#define STDAFX_H + +// CGAL +#include +#include +#include +#include +#include +#include +#include +#include + +#endif //STDAFX_H \ No newline at end of file diff --git a/Tangential_complex/test/Tangential_complex/aniso_TC.cpp b/Tangential_complex/test/Tangential_complex/aniso_TC.cpp new file mode 100644 index 00000000000..6983311773a --- /dev/null +++ b/Tangential_complex/test/Tangential_complex/aniso_TC.cpp @@ -0,0 +1,297 @@ +#include +#include +#include +#include + +#include "../../test/Tangential_complex/testing_utilities.h" + +#include // Anisotropic metrics +#include +#include + +#include +#include +#include + +const int k = 2; // intrinsic +const int d = 5; // ambiant + +typedef CGAL::Epick_d< CGAL::Dimension_tag > Kk; +typedef CGAL::Epick_d< CGAL::Dimension_tag > Kd; +typedef Kk::FT FT; +typedef Kk::Point_d Point_k; +typedef Kd::Point_d Point_d; +typedef Kd::Weighted_point_d Weighted_point_d; +typedef Kd::Vector_d Vector_d; +typedef CGAL::Tangential_complex, + CGAL::Parallel_tag> TC; + +typedef CGAL::Tangential_complex_::Basis Basis; +typedef CGAL::Anisotropic_mesh_TC::Metric_base Metric; +typedef CGAL::Anisotropic_mesh_TC::Metric_field Metric_field; + +typedef typename Metric::E_Matrix E_Matrix_k; +typedef typename Metric::E_Vector E_Vector_k; +typedef Eigen::Matrix E_Vector_d; +typedef Eigen::Matrix E_Matrix_dk; + +typedef CGAL::Anisotropic_mesh_TC::Euclidean_metric_field Euclidean_mf; +typedef CGAL::Anisotropic_mesh_TC::Custom_metric_field Custom_mf; + +void read_points(std::vector& points, + const Kk kerk = Kk()) +{ + std::ifstream in("../../../../Anisotropic_mesh_2/examples/Anisotropic_mesh_2/bambimboum.mesh"); +// std::ifstream in("aniso_regular.mesh"); + std::string word; + int useless, nv, dd; + FT x; + + in >> word >> useless; //MeshVersionFormatted i + in >> word >> dd; //Dimension d + in >> word >> nv; + + assert(dd == Kk::Dimension::value); + + for(int i=0; i ids; + for(int j=0; j> x; + ids.push_back(x); + } + in >> useless; + points.push_back(kerk.construct_point_d_object()(ids.begin(), ids.begin() + k)); + + if(points.size() == 200) + break; + } + + std::cout << points.size() << " points" << std::endl; +} + +// compute the corresponding point on the paraboloid (these points will be the +// points at which we compute tangent planes) +Point_d to_Q(const Point_k& p) +{ + typename Kk::Compute_coordinate_d k_coord = Kk().compute_coordinate_d_object(); + + E_Vector_d p_on_Q; + for(int i=0; i const & points_k, + Metric_field const * const mf, + std::vector& points_d, + std::vector& weights, + Kd const kerd = Kd()) +{ + typename Kd::Point_drop_weight_d k_drop_w = kerd.point_drop_weight_d_object(); + typename Kd::Point_weight_d k_point_weight = kerd.point_weight_d_object(); + + for(std::size_t i=0; icompute_metric(p); + Weighted_point_d wp = to_S(p, met, kerd); + points_d.push_back(k_drop_w(wp)); + weights.push_back(k_point_weight(wp)); + } +} + +void compute_and_set_tangent_planes(TC& tc, + const std::vector& points_k, + Kd const kerd = Kd()) +{ + typedef TC::TS_container TS_container; + typedef TC::OS_container OS_container; + + typename Kd::Compute_coordinate_d coord = kerd.compute_coordinate_d_object(); + typename Kd::Construct_vector_d constr_vec = kerd.construct_vector_d_object(); + TS_container tsc; + OS_container osc; + std::size_t n = tc.number_of_vertices(); + CGAL_assertion(n == points_k.size()); + + for(std::size_t c=0; c& points_k) +{ + std::ofstream out("aniso.off"); + std::stringstream output; + std::size_t num_simplices; + + typename Kk::Compute_coordinate_d k_coord = Kk().compute_coordinate_d_object(); + + + for(std::size_t i=0; i& points_k, + Metric_field const * const mf) +{ + Kd ker_d; + + std::vector points_d; + std::vector weights; + compute_points_in_ambiant_space(points_k, mf, points_d, weights, ker_d); + + TC tc(points_d.begin(), points_d.end(), 0./*sparsity*/, k /*intr dim*/, ker_d); + tc.set_weights(weights); + compute_and_set_tangent_planes(tc, points_k, ker_d); + + tc.compute_tangential_complex(); + + TC::Simplicial_complex complex; + int max_dim = tc.export_TC(complex, false); + complex.display_stats(); + { + std::ofstream off_stream("aniso_alpha_complex.off"); + tc.export_to_off(complex, off_stream); + } + + // Collapse + complex.collapse(max_dim); + { + std::ofstream off_stream("aniso_after_collapse.off"); + tc.export_to_off(complex, off_stream); + } + + std::size_t num_wrong_dim_simplices, num_wrong_number_of_cofaces; + bool pure_manifold = complex.is_pure_manifold(k, false, 1, + &num_wrong_dim_simplices, + &num_wrong_number_of_cofaces); + complex.display_stats(); + + export_complex_in_origin_space(tc, complex, points_k); + return; +} + +int main() +{ + CGAL::default_random = CGAL::Random(); + std::vector points_k; + + //Custom_mf* mf = new Custom_mf(); + Euclidean_mf* mf = new Euclidean_mf(); + + read_points(points_k); + make_tc(points_k, mf); + + return 0; +} diff --git a/Tangential_complex/test/Tangential_complex/test_tangential_complex.cpp b/Tangential_complex/test/Tangential_complex/test_tangential_complex.cpp new file mode 100644 index 00000000000..f94f004a504 --- /dev/null +++ b/Tangential_complex/test/Tangential_complex/test_tangential_complex.cpp @@ -0,0 +1,163 @@ +//#undef CGAL_LINKED_WITH_TBB // CJTODO TEMP + +// Without TBB_USE_THREADING_TOOL Intel Inspector XE will report false positives in Intel TBB +// (http://software.intel.com/en-us/articles/compiler-settings-for-threading-error-analysis-in-intel-inspector-xe/) +#ifdef _DEBUG +# define TBB_USE_THREADING_TOOL +#endif + +#include +#include +#include +#include +#include + +#include "testing_utilities.h" + +#include +#include + +#ifdef CGAL_LINKED_WITH_TBB +# include +#endif + +//=============== Constants ================= +const double INPUT_SPARSITY = 0.05; +#ifdef _DEBUG + const int NUM_POINTS = 50; +#else + const int NUM_POINTS = 30000; +#endif +//=========================================== + +int main() +{ +#if defined(CHECK_MEMORY_LEAKS_ON_MSVC) && defined(_MSC_VER) + _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); +#endif + CGAL::set_error_behaviour(CGAL::ABORT); + +#ifdef CGAL_LINKED_WITH_TBB +# ifdef _DEBUG + tbb::task_scheduler_init init(1); +# else + tbb::task_scheduler_init init(10); +# endif +#endif + + const int INTRINSIC_DIMENSION = 3; + const int AMBIENT_DIMENSION = 9; + + typedef CGAL::Epick_d > Kernel; + typedef Kernel::FT FT; + typedef Kernel::Point_d Point; + typedef CGAL::Tangential_complex< + Kernel, CGAL::Dimension_tag, + CGAL::Parallel_tag> TC; + + int i = 0; + bool stop = false; + //for ( ; !stop ; ++i) + { + Kernel k; + Wall_clock_timer t; + CGAL::default_random = CGAL::Random(i); + std::cerr << "Random seed = " << i << std::endl; + +#ifdef CGAL_TC_PROFILING + Wall_clock_timer t_gen; +#endif + + /*std::vector points = + //generate_points_on_circle_2(NUM_POINTS, 3.); + //generate_points_on_moment_curve(NUM_POINTS, AMBIENT_DIMENSION, 0., 1.); + //generate_points_on_plane(NUM_POINTS); + //generate_points_on_sphere_3(NUM_POINTS, 3.0); + //generate_points_on_sphere_d(NUM_POINTS, AMBIENT_DIMENSION, 3.0); + //generate_points_on_klein_bottle_3D(NUM_POINTS, 4., 3.); + generate_points_on_klein_bottle_4D(NUM_POINTS, 4., 3.); + //generate_points_on_klein_bottle_variant_5D(NUM_POINTS, 4., 3.);*/ + + // LOAD FROM A FILE + std::vector points; + load_points_from_file( + "data/SO3_10000.txt", std::back_inserter(points)); + +#ifdef CGAL_TC_PROFILING + std::cerr << "Point set generated in " << t_gen.elapsed() + << " seconds." << std::endl; +#endif + + std::size_t num_points_before = points.size(); + points = sparsify_point_set( + k, points, FT(INPUT_SPARSITY)*FT(INPUT_SPARSITY)); + std::cerr << "Number of points before/after sparsification: " + << num_points_before << " / " << points.size() << std::endl; + + TC tc(points.begin(), points.end(), INPUT_SPARSITY, INTRINSIC_DIMENSION, k); + double init_time = t.elapsed(); t.reset(); + + tc.compute_tangential_complex(); + double computation_time = t.elapsed(); t.reset(); + + if (ambient_dim <= 4) + tc.check_if_all_simplices_are_in_the_ambient_delaunay(); + + double export_before_time = -1.; + if (INTRINSIC_DIMENSION <= 3) + { + t.reset(); + std::stringstream output_filename; + output_filename << "output/test_tc_" << INTRINSIC_DIMENSION + << "_in_R" << AMBIENT_DIMENSION << "_BEFORE_FIX.off"; + std::ofstream off_stream(output_filename.str().c_str()); + tc.export_to_off(off_stream, true); + export_before_time = t.elapsed(); t.reset(); + } + + + t.reset(); + unsigned int num_fix_steps; + std::size_t initial_num_inconsistent_local_tr; + std::size_t best_num_inconsistent_local_tr; + std::size_t final_num_inconsistent_local_tr; + CGAL::Fix_inconsistencies_status fix_ret = + tc.fix_inconsistencies_using_perturbation( + num_fix_steps, initial_num_inconsistent_local_tr, + best_num_inconsistent_local_tr, final_num_inconsistent_local_tr, 1000.); + double fix_time = t.elapsed(); t.reset(); + + double export_after_time = -1.; + if (INTRINSIC_DIMENSION <= 3) + { + t.reset(); + std::stringstream output_filename; + output_filename << "output/test_tc_" << INTRINSIC_DIMENSION + << "_in_R" << AMBIENT_DIMENSION << "_AFTER_FIX.off"; + std::ofstream off_stream(output_filename.str().c_str()); + tc.export_to_off(off_stream, true); + export_after_time = t.elapsed(); t.reset(); + } + /*else + tc.number_of_inconsistent_simplices();*/ + + + std::cerr << std::endl + << "================================================" << std::endl + << "Number of vertices: " << tc.number_of_vertices() << std::endl + << "Computation times (seconds): " << std::endl + << " * Tangential complex: " << init_time + computation_time + << std::endl + << " - Init + kd-tree = " << init_time << std::endl + << " - TC computation = " << computation_time << std::endl + << " * Export to OFF (before fix): " << export_before_time << std::endl + << " * Fix inconsistencies: " << fix_time + << " (" << num_fix_steps << " steps) ==> " + << (fix_ret == CGAL::TC_FIXED ? "FIXED" : "NOT fixed") << std::endl + << " * Export to OFF (after fix): " << export_after_time << std::endl + << "================================================" << std::endl + << std::endl; + } + + return 0; +} diff --git a/Tangential_complex/test/Tangential_complex/test_utilities.cpp b/Tangential_complex/test/Tangential_complex/test_utilities.cpp new file mode 100644 index 00000000000..85d1646cecf --- /dev/null +++ b/Tangential_complex/test/Tangential_complex/test_utilities.cpp @@ -0,0 +1,57 @@ + +// Without TBB_USE_THREADING_TOOL Intel Inspector XE will report false positives in Intel TBB +// (http://software.intel.com/en-us/articles/compiler-settings-for-threading-error-analysis-in-intel-inspector-xe/) +#ifdef _DEBUG +# define TBB_USE_THREADING_TOOL +#endif + +#include +#include + +#include + +using namespace CGAL::Tangential_complex_; + +void test_does_voronoi_face_and_alpha_tangent_subspace_intersect() +{ + typedef CGAL::Epick_d > K; + typedef K::Point_d Pt; + typedef K::Vector_d Vec; + std::vector P; + std::vector Q; + std::vector osb; + + K k; + + std::vector points; + points.push_back(Pt(0.02, -0.03)); + points.push_back(Pt(0.005, 2.3)); + points.push_back(Pt(4.5, 1.12)); + points.push_back(Pt(-3.5, 1.02)); + + P.push_back(0); + P.push_back(1); + + Q.push_back(2); + Q.push_back(3); + + osb.push_back(Vec(0.01, 0.995)); + + assert(does_voronoi_face_and_fixed_alpha_tangent_subspace_intersect( + points, 0, P, Q, osb, 0.0, k) == false); + + assert(does_voronoi_face_and_fixed_alpha_tangent_subspace_intersect( + points, 0, P, Q, osb, 0.5, k) == false); + + assert(does_voronoi_face_and_fixed_alpha_tangent_subspace_intersect( + points, 0, P, Q, osb, 1.0, k) == false); + + assert(does_voronoi_face_and_fixed_alpha_tangent_subspace_intersect( + points, 0, P, Q, osb, 1.5, k) == true); +} + +int main() +{ + test_does_voronoi_face_and_alpha_tangent_subspace_intersect(); + return 0; +} diff --git a/Tangential_complex/test/Tangential_complex/testing_utilities.h b/Tangential_complex/test/Tangential_complex/testing_utilities.h new file mode 100644 index 00000000000..810b5063fcd --- /dev/null +++ b/Tangential_complex/test/Tangential_complex/testing_utilities.h @@ -0,0 +1,870 @@ +// 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 +// +//****************************************************************************** +// File Description : +// +//****************************************************************************** + +#ifndef CGAL_TC_TEST_TEST_UTILITIES_H +#define CGAL_TC_TEST_TEST_UTILITIES_H + +#include +#include +#include +#include +#include +#include + +#include + +// Actually, this is very slow because the "m_points_ds->insert" +// cleans the tree, which is thus built at each query_ANN call +#ifdef CGAL_TC_USE_SLOW_BUT_ACCURATE_SPARSIFIER +template +class Point_sparsifier +{ +public: + typedef typename Kernel::FT FT; + typedef typename Point_container::value_type Point; + typedef typename CGAL::Tangential_complex_::Point_cloud_data_structure< + Kernel, Point_container> Points_ds; + typedef typename Points_ds::KNS_range KNS_range; + + // We can't instantiate m_points_ds right now since it requires that + // points is not empty (which be the case here) + Point_sparsifier(Point_container &points, + FT sparsity = FT(0.05*0.05)) + : m_points(points), m_sparsity(sparsity), m_points_ds(NULL) + {} + + bool try_to_insert_point(const Point &p) + { + if (m_points_ds == NULL) + { + m_points.push_back(p); + m_points_ds = new Points_ds(m_points); + m_points_ds->insert(0); + return true; + } + else + { + KNS_range kns_range = m_points_ds->query_ANN(p, 1, false); + if (kns_range.begin()->second >= m_sparsity) + { + m_points.push_back(p); + m_points_ds->insert(m_points.size() - 1); + return true; + } + } + + return false; + } + +private: + Point_container & m_points; + Points_ds * m_points_ds; + FT m_sparsity; +}; +#endif + +// construct_point: dim 2 +template +typename Kernel::Point_d construct_point( + const Kernel &k, + typename Kernel::FT x1, typename Kernel::FT x2) +{ + typename Kernel::FT tab[2]; + tab[0] = x1; tab[1] = x2; + return k.construct_point_d_object()(2, &tab[0], &tab[2]); +} + +// construct_point: dim 3 +template +typename Kernel::Point_d construct_point( + const Kernel &k, + typename Kernel::FT x1, typename Kernel::FT x2, typename Kernel::FT x3) +{ + typename Kernel::FT tab[3]; + tab[0] = x1; tab[1] = x2; tab[2] = x3; + return k.construct_point_d_object()(3, &tab[0], &tab[3]); +} + +// construct_point: dim 4 +template +typename Kernel::Point_d construct_point( + const Kernel &k, + typename Kernel::FT x1, typename Kernel::FT x2, typename Kernel::FT x3, + typename Kernel::FT x4) +{ + typename Kernel::FT tab[4]; + tab[0] = x1; tab[1] = x2; tab[2] = x3; tab[3] = x4; + return k.construct_point_d_object()(4, &tab[0], &tab[4]); +} + +// construct_point: dim 5 +template +typename Kernel::Point_d construct_point( + const Kernel &k, + typename Kernel::FT x1, typename Kernel::FT x2, typename Kernel::FT x3, + typename Kernel::FT x4, typename Kernel::FT x5) +{ + typename Kernel::FT tab[5]; + tab[0] = x1; tab[1] = x2; tab[2] = x3; tab[3] = x4; tab[4] = x5; + return k.construct_point_d_object()(5, &tab[0], &tab[5]); +} + +// construct_point: dim 6 +template +typename Kernel::Point_d construct_point( + const Kernel &k, + typename Kernel::FT x1, typename Kernel::FT x2, typename Kernel::FT x3, + typename Kernel::FT x4, typename Kernel::FT x5, typename Kernel::FT x6) +{ + typename Kernel::FT tab[6]; + tab[0] = x1; tab[1] = x2; tab[2] = x3; tab[3] = x4; tab[4] = x5; tab[5] = x6; + return k.construct_point_d_object()(6, &tab[0], &tab[6]); +} + +template +std::vector +sparsify_point_set( + const Kernel &k, Point_container const& input_pts, + typename Kernel::FT min_squared_dist) +{ + typedef typename CGAL::Tangential_complex_::Point_cloud_data_structure< + Kernel, Point_container> Points_ds; + typedef typename Points_ds::INS_iterator INS_iterator; + typedef typename Points_ds::INS_range INS_range; + + typename Kernel::Squared_distance_d sqdist = k.squared_distance_d_object(); + +#ifdef CGAL_TC_PROFILING + Wall_clock_timer t; +#endif + + // Create the output container + std::vector output; + + Points_ds points_ds(input_pts); + + std::vector dropped_points(input_pts.size(), false); + + // Parse the following points, and add them if they are not too close to + // the other points + std::size_t pt_idx = 0; + for (typename Point_container::const_iterator it_pt = input_pts.begin() ; + it_pt != input_pts.end(); + ++it_pt, ++pt_idx) + { + if (dropped_points[pt_idx]) + continue; + + output.push_back(*it_pt); + + INS_range ins_range = points_ds.query_incremental_ANN(*it_pt); + + // Drop it if there is another point that: + // - is closer that min_squared_dist + // - and has a higher index + for (INS_iterator nn_it = ins_range.begin() ; + nn_it != ins_range.end() ; + ++nn_it) + { + std::size_t neighbor_point_idx = nn_it->first; + typename Kernel::FT sq_dist = nn_it->second; + // The neighbor is too close, we drop the neighbor + if (sq_dist < min_squared_dist) + dropped_points[neighbor_point_idx] = true; + else + break; + } + } + +#ifdef CGAL_TC_PROFILING + std::cerr << "Point set sparsified in " << t.elapsed() + << " seconds." << std::endl; +#endif + + return output; +} + +template +bool load_points_from_file( + const std::string &filename, OutputIterator points, + std::size_t only_first_n_points = std::numeric_limits::max()) +{ + std::ifstream in(filename); + if (!in.is_open()) + { + std::cerr << "Could not open '" << filename << "'" << std::endl; + return false; + } + + Point p; + int dim_from_file; + in >> dim_from_file; + + std::size_t i = 0; + while(i < only_first_n_points && in >> p) + { + *points++ = p; + ++i; + } + +#ifdef CGAL_TC_VERBOSE + std::cerr << "'" << filename << "' loaded." << std::endl; +#endif + + return true; +} + +template +std::vector generate_points_on_plane( + std::size_t num_points, int intrinsic_dim, int ambient_dim) +{ + typedef typename Kernel::Point_d Point; + typedef typename Kernel::FT FT; + Kernel k; + CGAL::Random rng; + std::vector points; + points.reserve(num_points); +#ifdef CGAL_TC_USE_SLOW_BUT_ACCURATE_SPARSIFIER + Point_sparsifier > sparsifier(points); +#endif + for (std::size_t i = 0 ; i < num_points ; ) + { + std::vector pt(ambient_dim, FT(0)); + for (int j = 0 ; j < intrinsic_dim ; ++j) + pt[j] = rng.get_double(-5., 5.); + /*for (int j = intrinsic_dim ; j < ambient_dim ; ++j) + pt[j] = rng.get_double(-0.01, 0.01);*/ + + Point p = k.construct_point_d_object()(ambient_dim, pt.begin(), pt.end()); + +#ifdef CGAL_TC_USE_SLOW_BUT_ACCURATE_SPARSIFIER + if (sparsifier.try_to_insert_point(p)) + ++i; +#else + points.push_back(p); + ++i; +#endif + } + return points; +} + +template +std::vector generate_points_on_moment_curve( + std::size_t num_points, int dim, + typename Kernel::FT min_x , typename Kernel::FT max_x) +{ + typedef typename Kernel::Point_d Point; + typedef typename Kernel::FT FT; + Kernel k; + CGAL::Random rng; + std::vector points; + points.reserve(num_points); +#ifdef CGAL_TC_USE_SLOW_BUT_ACCURATE_SPARSIFIER + Point_sparsifier > sparsifier(points); +#endif + for (std::size_t i = 0 ; i < num_points ; ) + { + FT x = rng.get_double(min_x, max_x); + std::vector coords; + coords.reserve(dim); + for (int p = 1 ; p <= dim ; ++p) + coords.push_back(std::pow(CGAL::to_double(x), p)); + Point p = k.construct_point_d_object()( + dim, coords.begin(), coords.end()); +#ifdef CGAL_TC_USE_SLOW_BUT_ACCURATE_SPARSIFIER + if (sparsifier.try_to_insert_point(p)) + ++i; +#else + points.push_back(p); + ++i; +#endif + } + return points; +} + + +// R = big radius, r = small radius +template +std::vector generate_points_on_torus_3D( + std::size_t num_points, double R, double r, bool uniform = false + /*, std::vector *p_tangent_planes = NULL*/) +{ + typedef typename Kernel::Point_d Point; + typedef typename Kernel::Vector_d Vector; + typedef typename Kernel::FT FT; + Kernel k; + CGAL::Random rng; + + //typename Kernel::Construct_vector_d cstr_vec = k.construct_vector_d_object(); + + // if uniform + std::size_t num_lines = (std::size_t)sqrt(num_points); + std::size_t num_cols = num_points/num_lines + 1; + + std::vector points; + points.reserve(num_points); +#ifdef CGAL_TC_USE_SLOW_BUT_ACCURATE_SPARSIFIER + Point_sparsifier > sparsifier(points); +#endif + for (std::size_t i = 0 ; i < num_points ; ) + { + FT u, v; + if (uniform) + { + std::size_t k1 = i / num_lines; + std::size_t k2 = i % num_lines; + u = 6.2832 * k1 / num_lines; + v = 6.2832 * k2 / num_lines; + } + else + { + u = rng.get_double(0, 6.2832); + v = rng.get_double(0, 6.2832); + } + double tmp = cos(u/2)*sin(v) - sin(u/2)*sin(2.*v); + Point p = construct_point(k, + (R + r * std::cos(u)) * std::cos(v), + (R + r * std::cos(u)) * std::sin(v), + r * std::sin(u)); +#ifdef CGAL_TC_USE_SLOW_BUT_ACCURATE_SPARSIFIER + if (sparsifier.try_to_insert_point(p)) + ++i; +#else + points.push_back(p); + ++i; +#endif + /*if (p_tangent_planes) + { + TC_basis tp(p); + tp.push_back(cstr_vec( + -r * std::cos(v) * std::sin(u), + -r * std::sin(v) * std::sin(u), + r * std::cos(u))); + tp.push_back(cstr_vec( + -(R + r * std::cos(u)) * std::sin(v), + (R + r * std::cos(u)) * std::cos(v), + 0)); + p_tangent_planes->push_back( + CGAL::Tangential_complex_::compute_gram_schmidt_basis(sp, k)); + }*/ + } + return points; +} + +template +static void generate_uniform_points_on_torus_d( + const Kernel &k, int dim, std::size_t num_slices, + OutputIterator out, + double radius_noise_percentage = 0., + std::vector current_point = std::vector()) +{ + static CGAL::Random rng; + if (current_point.size() == 2*dim) + { + *out++ = k.construct_point_d_object()( + static_cast(current_point.size()), + current_point.begin(), current_point.end()); + } + else + { + for (std::size_t slice_idx = 0 ; slice_idx < num_slices ; ++slice_idx) + { + double radius_noise_ratio = 1.; + if (radius_noise_percentage > 0.) + { + radius_noise_ratio = rng.get_double( + (100. - radius_noise_percentage)/100., + (100. + radius_noise_percentage)/100.); + } + std::vector cp2 = current_point; + FT alpha = 6.2832 * slice_idx / num_slices; + cp2.push_back(radius_noise_ratio*std::cos(alpha)); + cp2.push_back(radius_noise_ratio*std::sin(alpha)); + generate_uniform_points_on_torus_d( + k, dim, num_slices, out, radius_noise_percentage, cp2); + } + } +} + +template +std::vector generate_points_on_torus_d( + std::size_t num_points, int dim, bool uniform = false, + double radius_noise_percentage = 0.) +{ + typedef typename Kernel::Point_d Point; + typedef typename Kernel::FT FT; + Kernel k; + static CGAL::Random rng; + + std::vector points; + points.reserve(num_points); + if (uniform) + { + std::size_t num_slices = (std::size_t)std::pow(num_points, 1./dim); + generate_uniform_points_on_torus_d( + k, dim, num_slices, std::back_inserter(points), radius_noise_percentage); + } + else + { +#ifdef CGAL_TC_USE_SLOW_BUT_ACCURATE_SPARSIFIER + Point_sparsifier > sparsifier(points); +#endif + for (std::size_t i = 0 ; i < num_points ; ) + { + double radius_noise_ratio = 1.; + if (radius_noise_percentage > 0.) + { + radius_noise_ratio = rng.get_double( + (100. - radius_noise_percentage)/100., + (100. + radius_noise_percentage)/100.); + } + std::vector pt; + pt.reserve(dim*2); + for (int curdim = 0 ; curdim < dim ; ++curdim) + { + FT alpha = rng.get_double(0, 6.2832); + pt.push_back(radius_noise_ratio*std::cos(alpha)); + pt.push_back(radius_noise_ratio*std::sin(alpha)); + } + + Point p = k.construct_point_d_object()(pt.begin(), pt.end()); +#ifdef CGAL_TC_USE_SLOW_BUT_ACCURATE_SPARSIFIER + if (sparsifier.try_to_insert_point(p)) + ++i; +#else + points.push_back(p); + ++i; +#endif + } + } + return points; +} + +template +std::vector generate_points_on_sphere_d( + std::size_t num_points, int dim, double radius, + double radius_noise_percentage = 0.) +{ + typedef typename Kernel::Point_d Point; + Kernel k; + CGAL::Random rng; + CGAL::Random_points_on_sphere_d generator(dim, radius); + std::vector points; + points.reserve(num_points); +#ifdef CGAL_TC_USE_SLOW_BUT_ACCURATE_SPARSIFIER + Point_sparsifier > sparsifier(points); +#endif + for (std::size_t i = 0 ; i < num_points ; ) + { + Point p = *generator++; + if (radius_noise_percentage > 0.) + { + double radius_noise_ratio = rng.get_double( + (100. - radius_noise_percentage)/100., + (100. + radius_noise_percentage)/100.); + + typename Kernel::Point_to_vector_d k_pt_to_vec = + k.point_to_vector_d_object(); + typename Kernel::Vector_to_point_d k_vec_to_pt = + k.vector_to_point_d_object(); + typename Kernel::Scaled_vector_d k_scaled_vec = + k.scaled_vector_d_object(); + p = k_vec_to_pt(k_scaled_vec(k_pt_to_vec(p), radius_noise_ratio)); + } +#ifdef CGAL_TC_USE_SLOW_BUT_ACCURATE_SPARSIFIER + if (sparsifier.try_to_insert_point(p)) + ++i; +#else + points.push_back(p); + ++i; +#endif + } + return points; +} + +template +std::vector generate_points_on_two_spheres_d( + std::size_t num_points, int dim, double radius, + double distance_between_centers, double radius_noise_percentage = 0.) +{ + typedef typename Kernel::FT FT; + typedef typename Kernel::Point_d Point; + typedef typename Kernel::Vector_d Vector; + Kernel k; + CGAL::Random rng; + CGAL::Random_points_on_sphere_d generator(dim, radius); + std::vector points; + points.reserve(num_points); + + std::vector t(dim, FT(0)); + t[0] = distance_between_centers; + Vector c1_to_c2(t.begin(), t.end()); + +#ifdef CGAL_TC_USE_SLOW_BUT_ACCURATE_SPARSIFIER + Point_sparsifier > sparsifier(points); +#endif + for (std::size_t i = 0 ; i < num_points ; ) + { + Point p = *generator++; + if (radius_noise_percentage > 0.) + { + double radius_noise_ratio = rng.get_double( + (100. - radius_noise_percentage)/100., + (100. + radius_noise_percentage)/100.); + + typename Kernel::Point_to_vector_d k_pt_to_vec = + k.point_to_vector_d_object(); + typename Kernel::Vector_to_point_d k_vec_to_pt = + k.vector_to_point_d_object(); + typename Kernel::Scaled_vector_d k_scaled_vec = + k.scaled_vector_d_object(); + p = k_vec_to_pt(k_scaled_vec(k_pt_to_vec(p), radius_noise_ratio)); + } + + typename Kernel::Translated_point_d k_transl = + k.translated_point_d_object(); + Point p2 = k_transl(p, c1_to_c2); + +#ifdef CGAL_TC_USE_SLOW_BUT_ACCURATE_SPARSIFIER + if (sparsifier.try_to_insert_point(p)) + ++i; + if (sparsifier.try_to_insert_point(p2)) + ++i; +#else + points.push_back(p); + points.push_back(p2); + i += 2; +#endif + } + return points; +} + +// Product of a 3-sphere and a circle => d = 3 / D = 5 +template +std::vector generate_points_on_3sphere_and_circle( + std::size_t num_points, double sphere_radius) +{ + typedef typename Kernel::FT FT; + typedef typename Kernel::Point_d Point; + typedef typename Kernel::Vector_d Vector; + Kernel k; + CGAL::Random rng; + CGAL::Random_points_on_sphere_d generator(3, sphere_radius); + std::vector points; + points.reserve(num_points); + + typename Kernel::Translated_point_d k_transl = + k.translated_point_d_object(); + typename Kernel::Compute_coordinate_d k_coord = + k.compute_coordinate_d_object(); + +#ifdef CGAL_TC_USE_SLOW_BUT_ACCURATE_SPARSIFIER + Point_sparsifier > sparsifier(points); +#endif + for (std::size_t i = 0 ; i < num_points ; ) + { + Point p_sphere = *generator++; // First 3 coords + + FT alpha = rng.get_double(0, 6.2832); + std::vector pt(5); + pt[0] = k_coord(p_sphere, 0); + pt[1] = k_coord(p_sphere, 1); + pt[2] = k_coord(p_sphere, 2); + pt[3] = std::cos(alpha); + pt[4] = std::sin(alpha); + Point p(pt.begin(), pt.end()); + +#ifdef CGAL_TC_USE_SLOW_BUT_ACCURATE_SPARSIFIER + if (sparsifier.try_to_insert_point(p)) + ++i; + if (sparsifier.try_to_insert_point(p2)) + ++i; +#else + points.push_back(p); + ++i; +#endif + } + return points; +} + +// a = big radius, b = small radius +template +std::vector generate_points_on_klein_bottle_3D( + std::size_t num_points, double a, double b, bool uniform = false) +{ + typedef typename Kernel::Point_d Point; + typedef typename Kernel::FT FT; + Kernel k; + CGAL::Random rng; + + // if uniform + std::size_t num_lines = (std::size_t)sqrt(num_points); + std::size_t num_cols = num_points/num_lines + 1; + + std::vector points; + points.reserve(num_points); +#ifdef CGAL_TC_USE_SLOW_BUT_ACCURATE_SPARSIFIER + Point_sparsifier > sparsifier(points); +#endif + for (std::size_t i = 0 ; i < num_points ; ) + { + FT u, v; + if (uniform) + { + std::size_t k1 = i / num_lines; + std::size_t k2 = i % num_lines; + u = 6.2832 * k1 / num_lines; + v = 6.2832 * k2 / num_lines; + } + else + { + u = rng.get_double(0, 6.2832); + v = rng.get_double(0, 6.2832); + } + double tmp = cos(u/2)*sin(v) - sin(u/2)*sin(2.*v); + Point p = construct_point(k, + (a + b*tmp)*cos(u), + (a + b*tmp)*sin(u), + b*(sin(u/2)*sin(v) + cos(u/2)*sin(2.*v))); +#ifdef CGAL_TC_USE_SLOW_BUT_ACCURATE_SPARSIFIER + if (sparsifier.try_to_insert_point(p)) + ++i; +#else + points.push_back(p); + ++i; +#endif + } + return points; +} + +// a = big radius, b = small radius +template +std::vector generate_points_on_klein_bottle_4D( + std::size_t num_points, double a, double b, double noise = 0., bool uniform = false) +{ + typedef typename Kernel::Point_d Point; + typedef typename Kernel::FT FT; + Kernel k; + CGAL::Random rng; + + // if uniform + std::size_t num_lines = (std::size_t)sqrt(num_points); + std::size_t num_cols = num_points/num_lines + 1; + + std::vector points; + points.reserve(num_points); +#ifdef CGAL_TC_USE_SLOW_BUT_ACCURATE_SPARSIFIER + Point_sparsifier > sparsifier(points); +#endif + for (std::size_t i = 0 ; i < num_points ; ) + { + FT u, v; + if (uniform) + { + std::size_t k1 = i / num_lines; + std::size_t k2 = i % num_lines; + u = 6.2832 * k1 / num_lines; + v = 6.2832 * k2 / num_lines; + } + else + { + u = rng.get_double(0, 6.2832); + v = rng.get_double(0, 6.2832); + } + Point p = construct_point(k, + (a + b*cos(v))*cos(u) + (noise == 0. ? 0. : rng.get_double(0, noise)), + (a + b*cos(v))*sin(u) + (noise == 0. ? 0. : rng.get_double(0, noise)), + b*sin(v)*cos(u/2) + (noise == 0. ? 0. : rng.get_double(0, noise)), + b*sin(v)*sin(u/2) + (noise == 0. ? 0. : rng.get_double(0, noise))); +#ifdef CGAL_TC_USE_SLOW_BUT_ACCURATE_SPARSIFIER + if (sparsifier.try_to_insert_point(p)) + ++i; +#else + points.push_back(p); + ++i; +#endif + } + return points; +} + + +// a = big radius, b = small radius +template +std::vector +generate_points_on_klein_bottle_variant_5D( + std::size_t num_points, double a, double b, bool uniform = false) +{ + typedef typename Kernel::Point_d Point; + typedef typename Kernel::FT FT; + Kernel k; + CGAL::Random rng; + + // if uniform + std::size_t num_lines = (std::size_t)sqrt(num_points); + std::size_t num_cols = num_points/num_lines + 1; + + std::vector points; + points.reserve(num_points); +#ifdef CGAL_TC_USE_SLOW_BUT_ACCURATE_SPARSIFIER + Point_sparsifier > sparsifier(points); +#endif + for (std::size_t i = 0 ; i < num_points ; ) + { + FT u, v; + if (uniform) + { + std::size_t k1 = i / num_lines; + std::size_t k2 = i % num_lines; + u = 6.2832 * k1 / num_lines; + v = 6.2832 * k2 / num_lines; + } + else + { + u = rng.get_double(0, 6.2832); + v = rng.get_double(0, 6.2832); + } + FT x1 = (a + b*cos(v))*cos(u); + FT x2 = (a + b*cos(v))*sin(u); + FT x3 = b*sin(v)*cos(u/2); + FT x4 = b*sin(v)*sin(u/2); + FT x5 = x1 + x2 + x3 + x4; + + Point p = construct_point(k, x1, x2, x3, x4, x5); +#ifdef CGAL_TC_USE_SLOW_BUT_ACCURATE_SPARSIFIER + if (sparsifier.try_to_insert_point(p)) + ++i; +#else + points.push_back(p); + ++i; +#endif + } + return points; +} + +template +void benchmark_spatial_search( + const std::vector &points, const Kernel &k, + std::ostream & csv_file) +{ + std::cout << + "****************************************\n" + "***** Benchmarking spatial search ******\n" + "****************************************\n\n"; + + const std::size_t NUM_QUERIES = 100000; + const std::size_t NUM_NEIGHBORS = 50; + + typedef Kernel::FT FT; + typedef Kernel::Point_d Point; + typedef std::vector Points; + + CGAL::Random random_generator; + Wall_clock_timer t; + + //****************************** CGAL *************************************** + { + std::cout << "\n=== CGAL ===\n"; + + typedef CGAL::Tangential_complex_::Point_cloud_data_structure + Points_ds; + typedef Points_ds::KNS_range KNS_range; + typedef Points_ds::KNS_iterator KNS_iterator; + typedef Points_ds::INS_range INS_range; + typedef Points_ds::INS_iterator INS_iterator; + + t.reset(); + Points_ds points_ds(points); + double init_time = t.elapsed(); + std::cout << "Init: " << init_time << std::endl; + t.reset(); + + for (std::size_t i = 0 ; i < NUM_QUERIES ; ++i) + { + std::size_t pt_idx = random_generator.get_int(0, points.size() - 1); + KNS_range kns_range = points_ds.query_ANN( + points[pt_idx], NUM_NEIGHBORS, true); + } + double queries_time = t.elapsed(); + std::cout << NUM_QUERIES << " queries among " + << points.size() << " points: " << queries_time << std::endl; + csv_file << queries_time << ";"; + } + //**************************** nanoflann ************************************ + { + std::cout << "\n=== nanoflann ===\n"; + + typedef CGAL::Tangential_complex_:: + Point_cloud_data_structure__nanoflann + Points_ds; + + t.reset(); + Points_ds points_ds(points, k); + double init_time = t.elapsed(); + std::cout << "Init: " << init_time << std::endl; + t.reset(); + + for (std::size_t i = 0 ; i < NUM_QUERIES ; ++i) + { + std::size_t pt_idx = random_generator.get_int(0, points.size() - 1); + std::size_t neighbors_indices[NUM_NEIGHBORS]; + FT neighbors_sq_distances[NUM_NEIGHBORS]; + points_ds.query_ANN( + points[pt_idx], NUM_NEIGHBORS, neighbors_indices, neighbors_sq_distances); + } + double queries_time = t.elapsed(); + std::cout << NUM_QUERIES << " queries among " + << points.size() << " points: " << queries_time << std::endl; + csv_file << queries_time << ";"; + } + + //******************************* ANN *************************************** + { + std::cout << "\n=== ANN ===\n"; + + typedef CGAL::Tangential_complex_:: + Point_cloud_data_structure__ANN + Points_ds; + + t.reset(); + Points_ds points_ds(points, k); + double init_time = t.elapsed(); + std::cout << "Init: " << init_time << std::endl; + t.reset(); + + for (std::size_t i = 0 ; i < NUM_QUERIES ; ++i) + { + std::size_t pt_idx = random_generator.get_int(0, points.size() - 1); + int neighbors_indices[NUM_NEIGHBORS]; + double neighbors_sq_distances[NUM_NEIGHBORS]; + points_ds.query_ANN( + points[pt_idx], NUM_NEIGHBORS, neighbors_indices, neighbors_sq_distances); + } + double queries_time = t.elapsed(); + std::cout << NUM_QUERIES << " queries among " + << points.size() << " points: " << queries_time << std::endl; + csv_file << queries_time << "\n"; + } +} +#endif // CGAL_MESH_3_TEST_TEST_UTILITIES_H diff --git a/Triangulation/include/CGAL/Delaunay_triangulation.h b/Triangulation/include/CGAL/Delaunay_triangulation.h index dbbbe0b8c5a..f1f46912fd9 100644 --- a/Triangulation/include/CGAL/Delaunay_triangulation.h +++ b/Triangulation/include/CGAL/Delaunay_triangulation.h @@ -123,7 +123,7 @@ public: using Base::vertices_begin; using Base::vertices_end; // using Base:: - + private: //*** Side_of_oriented_subsphere_d *** typedef typename Base::Flat_orientation_d Flat_orientation_d; @@ -160,12 +160,12 @@ public: } // With this constructor, - // the user can specify a Flat_orientation_d object to be used for - // orienting simplices of a specific dimension + // the user can specify a Flat_orientation_d object to be used for + // orienting simplices of a specific dimension // (= preset_flat_orientation_.first) // It it used by the dark triangulations created by DT::remove Delaunay_triangulation( - int dim, + int dim, const std::pair &preset_flat_orientation, const Geom_traits &k = Geom_traits()) : Base(dim, preset_flat_orientation, k) @@ -180,8 +180,8 @@ public: Side_of_oriented_subsphere_d side_of_oriented_subsphere_predicate() const { return Side_of_oriented_subsphere_d ( - flat_orientation_, - geom_traits().construct_flat_orientation_d_object(), + flat_orientation_, + geom_traits().construct_flat_orientation_d_object(), geom_traits().in_flat_side_of_oriented_sphere_d_object() ); } @@ -293,7 +293,7 @@ public: Orientation o = ori_( boost::make_transform_iterator(s->vertices_begin(), spivi), - boost::make_transform_iterator(s->vertices_begin() + cur_dim_ + 1, + boost::make_transform_iterator(s->vertices_begin() + cur_dim_ + 1, spivi)); if( POSITIVE == o ) @@ -322,9 +322,9 @@ public: return pred_(dc_.full_cell(f)->neighbor(dc_.index_of_covertex(f))); } }; - + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - VALIDITY - + bool is_valid(bool verbose = false, int level = 0) const; private: @@ -341,7 +341,7 @@ private: Conflict_traversal_pred_in_fullspace; }; -// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = // FUNCTIONS THAT ARE MEMBER METHODS: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - REMOVALS @@ -514,7 +514,7 @@ Delaunay_triangulation typedef typename Base::template Full_cell_set Dark_full_cells; Dark_full_cells conflict_zone; std::back_insert_iterator dark_out(conflict_zone); - + dark_ft = dark_side.compute_conflict_zone(v->point(), dark_s, dark_out); // Make the dark simplices in the conflict zone searchable conflict_zone.make_searchable(); @@ -612,7 +612,7 @@ Delaunay_triangulation int li = light_s->index(dark_s->vertex(di)->data()); Rotor light_r(light_s, li, light_i); typename Dark_triangulation::Rotor dark_r(dark_s, di, dark_i); - + while (simps.contains(cpp11::get<0>(light_r)->neighbor(cpp11::get<1>(light_r)))) light_r = rotate_rotor(light_r); @@ -871,27 +871,27 @@ template< typename DCTraits, typename TDS > bool Delaunay_triangulation ::is_valid(bool verbose, int level) const -{ +{ if (!Base::is_valid(verbose, level)) return false; int dim = current_dimension(); if (dim == maximal_dimension()) { - for (Finite_full_cell_const_iterator cit = finite_full_cells_begin() ; - cit != finite_full_cells_end() ; ++cit ) + for (Finite_full_cell_const_iterator cit = this->finite_full_cells_begin() ; + cit != this->finite_full_cells_end() ; ++cit ) { Full_cell_const_handle ch = cit.base(); - for(int i = 0; i < dim+1 ; ++i ) + for(int i = 0; i < dim+1 ; ++i ) { // If the i-th neighbor is not an infinite cell - Vertex_handle opposite_vh = + Vertex_handle opposite_vh = ch->neighbor(i)->vertex(ch->neighbor(i)->index(ch)); if (!is_infinite(opposite_vh)) { - Side_of_oriented_sphere_d side = + Side_of_oriented_sphere_d side = geom_traits().side_of_oriented_sphere_d_object(); - if (side(Point_const_iterator(ch->vertices_begin()), + if (side(Point_const_iterator(ch->vertices_begin()), Point_const_iterator(ch->vertices_end()), opposite_vh->point()) == ON_BOUNDED_SIDE) { diff --git a/Triangulation/include/CGAL/IO/Triangulation_off_ostream.h b/Triangulation/include/CGAL/IO/Triangulation_off_ostream.h index 0f12fdfcc6c..dc795045782 100644 --- a/Triangulation/include/CGAL/IO/Triangulation_off_ostream.h +++ b/Triangulation/include/CGAL/IO/Triangulation_off_ostream.h @@ -94,7 +94,7 @@ input_point(std::istream & is, const Traits &traits, P & p) { typedef typename Traits::FT FT; std::vector coords; - + std::string line; for(;;) { @@ -125,7 +125,7 @@ operator>>(std::istream &is, typename Wrap::Point_d & p) typedef typename Wrap::Point_d P; typedef typename K::FT FT; std::vector coords; - + std::string line; for(;;) { @@ -165,7 +165,7 @@ operator>>(std::istream &is, typename Wrap::Weighted_point_d & wp) std::vector coords; while (line_sstr >> temp) coords.push_back(temp); - + typename std::vector::iterator last = coords.end() - 1; P p = P(coords.begin(), last); wp = WP(p, *last); @@ -175,7 +175,7 @@ operator>>(std::istream &is, typename Wrap::Weighted_point_d & wp) template < class GT, class TDS > std::ostream & -export_triangulation_to_off(std::ostream & os, +export_triangulation_to_off(std::ostream & os, const Triangulation & tr, bool in_3D_export_surface_only = false) { @@ -188,7 +188,7 @@ export_triangulation_to_off(std::ostream & os, typedef typename Tr::Full_cell_const_iterator Full_cell_iterator; typedef typename Tr::Full_cell Full_cell; typedef typename Full_cell::Vertex_handle_const_iterator Full_cell_vertex_iterator; - + if (tr.maximal_dimension() < 2 || tr.maximal_dimension() > 3) { std::cerr << "Warning: export_tds_to_off => dimension should be 2 or 3."; @@ -199,11 +199,11 @@ export_triangulation_to_off(std::ostream & os, size_t n = tr.number_of_vertices(); std::stringstream output; - + // write the vertices std::map index_of_vertex; int i = 0; - for(Finite_vertex_iterator it = tr.finite_vertices_begin(); + for(Finite_vertex_iterator it = tr.finite_vertices_begin(); it != tr.finite_vertices_end(); ++it, ++i) { Triangulation_IO::output_point(output, tr.geom_traits(), it->point()); @@ -213,7 +213,7 @@ export_triangulation_to_off(std::ostream & os, index_of_vertex[it.base()] = i; } CGAL_assertion( i == n ); - + size_t number_of_triangles = 0; if (tr.maximal_dimension() == 2) { @@ -248,7 +248,7 @@ export_triangulation_to_off(std::ostream & os, output << index_of_vertex[*vit] << " "; } output << std::endl; - ++number_of_triangles; + ++number_of_triangles; } } } @@ -284,7 +284,7 @@ export_triangulation_to_off(std::ostream & os, } os << "OFF \n" - << n << " " + << n << " " << number_of_triangles << " 0\n" << output.str(); diff --git a/Triangulation/include/CGAL/Regular_triangulation.h b/Triangulation/include/CGAL/Regular_triangulation.h index a1f71d31c01..c546ff4c08b 100644 --- a/Triangulation/include/CGAL/Regular_triangulation.h +++ b/Triangulation/include/CGAL/Regular_triangulation.h @@ -46,7 +46,7 @@ class Regular_triangulation typedef CGAL::Regular_triangulation_euclidean_traits RTTraits; typedef typename RTTraits::Dimension Maximal_dimension_; typedef typename Default::Get< - TDS_, + TDS_, Triangulation_data_structure< Maximal_dimension_, Triangulation_vertex, @@ -70,8 +70,9 @@ public: // PUBLIC NESTED TYPES typedef typename Base::Full_cell Full_cell; typedef typename Base::Facet Facet; typedef typename Base::Face Face; - + typedef Maximal_dimension_ Maximal_dimension; + typedef typename Base::Point Point; typedef typename RTTraits::Bare_point Bare_point; typedef typename RTTraits::Weighted_point Weighted_point; @@ -154,7 +155,7 @@ private: } }; public: - + // - - - - - - - - - - - - - - - - - - - - - - - - - - CREATION / CONSTRUCTORS Regular_triangulation(int dim, const Geom_traits &k = Geom_traits()) @@ -163,12 +164,12 @@ public: } // With this constructor, - // the user can specify a Flat_orientation_d object to be used for - // orienting simplices of a specific dimension + // the user can specify a Flat_orientation_d object to be used for + // orienting simplices of a specific dimension // (= preset_flat_orientation_.first) // It it used by the dark triangulations created by DT::remove Regular_triangulation( - int dim, + int dim, const std::pair &preset_flat_orientation, const Geom_traits &k = Geom_traits()) : Base(dim, preset_flat_orientation, k) @@ -183,8 +184,8 @@ public: Power_test_in_flat_d power_test_in_flat_predicate() const { return Power_test_in_flat_d ( - flat_orientation_, - geom_traits().construct_flat_orientation_d_object(), + flat_orientation_, + geom_traits().construct_flat_orientation_d_object(), geom_traits().in_flat_power_test_d_object() ); } @@ -244,13 +245,13 @@ public: return number_of_vertices() - n; } - Vertex_handle insert(const Weighted_point &, - const Locate_type, - const Face &, - const Facet &, + Vertex_handle insert(const Weighted_point &, + const Locate_type, + const Face &, + const Facet &, const Full_cell_handle); - Vertex_handle insert(const Weighted_point & p, + Vertex_handle insert(const Weighted_point & p, const Full_cell_handle start = Full_cell_handle()) { Locate_type lt; @@ -270,14 +271,14 @@ public: Vertex_handle insert_in_conflicting_cell( const Weighted_point &, const Full_cell_handle, const Vertex_handle only_if_this_vertex_is_in_the_cz = Vertex_handle()); - - Vertex_handle insert_if_in_star(const Weighted_point &, - const Vertex_handle, - const Locate_type, - const Face &, - const Facet &, + + Vertex_handle insert_if_in_star(const Weighted_point &, + const Vertex_handle, + const Locate_type, + const Face &, + const Facet &, const Full_cell_handle); - + Vertex_handle insert_if_in_star( const Weighted_point & p, const Vertex_handle star_center, const Full_cell_handle start = Full_cell_handle()) @@ -290,7 +291,7 @@ public: } Vertex_handle insert_if_in_star( - const Weighted_point & p, const Vertex_handle star_center, + const Weighted_point & p, const Vertex_handle star_center, const Vertex_handle hint) { CGAL_assertion( Vertex_handle() != hint ); @@ -346,7 +347,7 @@ public: Orientation o = ori_( boost::make_transform_iterator(s->vertices_begin(), spivi), - boost::make_transform_iterator(s->vertices_begin() + cur_dim_ + 1, + boost::make_transform_iterator(s->vertices_begin() + cur_dim_ + 1, spivi)); if( POSITIVE == o ) @@ -375,16 +376,16 @@ public: return pred_(rt_.full_cell(f)->neighbor(rt_.index_of_covertex(f))); } }; - + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - VALIDITY - + bool is_valid(bool verbose = false, int level = 0) const; private: - + template bool - does_cell_range_contain_vertex(InputIterator cz_begin, InputIterator cz_end, + does_cell_range_contain_vertex(InputIterator cz_begin, InputIterator cz_end, Vertex_handle vh) const { // Check all vertices @@ -403,7 +404,7 @@ private: template void - process_conflict_zone(InputIterator cz_begin, InputIterator cz_end, + process_conflict_zone(InputIterator cz_begin, InputIterator cz_end, OutputIterator vertices_out) const { // Get all vertices @@ -423,10 +424,10 @@ private: } } - + template void - process_cz_vertices_after_insertion(InputIterator vertices_begin, + process_cz_vertices_after_insertion(InputIterator vertices_begin, InputIterator vertices_end) { // Get all vertices @@ -454,14 +455,14 @@ private: Conflict_traversal_pred_in_subspace; typedef Conflict_traversal_predicate Conflict_traversal_pred_in_fullspace; - + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - MEMBER VARIABLES std::vector m_hidden_points; }; // class Regular_triangulation -// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = // FUNCTIONS THAT ARE MEMBER METHODS: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - REMOVALS @@ -527,8 +528,8 @@ Regular_triangulation typedef Triangulation_full_cell< Geom_traits, internal::Triangulation::Dark_full_cell_data > Dark_full_cell_base; - typedef Triangulation_data_structure Dark_tds; typedef Regular_triangulation Dark_triangulation; @@ -639,7 +640,7 @@ Regular_triangulation typedef typename Base::template Full_cell_set Dark_full_cells; Dark_full_cells conflict_zone; std::back_insert_iterator dark_out(conflict_zone); - + dark_ft = dark_side.compute_conflict_zone(v->point(), dark_s, dark_out); // Make the dark simplices in the conflict zone searchable conflict_zone.make_searchable(); @@ -664,8 +665,8 @@ Regular_triangulation dark_incident_s.clear(); dark_out = std::back_inserter(dark_incident_s); dark_side.tds().incident_full_cells(dark_v, dark_out); - for(typename Dark_full_cells::iterator it = dark_incident_s.begin(); - it != dark_incident_s.end(); + for(typename Dark_full_cells::iterator it = dark_incident_s.begin(); + it != dark_incident_s.end(); ++it) { (*it)->data().count_ += 1; @@ -739,7 +740,7 @@ Regular_triangulation int li = light_s->index(dark_s->vertex(di)->data()); Rotor light_r(light_s, li, light_i); typename Dark_triangulation::Rotor dark_r(dark_s, di, dark_i); - + while( simps.contains(cpp11::get<0>(light_r)->neighbor(cpp11::get<1>(light_r))) ) light_r = rotate_rotor(light_r); @@ -877,9 +878,9 @@ Regular_triangulation template< typename Traits, typename TDS > typename Regular_triangulation::Vertex_handle Regular_triangulation -::insert_if_in_star(const Weighted_point & p, +::insert_if_in_star(const Weighted_point & p, const Vertex_handle star_center, - const Locate_type lt, const Face & f, const Facet & ft, + const Locate_type lt, const Face & f, const Facet & ft, const Full_cell_handle s) { switch( lt ) @@ -917,7 +918,7 @@ Regular_triangulation template< typename Traits, typename TDS > typename Regular_triangulation::Vertex_handle Regular_triangulation -::insert_in_conflicting_cell(const Weighted_point & p, +::insert_in_conflicting_cell(const Weighted_point & p, const Full_cell_handle s, const Vertex_handle only_if_this_vertex_is_in_the_cz) { @@ -938,10 +939,10 @@ Regular_triangulation cs.reserve(64); std::back_insert_iterator out(cs); Facet ft = compute_conflict_zone(p, s, out); - + // Check if the CZ contains "only_if_this_vertex_is_in_the_cz" if (only_if_this_vertex_is_in_the_cz != Vertex_handle() - && !does_cell_range_contain_vertex(cs.begin(), cs.end(), + && !does_cell_range_contain_vertex(cs.begin(), cs.end(), only_if_this_vertex_is_in_the_cz)) { return Vertex_handle(); @@ -950,7 +951,7 @@ Regular_triangulation // Otherwise, proceed with the insertion std::vector cz_vertices; cz_vertices.reserve(64); - process_conflict_zone(cs.begin(), cs.end(), + process_conflict_zone(cs.begin(), cs.end(), std::back_inserter(cz_vertices)); Vertex_handle ret = insert_in_hole(p, cs.begin(), cs.end(), ft); @@ -1031,8 +1032,8 @@ Regular_triangulation if( current_dimension() < maximal_dimension() ) { Conflict_pred_in_subspace c( - *this, p, - coaffine_orientation_predicate(), + *this, p, + coaffine_orientation_predicate(), power_test_in_flat_predicate()); return c(s); } @@ -1055,8 +1056,8 @@ Regular_triangulation if( current_dimension() < maximal_dimension() ) { Conflict_pred_in_subspace c( - *this, p, - coaffine_orientation_predicate(), + *this, p, + coaffine_orientation_predicate(), power_test_in_flat_predicate()); Conflict_traversal_pred_in_subspace tp(*this, c); return tds().gather_full_cells(s, tp, out); @@ -1077,7 +1078,7 @@ template< typename Traits, typename TDS > bool Regular_triangulation ::is_valid(bool verbose, int level) const -{ +{ if (!Base::is_valid(verbose, level)) return false; @@ -1088,16 +1089,16 @@ Regular_triangulation cit != finite_full_cells_end() ; ++cit ) { Full_cell_const_handle ch = cit.base(); - for(int i = 0; i < dim+1 ; ++i ) + for(int i = 0; i < dim+1 ; ++i ) { // If the i-th neighbor is not an infinite cell - Vertex_handle opposite_vh = + Vertex_handle opposite_vh = ch->neighbor(i)->vertex(ch->neighbor(i)->index(ch)); if (!is_infinite(opposite_vh)) { - Power_test_d side = + Power_test_d side = geom_traits().power_test_d_object(); - if (side(Point_const_iterator(ch->vertices_begin()), + if (side(Point_const_iterator(ch->vertices_begin()), Point_const_iterator(ch->vertices_end()), opposite_vh->point()) == ON_POSITIVE_SIDE) { diff --git a/Triangulation/include/CGAL/Triangulation_data_structure.h b/Triangulation/include/CGAL/Triangulation_data_structure.h index 6cdc3f4d48e..aab42e68b84 100644 --- a/Triangulation/include/CGAL/Triangulation_data_structure.h +++ b/Triangulation/include/CGAL/Triangulation_data_structure.h @@ -611,7 +611,8 @@ public: return incident_faces(v, dim, out, cmp, true); } template< typename OutputIterator, typename Comparator = std::less > - OutputIterator incident_faces(Vertex_const_handle, const int, OutputIterator, Comparator = Comparator(), bool = false) const; + OutputIterator incident_faces(Vertex_const_handle, const int, OutputIterator, + Comparator = Comparator(), bool = false) const; #else template< typename OutputIterator, typename Comparator > OutputIterator incident_upper_faces(Vertex_const_handle v, const int dim, OutputIterator out, Comparator cmp = Comparator()) @@ -664,6 +665,9 @@ Triangulation_data_structure { // CGAL_expensive_precondition(is_vertex(v)); CGAL_precondition(Vertex_handle() != v); + CGAL_precondition(v->full_cell()->has_vertex(v)); + if (!v->full_cell()->has_vertex(v)) // CJTODO TEMP + std::cout << "ERROR: incident_full_cells !v->full_cell()->has_vertex(v). Is the point cloud sparse enough?"; Face f(v->full_cell()); f.set_index(0, v->full_cell()->index(v)); return incident_full_cells(f, out); @@ -713,7 +717,9 @@ Triangulation_data_structure } } } - clear_visited_marks(start); + clear_visited_marks(start); // CJTODO: couldn't we use what is in "out" + // to make ot faster? (would require to + // replace the output iterator by a container) return ft; } @@ -998,7 +1004,7 @@ Triangulation_data_structure associate_vertex_with_full_cell(new_s, facet_index, v); set_neighbors(new_s, facet_index, - neighbor(old_s, facet_index), + outside_neighbor, mirror_index(old_s, facet_index)); // add the new full_cell to the list of new full_cells diff --git a/Triangulation_3/include/CGAL/Delaunay_triangulation_3.h b/Triangulation_3/include/CGAL/Delaunay_triangulation_3.h index 2630dbd46fe..3534f323308 100644 --- a/Triangulation_3/include/CGAL/Delaunay_triangulation_3.h +++ b/Triangulation_3/include/CGAL/Delaunay_triangulation_3.h @@ -355,7 +355,7 @@ public: #endif //CGAL_TRIANGULATION_3_DONT_INSERT_RANGE_OF_POINTS_WITH_INFO { #ifdef CGAL_TRIANGULATION_3_PROFILING - WallClockTimer t; + Wall_clock_timer t; #endif size_type n = number_of_vertices(); @@ -691,7 +691,7 @@ public: size_type n = number_of_vertices(); #ifdef CGAL_TRIANGULATION_3_PROFILING - WallClockTimer t; + Wall_clock_timer t; #endif // Parallel diff --git a/Triangulation_3/include/CGAL/Regular_triangulation_3.h b/Triangulation_3/include/CGAL/Regular_triangulation_3.h index 6060008e1a3..59870e21f94 100644 --- a/Triangulation_3/include/CGAL/Regular_triangulation_3.h +++ b/Triangulation_3/include/CGAL/Regular_triangulation_3.h @@ -289,7 +289,7 @@ namespace CGAL { #endif #ifdef CGAL_TRIANGULATION_3_PROFILING - WallClockTimer t; + Wall_clock_timer t; #endif size_type n = number_of_vertices(); @@ -695,7 +695,7 @@ namespace CGAL { size_type n = number_of_vertices(); #ifdef CGAL_TRIANGULATION_3_PROFILING - WallClockTimer t; + Wall_clock_timer t; #endif // Parallel