diff --git a/BGL/include/CGAL/boost/graph/parameters_interface.h b/BGL/include/CGAL/boost/graph/parameters_interface.h index 01e8114c5ef..4da552e77da 100644 --- a/BGL/include/CGAL/boost/graph/parameters_interface.h +++ b/BGL/include/CGAL/boost/graph/parameters_interface.h @@ -86,6 +86,7 @@ CGAL_add_named_parameter(query_point_t, query_point_map, query_point_map) CGAL_add_named_parameter(normal_t, normal_map, normal_map) CGAL_add_named_parameter(diagonalize_traits_t, diagonalize_traits, diagonalize_traits) CGAL_add_named_parameter(svd_traits_t, svd_traits, svd_traits) +CGAL_add_named_parameter(callback_t, callback, callback) CGAL_add_named_parameter(sharpness_angle_t, sharpness_angle, sharpness_angle) CGAL_add_named_parameter(edge_sensitivity_t, edge_sensitivity, edge_sensitivity) CGAL_add_named_parameter(neighbor_radius_t, neighbor_radius, neighbor_radius) diff --git a/Installation/CHANGES.md b/Installation/CHANGES.md index 9759bd998da..2ddd1e81301 100644 --- a/Installation/CHANGES.md +++ b/Installation/CHANGES.md @@ -49,6 +49,18 @@ Release date: September 2018 to reflect the real needs of the code (some types and operators were used in the code but did not appear in the concepts). +### Point Set Processing + +- Added a callback mechanism for functions + `CGAL::bilateral_smooth_point_set()`, + `CGAL::compute_average_spacing()`, + `CGAL::grid_simplify_point_set()`, + `CGAL::hierarchy_simplify_point_set()`, + `CGAL::jet_estimate_normals()`, `CGAL::jet_smooth_point_set()`, + `CGAL::pca_estimate_normals()`, `CGAL::remove_outliers()` and + `CGAL::wlop_simplify_and_regularize_point_set()`. + + ### Polygon Mesh Processing - Added a new named parameter for stitching that allows to perform diff --git a/Installation/include/CGAL/config.h b/Installation/include/CGAL/config.h index 7c3cde37f53..0c1d7a3cbb9 100644 --- a/Installation/include/CGAL/config.h +++ b/Installation/include/CGAL/config.h @@ -195,9 +195,14 @@ #if defined(BOOST_NO_0X_HDR_UNORDERED_SET) || \ defined(BOOST_NO_0X_HDR_UNORDERED_MAP) || \ defined(BOOST_NO_CXX11_HDR_UNORDERED_SET) || \ - defined(BOOST_NO_CXX11_HDR_UNORDERED_MAP) + defined(BOOST_NO_CXX11_HDR_UNORDERED_MAP) || \ + (defined(_MSC_VER) && (_MSC_VER == 1800)) // std::unordered_set is very bad in MSVC2013 #define CGAL_CFG_NO_CPP0X_UNORDERED 1 #endif +#if defined( BOOST_NO_0X_HDR_THREAD) || \ + defined( BOOST_NO_CXX11_HDR_THREAD) +#define CGAL_CFG_NO_STD_THREAD 1 +#endif #if defined(BOOST_NO_DECLTYPE) || \ defined(BOOST_NO_CXX11_DECLTYPE) || (BOOST_VERSION < 103600) #define CGAL_CFG_NO_CPP0X_DECLTYPE 1 diff --git a/Mesh_3/include/CGAL/Mesh_3/C3T3_helpers.h b/Mesh_3/include/CGAL/Mesh_3/C3T3_helpers.h index 784611a0195..92f8dd4906b 100644 --- a/Mesh_3/include/CGAL/Mesh_3/C3T3_helpers.h +++ b/Mesh_3/include/CGAL/Mesh_3/C3T3_helpers.h @@ -56,6 +56,7 @@ #ifdef CGAL_LINKED_WITH_TBB # include +# include #endif #include diff --git a/Mesh_3/include/CGAL/Mesh_3/Mesher_3.h b/Mesh_3/include/CGAL/Mesh_3/Mesher_3.h index a8e60a55232..b512efd7c05 100644 --- a/Mesh_3/include/CGAL/Mesh_3/Mesher_3.h +++ b/Mesh_3/include/CGAL/Mesh_3/Mesher_3.h @@ -57,11 +57,7 @@ #endif #ifdef CGAL_LINKED_WITH_TBB -# if TBB_IMPLEMENT_CPP0X -# include -# else -# include -# endif +# include #endif #include diff --git a/Mesh_3/include/CGAL/Mesh_3/Mesher_level.h b/Mesh_3/include/CGAL/Mesh_3/Mesher_level.h index dc629bd5b20..06170f3d91a 100644 --- a/Mesh_3/include/CGAL/Mesh_3/Mesher_level.h +++ b/Mesh_3/include/CGAL/Mesh_3/Mesher_level.h @@ -43,7 +43,6 @@ #ifdef CGAL_LINKED_WITH_TBB # include -# include #endif #include 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 3c0a50c9620..10aac5954f7 100644 --- a/Mesh_3/include/CGAL/Mesh_3/Refine_cells_3.h +++ b/Mesh_3/include/CGAL/Mesh_3/Refine_cells_3.h @@ -31,7 +31,9 @@ #include #include #ifdef CGAL_LINKED_WITH_TBB - #include + #include + #include + #include #endif #include 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 35cd8ac92be..6c1318f2f57 100644 --- a/Mesh_3/include/CGAL/Mesh_3/Refine_facets_3.h +++ b/Mesh_3/include/CGAL/Mesh_3/Refine_facets_3.h @@ -34,7 +34,8 @@ #include #include #ifdef CGAL_LINKED_WITH_TBB - #include + #include + #include #endif #include diff --git a/Point_set_processing_3/doc/Point_set_processing_3/NamedParameters.txt b/Point_set_processing_3/doc/Point_set_processing_3/NamedParameters.txt index ee5322600c1..154688a883d 100644 --- a/Point_set_processing_3/doc/Point_set_processing_3/NamedParameters.txt +++ b/Point_set_processing_3/doc/Point_set_processing_3/NamedParameters.txt @@ -58,6 +58,14 @@ No default value. \b Default: if \ref thirdpartyEigen "Eigen" 3.2 (or greater) is available and `CGAL_EIGEN3_ENABLED` is defined, then `CGAL::Eigen_svd` is used.\n \cgalNPEnd +\cgalNPBegin{callback} \anchor PSP_callback +is a mechanism to get feedback on the advancement of the algorithm while it's running and to interrupt it if needed. It is called regularly when the algorithm is running: the current advancement (between 0. and 1.) is passed as parameter. If it returns `true`, then the algorithm continues its execution normally; if it returns `false`, the algorithm is stopped.\n +The callback will be copied and therefore needs to be lightweight.\n +Note that when a callback is run on a parallelized algorithm with `CGAL::Parallel_tag`, it is called asynchronously on a separate thread and shouldn't access or modify the variables that are parameters of the algorithm.\n +\b Type: `CGAL::cpp11::function`.\n +\b Default: empty function.\n +\cgalNPEnd + \cgalNPBegin{query_point_map} \anchor PSP_query_point_map is the property map containing the points associated to the iterators of the point range `queries`.\n \b Type: a class model of `ReadablePropertyMap` with diff --git a/Point_set_processing_3/doc/Point_set_processing_3/Point_set_processing_3.txt b/Point_set_processing_3/doc/Point_set_processing_3/Point_set_processing_3.txt index 82b37b231c2..ccd0c750df0 100644 --- a/Point_set_processing_3/doc/Point_set_processing_3/Point_set_processing_3.txt +++ b/Point_set_processing_3/doc/Point_set_processing_3/Point_set_processing_3.txt @@ -632,6 +632,26 @@ point set: \cgalExample{Point_set_processing_3/structuring_example.cpp} +\section Point_set_processing_3Callbacks Callbacks + +Several functions of this package provide a callback mechanism that enables the user to track the progress of the algorithms and to interrupt them if needed. A callback, in this package, is an instance of `CGAL::cpp11::function` that takes the advancement as a parameter (between 0. when the algorithm begins to 1. when the algorithm is completed) and that returns `true` if the algorithm should carry on, `false` otherwise. It is passed as a named parameter with an empty function as default. + +Algorithms that support this mechanism are detailed in the [Reference Manual](@ref PkgPointSetProcessing), along with the effect that an early interruption has on the output. + +\subsection Point_set_processing_3Example_callbacks Example + +The following example defines a callback that displays the name of the current algorithm along with the progress (as a percentage) updated every \f$1/10th\f$ of a second. While the algorithm is running, the console output will typically look like this: + +\code{.sh} +Computing average spacing: 100% +Grid simplification: 100% +Jet smoothing: 54% +\endcode + +Thanks to the carriage return character `\r`, the lines are overwritten and the user sees the percentage increasing on each line. + +\cgalExample{Point_set_processing_3/callback_example.cpp} + \section Point_set_processing_3ImplementationHistory Implementation History diff --git a/Point_set_processing_3/doc/Point_set_processing_3/examples.txt b/Point_set_processing_3/doc/Point_set_processing_3/examples.txt index 7c9c599da36..43849ae022c 100644 --- a/Point_set_processing_3/doc/Point_set_processing_3/examples.txt +++ b/Point_set_processing_3/doc/Point_set_processing_3/examples.txt @@ -17,4 +17,5 @@ \example Point_set_processing_3/edge_aware_upsample_point_set_example.cpp \example Point_set_processing_3/edges_example.cpp \example Point_set_processing_3/structuring_example.cpp +\example Point_set_processing_3/callback_example.cpp */ diff --git a/Point_set_processing_3/examples/Point_set_processing_3/CMakeLists.txt b/Point_set_processing_3/examples/Point_set_processing_3/CMakeLists.txt index 1950f28e20e..d936b3be7ad 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/CMakeLists.txt +++ b/Point_set_processing_3/examples/Point_set_processing_3/CMakeLists.txt @@ -57,6 +57,7 @@ if ( CGAL_FOUND ) create_single_source_cgal_program( "wlop_simplify_and_regularize_point_set_example.cpp" ) create_single_source_cgal_program( "edge_aware_upsample_point_set_example.cpp" ) create_single_source_cgal_program( "structuring_example.cpp" ) + create_single_source_cgal_program( "callback_example.cpp" ) set(needed_cxx_features cxx_rvalue_references cxx_variadic_templates) create_single_source_cgal_program( "read_ply_points_with_colors_example.cpp" CXX_FEATURES ${needed_cxx_features} ) @@ -103,7 +104,8 @@ if ( CGAL_FOUND ) average_spacing_example normals_example jet_smoothing_example - normal_estimation) + normal_estimation + callback_example) if(TBB_FOUND AND TARGET ${target}) CGAL_target_use_TBB(${target}) endif() diff --git a/Point_set_processing_3/examples/Point_set_processing_3/callback_example.cpp b/Point_set_processing_3/examples/Point_set_processing_3/callback_example.cpp new file mode 100644 index 00000000000..3117627e022 --- /dev/null +++ b/Point_set_processing_3/examples/Point_set_processing_3/callback_example.cpp @@ -0,0 +1,95 @@ +#include +#include +#include + +#include +#include +#include + +#include +#include + +// Types +typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; +typedef Kernel::FT FT; +typedef Kernel::Point_3 Point; +typedef CGAL::Random_points_on_sphere_3 Generator; + +// Concurrency +#ifdef CGAL_LINKED_WITH_TBB +typedef CGAL::Parallel_tag Concurrency_tag; +#else +typedef CGAL::Sequential_tag Concurrency_tag; +#endif + +// instance of CGAL::cpp11::function +struct Progress_to_std_cerr_callback +{ + mutable std::size_t nb; + CGAL::Real_timer timer; + double t_start; + mutable double t_latest; + const std::string name; + + Progress_to_std_cerr_callback (const char* name) + : name (name) + { + timer.start(); + t_start = timer.time(); + t_latest = t_start; + } + + bool operator()(double advancement) const + { + // Avoid calling time() at every single iteration, which could + // impact performances very badly + ++ nb; + if (advancement != 1 && nb % 10000 != 0) + return true; + + double t = timer.time(); + if (advancement == 1 || (t - t_latest) > 0.1) // Update every 1/10th of second + { + std::cerr << "\r" // Return at the beginning of same line and overwrite + << name << ": " << int(advancement * 100) << "%"; + + if (advancement == 1) + std::cerr << std::endl; + t_latest = t; + } + + return true; + } +}; + + +int main () +{ + // Generate 1000000 points on a sphere of radius 100. + std::vector points; + points.reserve (1000000); + Generator generator(100.); + CGAL::cpp11::copy_n (generator, 1000000, std::back_inserter(points)); + + // Compute average spacing + FT average_spacing = CGAL::compute_average_spacing + (points, 6, + CGAL::parameters::callback + (Progress_to_std_cerr_callback("Computing average spacing"))); + + // Simplify on a grid with a size of twice the average spacing + points.erase(CGAL::grid_simplify_point_set + (points, 2. * average_spacing, + CGAL::parameters::callback + (Progress_to_std_cerr_callback("Grid simplification"))), + points.end()); + + // Smooth simplified point set + CGAL::jet_smooth_point_set + (points, 6, + CGAL::parameters::callback + (Progress_to_std_cerr_callback("Jet smoothing"))); + + return EXIT_SUCCESS; +} + diff --git a/Point_set_processing_3/include/CGAL/bilateral_smooth_point_set.h b/Point_set_processing_3/include/CGAL/bilateral_smooth_point_set.h index 817216de0d6..4c0d896f1fb 100644 --- a/Point_set_processing_3/include/CGAL/bilateral_smooth_point_set.h +++ b/Point_set_processing_3/include/CGAL/bilateral_smooth_point_set.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -45,9 +46,11 @@ #include #ifdef CGAL_LINKED_WITH_TBB +#include #include #include #include +#include #endif // CGAL_LINKED_WITH_TBB // Default allocator: use TBB allocators if available @@ -300,18 +303,27 @@ class Compute_pwns_neighbors const Tree & m_tree; const Pwns & m_pwns; Pwns_neighbors & m_pwns_neighbors; + cpp11::atomic& advancement; + cpp11::atomic& interrupted; public: Compute_pwns_neighbors(unsigned int k, const Tree &tree, - const Pwns &pwns, Pwns_neighbors &neighbors) - : m_k(k), m_tree(tree), m_pwns(pwns), m_pwns_neighbors(neighbors) {} + const Pwns &pwns, Pwns_neighbors &neighbors, + cpp11::atomic& advancement, + cpp11::atomic& interrupted) + : m_k(k), m_tree(tree), m_pwns(pwns), m_pwns_neighbors(neighbors) + , advancement (advancement), interrupted (interrupted) {} void operator() ( const tbb::blocked_range& r ) const { for (size_t i = r.begin(); i!=r.end(); i++) { + if (interrupted) + break; + m_pwns_neighbors[i] = bilateral_smooth_point_set_internal:: compute_kdtree_neighbors(m_pwns[i], m_tree, m_k); + ++ advancement; } } }; @@ -331,30 +343,37 @@ class Pwn_updater Pwns* pwns; Pwns* update_pwns; std::vector >* pwns_neighbors; + cpp11::atomic& advancement; + cpp11::atomic& interrupted; public: Pwn_updater(FT sharpness, FT r, Pwns *in, Pwns *out, - std::vector >* neighbors): + std::vector >* neighbors, + cpp11::atomic& advancement, + cpp11::atomic& interrupted): sharpness_angle(sharpness), radius(r), pwns(in), update_pwns(out), - pwns_neighbors(neighbors){} - + pwns_neighbors(neighbors), + advancement (advancement), + interrupted (interrupted) {} void operator() ( const tbb::blocked_range& r ) const { for (size_t i = r.begin(); i != r.end(); ++i) { + if (interrupted) + break; (*update_pwns)[i] = bilateral_smooth_point_set_internal:: compute_denoise_projection((*pwns)[i], (*pwns_neighbors)[i], radius, sharpness_angle); - + ++ advancement; } } }; @@ -402,6 +421,13 @@ public: \cgalParamBegin{normal_map} a model of `ReadWritePropertyMap` with value type `geom_traits::Vector_3`.\cgalParamEnd \cgalParamBegin{sharpness_angle} controls the sharpness of the result.\cgalParamEnd + \cgalParamBegin{callback} an instance of + `cpp11::function`. It is called regularly when the + algorithm is running: the current advancement (between 0. and + 1.) is passed as parameter. If it returns `true`, then the + algorithm continues its execution normally; if it returns + `false`, the algorithm is stopped, all points are left unchanged + and the function return `NaN`.\cgalParamEnd \cgalParamBegin{geom_traits} an instance of a geometric traits class, model of `Kernel`\cgalParamEnd \cgalNamedParamsEnd @@ -434,6 +460,8 @@ bilateral_smooth_point_set( typedef typename Kernel::FT FT; double sharpness_angle = choose_param(get_param(np, internal_np::sharpness_angle), 30.); + const cpp11::function& callback = choose_param(get_param(np, internal_np::callback), + cpp11::function()); CGAL_point_set_processing_precondition(points.begin() != points.end()); CGAL_point_set_processing_precondition(k > 1); @@ -513,8 +541,23 @@ bilateral_smooth_point_set( #else if (boost::is_convertible::value) { - Compute_pwns_neighbors f(k, tree, pwns, pwns_neighbors); + internal::Point_set_processing_3::Parallel_callback + parallel_callback (callback, 2 * nb_points); + + Compute_pwns_neighbors f(k, tree, pwns, pwns_neighbors, + parallel_callback.advancement(), + parallel_callback.interrupted()); tbb::parallel_for(tbb::blocked_range(0, nb_points), f); + + bool interrupted = parallel_callback.interrupted(); + + // We interrupt by hand as counter only goes halfway and won't terminate by itself + parallel_callback.interrupted() = true; + parallel_callback.join(); + + // If interrupted during this step, nothing is computed, we return NaN + if (interrupted) + return std::numeric_limits::quiet_NaN(); } else #endif @@ -522,10 +565,13 @@ bilateral_smooth_point_set( typename std::vector >::iterator pwns_iter = pwns_neighbors.begin(); - for(pwn_iter = pwns.begin(); pwn_iter != pwns.end(); ++pwn_iter, ++pwns_iter) + std::size_t nb = 0; + for(pwn_iter = pwns.begin(); pwn_iter != pwns.end(); ++pwn_iter, ++pwns_iter, ++ nb) { *pwns_iter = bilateral_smooth_point_set_internal:: compute_kdtree_neighbors(*pwn_iter, tree, k); + if (callback && !callback ((nb+1) / double(2. * nb_points))) + return std::numeric_limits::quiet_NaN(); } } @@ -545,24 +591,37 @@ bilateral_smooth_point_set( #ifdef CGAL_LINKED_WITH_TBB if(boost::is_convertible::value) { + internal::Point_set_processing_3::Parallel_callback + parallel_callback (callback, 2 * nb_points, nb_points); + //tbb::task_scheduler_init init(4); tbb::blocked_range block(0, nb_points); Pwn_updater pwn_updater(sharpness_angle, guess_neighbor_radius, &pwns, &update_pwns, - &pwns_neighbors); + &pwns_neighbors, + parallel_callback.advancement(), + parallel_callback.interrupted()); tbb::parallel_for(block, pwn_updater); + + parallel_callback.join(); + + // If interrupted during this step, nothing is computed, we return NaN + if (parallel_callback.interrupted()) + return std::numeric_limits::quiet_NaN(); } else #endif // CGAL_LINKED_WITH_TBB { + std::size_t nb = nb_points; + typename std::vector >::iterator update_iter = update_pwns.begin(); typename std::vector >::iterator neighbor_iter = pwns_neighbors.begin(); for(pwn_iter = pwns.begin(); pwn_iter != pwns.end(); - ++pwn_iter, ++update_iter, ++neighbor_iter) + ++pwn_iter, ++update_iter, ++neighbor_iter, ++ nb) { *update_iter = bilateral_smooth_point_set_internal:: compute_denoise_projection @@ -570,6 +629,8 @@ bilateral_smooth_point_set( *neighbor_iter, guess_neighbor_radius, sharpness_angle); + if (callback && !callback ((nb+1) / double(2. * nb_points))) + return std::numeric_limits::quiet_NaN(); } } #ifdef CGAL_PSP3_VERBOSE diff --git a/Point_set_processing_3/include/CGAL/compute_average_spacing.h b/Point_set_processing_3/include/CGAL/compute_average_spacing.h index 6488d1ba986..b557ac389d4 100644 --- a/Point_set_processing_3/include/CGAL/compute_average_spacing.h +++ b/Point_set_processing_3/include/CGAL/compute_average_spacing.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -39,9 +40,10 @@ #include #ifdef CGAL_LINKED_WITH_TBB +#include #include #include -#include +#include #endif // CGAL_LINKED_WITH_TBB namespace CGAL { @@ -109,17 +111,29 @@ compute_average_spacing(const typename Kernel::Point_3& query, ///< 3D point who const unsigned int k; const std::vector& input; std::vector& output; + cpp11::atomic& advancement; + cpp11::atomic& interrupted; public: Compute_average_spacings(Tree& tree, unsigned int k, std::vector& points, - std::vector& output) + std::vector& output, + cpp11::atomic& advancement, + cpp11::atomic& interrupted) : tree(tree), k (k), input (points), output (output) + , advancement (advancement) + , interrupted (interrupted) { } void operator()(const tbb::blocked_range& r) const { for( std::size_t i = r.begin(); i != r.end(); ++i) + { + if (interrupted) + break; + output[i] = CGAL::internal::compute_average_spacing(input[i], tree, k); + ++ advancement; + } } }; @@ -153,6 +167,13 @@ compute_average_spacing(const typename Kernel::Point_3& query, ///< 3D point who \cgalNamedParamsBegin \cgalParamBegin{point_map} a model of `ReadablePropertyMap` with value type `geom_traits::Point_3`. If this parameter is omitted, `CGAL::Identity_property_map` is used.\cgalParamEnd + \cgalParamBegin{callback} an instance of + `cpp11::function`. It is called regularly when the + algorithm is running: the current advancement (between 0. and + 1.) is passed as parameter. If it returns `true`, then the + algorithm continues its execution normally; if it returns + `false`, the algorithm is stopped and the average spacing value + estimated on the processed subset is returned.\cgalParamEnd \cgalParamBegin{geom_traits} an instance of a geometric traits class, model of `Kernel`\cgalParamEnd \cgalNamedParamsEnd @@ -184,6 +205,8 @@ compute_average_spacing( typedef typename Kernel::Point_3 Point; PointMap point_map = choose_param(get_param(np, internal_np::point_map), PointMap()); + const cpp11::function& callback = choose_param(get_param(np, internal_np::callback), + cpp11::function()); // types for K nearest neighbors search structure typedef typename Kernel::FT FT; @@ -209,6 +232,7 @@ compute_average_spacing( // iterate over input points, compute and output normal // vectors (already normalized) FT sum_spacings = (FT)0.0; + std::size_t nb = 0; #ifndef CGAL_LINKED_WITH_TBB CGAL_static_assertion_msg (!(boost::is_convertible::value), @@ -216,26 +240,43 @@ compute_average_spacing( #else if (boost::is_convertible::value) { - std::vector spacings (kd_tree_points.size ()); + internal::Point_set_processing_3::Parallel_callback + parallel_callback (callback, kd_tree_points.size()); + + std::vector spacings (kd_tree_points.size (), -1); CGAL::internal::Compute_average_spacings - f (tree, k, kd_tree_points, spacings); + f (tree, k, kd_tree_points, spacings, + parallel_callback.advancement(), + parallel_callback.interrupted()); tbb::parallel_for(tbb::blocked_range(0, kd_tree_points.size ()), f); + for (unsigned int i = 0; i < spacings.size (); ++ i) - sum_spacings += spacings[i]; + if (spacings[i] >= 0.) + { + sum_spacings += spacings[i]; + ++ nb; + } + + parallel_callback.join(); } else #endif { - for(typename PointRange::const_iterator it = points.begin(); it != points.end(); it++) - { - sum_spacings += internal::compute_average_spacing( - get(point_map,*it), - tree,k); - } + for(typename PointRange::const_iterator it = points.begin(); it != points.end(); it++, nb++) + { + sum_spacings += internal::compute_average_spacing( + get(point_map,*it), + tree,k); + if (callback && !callback ((nb+1) / double(kd_tree_points.size()))) + { + ++ nb; + break; + } + } } // return average spacing - return sum_spacings / (FT)(kd_tree_points.size ()); + return sum_spacings / (FT)(nb); } /// \cond SKIP_IN_MANUAL diff --git a/Point_set_processing_3/include/CGAL/grid_simplify_point_set.h b/Point_set_processing_3/include/CGAL/grid_simplify_point_set.h index fb488710b73..2dcb7ab6c7e 100644 --- a/Point_set_processing_3/include/CGAL/grid_simplify_point_set.h +++ b/Point_set_processing_3/include/CGAL/grid_simplify_point_set.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -188,6 +189,13 @@ public: \cgalNamedParamsBegin \cgalParamBegin{point_map} a model of `ReadWritePropertyMap` with value type `geom_traits::Point_3`. If this parameter is omitted, `CGAL::Identity_property_map` is used.\cgalParamEnd + \cgalParamBegin{callback} an instance of + `cpp11::function`. It is called regularly when the + algorithm is running: the current advancement (between 0. and + 1.) is passed as parameter. If it returns `true`, then the + algorithm continues its execution normally; if it returns + `false`, the algorithm is stopped and simplification stops with + no guarantee on the output.\cgalParamEnd \cgalParamBegin{geom_traits} an instance of a geometric traits class, model of `Kernel`\cgalParamEnd \cgalNamedParamsEnd @@ -204,6 +212,8 @@ grid_simplify_point_set( typedef typename Point_set_processing_3::GetPointMap::const_type PointMap; PointMap point_map = choose_param(get_param(np, internal_np::point_map), PointMap()); + const cpp11::function& callback = choose_param(get_param(np, internal_np::callback), + cpp11::function()); // actual type of input points typedef typename std::iterator_traits::value_type Enriched_point; @@ -214,12 +224,15 @@ grid_simplify_point_set( // points_to_keep[] will contain 1 point per cell; the others will be in points_to_remove[]. Epsilon_point_set_3 points_to_keep(epsilon, point_map); std::deque points_to_remove; - for (typename PointRange::iterator it = points.begin(); it != points.end(); it++) + std::size_t nb = 0, nb_points = points.size(); + for (typename PointRange::iterator it = points.begin(); it != points.end(); it++, ++ nb) { std::pair::iterator,bool> result; result = points_to_keep.insert(*it); if (!result.second) // if not inserted points_to_remove.push_back(*it); + if (callback && !callback ((nb+1) / double(nb_points))) + break; } // Replaces `[first, beyond)` range by the content of points_to_keep, then points_to_remove. diff --git a/Point_set_processing_3/include/CGAL/hierarchy_simplify_point_set.h b/Point_set_processing_3/include/CGAL/hierarchy_simplify_point_set.h index bbb94e0d81e..15580c55cdb 100644 --- a/Point_set_processing_3/include/CGAL/hierarchy_simplify_point_set.h +++ b/Point_set_processing_3/include/CGAL/hierarchy_simplify_point_set.h @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -142,6 +143,13 @@ namespace CGAL { if Eigen 3 (or greater) is available and `CGAL_EIGEN3_ENABLED` is defined then an overload using `Eigen_diagonalize_traits` is provided. Otherwise, the internal implementation `CGAL::Diagonalize_traits` is used.\cgalParamEnd + \cgalParamBegin{callback} an instance of + `cpp11::function`. It is called regularly when the + algorithm is running: the current advancement (between 0. and + 1.) is passed as parameter. If it returns `true`, then the + algorithm continues its execution normally; if it returns + `false`, the algorithm is stopped and simplification stops with + no guarantee on the output.\cgalParamEnd \cgalParamBegin{geom_traits} an instance of a geometric traits class, model of `Kernel`\cgalParamEnd \cgalNamedParamsEnd @@ -167,6 +175,8 @@ namespace CGAL { PointMap point_map = choose_param(get_param(np, internal_np::point_map), PointMap()); unsigned int size = choose_param(get_param(np, internal_np::size), 10); double var_max = choose_param(get_param(np, internal_np::maximum_variation), 1./3.); + const cpp11::function& callback = choose_param(get_param(np, internal_np::callback), + cpp11::function()); typedef typename std::iterator_traits::value_type Input_type; @@ -191,6 +201,8 @@ namespace CGAL { std::list points_to_keep; std::list points_to_remove; + + std::size_t nb_done = 0; while (!(clusters_stack.empty ())) { @@ -203,6 +215,7 @@ namespace CGAL { points_to_keep.splice (points_to_keep.end (), current_cluster->first, current_cluster->first.begin ()); clusters_stack.pop_front (); + ++ nb_done; continue; } @@ -268,6 +281,7 @@ namespace CGAL { cluster_iterator nonempty = (current_cluster->first.empty () ? negative_side : current_cluster); + nb_done += nonempty->first.size(); // Compute the centroid nonempty->second = internal::hsps_centroid (nonempty->first.begin (), nonempty->first.end (), @@ -279,6 +293,7 @@ namespace CGAL { point_map, nonempty->second, Kernel ()); + clusters_stack.pop_front (); clusters_stack.pop_front (); } @@ -311,6 +326,7 @@ namespace CGAL { // and output point else { + nb_done += current_cluster->first.size(); internal::hsc_terminate_cluster (current_cluster->first, points_to_keep, points_to_remove, @@ -319,7 +335,14 @@ namespace CGAL { Kernel ()); clusters_stack.pop_front (); } + + if (callback && !callback (nb_done / double (points.size()))) + break; } + + if (callback) + callback (1.); + typename PointRange::iterator first_point_to_remove = std::copy (points_to_keep.begin(), points_to_keep.end(), points.begin()); std::copy (points_to_remove.begin(), points_to_remove.end(), first_point_to_remove); diff --git a/Point_set_processing_3/include/CGAL/internal/Parallel_callback.h b/Point_set_processing_3/include/CGAL/internal/Parallel_callback.h new file mode 100644 index 00000000000..fd9a3335794 --- /dev/null +++ b/Point_set_processing_3/include/CGAL/internal/Parallel_callback.h @@ -0,0 +1,117 @@ +// Copyright (c) 2018 GeometryFactory (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$ +// SPDX-License-Identifier: GPL-3.0+ +// +// Author(s) : Simon Giraudot + +#ifndef CGAL_INTERNAL_PSP_PARALLEL_CALLBACK_H +#define CGAL_INTERNAL_PSP_PARALLEL_CALLBACK_H + +#include + +#include + +#include + +namespace CGAL { +namespace internal { +namespace Point_set_processing_3 { + +class Parallel_callback +{ + const cpp11::function& m_callback; + cpp11::atomic* m_advancement; + cpp11::atomic* m_interrupted; + std::size_t m_size; + bool m_creator; + cpp11::thread* m_thread; + + // assignment operator shouldn't be used (m_callback is const ref) + Parallel_callback& operator= (const Parallel_callback&) + { + return *this; + } + +public: + Parallel_callback (const cpp11::function& callback, + std::size_t size, + std::size_t advancement = 0, + bool interrupted = false) + : m_callback (callback) + , m_advancement (new cpp11::atomic()) + , m_interrupted (new cpp11::atomic()) + , m_size (size) + , m_creator (true) + , m_thread (NULL) + { + // cpp11::atomic only has default constructor, initialization done in two steps + *m_advancement = advancement; + *m_interrupted = interrupted; + if (m_callback) + m_thread = new cpp11::thread (*this); + } + + Parallel_callback (const Parallel_callback& other) + : m_callback (other.m_callback) + , m_advancement (other.m_advancement) + , m_interrupted (other.m_interrupted) + , m_size (other.m_size) + , m_creator (false) + , m_thread (NULL) + { + + } + + + ~Parallel_callback () + { + if (m_creator) + { + delete m_advancement; + delete m_interrupted; + } + if (m_thread != NULL) + delete m_thread; + } + + cpp11::atomic& advancement() { return *m_advancement; } + cpp11::atomic& interrupted() { return *m_interrupted; } + void join() + { + if (m_thread != NULL) + m_thread->join(); + } + + void operator()() + { + while (*m_advancement != m_size) + { + if (!m_callback (*m_advancement / double(m_size))) + *m_interrupted = true; + if (*m_interrupted) + return; + cpp11::sleep_for (0.00001); + } + m_callback (1.); + } +}; + +} // namespace Point_set_processing_3 +} // namespace internal +} // namespace CGAL + +#endif // CGAL_INTERNAL_PSP_PARALLEL_CALLBACK_H diff --git a/Point_set_processing_3/include/CGAL/jet_estimate_normals.h b/Point_set_processing_3/include/CGAL/jet_estimate_normals.h index edc1cc6ee6a..362bf0abf19 100644 --- a/Point_set_processing_3/include/CGAL/jet_estimate_normals.h +++ b/Point_set_processing_3/include/CGAL/jet_estimate_normals.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -40,6 +41,7 @@ #include #ifdef CGAL_LINKED_WITH_TBB +#include #include #include #include @@ -125,17 +127,28 @@ jet_estimate_normal(const typename Kernel::Point_3& query, ///< point to compute const unsigned int degree_fitting; const std::vector& input; std::vector& output; + cpp11::atomic& advancement; + cpp11::atomic& interrupted; public: Jet_estimate_normals(Tree& tree, unsigned int k, std::vector& points, - unsigned int degree_fitting, std::vector& output) + unsigned int degree_fitting, std::vector& output, + cpp11::atomic& advancement, + cpp11::atomic& interrupted) : tree(tree), k (k), degree_fitting (degree_fitting), input (points), output (output) + , advancement (advancement) + , interrupted (interrupted) { } void operator()(const tbb::blocked_range& r) const { for( std::size_t i = r.begin(); i != r.end(); ++i) + { + if (interrupted) + break; output[i] = CGAL::internal::jet_estimate_normal(input[i], tree, k, degree_fitting); + ++ advancement; + } } }; @@ -179,6 +192,13 @@ jet_estimate_normal(const typename Kernel::Point_3& query, ///< point to compute \cgalParamBegin{svd_traits} template parameter for the class `Monge_via_jet_fitting`. If \ref thirdpartyEigen "Eigen" 3.2 (or greater) is available and `CGAL_EIGEN3_ENABLED` is defined, then `CGAL::Eigen_svd` is used.\cgalParamEnd + \cgalParamBegin{callback} an instance of + `cpp11::function`. It is called regularly when the + algorithm is running: the current advancement (between 0. and + 1.) is passed as parameter. If it returns `true`, then the + algorithm continues its execution normally; if it returns + `false`, the algorithm is stopped and the remaining normals are + left unchanged.\cgalParamEnd \cgalParamBegin{geom_traits} an instance of a geometric traits class, model of `Kernel`\cgalParamEnd \cgalNamedParamsEnd */ @@ -212,6 +232,8 @@ jet_estimate_normals( PointMap point_map = choose_param(get_param(np, internal_np::point_map), PointMap()); NormalMap normal_map = choose_param(get_param(np, internal_np::normal_map), NormalMap()); unsigned int degree_fitting = choose_param(get_param(np, internal_np::degree_fitting), 2); + const cpp11::function& callback = choose_param(get_param(np, internal_np::callback), + cpp11::function()); typedef typename Kernel::Point_3 Point; @@ -254,28 +276,37 @@ jet_estimate_normals( #else if (boost::is_convertible::value) { - std::vector normals (kd_tree_points.size ()); + internal::Point_set_processing_3::Parallel_callback + parallel_callback (callback, kd_tree_points.size()); + + std::vector normals (kd_tree_points.size (), + CGAL::NULL_VECTOR); CGAL::internal::Jet_estimate_normals - f (tree, k, kd_tree_points, degree_fitting, normals); + f (tree, k, kd_tree_points, degree_fitting, normals, + parallel_callback.advancement(), + parallel_callback.interrupted()); tbb::parallel_for(tbb::blocked_range(0, kd_tree_points.size ()), f); - unsigned int i = 0; + std::size_t i = 0; for(it = points.begin(); it != points.end(); ++ it, ++ i) - { - put (normal_map, *it, normals[i]); - } + if (normals[i] != CGAL::NULL_VECTOR) + put (normal_map, *it, normals[i]); + + parallel_callback.join(); } else #endif { - for(it = points.begin(); it != points.end(); it++) + std::size_t nb = 0; + for(it = points.begin(); it != points.end(); it++, ++ nb) { Vector normal = internal::jet_estimate_normal( get(point_map,*it), tree, k, degree_fitting); put(normal_map, *it, normal); // normal_map[it] = normal - - } + if (callback && !callback ((nb+1) / double(kd_tree_points.size()))) + break; + } } diff --git a/Point_set_processing_3/include/CGAL/jet_smooth_point_set.h b/Point_set_processing_3/include/CGAL/jet_smooth_point_set.h index 83edb6dd1c0..e9054ba2147 100644 --- a/Point_set_processing_3/include/CGAL/jet_smooth_point_set.h +++ b/Point_set_processing_3/include/CGAL/jet_smooth_point_set.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -39,6 +40,7 @@ #include #ifdef CGAL_LINKED_WITH_TBB +#include #include #include #include @@ -125,20 +127,31 @@ jet_smooth_point( unsigned int degree_monge; const std::vector& input; std::vector& output; + cpp11::atomic& advancement; + cpp11::atomic& interrupted; public: Jet_smooth_pwns (Tree& tree, unsigned int k, std::vector& points, - unsigned int degree_fitting, unsigned int degree_monge, std::vector& output) + unsigned int degree_fitting, unsigned int degree_monge, std::vector& output, + cpp11::atomic& advancement, + cpp11::atomic& interrupted) : tree(tree), k (k), degree_fitting (degree_fitting), degree_monge (degree_monge), input (points), output (output) + , advancement (advancement) + , interrupted (interrupted) { } void operator()(const tbb::blocked_range& r) const { for( std::size_t i = r.begin(); i != r.end(); ++i) + { + if (interrupted) + break; output[i] = CGAL::internal::jet_smooth_point(input[i], tree, k, degree_fitting, degree_monge); + ++ advancement; + } } }; @@ -182,6 +195,13 @@ jet_smooth_point( \cgalParamBegin{svd_traits} template parameter for the class `Monge_via_jet_fitting`. If \ref thirdpartyEigen "Eigen" 3.2 (or greater) is available and `CGAL_EIGEN3_ENABLED` is defined, then `CGAL::Eigen_svd` is used.\cgalParamEnd + \cgalParamBegin{callback} an instance of + `cpp11::function`. It is called regularly when the + algorithm is running: the current advancement (between 0. and + 1.) is passed as parameter. If it returns `true`, then the + algorithm continues its execution normally; if it returns + `false`, the algorithm is stopped and the remaining points are + left unchanged.\cgalParamEnd \cgalParamBegin{geom_traits} an instance of a geometric traits class, model of `Kernel`\cgalParamEnd \cgalNamedParamsEnd @@ -210,6 +230,8 @@ jet_smooth_point_set( PointMap point_map = choose_param(get_param(np, internal_np::point_map), PointMap()); unsigned int degree_fitting = choose_param(get_param(np, internal_np::degree_fitting), 2); unsigned int degree_monge = choose_param(get_param(np, internal_np::degree_monge), 2); + const cpp11::function& callback = choose_param(get_param(np, internal_np::callback), + cpp11::function()); typedef typename Kernel::Point_3 Point; @@ -244,27 +266,36 @@ jet_smooth_point_set( #else if (boost::is_convertible::value) { - std::vector mutated_points (kd_tree_points.size ()); + internal::Point_set_processing_3::Parallel_callback + parallel_callback (callback, kd_tree_points.size()); + + std::vector mutated_points (kd_tree_points.size (), CGAL::ORIGIN); CGAL::internal::Jet_smooth_pwns f (tree, k, kd_tree_points, degree_fitting, degree_monge, - mutated_points); + mutated_points, + parallel_callback.advancement(), + parallel_callback.interrupted()); tbb::parallel_for(tbb::blocked_range(0, kd_tree_points.size ()), f); unsigned int i = 0; for(it = points.begin(); it != points.end(); ++ it, ++ i) - { - put(point_map, *it, mutated_points[i]); + if (mutated_points[i] != CGAL::ORIGIN) + put(point_map, *it, mutated_points[i]); + + parallel_callback.join(); - } } else #endif { - for(it = points.begin(); it != points.end(); it++) + std::size_t nb = 0; + for(it = points.begin(); it != points.end(); it++, ++ nb) { const typename boost::property_traits::reference p = get(point_map, *it); put(point_map, *it , internal::jet_smooth_point( p,tree,k,degree_fitting,degree_monge) ); + if (callback && !callback ((nb+1) / double(kd_tree_points.size()))) + break; } } } diff --git a/Point_set_processing_3/include/CGAL/pca_estimate_normals.h b/Point_set_processing_3/include/CGAL/pca_estimate_normals.h index 6011e1428cb..68da25df8d8 100644 --- a/Point_set_processing_3/include/CGAL/pca_estimate_normals.h +++ b/Point_set_processing_3/include/CGAL/pca_estimate_normals.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -41,6 +42,7 @@ #include #ifdef CGAL_LINKED_WITH_TBB +#include #include #include #include @@ -117,17 +119,28 @@ pca_estimate_normal(const typename Kernel::Point_3& query, ///< point to compute const unsigned int k; const std::vector& input; std::vector& output; + cpp11::atomic& advancement; + cpp11::atomic& interrupted; public: PCA_estimate_normals(Tree& tree, unsigned int k, std::vector& points, - std::vector& output) + std::vector& output, + cpp11::atomic& advancement, + cpp11::atomic& interrupted) : tree(tree), k (k), input (points), output (output) + , advancement (advancement) + , interrupted (interrupted) { } void operator()(const tbb::blocked_range& r) const { for( std::size_t i = r.begin(); i != r.end(); ++i) + { + if (interrupted) + break; output[i] = CGAL::internal::pca_estimate_normal(input[i], tree, k); + ++ advancement; + } } }; @@ -166,6 +179,13 @@ pca_estimate_normal(const typename Kernel::Point_3& query, ///< point to compute If this parameter is omitted, `CGAL::Identity_property_map` is used.\cgalParamEnd \cgalParamBegin{normal_map} a model of `WritablePropertyMap` with value type `geom_traits::Vector_3`.\cgalParamEnd + \cgalParamBegin{callback} an instance of + `cpp11::function`. It is called regularly when the + algorithm is running: the current advancement (between 0. and + 1.) is passed as parameter. If it returns `true`, then the + algorithm continues its execution normally; if it returns + `false`, the algorithm is stopped and the remaining normals are + left unchanged.\cgalParamEnd \cgalParamBegin{geom_traits} an instance of a geometric traits class, model of `Kernel`\cgalParamEnd \cgalNamedParamsEnd */ @@ -193,6 +213,8 @@ pca_estimate_normals( PointMap point_map = choose_param(get_param(np, internal_np::point_map), PointMap()); NormalMap normal_map = choose_param(get_param(np, internal_np::normal_map), NormalMap()); + const cpp11::function& callback = choose_param(get_param(np, internal_np::callback), + cpp11::function()); typedef typename Kernel::Point_3 Point; @@ -235,20 +257,28 @@ pca_estimate_normals( #else if (boost::is_convertible::value) { - std::vector normals (kd_tree_points.size ()); + internal::Point_set_processing_3::Parallel_callback + parallel_callback (callback, kd_tree_points.size()); + + std::vector normals (kd_tree_points.size (), + CGAL::NULL_VECTOR); CGAL::internal::PCA_estimate_normals - f (tree, k, kd_tree_points, normals); + f (tree, k, kd_tree_points, normals, + parallel_callback.advancement(), + parallel_callback.interrupted()); tbb::parallel_for(tbb::blocked_range(0, kd_tree_points.size ()), f); unsigned int i = 0; for(it = points.begin(); it != points.end(); ++ it, ++ i) - { - put (normal_map, *it, normals[i]); - } + if (normals[i] != CGAL::NULL_VECTOR) + put (normal_map, *it, normals[i]); + + parallel_callback.join(); } else #endif { - for(it = points.begin(); it != points.end(); it++) + std::size_t nb = 0; + for(it = points.begin(); it != points.end(); it++, ++ nb) { Vector normal = internal::pca_estimate_normal( get(point_map,*it), @@ -256,6 +286,8 @@ pca_estimate_normals( k); put(normal_map, *it, normal); // normal_map[it] = normal + if (callback && !callback ((nb+1) / double(kd_tree_points.size()))) + break; } } diff --git a/Point_set_processing_3/include/CGAL/remove_outliers.h b/Point_set_processing_3/include/CGAL/remove_outliers.h index d268a26adf4..3300d2c6696 100644 --- a/Point_set_processing_3/include/CGAL/remove_outliers.h +++ b/Point_set_processing_3/include/CGAL/remove_outliers.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -132,6 +133,13 @@ compute_avg_knn_sq_distance_3( \cgalParamBegin{threshold_percent} maximum percentage of points to remove.\cgalParamEnd \cgalParamBegin{threshold_distance} minimum distance for a point to be considered as outlier (distance here is the square root of the average squared distance to K nearest neighbors).\cgalParamEnd + \cgalParamBegin{callback} an instance of + `cpp11::function`. It is called regularly when the + algorithm is running: the current advancement (between 0. and + 1.) is passed as parameter. If it returns `true`, then the + algorithm continues its execution normally; if it returns + `false`, the algorithm is stopped, all points are left unchanged + and the function return `points.end()`.\cgalParamEnd \cgalParamBegin{geom_traits} an instance of a geometric traits class, model of `Kernel`\cgalParamEnd \cgalNamedParamsEnd @@ -163,6 +171,8 @@ remove_outliers( PointMap point_map = choose_param(get_param(np, internal_np::point_map), PointMap()); double threshold_percent = choose_param(get_param(np, internal_np::threshold_percent), 10.); double threshold_distance = choose_param(get_param(np, internal_np::threshold_distance), 0.); + const cpp11::function& callback = choose_param(get_param(np, internal_np::callback), + cpp11::function()); typedef typename Kernel::FT FT; @@ -198,12 +208,15 @@ remove_outliers( // iterate over input points and add them to multimap sorted by distance to k std::multimap sorted_points; - for(it = points.begin(); it != points.end(); it++) + std::size_t nb = 0; + for(it = points.begin(); it != points.end(); it++, ++ nb) { FT sq_distance = internal::compute_avg_knn_sq_distance_3( get(point_map,*it), tree, k); sorted_points.insert( std::make_pair(sq_distance, *it) ); + if (callback && !callback ((nb+1) / double(kd_tree_points.size()))) + return points.end(); } // Replaces [points.begin(), points.end()) range by the multimap content. diff --git a/Point_set_processing_3/include/CGAL/wlop_simplify_and_regularize_point_set.h b/Point_set_processing_3/include/CGAL/wlop_simplify_and_regularize_point_set.h index c47bb214eff..3d9c0f43c52 100644 --- a/Point_set_processing_3/include/CGAL/wlop_simplify_and_regularize_point_set.h +++ b/Point_set_processing_3/include/CGAL/wlop_simplify_and_regularize_point_set.h @@ -42,8 +42,10 @@ #include #ifdef CGAL_LINKED_WITH_TBB +#include #include #include +#include #endif // CGAL_LINKED_WITH_TBB #include @@ -354,6 +356,8 @@ class Sample_point_updater const typename Kernel::FT radius; const std::vector &original_densities; const std::vector &sample_densities; + cpp11::atomic& advancement; + cpp11::atomic& interrupted; public: Sample_point_updater( @@ -363,20 +367,25 @@ public: const Tree &_sample_kd_tree, const typename Kernel::FT _radius, const std::vector &_original_densities, - const std::vector &_sample_densities): + const std::vector &_sample_densities, + cpp11::atomic& advancement, + cpp11::atomic& interrupted): update_sample_points(out), sample_points(in), original_kd_tree(_original_kd_tree), sample_kd_tree(_sample_kd_tree), radius(_radius), original_densities(_original_densities), - sample_densities(_sample_densities){} - + sample_densities(_sample_densities), + advancement (advancement), + interrupted (interrupted) {} void operator() ( const tbb::blocked_range& r ) const { for (size_t i = r.begin(); i != r.end(); ++i) { + if (interrupted) + break; update_sample_points[i] = simplify_and_regularize_internal:: compute_update_sample_point( sample_points[i], @@ -385,6 +394,7 @@ public: radius, original_densities, sample_densities); + ++ advancement; } } }; @@ -440,6 +450,13 @@ public: value is 35. More iterations give a more regular result but increase the runtime.\cgalParamEnd \cgalParamBegin{require_uniform_sampling} an optional preprocessing, which will give better result if the distribution of the input points is highly non-uniform. The default value is `false`. \cgalParamEnd + \cgalParamBegin{callback} an instance of + `cpp11::function`. It is called regularly when the + algorithm is running: the current advancement (between 0. and + 1.) is passed as parameter. If it returns `true`, then the + algorithm continues its execution normally; if it returns + `false`, the algorithm is stopped, no output points are + generated.\cgalParamEnd \cgalParamBegin{geom_traits} an instance of a geometric traits class, model of `Kernel`\cgalParamEnd \cgalNamedParamsEnd @@ -466,6 +483,8 @@ wlop_simplify_and_regularize_point_set( double radius = choose_param(get_param(np, internal_np::neighbor_radius), -1); unsigned int iter_number = choose_param(get_param(np, internal_np::number_of_iterations), 35); bool require_uniform_sampling = choose_param(get_param(np, internal_np::require_uniform_sampling), false); + const cpp11::function& callback = choose_param(get_param(np, internal_np::callback), + cpp11::function()); typedef typename Kernel::Point_3 Point; typedef typename Kernel::FT FT; @@ -589,6 +608,9 @@ wlop_simplify_and_regularize_point_set( //parallel if (boost::is_convertible::value) { + internal::Point_set_processing_3::Parallel_callback + parallel_callback (callback, iter_number * number_of_sample, iter_n * number_of_sample); + tbb::blocked_range block(0, number_of_sample); Sample_point_updater sample_updater( update_sample_points, @@ -597,15 +619,28 @@ wlop_simplify_and_regularize_point_set( sample_kd_tree, radius2, original_density_weights, - sample_density_weights); + sample_density_weights, + parallel_callback.advancement(), + parallel_callback.interrupted()); tbb::parallel_for(block, sample_updater); + + bool interrupted = parallel_callback.interrupted(); + + // We interrupt by hand as counter only goes halfway and won't terminate by itself + parallel_callback.interrupted() = true; + parallel_callback.join(); + + // If interrupted during this step, nothing is computed, we return NaN + if (interrupted) + return output; }else #endif { //sequential + std::size_t nb = iter_n * number_of_sample; for (sample_iter = sample_points.begin(); - sample_iter != sample_points.end(); ++sample_iter, ++update_iter) + sample_iter != sample_points.end(); ++sample_iter, ++update_iter, ++ nb) { *update_iter = simplify_and_regularize_internal:: compute_update_sample_point + +#include +#ifdef scene_callback_signaler_EXPORTS +# define SCENE_CALLBACK_SIGNALER_EXPORT Q_DECL_EXPORT +#else +# define SCENE_CALLBACK_SIGNALER_EXPORT Q_DECL_IMPORT +#endif + +class SCENE_CALLBACK_SIGNALER_EXPORT Callback_signaler + : public QObject +{ + Q_OBJECT + +public: + + bool is_canceled; + + Callback_signaler(); + + void emit_signal (int value) const; + +public Q_SLOTS: + + void cancel() + { + is_canceled = true; + } + +Q_SIGNALS: + + void progressChanged(int) const; +}; + + +#endif // CALLBACK_SIGNALER_H diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/CMakeLists.txt b/Polyhedron/demo/Polyhedron/Plugins/Point_set/CMakeLists.txt index d538c96e658..3725e4b61f8 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/CMakeLists.txt +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/CMakeLists.txt @@ -6,17 +6,17 @@ if(EIGEN3_FOUND) qt5_wrap_ui( point_set_normal_estimationUI_FILES Point_set_normal_estimation_plugin.ui) polyhedron_demo_plugin(point_set_normal_estimation_plugin Point_set_normal_estimation_plugin ${point_set_normal_estimationUI_FILES}) - target_link_libraries(point_set_normal_estimation_plugin PUBLIC scene_points_with_normal_item) + target_link_libraries(point_set_normal_estimation_plugin PUBLIC scene_points_with_normal_item scene_callback_signaler) qt5_wrap_ui( features_detection_pluginUI_FILES Features_detection_plugin.ui) polyhedron_demo_plugin(features_detection_plugin Features_detection_plugin ${features_detection_pluginUI_FILES}) target_link_libraries(features_detection_plugin PUBLIC scene_points_with_normal_item) polyhedron_demo_plugin(point_set_smoothing_plugin Point_set_smoothing_plugin) - target_link_libraries(point_set_smoothing_plugin PUBLIC scene_points_with_normal_item) + target_link_libraries(point_set_smoothing_plugin PUBLIC scene_points_with_normal_item scene_callback_signaler) polyhedron_demo_plugin(point_set_average_spacing_plugin Point_set_average_spacing_plugin) - target_link_libraries(point_set_average_spacing_plugin PUBLIC scene_points_with_normal_item) + target_link_libraries(point_set_average_spacing_plugin PUBLIC scene_points_with_normal_item scene_callback_signaler) else(EIGEN3_FOUND) message(STATUS "NOTICE: Eigen 3.1 (or greater) was not found. Surface reconstruction plugin will not be available.") @@ -29,11 +29,11 @@ endif() qt5_wrap_ui(point_set_bilateral_smoothingUI_FILES Point_set_bilateral_smoothing_plugin.ui) polyhedron_demo_plugin(point_set_bilateral_smoothing_plugin Point_set_bilateral_smoothing_plugin ${point_set_bilateral_smoothingUI_FILES}) - target_link_libraries(point_set_bilateral_smoothing_plugin PUBLIC scene_points_with_normal_item) + target_link_libraries(point_set_bilateral_smoothing_plugin PUBLIC scene_points_with_normal_item scene_callback_signaler) qt5_wrap_ui( ps_outliers_removal_UI_FILES Point_set_outliers_removal_plugin.ui) polyhedron_demo_plugin(point_set_outliers_removal_plugin Point_set_outliers_removal_plugin ${ps_outliers_removal_UI_FILES}) - target_link_libraries(point_set_outliers_removal_plugin PUBLIC scene_points_with_normal_item) + target_link_libraries(point_set_outliers_removal_plugin PUBLIC scene_points_with_normal_item scene_callback_signaler) qt5_wrap_ui( point_set_selectionUI_FILES Point_set_selection_widget.ui) polyhedron_demo_plugin(point_set_selection_plugin Point_set_selection_plugin ${point_set_selectionUI_FILES}) @@ -41,12 +41,11 @@ endif() qt5_wrap_ui(point_set_shape_detectionUI_FILES Point_set_shape_detection_plugin.ui) polyhedron_demo_plugin(point_set_shape_detection_plugin Point_set_shape_detection_plugin ${point_set_shape_detectionUI_FILES}) - target_link_libraries(point_set_shape_detection_plugin PUBLIC scene_surface_mesh_item scene_polyhedron_item scene_points_with_normal_item scene_polygon_soup_item) + target_link_libraries(point_set_shape_detection_plugin PUBLIC scene_surface_mesh_item scene_polyhedron_item scene_points_with_normal_item scene_polygon_soup_item scene_callback_signaler) qt5_wrap_ui(point_set_simplificationUI_FILES Point_set_simplification_plugin.ui) polyhedron_demo_plugin(point_set_simplification_plugin Point_set_simplification_plugin ${point_set_simplificationUI_FILES}) - target_link_libraries(point_set_simplification_plugin PUBLIC scene_points_with_normal_item) - + target_link_libraries(point_set_simplification_plugin PUBLIC scene_points_with_normal_item scene_callback_signaler) qt5_wrap_ui(point_set_upsamplingUI_FILES Point_set_upsampling_plugin.ui) polyhedron_demo_plugin(point_set_upsampling_plugin Point_set_upsampling_plugin ${point_set_upsamplingUI_FILES}) @@ -54,7 +53,7 @@ endif() qt5_wrap_ui(point_set_wlopFILES Point_set_wlop_plugin.ui) polyhedron_demo_plugin(point_set_wlop_plugin Point_set_wlop_plugin ${point_set_wlopFILES}) - target_link_libraries(point_set_wlop_plugin PUBLIC scene_points_with_normal_item) + target_link_libraries(point_set_wlop_plugin PUBLIC scene_points_with_normal_item scene_callback_signaler) polyhedron_demo_plugin(merge_point_sets_plugin Merge_point_sets_plugin) target_link_libraries(merge_point_sets_plugin PUBLIC scene_points_with_normal_item) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_average_spacing_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_average_spacing_plugin.cpp index 33e0cccdd0f..13e9e0fa7db 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_average_spacing_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_average_spacing_plugin.cpp @@ -1,10 +1,12 @@ +//#undef CGAL_LINKED_WITH_TBB + #include "config.h" #include "Scene_points_with_normal_item.h" #include #include #include -#include +#include #include #include @@ -14,6 +16,9 @@ #include #include #include + +#include "run_with_qprogressdialog.h" + // Concurrency #ifdef CGAL_LINKED_WITH_TBB typedef CGAL::Parallel_tag Concurrency_tag; @@ -21,6 +26,26 @@ typedef CGAL::Parallel_tag Concurrency_tag; typedef CGAL::Sequential_tag Concurrency_tag; #endif +struct Compute_average_spacing_functor + : public Functor_with_signal_callback +{ + Point_set* points; + const int nb_neighbors; + boost::shared_ptr result; + + Compute_average_spacing_functor (Point_set* points, const int nb_neighbors) + : points (points), nb_neighbors (nb_neighbors), result (new double(0)) { } + + void operator()() + { + *result = CGAL::compute_average_spacing( + points->all_or_selection_if_not_empty(), + nb_neighbors, + points->parameters(). + callback (*(this->callback()))); + } +}; + using namespace CGAL::Three; class Polyhedron_demo_point_set_average_spacing_plugin : public QObject, @@ -85,27 +110,27 @@ void Polyhedron_demo_point_set_average_spacing_plugin::on_actionAverageSpacing_t &ok); if(!ok) return; - - QApplication::setOverrideCursor(Qt::WaitCursor); + + QApplication::setOverrideCursor(Qt::BusyCursor); QApplication::processEvents(); - CGAL::Timer task_timer; task_timer.start(); + CGAL::Real_timer task_timer; task_timer.start(); std::cerr << "Average spacing (k=" << nb_neighbors <<")...\n"; // Computes average spacing - double average_spacing = CGAL::compute_average_spacing( - points->all_or_selection_if_not_empty(), - nb_neighbors, - points->parameters()); + Compute_average_spacing_functor functor (points, nb_neighbors); + run_with_qprogressdialog (functor, "Computing average spacing...", mw); + + double average_spacing = *functor.result; // Print result Kernel::Sphere_3 bsphere = points->bounding_sphere(); double radius = std::sqrt(bsphere.squared_radius()); std::size_t memory = CGAL::Memory_sizer().virtual_size(); std::cerr << "Average spacing = " << average_spacing - << " = " << average_spacing/radius << " * point set radius (" - << task_timer.time() << " seconds, " - << (memory>>20) << " Mb allocated)" - << std::endl; + << " = " << average_spacing/radius << " * point set radius (" + << task_timer.time() << " seconds, " + << (memory>>20) << " Mb allocated)" + << std::endl; QApplication::restoreOverrideCursor(); QMessageBox::information(NULL, @@ -113,6 +138,7 @@ void Polyhedron_demo_point_set_average_spacing_plugin::on_actionAverageSpacing_t tr("Average Spacing = %1 = %2 * point set radius") .arg(average_spacing) .arg(average_spacing/radius)); + } } diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_bilateral_smoothing_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_bilateral_smoothing_plugin.cpp index 4e0e5020fd0..14c45d7391b 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_bilateral_smoothing_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_bilateral_smoothing_plugin.cpp @@ -14,6 +14,10 @@ #include #include +#include + +#include "run_with_qprogressdialog.h" + #include "ui_Point_set_bilateral_smoothing_plugin.h" // Concurrency @@ -23,6 +27,30 @@ typedef CGAL::Parallel_tag Concurrency_tag; typedef CGAL::Sequential_tag Concurrency_tag; #endif +struct Bilateral_smoothing_functor + : public Functor_with_signal_callback +{ + Point_set* points; + unsigned int neighborhood_size; + unsigned int sharpness_angle; + boost::shared_ptr result; + + Bilateral_smoothing_functor (Point_set* points, + unsigned int neighborhood_size, + unsigned int sharpness_angle) + : points (points), neighborhood_size (neighborhood_size) + , sharpness_angle (sharpness_angle), result (new double(0)) { } + + void operator()() + { + *result = CGAL::bilateral_smooth_point_set + (points->all_or_selection_if_not_empty(), + neighborhood_size, + points->parameters(). + sharpness_angle(sharpness_angle). + callback (*(this->callback()))); + } +}; using namespace CGAL::Three; class Polyhedron_demo_point_set_bilateral_smoothing_plugin : @@ -103,21 +131,24 @@ void Polyhedron_demo_point_set_bilateral_smoothing_plugin::on_actionBilateralSmo << dialog.iterations () << " iteration(s), neighborhood size of " << dialog.neighborhood_size () << " and sharpness angle of " << dialog.sharpness_angle () << "... "; - QApplication::setOverrideCursor(Qt::WaitCursor); + QApplication::setOverrideCursor(Qt::BusyCursor); CGAL::Timer task_timer; task_timer.start(); for (unsigned int i = 0; i < dialog.iterations (); ++i) { - /* double error = */ - CGAL::bilateral_smooth_point_set - (points->all_or_selection_if_not_empty(), - dialog.neighborhood_size (), - points->parameters(). - sharpness_angle(dialog.sharpness_angle ())); + std::ostringstream oss; + oss << "Bilateral smoothing (iteration " << i+1 << "/" << dialog.iterations() << ")"; + + Bilateral_smoothing_functor functor (points, + dialog.neighborhood_size (), + dialog.sharpness_angle ()); + run_with_qprogressdialog (functor, oss.str().c_str(), mw); + double error = *functor.result; + + if (std::isnan(error)) // NaN return means algorithm was interrupted + break; } - - std::size_t memory = CGAL::Memory_sizer().virtual_size(); std::cerr << task_timer.time() << " seconds, " diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_normal_estimation_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_normal_estimation_plugin.cpp index c5698c8094e..398f08f707d 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_normal_estimation_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_normal_estimation_plugin.cpp @@ -18,6 +18,8 @@ #include #include +#include "run_with_qprogressdialog.h" + #include "ui_Point_set_normal_estimation_plugin.h" #if BOOST_VERSION == 105700 @@ -32,6 +34,49 @@ typedef CGAL::Parallel_tag Concurrency_tag; #else typedef CGAL::Sequential_tag Concurrency_tag; #endif + +struct PCA_estimate_normals_functor + : public Functor_with_signal_callback +{ + Point_set* points; + int neighborhood_size; + unsigned int sharpness_angle; + + PCA_estimate_normals_functor (Point_set* points, + int neighborhood_size) + : points (points), neighborhood_size (neighborhood_size) + { } + + void operator()() + { + CGAL::pca_estimate_normals(points->all_or_selection_if_not_empty(), + neighborhood_size, + points->parameters(). + callback (*(this->callback()))); + } +}; + +struct Jet_estimate_normals_functor + : public Functor_with_signal_callback +{ + Point_set* points; + int neighborhood_size; + unsigned int sharpness_angle; + + Jet_estimate_normals_functor (Point_set* points, + int neighborhood_size) + : points (points), neighborhood_size (neighborhood_size) + { } + + void operator()() + { + CGAL::jet_estimate_normals(points->all_or_selection_if_not_empty(), + neighborhood_size, + points->parameters(). + callback (*(this->callback()))); + } +}; + using namespace CGAL::Three; class Polyhedron_demo_point_set_normal_estimation_plugin : @@ -49,6 +94,7 @@ public: void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, Messages_interface*) { scene = scene_interface; + mw = mainWindow; actionNormalEstimation = new QAction(tr("Normal Estimation"), mainWindow); actionNormalEstimation->setObjectName("actionNormalEstimation"); actionNormalEstimation->setProperty("subMenuName","Point Set Processing"); @@ -160,7 +206,7 @@ void Polyhedron_demo_point_set_normal_estimation_plugin::on_actionNormalEstimati if(!dialog.exec()) return; - QApplication::setOverrideCursor(Qt::WaitCursor); + QApplication::setOverrideCursor(Qt::BusyCursor); QApplication::processEvents(); // First point to delete @@ -175,9 +221,8 @@ void Polyhedron_demo_point_set_normal_estimation_plugin::on_actionNormalEstimati std::cerr << "Estimates normal direction by PCA (k=" << dialog.pca_neighbors() <<")...\n"; // Estimates normals direction. - CGAL::pca_estimate_normals(points->all_or_selection_if_not_empty(), - dialog.pca_neighbors(), - points->parameters()); + PCA_estimate_normals_functor functor (points, dialog.pca_neighbors()); + run_with_qprogressdialog (functor, "Estimating normals by PCA...", mw); std::size_t memory = CGAL::Memory_sizer().virtual_size(); std::cerr << "Estimates normal direction: " << task_timer.time() << " seconds, " @@ -190,9 +235,8 @@ void Polyhedron_demo_point_set_normal_estimation_plugin::on_actionNormalEstimati std::cerr << "Estimates normal direction by Jet Fitting (k=" << dialog.jet_neighbors() <<")...\n"; // Estimates normals direction. - CGAL::jet_estimate_normals(points->all_or_selection_if_not_empty(), - dialog.jet_neighbors(), - points->parameters()); + Jet_estimate_normals_functor functor (points, dialog.pca_neighbors()); + run_with_qprogressdialog (functor, "Estimating normals by jet fitting...", mw); std::size_t memory = CGAL::Memory_sizer().virtual_size(); std::cerr << "Estimates normal direction: " << task_timer.time() << " seconds, " diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_outliers_removal_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_outliers_removal_plugin.cpp index cce263b6174..db4b46da4eb 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_outliers_removal_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_outliers_removal_plugin.cpp @@ -16,6 +16,39 @@ #include #include "ui_Point_set_outliers_removal_plugin.h" + +#include "run_with_qprogressdialog.h" + +struct Outlier_removal_functor + : public Functor_with_signal_callback +{ + Point_set* points; + int nb_neighbors; + double removed_percentage; + double distance_threshold; + boost::shared_ptr result; + + Outlier_removal_functor (Point_set* points, + int nb_neighbors, + double removed_percentage, + double distance_threshold) + : points (points), nb_neighbors (nb_neighbors) + , removed_percentage (removed_percentage), distance_threshold (distance_threshold) + , result (new Point_set::iterator) { } + + void operator()() + { + // Computes outliers + *result = + CGAL::remove_outliers(*points, + nb_neighbors, + points->parameters(). + threshold_percent(removed_percentage). + threshold_distance(distance_threshold). + callback (*(this->callback()))); + } +}; + using namespace CGAL::Three; class Polyhedron_demo_point_set_outliers_removal_plugin : public QObject, @@ -31,6 +64,7 @@ private: public: void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, Messages_interface*) { scene = scene_interface; + mw = mainWindow; actionOutlierRemoval = new QAction(tr("Outliers Selection"), mainWindow); actionOutlierRemoval->setProperty("subMenuName","Point Set Processing"); actionOutlierRemoval->setObjectName("actionOutlierRemoval"); @@ -87,27 +121,23 @@ void Polyhedron_demo_point_set_outliers_removal_plugin::on_actionOutlierRemoval_ const double distance_threshold = dialog.distance(); const int nb_neighbors = dialog.nbNeighbors(); - QApplication::setOverrideCursor(Qt::WaitCursor); + QApplication::setOverrideCursor(Qt::BusyCursor); CGAL::Timer task_timer; task_timer.start(); std::cerr << "Select outliers (" << removed_percentage <<"% with distance threshold " << distance_threshold << ")...\n"; // Computes outliers - Point_set::iterator first_point_to_remove = - CGAL::remove_outliers(*points, - nb_neighbors, - points->parameters(). - threshold_percent(removed_percentage). - threshold_distance(distance_threshold)); - + Outlier_removal_functor functor (points, nb_neighbors, removed_percentage, distance_threshold); + run_with_qprogressdialog (functor, "Selecting outliers...", mw); + Point_set::iterator first_point_to_remove = *functor.result; std::size_t nb_points_to_remove = std::distance(first_point_to_remove, points->end()); std::size_t memory = CGAL::Memory_sizer().virtual_size(); - std::cerr << "Simplification: " << nb_points_to_remove << " point(s) are selected (" - << task_timer.time() << " seconds, " - << (memory>>20) << " Mb allocated)" - << std::endl; + std::cerr << "Outliers: " << nb_points_to_remove << " point(s) are selected (" + << task_timer.time() << " seconds, " + << (memory>>20) << " Mb allocated)" + << std::endl; // Selects points to delete points->set_first_selected (first_point_to_remove); diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_shape_detection_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_shape_detection_plugin.cpp index 0ae33010333..eef452a345c 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_shape_detection_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_shape_detection_plugin.cpp @@ -38,8 +38,27 @@ #include #include +#include "run_with_qprogressdialog.h" + #include "ui_Point_set_shape_detection_plugin.h" +template +struct Detect_shapes_functor + : public Functor_with_signal_callback +{ + Shape_detection& shape_detection; + typename Shape_detection::Parameters& op; + + Detect_shapes_functor (Shape_detection& shape_detection, + typename Shape_detection::Parameters& op) + : shape_detection (shape_detection), op (op) + { } + + void operator()() + { + shape_detection.detect(op, *(this->callback())); + } +}; struct build_from_pair { @@ -55,93 +74,6 @@ struct build_from_pair }; -struct Progress_bar_callback -{ - mutable int nb; - CGAL::Real_timer timer; - double t_start; - mutable double t_latest; - int bar_size; - mutable std::size_t string_size; - - Progress_bar_callback() : nb(0), bar_size (30), string_size(0) - { - timer.start(); - t_start = timer.time(); - t_latest = t_start; - } - - bool operator()(double advancement) const - { - // Avoid calling time() at every single iteration, which could - // impact performances very badly - ++ nb; - if (advancement != 1 && nb % 1000 != 0) - return true; - - // If the limit is reach, interrupt the algorithm - double t = timer.time(); - if (advancement == 1 || (t - t_latest) > 1.) - { - std::ostringstream oss; - oss << "["; - int adv = int(advancement * bar_size); - for (int i = 0; i < adv; ++ i) - oss << "="; - if (adv != bar_size) - oss << ">"; - for (int i = adv; i < bar_size; ++ i) - oss << " "; - - oss << "] " << int(advancement * 100) << "% ("; - - display_time (oss, t); - - oss << " elapsed, "; - - display_time (oss, estimate_remaining(t, advancement)); - - oss << " remaining)"; - - std::string bar_string = oss.str(); - std::cerr << "\r" << bar_string; - for (std::size_t i = bar_string.size(); i < string_size; ++ i) - std::cerr << " "; - string_size = (std::max) (string_size, bar_string.size()); - - if (advancement == 1) - std::cerr << std::endl; - - t_latest = t; - } - - return true; - } - - void display_time (std::ostringstream& oss, double seconds) const - { - if (seconds > 3600.) - { - int hours = int(seconds / 3600.); - oss << hours << "h"; - seconds -= hours * 3600.; - } - if (seconds > 60.) - { - int minutes = (int)(seconds / 60.); - oss << minutes << "min"; - seconds -= minutes * 60.; - } - oss << int(seconds) << "sec"; - } - - double estimate_remaining (double seconds, double advancement) const - { - return ((1. - advancement) * (seconds - t_start) / advancement); - } -}; - - class Point_set_demo_point_set_shape_detection_dialog : public QDialog, private Ui::PointSetShapeDetectionDialog { Q_OBJECT @@ -303,7 +235,7 @@ private: comments += "shape -1 no assigned shape\n"; } - QApplication::setOverrideCursor(Qt::WaitCursor); + QApplication::setOverrideCursor(Qt::BusyCursor); Shape_detection shape_detection; shape_detection.set_input(*points, points->point_map(), points->normal_map()); @@ -340,8 +272,8 @@ private: // The actual shape detection. CGAL::Real_timer t; t.start(); - Progress_bar_callback callback; - shape_detection.detect(op, callback); + Detect_shapes_functor functor (shape_detection, op); + run_with_qprogressdialog (functor, "Detecting shapes...", mw); t.stop(); std::cout << shape_detection.shapes().size() << " shapes found in " diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_simplification_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_simplification_plugin.cpp index 3711bb10053..9d838794b69 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_simplification_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_simplification_plugin.cpp @@ -17,6 +17,8 @@ #include #include +#include "run_with_qprogressdialog.h" + #include "ui_Point_set_simplification_plugin.h" // Concurrency @@ -26,6 +28,68 @@ typedef CGAL::Parallel_tag Concurrency_tag; typedef CGAL::Sequential_tag Concurrency_tag; #endif +struct Compute_average_spacing_functor + : public Functor_with_signal_callback +{ + Point_set* points; + const int nb_neighbors; + boost::shared_ptr result; + + Compute_average_spacing_functor (Point_set* points, const int nb_neighbors) + : points (points), nb_neighbors (nb_neighbors), result (new double(0)) { } + + void operator()() + { + *result = CGAL::compute_average_spacing( + points->all_or_selection_if_not_empty(), + nb_neighbors, + points->parameters(). + callback (*(this->callback()))); + } +}; + +struct Grid_simplify_functor + : public Functor_with_signal_callback +{ + Point_set* points; + double grid_size; + boost::shared_ptr result; + + Grid_simplify_functor (Point_set* points, double grid_size) + : points (points), grid_size (grid_size), result (new Point_set::iterator) { } + + void operator()() + { + *result = CGAL::grid_simplify_point_set(*points, + grid_size, + points->parameters(). + callback (*(this->callback()))); + } +}; + +struct Hierarchy_simplify_functor + : public Functor_with_signal_callback +{ + Point_set* points; + unsigned int max_cluster_size; + double max_surface_variation; + boost::shared_ptr result; + + Hierarchy_simplify_functor (Point_set* points, + double max_cluster_size, + double max_surface_variation) + : points (points), max_cluster_size (max_cluster_size) + , max_surface_variation (max_surface_variation), result (new Point_set::iterator) { } + + void operator()() + { + *result = CGAL::hierarchy_simplify_point_set(*points, + points->parameters(). + size(max_cluster_size). + maximum_variation(max_surface_variation). + callback (*(this->callback()))); + } +}; using namespace CGAL::Three; class Polyhedron_demo_point_set_simplification_plugin : @@ -41,6 +105,7 @@ class Polyhedron_demo_point_set_simplification_plugin : public: void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface,Messages_interface*) { scene = scene_interface; + mw = mainWindow; actionSimplify = new QAction(tr("Simplification Selection"), mainWindow); actionSimplify->setProperty("subMenuName","Point Set Processing"); @@ -130,7 +195,7 @@ void Polyhedron_demo_point_set_simplification_plugin::on_actionSimplify_triggere if(!dialog.exec()) return; - QApplication::setOverrideCursor(Qt::WaitCursor); + QApplication::setOverrideCursor(Qt::BusyCursor); CGAL::Timer task_timer; task_timer.start(); @@ -152,24 +217,30 @@ void Polyhedron_demo_point_set_simplification_plugin::on_actionSimplify_triggere std::cerr << "Point set grid simplification (cell size = " << dialog.gridCellSize() <<" * average spacing)...\n"; // Computes average spacing - double average_spacing = CGAL::compute_average_spacing(*points, 6 /* knn = 1 ring */); + Compute_average_spacing_functor functor_as (points, 6); + run_with_qprogressdialog (functor_as, "Simplification: computing average spacing...", mw); + double average_spacing = *functor_as.result; + + Grid_simplify_functor functor (points, dialog.gridCellSize() * average_spacing); + run_with_qprogressdialog (functor, "Grid simplyfing...", mw); + // Computes points to remove by Grid Clustering - first_point_to_remove = - CGAL::grid_simplify_point_set(*points, - dialog.gridCellSize()*average_spacing); + first_point_to_remove = *functor.result; + } else { std::cerr << "Point set hierarchy simplification (cluster size = " << dialog.maximumClusterSize() << ", maximum variation = " << dialog.maximumSurfaceVariation() << ")...\n"; - // Computes points to remove by Grid Clustering - first_point_to_remove = - CGAL::hierarchy_simplify_point_set(*points, - points->parameters(). - size(dialog.maximumClusterSize()). - maximum_variation(dialog.maximumSurfaceVariation())); + // Computes points to remove by Hierarchy + Hierarchy_simplify_functor functor (points, dialog.maximumClusterSize(), + dialog.maximumSurfaceVariation()); + run_with_qprogressdialog (functor, "Hierarchy simplyfing...", mw); + + first_point_to_remove = *functor.result; + } std::size_t nb_points_to_remove = std::distance(first_point_to_remove, points->end()); diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_smoothing_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_smoothing_plugin.cpp index adf56f72e8e..1436a2b4ae5 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_smoothing_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_smoothing_plugin.cpp @@ -12,6 +12,8 @@ #include +#include "run_with_qprogressdialog.h" + // Concurrency #ifdef CGAL_LINKED_WITH_TBB typedef CGAL::Parallel_tag Concurrency_tag; @@ -19,6 +21,24 @@ typedef CGAL::Parallel_tag Concurrency_tag; typedef CGAL::Sequential_tag Concurrency_tag; #endif +struct Jet_smoothing_functor + : public Functor_with_signal_callback +{ + Point_set* points; + const int nb_neighbors; + + Jet_smoothing_functor (Point_set* points, const int nb_neighbors) + : points (points), nb_neighbors (nb_neighbors) { } + + void operator()() + { + CGAL::jet_smooth_point_set(points->all_or_selection_if_not_empty(), + nb_neighbors, + points->parameters(). + callback (*(this->callback()))); + } +}; + using namespace CGAL::Three; class Polyhedron_demo_point_set_smoothing_plugin : public QObject, @@ -79,17 +99,14 @@ void Polyhedron_demo_point_set_smoothing_plugin::on_actionJetSmoothing_triggered &ok); if(!ok) return; - QApplication::setOverrideCursor(Qt::WaitCursor); + QApplication::setOverrideCursor(Qt::BusyCursor); QApplication::processEvents(); - CGAL::jet_smooth_point_set(points->all_or_selection_if_not_empty(), - nb_neighbors, - points->parameters()); + + Jet_smoothing_functor functor (points, nb_neighbors); + run_with_qprogressdialog (functor, "Smoothing point set...", mw); points->invalidate_bounds(); - // calling jet_smooth_point_set breaks the normals - points->remove_normal_map(); - // update scene item->invalidateOpenGLBuffers(); scene->itemChanged(index); diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_wlop_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_wlop_plugin.cpp index 2c340be75e3..561b849c4f0 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_wlop_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_wlop_plugin.cpp @@ -15,6 +15,8 @@ #include #include +#include "run_with_qprogressdialog.h" + #include "ui_Point_set_wlop_plugin.h" // Concurrency @@ -24,6 +26,53 @@ typedef CGAL::Parallel_tag Concurrency_tag; typedef CGAL::Sequential_tag Concurrency_tag; #endif +struct Compute_average_spacing_functor + : public Functor_with_signal_callback +{ + Point_set* points; + const int nb_neighbors; + boost::shared_ptr result; + + Compute_average_spacing_functor (Point_set* points, const int nb_neighbors) + : points (points), nb_neighbors (nb_neighbors), result (new double(0)) { } + + void operator()() + { + *result = CGAL::compute_average_spacing( + points->all_or_selection_if_not_empty(), + nb_neighbors, + points->parameters(). + callback (*(this->callback()))); + } +}; + +struct Wlop_functor + : public Functor_with_signal_callback +{ + Point_set* points; + double select_percentage; + double neighbor_radius; + Scene_points_with_normal_item* new_item; + + Wlop_functor (Point_set* points, double select_percentage, double neighbor_radius, + Scene_points_with_normal_item* new_item) + : points (points), select_percentage (select_percentage) + , neighbor_radius (neighbor_radius), new_item (new_item) + { } + + void operator()() + { + CGAL::wlop_simplify_and_regularize_point_set + (points->all_or_selection_if_not_empty(), + new_item->point_set()->point_back_inserter(), + points->parameters(). + select_percentage (select_percentage). + neighbor_radius (neighbor_radius). + callback (*(this->callback()))); + } +}; + + using namespace CGAL::Three; class Polyhedron_demo_point_set_wlop_plugin : public QObject, @@ -38,6 +87,7 @@ class Polyhedron_demo_point_set_wlop_plugin : public: void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, Messages_interface*) { scene = scene_interface; + mw = mainWindow; actionSimplifyAndRegularize = new QAction(tr("WLOP Simplification and Regularization Selection"), mainWindow); actionSimplifyAndRegularize->setProperty("subMenuName","Point Set Processing"); actionSimplifyAndRegularize->setObjectName("actionSimplifyAndRegularize"); @@ -89,7 +139,7 @@ void Polyhedron_demo_point_set_wlop_plugin::on_actionSimplifyAndRegularize_trigg if(!dialog.exec()) return; - QApplication::setOverrideCursor(Qt::WaitCursor); + QApplication::setOverrideCursor(Qt::BusyCursor); CGAL::Timer task_timer; task_timer.start(); @@ -98,9 +148,9 @@ void Polyhedron_demo_point_set_wlop_plugin::on_actionSimplifyAndRegularize_trigg << dialog.neighborhoodRadius() <<" * average spacing)...\n"; // Computes average spacing - double average_spacing = CGAL::compute_average_spacing(points->all_or_selection_if_not_empty(), - 6 /* knn = 1 ring */, - points->parameters()); + Compute_average_spacing_functor functor_as (points, 6); + run_with_qprogressdialog (functor_as, "WLOP: computing average spacing...", mw); + double average_spacing = *functor_as.result; Scene_points_with_normal_item* new_item = new Scene_points_with_normal_item(); @@ -108,13 +158,10 @@ void Polyhedron_demo_point_set_wlop_plugin::on_actionSimplifyAndRegularize_trigg new_item->setVisible(true); item->setVisible(false); scene->addItem(new_item); - - CGAL::wlop_simplify_and_regularize_point_set - (points->all_or_selection_if_not_empty(), - new_item->point_set()->point_back_inserter(), - points->parameters(). - select_percentage (dialog.retainedPercentage()). - neighbor_radius (dialog.neighborhoodRadius()*average_spacing)); + + Wlop_functor functor (points, dialog.retainedPercentage(), + dialog.neighborhoodRadius() * average_spacing, new_item); + run_with_qprogressdialog (functor, "WLOP simplification and regularization...", mw); std::size_t memory = CGAL::Memory_sizer().virtual_size(); std::cerr << "Simplification and regularization: " diff --git a/Polyhedron/demo/Polyhedron/include/Progress_bar_callback.h b/Polyhedron/demo/Polyhedron/include/Progress_bar_callback.h new file mode 100644 index 00000000000..9a0eb468ec2 --- /dev/null +++ b/Polyhedron/demo/Polyhedron/include/Progress_bar_callback.h @@ -0,0 +1,111 @@ +#ifndef PROGRESS_BAR_CALLBACK_H +#define PROGRESS_BAR_CALLBACK_H + +#include + +struct Progress_bar_callback +{ + mutable std::size_t nb; + CGAL::Real_timer timer; + double t_start; + mutable double t_start_estimate; + mutable double t_latest; + int bar_size; + mutable std::size_t string_size; + + Progress_bar_callback(const char* title = NULL, + void* = NULL) // Hack to have same constructor as Qt_progress_bar_callback + : nb(0), bar_size (30), string_size(0) + { + if (title != NULL) + std::cerr << title << std::endl; + + timer.start(); + t_start = timer.time(); + t_start_estimate = 0.; + t_latest = t_start; + } + + bool operator()(double advancement) const + { + // Avoid calling time() at every single iteration, which could + // impact performances very badly + ++ nb; + if (advancement != 1 && nb % 1000 != 0) + return true; + + // If the limit is reach, interrupt the algorithm + double t = timer.time(); + if (advancement == 1 || (t - t_latest) > 1.) + { + if (advancement != 0. && t_start_estimate == 0.) + t_start_estimate = t_latest; + + std::ostringstream oss; + oss << "["; + int adv = int(advancement * bar_size); + for (int i = 0; i < adv; ++ i) + oss << "="; + if (adv != bar_size) + oss << ">"; + for (int i = adv; i < bar_size; ++ i) + oss << " "; + + oss << "] " << int(advancement * 100) << "% ("; + + display_time (oss, t); + + oss << " elapsed, "; + + display_time (oss, estimate_remaining(t, advancement)); + + oss << " remaining)"; + + std::string bar_string = oss.str(); + std::cerr << "\r" << bar_string; + for (std::size_t i = bar_string.size(); i < string_size; ++ i) + std::cerr << " "; + string_size = (std::max) (string_size, bar_string.size()); + + t_latest = t; + + if (advancement == 1) + std::cerr << std::endl; + } + + return true; + } + + void display_time (std::ostringstream& oss, double seconds) const + { + if (seconds == std::numeric_limits::infinity()) + { + oss << "unknown"; + return; + } + if (seconds > 3600.) + { + int hours = int(seconds / 3600.); + oss << hours << "h"; + seconds -= hours * 3600.; + } + if (seconds > 60.) + { + int minutes = (int)(seconds / 60.); + oss << minutes << "min"; + seconds -= minutes * 60.; + } + oss << int(seconds) << "sec"; + } + + double estimate_remaining (double seconds, double advancement) const + { + if (advancement == 0.) + return std::numeric_limits::infinity(); + + return ((1. - advancement) * (seconds - t_start_estimate) / advancement); + } +}; + + +#endif // PROGRESS_BAR_CALLBACK_H diff --git a/Polyhedron/demo/Polyhedron/include/Qt_progress_bar_callback.h b/Polyhedron/demo/Polyhedron/include/Qt_progress_bar_callback.h new file mode 100644 index 00000000000..d074bacdaa4 --- /dev/null +++ b/Polyhedron/demo/Polyhedron/include/Qt_progress_bar_callback.h @@ -0,0 +1,68 @@ +#ifndef QT_PROGRESS_BAR_CALLBACK_H +#define QT_PROGRESS_BAR_CALLBACK_H + +#include + +#include +#include + +class Qt_progress_bar_callback +{ + +private: + + CGAL::Real_timer timer; + double t_start; + mutable double t_latest; + + mutable std::size_t nb; + + QProgressDialog* dialog; + +public: + + Qt_progress_bar_callback() + { + } + + Qt_progress_bar_callback(const char* title, QWidget* parent) + : dialog (new QProgressDialog (QString(title), + QString("Cancel"), + 0, 100, + parent)) + { + dialog->setMinimumDuration(0); + dialog->setWindowModality(Qt::WindowModal); + timer.start(); + t_start = timer.time(); + t_latest = t_start; + } + ~Qt_progress_bar_callback() + { + } + + bool operator() (double advancement) const + { + // Avoid calling time() at every single iteration, which could + // impact performances very badly + ++ nb; + if (advancement != 1 && nb % 1000 != 0) + return true; + + // If the limit is reach, interrupt the algorithm + double t = timer.time(); + if (advancement == 1 || (t - t_latest) > 0.25) + { + dialog->setValue (int (100. * advancement)); + t_latest = t; + + if (dialog->wasCanceled()) + return false; + } + + return true; + } +}; + + +#endif // QT_PROGRESS_BAR_CALLBACK_H diff --git a/Polyhedron/demo/Polyhedron/include/run_with_qprogressdialog.h b/Polyhedron/demo/Polyhedron/include/run_with_qprogressdialog.h new file mode 100644 index 00000000000..f1bef8fb5fd --- /dev/null +++ b/Polyhedron/demo/Polyhedron/include/run_with_qprogressdialog.h @@ -0,0 +1,142 @@ +#ifndef RUN_WITH_QPROGRESSDIALOG_H +#define RUN_WITH_QPROGRESSDIALOG_H + +#include +#include +#include + +#include "Callback_signaler.h" + +#ifdef CGAL_LINKED_WITH_TBB +typedef CGAL::Parallel_tag Concurrency_tag; +#else +typedef CGAL::Sequential_tag Concurrency_tag; +#endif + + +class Signal_callback +{ +private: + + CGAL::Real_timer timer; + double t_start; + mutable double t_latest; + mutable std::size_t nb; + +public: + boost::shared_ptr latest_adv; + boost::shared_ptr state; + boost::shared_ptr signaler; + + Signal_callback(bool) + : latest_adv (new double(0)) + , state (new bool(true)) + , signaler (new Callback_signaler()) + { + timer.start(); + t_start = timer.time(); + t_latest = t_start; + } + ~Signal_callback() + { + } + + bool operator() (double advancement) const + { + if (!state) + return false; + + *latest_adv = advancement; + + // Avoid calling time() at every single iteration, which could + // impact performances very badly + ++ nb; + if (advancement != 1 && nb % 1000 != 0) + return *state; + + // If the limit is reach, interrupt the algorithm + double t = timer.time(); + if (advancement == 1 || (t - t_latest) > 0.25) + { + signaler->emit_signal (int (100. * advancement)); + + t_latest = t; + + if (signaler->is_canceled) + *state = false; + } + + return *state; + } + +}; + + +class Functor_with_signal_callback +{ +protected: + boost::shared_ptr m_callback; +public: + + Signal_callback* callback() { return m_callback.get(); } + + Functor_with_signal_callback() + : m_callback (new Signal_callback(true)) { } + + virtual void operator()() = 0; +}; + + +template +void run_with_qprogressdialog (Functor& functor, + const char* title, + QWidget* mainWindow) +{ + return run_with_qprogressdialog (functor, title, mainWindow); +} + +template +void run_with_qprogressdialog (Functor& functor, + const char* title, + QWidget* mainWindow) +{ + mainWindow->setEnabled(false); + QProgressDialog progress (QString(title), + QString("Cancel"), + 0, 100, + mainWindow); + progress.setMinimumDuration(0); + + Signal_callback* signal_callback = functor.callback(); + + QEventLoop::connect (signal_callback->signaler.get(), SIGNAL(progressChanged(int)), + &progress, SLOT(setValue(int))); + QEventLoop::connect (&progress, SIGNAL(canceled()), + signal_callback->signaler.get(), SLOT(cancel())); + +#ifdef CGAL_HAS_STD_THREADS + if (boost::is_convertible::value) + { + CGAL::cpp11::thread thread (functor); + + while (*signal_callback->latest_adv != 1. && + *signal_callback->state) + { + CGAL::cpp11::sleep_for (0.1); + QApplication::processEvents (); + } + + thread.join(); + } + else +#endif // Sequential version + { + progress.setWindowModality(Qt::WindowModal); + functor(); + } + + mainWindow->setEnabled(true); +} + + +#endif // RUN_WITH_QPROGRESSDIALOG_H diff --git a/STL_Extension/include/CGAL/Concurrent_compact_container.h b/STL_Extension/include/CGAL/Concurrent_compact_container.h index 353f1628b2e..3efc60d182b 100644 --- a/STL_Extension/include/CGAL/Concurrent_compact_container.h +++ b/STL_Extension/include/CGAL/Concurrent_compact_container.h @@ -37,7 +37,8 @@ #include #include -#include +#include +#include #include diff --git a/STL_Extension/include/CGAL/thread.h b/STL_Extension/include/CGAL/thread.h new file mode 100644 index 00000000000..a777d7e7286 --- /dev/null +++ b/STL_Extension/include/CGAL/thread.h @@ -0,0 +1,101 @@ +// Copyright (c) 2018 GeometryFactory Sarl (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 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$ +// SPDX-License-Identifier: LGPL-3.0+ +// +// Author(s) : Simon Giraudot + +#ifndef CGAL_THREAD_H +#define CGAL_THREAD_H + +#include + +/* + This file defines the following: + - CGAL::cpp11::thread + - CGAL::cpp11::atomic + - CGAL::cpp11::sleep_for + + It uses either TBB or STD depending on what's available: as TBB can + quite often override `std::thread`, it is possible that TBB will be + used instead of STD even if the real CXX11 `std::thread` is + available. + + As the conflicting API between TBB and STD can be quite complicated, + we offer a more generic `sleep_for()` function that takes + double-typed seconds as argument and deals with it. +*/ + +#if defined(CGAL_LINKED_WITH_TBB) +# include +# if TBB_IMPLEMENT_CPP0X +# include +# include +# include +# define CGAL_USE_TBB_THREADS 1 +# else +# define CGAL_USE_TBB_THREADS 0 +# endif +#else +# define CGAL_USE_TBB_THREADS 0 +#endif + +#if !CGAL_USE_TBB_THREADS +# include +# include +# include +#endif + +namespace CGAL { + +namespace cpp11 { + +#if CGAL_USE_TBB_THREADS + + using std::thread; // std::thread is declared by TBB if TBB_IMPLEMENT_CPP0X == 1 + using tbb::atomic; + + inline void sleep_for (double seconds) + { + // std::this_thread::sleep_for is declared by TBB if TBB_IMPLEMENT_CPP0X == 1 + // It takes interval_t types as argument (!= from the std norm) + std::this_thread::sleep_for(tbb::tick_count::interval_t(seconds)); + } + +#else + + using std::thread; + using std::atomic; + + inline void sleep_for (double seconds) + { + // MSVC2013 cannot call `sleep_for()` with other types than + // std::chrono::nanoseconds (bug in the implementation of the norm). + typedef std::chrono::nanoseconds nanoseconds; + nanoseconds ns (nanoseconds::rep (1000000000.0 * seconds)); + std::this_thread::sleep_for(ns); + } + +#endif + +} // cpp11 + + +} //namespace CGAL + +#undef CGAL_USE_TBB_THREADS + +#endif // CGAL_THREAD_H