diff --git a/Documentation/doc/biblio/cgal_manual.bib b/Documentation/doc/biblio/cgal_manual.bib
index e95c3acf18a..8b45267c361 100644
--- a/Documentation/doc/biblio/cgal_manual.bib
+++ b/Documentation/doc/biblio/cgal_manual.bib
@@ -725,6 +725,17 @@ note = {\url{ttp://hal.inria.fr/inria-00090522}}
,pages = "325--338"
}
+@article{cgal:gcsa-nasr-13,
+ journal = {Computer Graphics Forum},
+ title = {{Noise-Adaptive Shape Reconstruction from Raw Point Sets}},
+ author = {Simon Giraudot and David Cohen-Steiner and Pierre Alliez },
+ pages = {229-238},
+ volume= {32},
+ number= {5},
+ year = {2013},
+ DOI = {10.1111/cgf.12189},
+}
+
@manual{ cgal:g-gmpal-96
,author = "T. Granlund"
,title = "{GNU MP}, The {GNU} Multiple Precision Arithmetic Library,
diff --git a/Installation/changes.html b/Installation/changes.html
index 136e66e9479..4741500449a 100644
--- a/Installation/changes.html
+++ b/Installation/changes.html
@@ -266,6 +266,10 @@ and src/ directories).
Function CGAL::remove_outliers() has an
additional parameter based on a distance threshold to make it
easier and more intuitive to use.
+ New functions for automatic scale estimations: either a global
+ scale or multiple local scales can be estimated for both 2D and 3D
+ point sets based on the assumption that they sample a curve in 2D
+ or a surface in 3D.
diff --git a/Point_set_processing_3/doc/Point_set_processing_3/PackageDescription.txt b/Point_set_processing_3/doc/Point_set_processing_3/PackageDescription.txt
index 5e55112ba46..6d08de81d6f 100644
--- a/Point_set_processing_3/doc/Point_set_processing_3/PackageDescription.txt
+++ b/Point_set_processing_3/doc/Point_set_processing_3/PackageDescription.txt
@@ -36,6 +36,10 @@
- `CGAL::read_ply_custom_points()`
- `CGAL::read_xyz_points()`
- `CGAL::compute_average_spacing()`
+- `CGAL::estimate_global_k_neighbor_scale()`
+- `CGAL::estimate_global_range_scale()`
+- `CGAL::estimate_local_k_neighbor_scales()`
+- `CGAL::estimate_local_range_scales()`
- `CGAL::remove_outliers()`
- `CGAL::grid_simplify_point_set()`
- `CGAL::random_simplify_point_set()`
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 83e7a140978..9c09f824f3f 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
@@ -124,7 +124,7 @@ these attributes in user-defined containers.
\cgalExample{Point_set_processing_3/read_ply_points_with_colors_example.cpp}
-\section Point_set_processing_3Analysis Analysis
+\section Point_set_processing_3Spacing Average Spacing
Function `compute_average_spacing()` computes the average
spacing of all input points to their `k` nearest neighbor points,
@@ -147,6 +147,58 @@ found in other \cgal components:
- `bounding_box()`
- `Min_sphere_of_spheres_d`
+\section Point_set_processing_3Scale Automatic Scale Estimation
+
+Point sets are often used to sample objects with a higher dimension,
+typically a curve in 2D or a surface in 3D. In such cases, finding the
+scale of the objet is crucial, that is to say finding the minimal
+number of points (or the minimal local range) such that the subset of
+points has the appearance of a curve in 2D or a surface in 3D
+\cgalCite{cgal:gcsa-nasr-13}.
+
+\cgal provides 2 functions that automatically estimate the scale of a
+2D point set sampling a curve or a 3D point set sampling a surface:
+
+- `estimate_global_k_neighbor_scale()`
+- `estimate_global_range_scale()`
+
+Functions such as `jet_estimate_normals()` or
+`remove_outliers()` require a K neighbor scale while others such as
+`grid_simplify_point_set()` require a range
+scale. `vcm_estimate_normals()` is an example of a function that
+accepts both.
+
+In some specific cases, the scale of a point set might not be
+homogeneous (for example if the point set contains variable
+noise). \cgal also provides 2 functions that automatically estimate
+the scales of a point set at a set of user-defined query points:
+
+- `estimate_local_k_neighbor_scales()`
+- `estimate_local_range_scales()`
+
+The 4 functions presented here work both with 2D points and 3D points
+and they shouldn't be used if the point sets do not sample a curve in
+2D or a surface in 3D.
+
+\subsection Point_set_processing_3Example_scale_estimation_global Global Scale Example
+
+The following example reads a 3D point set in the `xyz` format and:
+
+- Estimates the K neighbor global scale
+- Uses it to smooth the point set
+- Estimates the range global scale
+- Uses it to simplify the point set
+
+\cgalExample{Point_set_processing_3/scale_estimation_example.cpp}
+
+\subsection Point_set_processing_3Example_scale_estimation_local Local Scales Example
+
+This second example generates a 2D point set sampling a circle with
+variable noise. It then estimates the scale at 3 different query
+points in the domain.
+
+\cgalExample{Point_set_processing_3/scale_estimation_2d_example.cpp}
+
\section Point_set_processing_3OutlierRemoval Outlier Removal
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 ff119f543c5..25aeee34542 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
@@ -2,6 +2,8 @@
\example Point_set_processing_3/read_write_xyz_point_set_example.cpp
\example Point_set_processing_3/read_ply_points_with_colors_example.cpp
\example Point_set_processing_3/average_spacing_example.cpp
+\example Point_set_processing_3/scale_estimation_example.cpp
+\example Point_set_processing_3/scale_estimation_2d_example.cpp
\example Point_set_processing_3/remove_outliers_example.cpp
\example Point_set_processing_3/grid_simplification_example.cpp
\example Point_set_processing_3/grid_simplify_indices.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 92dc32c4504..3954511d182 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
@@ -53,6 +53,8 @@ if ( CGAL_FOUND )
create_single_source_cgal_program( "read_write_xyz_point_set_example.cpp" )
create_single_source_cgal_program( "read_ply_points_with_colors_example.cpp" )
create_single_source_cgal_program( "remove_outliers_example.cpp" )
+ create_single_source_cgal_program( "scale_estimation_example.cpp" )
+ create_single_source_cgal_program( "scale_estimation_2d_example.cpp" )
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" )
diff --git a/Point_set_processing_3/examples/Point_set_processing_3/scale_estimation_2d_example.cpp b/Point_set_processing_3/examples/Point_set_processing_3/scale_estimation_2d_example.cpp
new file mode 100644
index 00000000000..9f1599eb2a7
--- /dev/null
+++ b/Point_set_processing_3/examples/Point_set_processing_3/scale_estimation_2d_example.cpp
@@ -0,0 +1,52 @@
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+// Types
+typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
+typedef Kernel::FT FT;
+typedef Kernel::Point_2 Point_2;
+
+int main (int, char**)
+{
+ std::vector samples;
+ samples.reserve (100000);
+
+ // Generate circle with gradually variable noise
+ // - noise-free for points with x close to (-1)
+ // - noisy for points with x close to (+1)
+ for (std::size_t i = 0; i < 100000; ++ i)
+ {
+ FT theta = CGAL::get_default_random().get_double(0, 2. * CGAL_PI);
+ FT noise = 0.5 * (std::cos(theta) + 1.) * CGAL::get_default_random().get_double(0., 0.2);
+ int mult = (CGAL::get_default_random().get_bool() ? 1 : -1);
+ samples.push_back (Point_2 (std::cos(theta) * (1. + mult * noise * noise),
+ std::sin(theta) * (1. + mult * noise * noise)));
+ }
+
+ // Search for local scales on 3 different locations
+ std::vector queries;
+ queries.reserve (3);
+ queries.push_back (Point_2 (-1., 0.));
+ queries.push_back (Point_2 (0., 1.));
+ queries.push_back (Point_2 (1., 0.));
+
+ std::vector k_scales;
+ CGAL::estimate_local_k_neighbor_scales (samples.begin(), samples.end(),
+ queries.begin(), queries.end(),
+ std::back_inserter (k_scales));
+
+ // Display results
+ std::cerr << "K-Scales found:" << std::endl
+ << " - On noise-free region: " << k_scales[0] << std::endl // Should be small
+ << " - On moderately noisy region: " << k_scales[1] << std::endl // Should be higher
+ << " - On very noisy region: " << k_scales[2] << std::endl; // Should be even higher
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/Point_set_processing_3/examples/Point_set_processing_3/scale_estimation_example.cpp b/Point_set_processing_3/examples/Point_set_processing_3/scale_estimation_example.cpp
new file mode 100644
index 00000000000..bac9900fecf
--- /dev/null
+++ b/Point_set_processing_3/examples/Point_set_processing_3/scale_estimation_example.cpp
@@ -0,0 +1,77 @@
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+
+// Concurrency
+#ifdef CGAL_LINKED_WITH_TBB
+typedef CGAL::Parallel_tag Concurrency_tag;
+#else
+typedef CGAL::Sequential_tag Concurrency_tag;
+#endif
+
+// Types
+typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
+typedef Kernel::FT FT;
+typedef Kernel::Point_3 Point_3;
+
+int main (int argc, char** argv)
+{
+
+ const char* fname = (argc>1)?argv[1]:"data/sphere_20k.xyz";
+
+ CGAL::Timer task_timer;
+
+ std::vector points;
+ std::ifstream stream(fname);
+
+ // read input
+ if (!(stream
+ && CGAL::read_xyz_points(stream, std::back_inserter(points))))
+ {
+ std::cerr << "Error: can't read input file" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // estimate k scale
+ task_timer.start();
+ std::size_t k_scale = CGAL::estimate_global_k_neighbor_scale (points.begin(), points.end());
+ task_timer.stop();
+
+ // Example: use estimated k as scale for jet smoothing
+ CGAL::jet_smooth_point_set
+ (points.begin(), points.end(),
+ static_cast(k_scale));
+
+ // estimate range scale
+ task_timer.start();
+ FT range_scale = CGAL::estimate_global_range_scale (points.begin(), points.end());
+ task_timer.stop();
+
+ // Example: use estimated range for grid simplification
+ points.erase (CGAL::grid_simplify_point_set (points.begin(), points.end(), range_scale),
+ points.end());
+
+
+ // print some informations on runtime
+ std::size_t memory = CGAL::Memory_sizer().virtual_size();
+ double time = task_timer.time();
+
+ std::cout << "Scales computed in " << time << " second(s) using "
+ << (memory>>20) << " MiB of memory:" << std::endl;
+ std::cout << " * Global K scale: " << k_scale << std::endl;
+ std::cout << " * Global range scale: " << range_scale << std::endl;
+
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/Point_set_processing_3/include/CGAL/estimate_scale.h b/Point_set_processing_3/include/CGAL/estimate_scale.h
new file mode 100644
index 00000000000..1ac7897e0f5
--- /dev/null
+++ b/Point_set_processing_3/include/CGAL/estimate_scale.h
@@ -0,0 +1,765 @@
+// Copyright (c) 2013 INRIA Sophia-Antipolis (France).
+// Copyright (c) 2016 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
+// 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) : Simon Giraudot
+
+#ifndef CGAL_ESTIMATE_SCALE_H
+#define CGAL_ESTIMATE_SCALE_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+
+
+namespace CGAL {
+
+
+// ----------------------------------------------------------------------------
+// Private section
+// ----------------------------------------------------------------------------
+/// \cond SKIP_IN_MANUAL
+namespace internal {
+
+template
+class Quick_multiscale_approximate_knn_distance
+{
+
+};
+
+
+template
+class Quick_multiscale_approximate_knn_distance
+{
+ typedef typename Kernel::FT FT;
+ typedef Search_traits_3 Tree_traits;
+ typedef Orthogonal_k_neighbor_search Neighbor_search;
+ typedef typename Neighbor_search::Tree Tree;
+ typedef typename Neighbor_search::iterator Iterator;
+
+ template
+ struct Pmap_unary_function : public std::unary_function
+ {
+ PointPMap point_pmap;
+ Pmap_unary_function (PointPMap point_pmap) : point_pmap (point_pmap) { }
+ const typename Kernel::Point_3& operator() (const ValueType& v) const { return get(point_pmap, v); }
+ };
+
+ std::size_t m_cluster_size;
+ std::vector m_trees;
+ std::vector m_weights;
+ std::vector m_precomputed_factor;
+
+public:
+
+ template
+ Quick_multiscale_approximate_knn_distance (InputIterator first,
+ InputIterator beyond,
+ PointPMap point_pmap,
+ std::size_t cluster_size = 25)
+ : m_cluster_size (cluster_size)
+ {
+ typedef Pmap_unary_function::value_type,
+ PointPMap> Unary_f;
+
+ m_trees.push_back (new Tree (boost::make_transform_iterator (first, Unary_f(point_pmap)),
+ boost::make_transform_iterator (beyond, Unary_f(point_pmap))));
+ m_weights.push_back (1.);
+ std::size_t nb_pts = m_trees[0]->size();
+
+ std::size_t nb_trees = 0;
+ while (nb_pts > m_cluster_size)
+ {
+ nb_trees ++;
+ nb_pts /= m_cluster_size;
+ }
+
+ m_trees.reserve (nb_trees);
+ m_weights.reserve (nb_trees);
+
+ InputIterator first_unused = beyond;
+
+ nb_pts = m_trees[0]->size();
+ for (std::size_t i = 1; i < nb_trees; ++ i)
+ {
+ first_unused
+ = CGAL::hierarchy_simplify_point_set (first, first_unused, point_pmap,
+ static_cast(m_cluster_size), 1./3.);
+
+ m_trees.push_back (new Tree(boost::make_transform_iterator (first, Unary_f(point_pmap)),
+ boost::make_transform_iterator (first_unused, Unary_f(point_pmap))));
+
+ m_weights.push_back (m_trees[0]->size() / (FT)(m_trees.back()->size()));
+ }
+ }
+
+ ~Quick_multiscale_approximate_knn_distance()
+ {
+ for (std::size_t i = 0; i < m_trees.size(); ++ i)
+ delete m_trees[i];
+ }
+
+ template
+ std::size_t compute_k_scale (InputIterator query, PointPMap point_pmap)
+ {
+ std::size_t out;
+ FT dummy;
+ compute_scale (query, point_pmap, out, dummy);
+ return out;
+ }
+
+ template
+ FT compute_range_scale (InputIterator query, PointPMap point_pmap)
+ {
+ std::size_t dummy;
+ FT out;
+ compute_scale (query, point_pmap, dummy, out);
+ return out;
+ }
+
+ void precompute_factors ()
+ {
+ FT nb = 0.;
+ for (std::size_t t = 0; t < m_trees.size(); ++ t)
+ {
+ std::size_t size = (t == (m_trees.size() - 1)
+ ? m_trees[t]->size()
+ : static_cast(m_weights[t+1] / m_weights[t]));
+ for (std::size_t i = (t == 0 ? 0 : 1); i < size; ++ i)
+ {
+ nb += m_weights[t];
+ if (nb < 6.) // do not consider values under 6
+ continue;
+ m_precomputed_factor.push_back (0.91666666 * std::log (nb));
+ }
+ }
+ }
+
+
+ template
+ void compute_scale (InputIterator query, PointPMap point_pmap,
+ std::size_t& k, FT& d)
+ {
+ if (m_precomputed_factor.empty())
+ precompute_factors();
+
+ k = 0;
+ d = 0.;
+
+ FT dist_min = (std::numeric_limits::max)();
+ FT sum_sq_distances = 0.;
+ FT nb = 0.;
+ std::size_t index = 0;
+ for (std::size_t t = 0; t < m_trees.size(); ++ t)
+ {
+ Neighbor_search search (*(m_trees[t]), get(point_pmap, *query),
+ static_cast((t == (m_trees.size() - 1)
+ ? m_trees[t]->size()
+ : m_weights[t+1] / m_weights[t])));
+ Iterator it = search.begin();
+
+ if (t != 0) // Skip first point except on first scale
+ ++ it;
+
+ for (; it != search.end(); ++ it)
+ {
+ sum_sq_distances += m_weights[t] * it->second;
+ nb += m_weights[t];
+
+ if (nb < 6.) // do not consider values under 6
+ continue;
+
+ // sqrt(sum_sq_distances / nb) / nb^(5/12)
+ // Computed in log space with precomputed factor for time optimization
+ FT dist = 0.5 * std::log (sum_sq_distances) - m_precomputed_factor[index ++];
+
+ if (dist < dist_min)
+ {
+ dist_min = dist;
+ k = (std::size_t)nb;
+ d = it->second;
+ }
+ }
+ }
+ }
+
+};
+
+
+template
+class Quick_multiscale_approximate_knn_distance
+{
+ typedef typename Kernel::FT FT;
+ typedef CGAL::Point_set_2 Point_set;
+ typedef typename Point_set::Vertex_handle Vertex_handle;
+
+ template
+ struct Pmap_unary_function : public std::unary_function
+ {
+ PointPMap point_pmap;
+ Pmap_unary_function (PointPMap point_pmap) : point_pmap (point_pmap) { }
+ const typename Kernel::Point_2& operator() (const ValueType& v) const { return get(point_pmap, v); }
+ };
+
+ template
+ struct Pmap_to_3d
+ {
+ PointPMap point_pmap;
+ typedef typename Kernel::Point_3 value_type;
+ typedef const value_type& reference;
+ typedef typename boost::property_traits::key_type key_type;
+ typedef boost::lvalue_property_map_tag category;
+ Pmap_to_3d () { }
+ Pmap_to_3d (PointPMap point_pmap)
+ : point_pmap (point_pmap) { }
+ friend inline value_type get (const Pmap_to_3d& ppmap, key_type i)
+ {
+ typename Kernel::Point_2 p2 = get(ppmap.point_pmap, i);
+ return value_type (p2.x(), p2.y(), 0.);
+ }
+
+ };
+
+ struct Sort_by_distance_to_point
+ {
+ const typename Kernel::Point_2& ref;
+ Sort_by_distance_to_point (const typename Kernel::Point_2& ref) : ref (ref) { }
+ bool operator() (const Vertex_handle& a, const Vertex_handle& b)
+ {
+ return (CGAL::squared_distance (a->point(), ref)
+ < CGAL::squared_distance (b->point(), ref));
+ }
+ };
+
+
+ std::size_t m_cluster_size;
+ std::vector m_point_sets;
+ std::vector m_weights;
+ std::vector m_precomputed_factor;
+
+public:
+
+ template
+ Quick_multiscale_approximate_knn_distance (InputIterator first,
+ InputIterator beyond,
+ PointPMap point_pmap,
+ std::size_t cluster_size = 25)
+ : m_cluster_size (cluster_size)
+ {
+ typedef Pmap_unary_function::value_type,
+ PointPMap> Unary_f;
+
+ m_point_sets.push_back (new Point_set (boost::make_transform_iterator (first, Unary_f(point_pmap)),
+ boost::make_transform_iterator (beyond, Unary_f(point_pmap))));
+ m_weights.push_back (1.);
+
+ std::size_t nb_pts = m_point_sets[0]->number_of_vertices();
+ std::size_t nb_trees = 0;
+ while (nb_pts > m_cluster_size)
+ {
+ nb_trees ++;
+ nb_pts /= m_cluster_size;
+ }
+
+ m_point_sets.reserve (nb_trees);
+ m_weights.reserve (nb_trees);
+
+ InputIterator first_unused = beyond;
+ nb_pts = m_point_sets[0]->number_of_vertices();
+
+ for (std::size_t i = 1; i < nb_trees; ++ i)
+ {
+ first_unused
+ = CGAL::hierarchy_simplify_point_set (first, first_unused, Pmap_to_3d (point_pmap),
+ static_cast(m_cluster_size), 1./3.);
+
+ m_point_sets.push_back (new Point_set (boost::make_transform_iterator (first, Unary_f(point_pmap)),
+ boost::make_transform_iterator (first_unused, Unary_f(point_pmap))));
+
+ m_weights.push_back (nb_pts / (FT)(m_point_sets.back()->number_of_vertices()));
+ }
+
+ m_cluster_size = cluster_size;
+ }
+
+ ~Quick_multiscale_approximate_knn_distance()
+ {
+ for (std::size_t i = 0; i < m_point_sets.size(); ++ i)
+ delete m_point_sets[i];
+ }
+
+ template
+ std::size_t compute_k_scale (InputIterator query, PointPMap point_pmap)
+ {
+ std::size_t out;
+ FT dummy;
+ compute_scale (query, point_pmap, out, dummy);
+ return out;
+ }
+
+ template
+ FT compute_range_scale (InputIterator query, PointPMap point_pmap)
+ {
+ std::size_t dummy;
+ FT out;
+ compute_scale (query, point_pmap, dummy, out);
+ return out;
+ }
+
+ void precompute_factors ()
+ {
+ FT nb = 0.;
+ for (std::size_t t = 0; t < m_point_sets.size(); ++ t)
+ {
+ std::size_t size = (t == m_point_sets.size() - 1
+ ? m_point_sets[t]->number_of_vertices()
+ : static_cast(m_weights[t+1] / m_weights[t]));
+ for (std::size_t i = (t == 0 ? 0 : 1); i < size; ++ i)
+ {
+ nb += m_weights[t];
+ if (nb < 6.) // do not consider values under 6
+ continue;
+ m_precomputed_factor.push_back (1.25 * std::log (nb));
+ }
+ }
+ }
+
+ template
+ void compute_scale (InputIterator query, PointPMap point_pmap,
+ std::size_t& k, FT& d)
+ {
+ if (m_precomputed_factor.empty())
+ precompute_factors();
+
+ k = 0;
+ d = 0.;
+
+ FT dist_min = (std::numeric_limits::max)();
+ FT sum_sq_distances = 0.;
+ FT nb = 0.;
+ std::size_t index = 0;
+
+ const typename Kernel::Point_2& pquery = get(point_pmap, *query);
+ for (std::size_t t = 0; t < m_point_sets.size(); ++ t)
+ {
+ std::size_t size = ((t == m_point_sets.size() - 1)
+ ? m_point_sets[t]->number_of_vertices()
+ : static_cast(m_weights[t+1] / m_weights[t]));
+ std::vector neighbors;
+ neighbors.reserve (size);
+ m_point_sets[t]->nearest_neighbors (pquery, size, std::back_inserter (neighbors));
+
+ std::sort (neighbors.begin(), neighbors.end(),
+ Sort_by_distance_to_point (pquery));
+ for (std::size_t n = (t == 0 ? 0 : 1); n < neighbors.size(); ++ n)
+ {
+ FT sq_dist = CGAL::squared_distance (pquery, neighbors[n]->point());
+
+ sum_sq_distances += m_weights[t] * sq_dist;
+ nb += m_weights[t];
+
+ if (nb < 6.) // do not consider values under 6
+ continue;
+
+ // sqrt(sum_sq_distances / nb) / nb^(3/4)
+ // Computed in log space with precomputed factor for time optimization
+ FT dist = 0.5 * std::log (sum_sq_distances) - m_precomputed_factor[index ++];
+
+ if (dist < dist_min)
+ {
+ dist_min = dist;
+ k = (std::size_t)nb;
+ d = sq_dist;
+ }
+ }
+ }
+ }
+
+};
+
+} /* namespace internal */
+/// \endcond
+
+
+
+// ----------------------------------------------------------------------------
+// Public section
+// ----------------------------------------------------------------------------
+
+/// \ingroup PkgPointSetProcessing
+
+/// Estimates the local scale in a K nearest neighbors sense on a set
+/// of user-defined query points. The computed scales correspond to
+/// the smallest scales such that the K subsets of points have the
+/// appearance of a surface in 3D or the appearance of a curve in 2D
+/// (see \ref Point_set_processing_3Scale).
+///
+///
+/// @tparam SamplesInputIterator iterator over input sample points.
+/// @tparam SamplesPointPMap is a model of `ReadablePropertyMap` with
+/// value type `Point_3` or `Point_2`. It can
+/// be omitted if the value type of `SamplesInputIterator` is
+/// convertible to `Point_3` or to `Point_2`.
+/// @tparam QueriesInputIterator iterator over points where scale
+/// should be computed.
+/// @tparam QueriesInputIterator is a model of `ReadablePropertyMap`
+/// with value type `Point_3` or `Point_2`. It
+/// can be omitted if the value type of `QueriesInputIterator` is
+/// convertible to `Point_3` or to `Point_2`.
+/// @tparam OutputIterator is used to store the computed scales. It accepts
+/// values of type `std::size_t`.
+/// @tparam Kernel Geometric traits class. It can be omitted and
+/// deduced automatically from the value type of `SamplesPointPMap`.
+///
+/// @note This function accepts both 2D and 3D points, but sample
+/// points and query must have the same dimension.
+
+// This variant requires all parameters.
+template
+OutputIterator
+estimate_local_k_neighbor_scales(
+ SamplesInputIterator first, ///< iterator over the first input sample.
+ SamplesInputIterator beyond, ///< past-the-end iterator over the input samples.
+ SamplesPointPMap samples_pmap, ///< property map: value_type of InputIterator -> Point_3 or Point_2
+ QueriesInputIterator first_query, ///< iterator over the first point where scale must be estimated
+ QueriesInputIterator beyond_query, ///< past-the-end iterator over the points where scale must be estimated
+ QueriesPointPMap queries_pmap, ///< property map: value_type of InputIterator -> Point_3 or Point_2
+ OutputIterator output, ///< output iterator to store the computed scales
+ const Kernel& /*kernel*/) ///< geometric traits.
+{
+ typedef typename boost::property_traits::value_type Point_d;
+
+ // Build multi-scale KD-tree
+ internal::Quick_multiscale_approximate_knn_distance kdtree (first, beyond, samples_pmap);
+
+ // Compute local scales everywhere
+ for (QueriesInputIterator it = first_query; it != beyond_query; ++ it)
+ *(output ++) = kdtree.compute_k_scale (it, queries_pmap);
+
+ return output;
+}
+
+
+/// \ingroup PkgPointSetProcessing
+
+/// Estimates the global scale in a K nearest neighbors sense. The
+/// computed scale corresponds to the smallest scale such that the K
+/// subsets of points have the appearance of a surface in 3D or the
+/// appearance of a curve in 2D (see \ref Point_set_processing_3Scale).
+///
+///
+/// @tparam InputIterator iterator over input points.
+/// @tparam PointPMap is a model of `ReadablePropertyMap` with
+/// value type `Point_3` or `Point_2`. It can
+/// be omitted if the value type of `InputIterator` is
+/// convertible to `Point_3` or to `Point_2`.
+/// @tparam Kernel Geometric traits class. It can be omitted and
+/// deduced automatically from the value type of `PointPMap`.
+///
+/// @note This function accepts both 2D and 3D points.
+///
+/// @return The estimated scale in the K nearest neighbors sense.
+// This variant requires all parameters.
+template
+std::size_t
+estimate_global_k_neighbor_scale(
+ InputIterator first, ///< iterator over the first input point.
+ InputIterator beyond, ///< past-the-end iterator over the input points.
+ PointPMap point_pmap, ///< property map: value_type of InputIterator -> Point_3 or Point_2
+ const Kernel& kernel) ///< geometric traits.
+{
+ std::vector scales;
+ estimate_local_k_neighbor_scales (first, beyond, point_pmap,
+ first, beyond, point_pmap,
+ std::back_inserter (scales),
+ kernel);
+ std::sort (scales.begin(), scales.end());
+ return scales[scales.size() / 2];
+}
+
+
+/// \ingroup PkgPointSetProcessing
+
+/// Estimates the local scale in a range sense on a set of
+/// user-defined query points. The computed scales correspond to the
+/// smallest scales such that the subsets of points included in the
+/// sphere range have the appearance of a surface in 3D or the
+/// appearance of a curve in 2D (see \ref Point_set_processing_3Scale).
+///
+///
+/// @tparam SamplesInputIterator iterator over input sample points.
+/// @tparam SamplesPointPMap is a model of `ReadablePropertyMap` with
+/// value type `Point_3` or `Point_2`. It can
+/// be omitted if the value type of `SamplesInputIterator` is
+/// convertible to `Point_3` or to `Point_2`.
+/// @tparam QueriesInputIterator iterator over points where scale
+/// should be computed.
+/// @tparam QueriesInputIterator is a model of `ReadablePropertyMap`
+/// with value type `Point_3` or `Point_2`. It
+/// can be omitted if the value type of `QueriesInputIterator` is
+/// convertible to `Point_3` or to `Point_2`.
+/// @tparam OutputIterator is used to store the computed scales. It accepts
+/// values of type `Kernel::FT`.
+/// @tparam Kernel Geometric traits class. It can be omitted and
+/// deduced automatically from the value type of `SamplesPointPMap`.
+///
+/// @note This function accepts both 2D and 3D points, but sample
+/// points and query must have the same dimension.
+
+// This variant requires all parameters.
+template
+OutputIterator
+estimate_local_range_scales(
+ SamplesInputIterator first, ///< iterator over the first input sample.
+ SamplesInputIterator beyond, ///< past-the-end iterator over the input samples.
+ SamplesPointPMap samples_pmap, ///< property map: value_type of InputIterator -> Point_3 or Point_2
+ QueriesInputIterator first_query, ///< iterator over the first point where scale must be estimated
+ QueriesInputIterator beyond_query, ///< past-the-end iterator over the points where scale must be estimated
+ QueriesPointPMap queries_pmap, ///< property map: value_type of InputIterator -> Point_3 or Point_2
+ OutputIterator output, ///< output iterator to store the computed scales
+ const Kernel& /*kernel*/) ///< geometric traits.
+{
+ typedef typename boost::property_traits::value_type Point_d;
+
+ // Build multi-scale KD-tree
+ internal::Quick_multiscale_approximate_knn_distance kdtree (first, beyond, samples_pmap);
+
+ // Compute local scales everywhere
+ for (QueriesInputIterator it = first_query; it != beyond_query; ++ it)
+ *(output ++) = kdtree.compute_range_scale (it, queries_pmap);
+
+ return output;
+}
+
+
+/// \ingroup PkgPointSetProcessing
+
+/// Estimates the global scale in a range sense. The computed scale
+/// corresponds to the smallest scale such that the subsets of points
+/// inside the sphere range have the appearance of a surface in 3D or
+/// the appearance of a curve in 2D (see \ref Point_set_processing_3Scale).
+///
+///
+/// @tparam InputIterator iterator over input points.
+/// @tparam PointPMap is a model of `ReadablePropertyMap` with
+/// value type `Point_3` or `Point_2`. It can
+/// be omitted if the value type of `InputIterator` is
+/// convertible to `Point_3` or to `Point_2`.
+/// @tparam Kernel Geometric traits class. It can be omitted and
+/// deduced automatically from the value type of `PointPMap`.
+///
+/// @note This function accepts both 2D and 3D points.
+///
+/// @return The estimated scale in the range sense.
+// This variant requires all parameters.
+template
+typename Kernel::FT
+estimate_global_range_scale(
+ InputIterator first, ///< iterator over the first input point.
+ InputIterator beyond, ///< past-the-end iterator over the input points.
+ PointPMap point_pmap, ///< property map: value_type of InputIterator -> Point_3 or Point_3
+ const Kernel& kernel) ///< geometric traits.
+{
+ std::vector scales;
+ estimate_local_range_scales (first, beyond, point_pmap,
+ first, beyond, point_pmap,
+ std::back_inserter (scales),
+ kernel);
+ std::sort (scales.begin(), scales.end());
+ return std::sqrt (scales[scales.size() / 2]);
+}
+
+
+// ----------------------------------------------------------------------------
+// Useful overloads
+// ----------------------------------------------------------------------------
+/// \cond SKIP_IN_MANUAL
+
+template
+OutputIterator
+estimate_local_k_neighbor_scales(
+ SamplesInputIterator first, ///< iterator over the first input sample.
+ SamplesInputIterator beyond, ///< past-the-end iterator over the input samples.
+ SamplesPointPMap samples_pmap, ///< property map: value_type of InputIterator -> Point_3 or Point_2
+ QueriesInputIterator first_query, ///< iterator over the first point where scale must be estimated
+ QueriesInputIterator beyond_query, ///< past-the-end iterator over the points where scale must be estimated
+ QueriesPointPMap queries_pmap, ///< property map: value_type of InputIterator -> Point_3 or Point_2
+ OutputIterator output) ///< output iterator to store the computed scales
+{
+ typedef typename boost::property_traits::value_type Point;
+ typedef typename Kernel_traits::Kernel Kernel;
+ return estimate_local_k_neighbor_scales (first, beyond, samples_pmap, first_query, beyond_query,
+ queries_pmap, output, Kernel());
+}
+
+template
+OutputIterator
+estimate_local_k_neighbor_scales(
+ SamplesInputIterator first, ///< iterator over the first input sample.
+ SamplesInputIterator beyond, ///< past-the-end iterator over the input samples.
+ QueriesInputIterator first_query, ///< iterator over the first point where scale must be estimated
+ QueriesInputIterator beyond_query, ///< past-the-end iterator over the points where scale must be estimated
+ OutputIterator output) ///< output iterator to store the computed scales
+{
+ return estimate_local_k_neighbor_scales
+ (first, beyond,
+ make_identity_property_map (typename std::iterator_traits::value_type()),
+ first_query, beyond_query,
+ make_identity_property_map (typename std::iterator_traits::value_type()),
+ output);
+ }
+
+
+template
+std::size_t
+estimate_global_k_neighbor_scale(
+ InputIterator first, ///< iterator over the first input point.
+ InputIterator beyond, ///< past-the-end iterator over the input points.
+ PointPMap point_pmap) ///< property map: value_type of InputIterator -> Point_3 or Point_2
+{
+ typedef typename boost::property_traits::value_type Point;
+ typedef typename Kernel_traits::Kernel Kernel;
+ return estimate_global_k_neighbor_scale (first, beyond, point_pmap, Kernel());
+}
+
+template
+std::size_t
+estimate_global_k_neighbor_scale(
+ InputIterator first, ///< iterator over the first input point.
+ InputIterator beyond) ///< past-the-end iterator over the input points.
+{
+ return estimate_global_k_neighbor_scale
+ (first, beyond, make_identity_property_map (typename std::iterator_traits::value_type()));
+}
+
+
+template
+OutputIterator
+estimate_local_range_scales(
+ SamplesInputIterator first, ///< iterator over the first input sample.
+ SamplesInputIterator beyond, ///< past-the-end iterator over the input samples.
+ SamplesPointPMap samples_pmap, ///< property map: value_type of InputIterator -> Point_3 or Point_2
+ QueriesInputIterator first_query, ///< iterator over the first point where scale must be estimated
+ QueriesInputIterator beyond_query, ///< past-the-end iterator over the points where scale must be estimated
+ QueriesPointPMap queries_pmap, ///< property map: value_type of InputIterator -> Point_3 or Point_2
+ OutputIterator output) ///< output iterator to store the computed scales
+{
+ typedef typename boost::property_traits::value_type Point;
+ typedef typename Kernel_traits::Kernel Kernel;
+ return estimate_local_range_scales(first, beyond, samples_pmap, first_query, beyond_query,
+ queries_pmap, output, Kernel());
+}
+
+
+template
+OutputIterator
+estimate_local_range_scales(
+ SamplesInputIterator first, ///< iterator over the first input sample.
+ SamplesInputIterator beyond, ///< past-the-end iterator over the input samples.
+ QueriesInputIterator first_query, ///< iterator over the first point where scale must be estimated
+ QueriesInputIterator beyond_query, ///< past-the-end iterator over the points where scale must be estimated
+ OutputIterator output) ///< output iterator to store the computed scales
+{
+ return estimate_local_range_scales
+ (first, beyond,
+ make_identity_property_map (typename std::iterator_traits::value_type()),
+ first_query, beyond_query,
+ make_identity_property_map (typename std::iterator_traits::value_type()),
+ output);
+}
+
+
+
+template
+typename Kernel_traits::value_type>::Kernel::FT
+estimate_global_range_scale(
+ InputIterator first, ///< iterator over the first input point.
+ InputIterator beyond, ///< past-the-end iterator over the input points.
+ PointPMap point_pmap) ///< property map: value_type of InputIterator -> Point_3 or Point_3
+{
+ typedef typename boost::property_traits::value_type Point;
+ typedef typename Kernel_traits::Kernel Kernel;
+ return estimate_global_range_scale (first, beyond, point_pmap, Kernel());
+}
+
+
+
+template
+typename Kernel_traits::value_type>::Kernel::FT
+estimate_global_range_scale(
+ InputIterator first, ///< iterator over the first input point.
+ InputIterator beyond) ///< past-the-end iterator over the input points.
+{
+ return estimate_global_range_scale
+ (first, beyond, make_identity_property_map (typename std::iterator_traits::value_type()));
+
+}
+/// \endcond
+
+} //namespace CGAL
+
+#endif // CGAL_ESTIMATE_SCALE_3_H