From 74070d3356b91d4326143cd690ce7eba4e3cbcfa Mon Sep 17 00:00:00 2001 From: Simon Giraudot Date: Mon, 9 Mar 2020 11:56:03 +0100 Subject: [PATCH 01/23] First version of clustering algorithm --- .../CGAL/boost/graph/named_params_helper.h | 13 ++ .../CGAL/boost/graph/parameters_interface.h | 1 + .../Point_set_processing_3/CMakeLists.txt | 5 +- .../clustering_example.cpp | 56 +++++ .../include/CGAL/cluster_point_set.h | 199 ++++++++++++++++++ 5 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 Point_set_processing_3/examples/Point_set_processing_3/clustering_example.cpp create mode 100644 Point_set_processing_3/include/CGAL/cluster_point_set.h diff --git a/BGL/include/CGAL/boost/graph/named_params_helper.h b/BGL/include/CGAL/boost/graph/named_params_helper.h index 4c432a311c6..f5cdbf4ca63 100644 --- a/BGL/include/CGAL/boost/graph/named_params_helper.h +++ b/BGL/include/CGAL/boost/graph/named_params_helper.h @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -427,6 +428,18 @@ namespace CGAL { > ::type type; }; + template + class GetNeighborhood + { + public: + typedef Emptyset_iterator Empty; + typedef typename internal_np::Lookup_named_param_def < + internal_np::neighborhood_t, + NamedParameters, + Empty//default + > ::type type; + }; + } // namespace Point_set_processing_3 template diff --git a/BGL/include/CGAL/boost/graph/parameters_interface.h b/BGL/include/CGAL/boost/graph/parameters_interface.h index ebdce042e75..4e3232830b1 100644 --- a/BGL/include/CGAL/boost/graph/parameters_interface.h +++ b/BGL/include/CGAL/boost/graph/parameters_interface.h @@ -122,6 +122,7 @@ CGAL_add_named_parameter(plane_index_t, plane_index_map, plane_index_map) CGAL_add_named_parameter(select_percentage_t, select_percentage, select_percentage) CGAL_add_named_parameter(require_uniform_sampling_t, require_uniform_sampling, require_uniform_sampling) CGAL_add_named_parameter(point_is_constrained_t, point_is_constrained, point_is_constrained_map) +CGAL_add_named_parameter(neighborhood_t, neighborhood, neighborhood) // List of named parameters used in Surface_mesh_approximation package CGAL_add_named_parameter(verbose_level_t, verbose_level, verbose_level) 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 c2da826d49b..2b174b46f19 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 @@ -54,6 +54,7 @@ if ( CGAL_FOUND ) 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" ) + create_single_source_cgal_program( "clustering_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} ) @@ -66,6 +67,7 @@ if ( CGAL_FOUND ) include_directories(${LASZIP_INCLUDE_DIR}) create_single_source_cgal_program( "read_las_example.cpp" CXX_FEATURES ${needed_cxx_features} ) target_link_libraries(read_las_example PRIVATE ${LASLIB_LIBRARIES}) + target_link_libraries(clustering_example PRIVATE ${LASLIB_LIBRARIES}) else() message(STATUS "NOTICE : the LAS reader test requires LASlib and will not be compiled.") endif() @@ -92,7 +94,8 @@ if ( CGAL_FOUND ) normals_example jet_smoothing_example normal_estimation - callback_example) + callback_example + clustering_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/clustering_example.cpp b/Point_set_processing_3/examples/Point_set_processing_3/clustering_example.cpp new file mode 100644 index 00000000000..ad6712a4670 --- /dev/null +++ b/Point_set_processing_3/examples/Point_set_processing_3/clustering_example.cpp @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include + +#include +#include + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using Point_3 = Kernel::Point_3; +using Point_set = CGAL::Point_set_3; + +int main (int argc, char** argv) +{ + std::ifstream ifile (argv[1], std::ios_base::binary); + Point_set points; + ifile >> points; + + Point_set::Property_map cluster_map = points.add_property_map ("cluster", -1).first; + + double spacing = CGAL::compute_average_spacing (points, 12); + + std::cerr << "Spacing = " << spacing << std::endl; + + std::vector > adjacencies; + + CGAL::Real_timer t; + t.start(); + std::size_t nb_clusters + = CGAL::cluster_point_set (points, cluster_map, 0, + points.parameters().neighbor_radius(spacing). + neighborhood (std::back_inserter (adjacencies))); + t.stop(); + std::cerr << "Found " << nb_clusters << " clusters with " << adjacencies.size() + << " adjacencies in " << t.time() << " seconds" << std::endl; + + Point_set::Property_map red = points.add_property_map("red", 0).first; + Point_set::Property_map green = points.add_property_map("green", 0).first; + Point_set::Property_map blue = points.add_property_map("blue", 0).first; + + for (Point_set::Index idx : points) + { + CGAL::Random rand (cluster_map[idx]); + + red[idx] = rand.get_int(64, 192); + green[idx] = rand.get_int(64, 192); + blue[idx] = rand.get_int(64, 192); + } + + std::ofstream ofile ("out.ply", std::ios_base::binary); + CGAL::set_binary_mode (ofile); + ofile << points; + + return EXIT_SUCCESS; +} diff --git a/Point_set_processing_3/include/CGAL/cluster_point_set.h b/Point_set_processing_3/include/CGAL/cluster_point_set.h new file mode 100644 index 00000000000..c15c38cec2c --- /dev/null +++ b/Point_set_processing_3/include/CGAL/cluster_point_set.h @@ -0,0 +1,199 @@ +// Copyright (c) 2020 GeometryFactory Sarl (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Simon Giraudot + +#ifndef CGAL_CLUSTER_POINT_SET_H +#define CGAL_CLUSTER_POINT_SET_H + +#include + +#include +#include +#include +#include + +#include + +namespace CGAL +{ + +namespace Point_set_processing_3 +{ + +namespace internal +{ + +// Trick to both compile version with Emptyset_iterator and with +// user-provided OutputIterator. Many output iterators (such as +// `std::back_insert_iterator`) cannot be default constructed, which +// makes the mechanism `choose_param(get_param(...),Default())` fails. +template +OutputIterator get_neighborhood (const NamedParameters& np, OutputIterator*) +{ + return CGAL::parameters::get_parameter(np, internal_np::neighborhood); +} + +template +CGAL::Emptyset_iterator get_neighborhood (const NamedParameters&, CGAL::Emptyset_iterator*) +{ + return CGAL::Emptyset_iterator(); +} + +} // namespace internal + +} // namespace Point_set_processing_3 + +template +std::size_t cluster_point_set (PointRange& points, + ClusterMap cluster_map, + unsigned int k, + const NamedParameters& np) +{ + using parameters::choose_parameter; + using parameters::get_parameter; + + // basic geometric types + typedef typename PointRange::iterator iterator; + typedef typename iterator::value_type value_type; + typedef typename Point_set_processing_3::GetPointMap::type PointMap; + typedef typename Point_set_processing_3::GetK::Kernel Kernel; + typedef typename Point_set_processing_3::GetNeighborhood::type Neighborhood; + typedef typename GetSvdTraits::type SvdTraits; + + CGAL_static_assertion_msg(!(boost::is_same::NoTraits>::value), + "Error: no SVD traits"); + + PointMap point_map = choose_parameter(get_parameter(np, internal_np::point_map), PointMap()); + typename Kernel::FT neighbor_radius = choose_parameter(get_parameter(np, internal_np::neighbor_radius), + typename Kernel::FT(0)); + typename Kernel::FT factor = choose_parameter(get_parameter(np, internal_np::attraction_factor), + typename Kernel::FT(2)); + + const std::function& callback = choose_parameter(get_parameter(np, internal_np::callback), + std::function()); + + double callback_factor = 1.; + if (!std::is_same::Empty>::value) + callback_factor = 0.5; + + typedef typename Kernel::Point_3 Point; + + // types for K nearest neighbors search structure + typedef Point_set_processing_3::internal::Neighbor_query Neighbor_query; + + // precondition: at least one element in the container. + // to fix: should have at least three distinct points + // but this is costly to check + CGAL_point_set_processing_precondition(points.begin() != points.end()); + + // precondition: at least 2 nearest neighbors + CGAL_point_set_processing_precondition(k >= 2); + + // Init cluster map with -1 + for (const value_type& p : points) + put (cluster_map, p, -1); + + Neighbor_query neighbor_query (points, point_map); + + std::queue todo; + std::size_t nb_clusters = 0; + + // Flooding algorithm from each point + std::size_t done = 0; + std::size_t size = points.size(); + + for (iterator it = points.begin(); it != points.end(); ++ it) + { + const value_type& p = *it; + + if (get (cluster_map, p) != -1) + continue; + + todo.push (it); + + while (!todo.empty()) + { + iterator current = todo.front(); + todo.pop(); + + if (get (cluster_map, *current) != -1) + continue; + + put (cluster_map, *current, nb_clusters); + ++ done; + + if (callback && !callback (callback_factor * (done + 1) / double(size))) + return (nb_clusters + 1); + + neighbor_query.get_iterators (get (point_map, *current), k, neighbor_radius, + boost::make_function_output_iterator + ([&](const iterator& it) { todo.push(it); })); + + } + + ++ nb_clusters; + } + + if (!std::is_same::Empty>::value) + { + Neighborhood neighborhood = Point_set_processing_3::internal::get_neighborhood(np, (Neighborhood*)(nullptr)); + k *= factor; + neighbor_radius *= factor; + + std::vector neighbors; + std::vector > adjacencies; + + done = 0; + for (const value_type& p : points) + { + std::size_t c0 = get (cluster_map, p); + + neighbors.clear(); + neighbor_query.get_iterators (get (point_map, p), k, neighbor_radius, + std::back_inserter (neighbors)); + + for (const iterator& it : neighbors) + { + std::size_t c1 = get (cluster_map, *it); + if (c0 < c1) + adjacencies.push_back (std::make_pair (c0, c1)); + else if (c0 > c1) + adjacencies.push_back (std::make_pair (c1, c0)); + // else c0 == c1, ignore + } + + ++ done; + if (callback && !callback (callback_factor + callback_factor * (done + 1) / double(size))) + return nb_clusters; + } + std::sort (adjacencies.begin(), adjacencies.end()); + auto last = std::unique (adjacencies.begin(), adjacencies.end()); + std::copy (adjacencies.begin(), last, neighborhood); + } + + return nb_clusters; +} + +template +std::size_t cluster_point_set (PointRange& points, + ClusterMap cluster_map, + unsigned int k) +{ + return cluster_point_set (points, cluster_map, k, + CGAL::Point_set_processing_3::parameters::all_default(points)); +} + +} // namespace CGAL + + +#endif // CGAL_CLUSTER_POINT_SET_H From 2bfac0bc86a9c81955f6c3398b64772ede7765a3 Mon Sep 17 00:00:00 2001 From: Simon Giraudot Date: Mon, 9 Mar 2020 12:44:49 +0100 Subject: [PATCH 02/23] Reference manual for cluster_point_set() --- .../PackageDescription.txt | 1 + .../include/CGAL/cluster_point_set.h | 45 +++++++++++++++++++ 2 files changed, 46 insertions(+) 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 dc3bf7ab968..cfe7e23f2a5 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 @@ -52,6 +52,7 @@ format. - `CGAL::estimate_local_k_neighbor_scales()` - `CGAL::estimate_local_range_scales()` - `CGAL::remove_outliers()` +- `CGAL::cluster_point_set()` - `CGAL::grid_simplify_point_set()` - `CGAL::random_simplify_point_set()` - `CGAL::hierarchy_simplify_point_set()` diff --git a/Point_set_processing_3/include/CGAL/cluster_point_set.h b/Point_set_processing_3/include/CGAL/cluster_point_set.h index c15c38cec2c..b1d30c8654f 100644 --- a/Point_set_processing_3/include/CGAL/cluster_point_set.h +++ b/Point_set_processing_3/include/CGAL/cluster_point_set.h @@ -24,6 +24,8 @@ namespace CGAL { + +/// \cond SKIP_IN_MANUAL namespace Point_set_processing_3 { @@ -49,7 +51,47 @@ CGAL::Emptyset_iterator get_neighborhood (const NamedParameters&, CGAL::Emptyset } // namespace internal } // namespace Point_set_processing_3 +/// \endcond +// ---------------------------------------------------------------------------- +// Public section +// ---------------------------------------------------------------------------- + +/** + \ingroup PkgPointSetProcessing3Algorithms + Identifies simply connected clusters on a nearest neighbors graph. + + \tparam PointRange is a model of `Range`. The value type of its + iterator is the key type of the named parameter `point_map`. + \tparam ClusterMap is a model of `ReadWritePropertyMap` with value + type `std::size_t`. + + \param points input point range. + \param cluster_map maps each point to the index of the cluster it belongs to. + \param k number of neighbors. + \param np optional sequence of \ref psp_namedparameters "Named Parameters" among the ones listed below. + + \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 + `std::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 number of already + computed clusters is returned.\cgalParamEnd + \cgalParamBegin{neighbor_radius} spherical neighborhood radius. If + provided, the neighborhood of a query point is computed with a fixed spherical + radius instead of a fixed number of neighbors. In that case, the parameter + `k` is used as a limit on the number of points returned by each spherical + query (to avoid overly large number of points in high density areas). If no + limit is wanted, use `k=0`.\cgalParamEnd + \cgalParamBegin{geom_traits} an instance of a geometric traits class, model of `Kernel`\cgalParamEnd + \cgalNamedParamsEnd + + \return the number of clusters identified. +*/ template std::size_t cluster_point_set (PointRange& points, ClusterMap cluster_map, @@ -184,6 +226,8 @@ std::size_t cluster_point_set (PointRange& points, return nb_clusters; } +/// \cond SKIP_IN_MANUAL +// overload with default NP template std::size_t cluster_point_set (PointRange& points, ClusterMap cluster_map, @@ -192,6 +236,7 @@ std::size_t cluster_point_set (PointRange& points, return cluster_point_set (points, cluster_map, k, CGAL::Point_set_processing_3::parameters::all_default(points)); } +/// \endcond } // namespace CGAL From 61ee057c40a35813d036c6f8b67f3a7432fb77c2 Mon Sep 17 00:00:00 2001 From: Simon Giraudot Date: Mon, 9 Mar 2020 16:41:01 +0100 Subject: [PATCH 03/23] Add clustering plugin --- .../Plugins/Point_set/CMakeLists.txt | 3 + .../Point_set/Point_set_clustering_plugin.cpp | 176 ++++++++++++++++++ 2 files changed, 179 insertions(+) create mode 100644 Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/CMakeLists.txt b/Polyhedron/demo/Polyhedron/Plugins/Point_set/CMakeLists.txt index 12b2e9f9056..005a075bf7f 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/CMakeLists.txt +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/CMakeLists.txt @@ -77,6 +77,9 @@ endif() polyhedron_demo_plugin(point_set_wlop_plugin Point_set_wlop_plugin ${point_set_wlopFILES} KEYWORDS PointSetProcessing) target_link_libraries(point_set_wlop_plugin PUBLIC scene_points_with_normal_item scene_callback_signaler) + polyhedron_demo_plugin(point_set_clustering_plugin Point_set_clustering_plugin KEYWORDS PointSetProcessing) + target_link_libraries(point_set_clustering_plugin PUBLIC scene_points_with_normal_item scene_callback_signaler) + polyhedron_demo_plugin(merge_point_sets_plugin Merge_point_sets_plugin KEYWORDS PointSetProcessing Classification) target_link_libraries(merge_point_sets_plugin PUBLIC scene_points_with_normal_item) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp new file mode 100644 index 00000000000..71599e9f6e2 --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp @@ -0,0 +1,176 @@ +#include "config.h" +#include "Scene_points_with_normal_item.h" +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "run_with_qprogressdialog.h" + +struct Clustering_functor + : public Functor_with_signal_callback +{ + Point_set* points; + Point_set::Property_map cluster_map; + const int nb_neighbors; + const double neighbor_radius; + boost::shared_ptr result; + + Clustering_functor (Point_set* points, const int nb_neighbors, + const double neighbor_radius, + Point_set::Property_map cluster_map) + : points (points), cluster_map (cluster_map), + nb_neighbors (nb_neighbors), neighbor_radius (neighbor_radius), + result (new std::size_t(0)) { } + + void operator()() + { + *result = CGAL::cluster_point_set (*points, cluster_map, nb_neighbors, + points->parameters().neighbor_radius(neighbor_radius). + callback(*(this->callback()))); + } +}; + +using namespace CGAL::Three; + +class Polyhedron_demo_point_set_clustering_plugin : + public QObject, + public Polyhedron_demo_plugin_helper +{ + Q_OBJECT + Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface) + Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0") + + QAction* actionCluster; + +public: + void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, Messages_interface*) { + + scene = scene_interface; + mw = mainWindow; + actionCluster = new QAction(tr("Cluster Point Set"), mainWindow); + actionCluster->setObjectName("actionCluster"); + actionCluster->setProperty("subMenuName","Point Set Processing"); + autoConnectActions(); + } + + QList actions() const { + return QList() << actionCluster; + } + + bool applicable(QAction* action) const { + Scene_points_with_normal_item* item = qobject_cast(scene->item(scene->mainSelectionIndex())); + return item; + } + +public Q_SLOTS: + void on_actionCluster_triggered(); + +}; // end + +void Polyhedron_demo_point_set_clustering_plugin::on_actionCluster_triggered() +{ + const CGAL::Three::Scene_interface::Item_id index = scene->mainSelectionIndex(); + + Scene_points_with_normal_item* item = + qobject_cast(scene->item(index)); + + if(item) + { + // Gets point set + Point_set* points = item->point_set(); + if(points == NULL) + return; + + QMultipleInputDialog dialog ("Clustering", mw); + QSpinBox* nb_neighbors = dialog.add ("Number of neighbors (0 = use radius):"); + nb_neighbors->setRange (0, 10000000); + nb_neighbors->setValue (12); + QDoubleSpinBox* neighbor_radius = dialog.add ("Neighbor radius (0 = use number):"); + neighbor_radius->setRange (0, 10000000); + neighbor_radius->setValue (0); + QSpinBox* min_nb = dialog.add ("Minimum number of points per cluster:"); + min_nb->setRange (1, 10000000); + min_nb->setValue (1); + + if (!dialog.exec()) + return; + + QApplication::setOverrideCursor(Qt::BusyCursor); + QApplication::processEvents(); + CGAL::Real_timer task_timer; task_timer.start(); + + Point_set::Property_map + cluster_map = points->add_property_map ("cluster_point_set_property_map").first; + + // Computes average spacing + Clustering_functor functor (points, nb_neighbors->value(), neighbor_radius->value(), cluster_map); + run_with_qprogressdialog (functor, "Clustering...", mw); + + std::size_t nb_clusters = *functor.result; + + CGAL::Random rand(static_cast(time(0))); + + Scene_group_item* group = new Scene_group_item(QString("%1 (clusters)").arg(item->name())); + scene->addItem(group); + + std::vector new_items; + new_items.reserve (nb_clusters); + for (std::size_t i = 0; i < nb_clusters; ++ i) + { + Scene_points_with_normal_item* new_item = new Scene_points_with_normal_item; + new_item->point_set()->copy_properties (*points); + unsigned char r, g, b; + r = static_cast(64 + rand.get_int(0, 192)); + g = static_cast(64 + rand.get_int(0, 192)); + b = static_cast(64 + rand.get_int(0, 192)); + new_item->setRgbColor(r, g, b); + new_item->setName (QString("Cluster %1 of %2").arg(i).arg(item->name())); + new_items.push_back (new_item); + } + + for (Point_set::Index idx : *points) + new_items[cluster_map[idx]]->point_set()->insert (*points, idx); + + for (Scene_points_with_normal_item* new_item : new_items) + { + if (new_item->point_set()->size() >= min_nb->value()) + { + scene->addItem(new_item); + scene->changeGroup (new_item, group); + } + else + delete new_item; + } + + std::size_t memory = CGAL::Memory_sizer().virtual_size(); + std::cerr << "Number of clusters = " << nb_clusters << " (" + << task_timer.time() << " seconds, " + << (memory>>20) << " Mb allocated)" + << std::endl; + QApplication::restoreOverrideCursor(); + + item->setVisible (false); + item->invalidateOpenGLBuffers(); + scene->itemChanged(item); + } +} + + + +#include "Point_set_clustering_plugin.moc" From 5cf4b4acfd99f57a40d5aa5462523ba1e038ac0a Mon Sep 17 00:00:00 2001 From: Simon Giraudot Date: Tue, 10 Mar 2020 13:39:04 +0100 Subject: [PATCH 04/23] Improve plugin --- .../Point_set/Point_set_clustering_plugin.cpp | 117 ++++++++++++++---- 1 file changed, 90 insertions(+), 27 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp index 71599e9f6e2..565c7e7e572 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -108,6 +109,15 @@ void Polyhedron_demo_point_set_clustering_plugin::on_actionCluster_triggered() min_nb->setRange (1, 10000000); min_nb->setValue (1); + QCheckBox* add_property = dialog.add ("Add a \"cluster\" property to the input item"); + add_property->setChecked (true); + + QCheckBox* gen_color = dialog.add ("Generate one colored point set"); + gen_color->setChecked (true); + + QCheckBox* gen_sub = dialog.add ("Generate N point subsets"); + gen_sub->setChecked (false); + if (!dialog.exec()) return; @@ -115,7 +125,12 @@ void Polyhedron_demo_point_set_clustering_plugin::on_actionCluster_triggered() QApplication::processEvents(); CGAL::Real_timer task_timer; task_timer.start(); - Point_set::Property_map + Point_set::Property_map cluster_map; + + if (add_property->isChecked()) + cluster_map = points->add_property_map ("cluster_map").first; + else + // Use long name to avoid overwriting potentially existing map cluster_map = points->add_property_map ("cluster_point_set_property_map").first; // Computes average spacing @@ -124,39 +139,87 @@ void Polyhedron_demo_point_set_clustering_plugin::on_actionCluster_triggered() std::size_t nb_clusters = *functor.result; - CGAL::Random rand(static_cast(time(0))); - Scene_group_item* group = new Scene_group_item(QString("%1 (clusters)").arg(item->name())); - scene->addItem(group); - + Scene_group_item* group; std::vector new_items; - new_items.reserve (nb_clusters); - for (std::size_t i = 0; i < nb_clusters; ++ i) - { - Scene_points_with_normal_item* new_item = new Scene_points_with_normal_item; - new_item->point_set()->copy_properties (*points); - unsigned char r, g, b; - r = static_cast(64 + rand.get_int(0, 192)); - g = static_cast(64 + rand.get_int(0, 192)); - b = static_cast(64 + rand.get_int(0, 192)); - new_item->setRgbColor(r, g, b); - new_item->setName (QString("Cluster %1 of %2").arg(i).arg(item->name())); - new_items.push_back (new_item); - } - for (Point_set::Index idx : *points) - new_items[cluster_map[idx]]->point_set()->insert (*points, idx); - - for (Scene_points_with_normal_item* new_item : new_items) + if (gen_sub->isChecked()) { - if (new_item->point_set()->size() >= min_nb->value()) + group = new Scene_group_item(QString("%1 (clusters)").arg(item->name())); + scene->addItem(group); + new_items.reserve (nb_clusters); + for (std::size_t i = 0; i < nb_clusters; ++ i) { - scene->addItem(new_item); - scene->changeGroup (new_item, group); + Scene_points_with_normal_item* new_item = new Scene_points_with_normal_item; + new_item->point_set()->copy_properties (*points); + CGAL::Random rand(i); + unsigned char r, g, b; + r = static_cast(64 + rand.get_int(0, 192)); + g = static_cast(64 + rand.get_int(0, 192)); + b = static_cast(64 + rand.get_int(0, 192)); + new_item->setRgbColor(r, g, b); + new_item->setName (QString("Cluster %1 of %2").arg(i).arg(item->name())); + new_items.push_back (new_item); } - else - delete new_item; } + + std::vector cluster_size (nb_clusters, 0); + for (Point_set::Index idx : *points) + { + if (gen_sub->isChecked()) + new_items[cluster_map[idx]]->point_set()->insert (*points, idx); + cluster_size[cluster_map[idx]] ++; + } + + if (gen_color->isChecked()) + { + Scene_points_with_normal_item* colored; + Point_set::Property_map red, green, blue; + + colored = new Scene_points_with_normal_item; + colored->setName (QString("%1 (clustering)").arg(item->name())); + + red = colored->point_set()->add_property_map("red", 0).first; + green = colored->point_set()->add_property_map("green", 0).first; + blue = colored->point_set()->add_property_map("blue", 0).first; + colored->point_set()->check_colors(); + + colored->point_set()->reserve (points->size()); + + for (Point_set::Index idx : *points) + { + Point_set::Index iidx = *(colored->point_set()->insert (points->point(idx))); + if (cluster_size[cluster_map[idx]] >= min_nb->value()) + { + CGAL::Random rand(cluster_map[idx]); + unsigned char r, g, b; + r = static_cast(64 + rand.get_int(0, 192)); + g = static_cast(64 + rand.get_int(0, 192)); + b = static_cast(64 + rand.get_int(0, 192)); + red[iidx] = r; + green[iidx] = g; + blue[iidx] = b; + } + } + scene->addItem(colored); + } + + if (gen_sub->isChecked()) + { + for (Scene_points_with_normal_item* new_item : new_items) + { + if (new_item->point_set()->size() >= min_nb->value()) + { + scene->addItem(new_item); + scene->changeGroup (new_item, group); + } + else + delete new_item; + } + } + + if (!add_property->isChecked()) + points->remove_property_map (cluster_map); std::size_t memory = CGAL::Memory_sizer().virtual_size(); std::cerr << "Number of clusters = " << nb_clusters << " (" From 7cba1cc1aac1585d7d1506740e2a9b1779bc0194 Mon Sep 17 00:00:00 2001 From: Simon Giraudot Date: Tue, 10 Mar 2020 14:40:41 +0100 Subject: [PATCH 05/23] Document cluster_point_set() --- .../CGAL/boost/graph/named_params_helper.h | 4 +- .../CGAL/boost/graph/parameters_interface.h | 2 +- .../NamedParameters.txt | 9 ++++- .../PackageDescription.txt | 2 +- .../Point_set_processing_3.txt | 16 ++++++++ .../doc/Point_set_processing_3/examples.txt | 1 + .../clustering_example.cpp | 14 ++++--- .../include/CGAL/cluster_point_set.h | 40 ++++++++++++------- 8 files changed, 63 insertions(+), 25 deletions(-) diff --git a/BGL/include/CGAL/boost/graph/named_params_helper.h b/BGL/include/CGAL/boost/graph/named_params_helper.h index f5cdbf4ca63..2bfe81a3d83 100644 --- a/BGL/include/CGAL/boost/graph/named_params_helper.h +++ b/BGL/include/CGAL/boost/graph/named_params_helper.h @@ -429,12 +429,12 @@ namespace CGAL { }; template - class GetNeighborhood + class GetAdjacencies { public: typedef Emptyset_iterator Empty; typedef typename internal_np::Lookup_named_param_def < - internal_np::neighborhood_t, + internal_np::adjacencies_t, NamedParameters, Empty//default > ::type type; diff --git a/BGL/include/CGAL/boost/graph/parameters_interface.h b/BGL/include/CGAL/boost/graph/parameters_interface.h index 4e3232830b1..6aca16a7090 100644 --- a/BGL/include/CGAL/boost/graph/parameters_interface.h +++ b/BGL/include/CGAL/boost/graph/parameters_interface.h @@ -122,7 +122,7 @@ CGAL_add_named_parameter(plane_index_t, plane_index_map, plane_index_map) CGAL_add_named_parameter(select_percentage_t, select_percentage, select_percentage) CGAL_add_named_parameter(require_uniform_sampling_t, require_uniform_sampling, require_uniform_sampling) CGAL_add_named_parameter(point_is_constrained_t, point_is_constrained, point_is_constrained_map) -CGAL_add_named_parameter(neighborhood_t, neighborhood, neighborhood) +CGAL_add_named_parameter(adjacencies_t, adjacencies, adjacencies) // List of named parameters used in Surface_mesh_approximation package CGAL_add_named_parameter(verbose_level_t, verbose_level, verbose_level) 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 e2c64e40efb..a262ab6c54e 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 @@ -140,7 +140,7 @@ is the minimum distance for a point to be considered as outlier \cgalNPEnd \cgalNPBegin{attraction_factor} \anchor PSP_attraction_factor -multiple of a tolerance `epsilon` used to connect simplices. +multiplication factor used for adjacency computations. \b Type: floating scalar value\n Default value: `3` \cgalNPEnd @@ -191,6 +191,13 @@ Constrained points are left unaltered and are used as seeds in `mst_orient_norma Default value: a property map with only the highest point constrained. \cgalNPEnd +\cgalNPBegin{adjacencies} \anchor PSP_adjacencies +is an output iterator used to store adjacencies.\n +\b Type: a class model of `OutputIterator` that accepts objects of +type `std::pair`. \n +Default value: `CGAL::Emptyset_iterator`. +\cgalNPEnd + \cgalNPTableEnd */ 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 cfe7e23f2a5..4d66256942b 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 @@ -55,7 +55,7 @@ format. - `CGAL::cluster_point_set()` - `CGAL::grid_simplify_point_set()` - `CGAL::random_simplify_point_set()` -- `CGAL::hierarchy_simplify_point_set()` +- `CGAL::hierarchy_simplify_point_set()` - `CGAL::wlop_simplify_and_regularize_point_set()` - `CGAL::jet_smooth_point_set()` - `CGAL::bilateral_smooth_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 275ecf450fb..3bb77da6a41 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 @@ -314,7 +314,23 @@ points in the domain. \cgalExample{Point_set_processing_3/scale_estimation_2d_example.cpp} +\section Point_set_processing_3Clustering Clustering +If an input point set represents several objects which are spatially +separated, a clustering algorithm can be applied to identify simply +connected clusters on a nearest neighbors graph. + +The clustering is stored in a cluster map which associates each input +point with the index of the cluster it belongs to: users can then use +this map however they find it relevant to their use case, for example +segmenting the input point set into several (one per cluster). + +\subsection Point_set_processing_3Example_clustering Example + +In the following example, clusters (and adjacencies between them) are +computed and stored as colors in a PLY file: + +\cgalExample{Point_set_processing_3/clustering_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 43849ae022c..b34a2fae0c2 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 @@ -6,6 +6,7 @@ \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/clustering_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/clustering_example.cpp b/Point_set_processing_3/examples/Point_set_processing_3/clustering_example.cpp index ad6712a4670..8ec3cc7e69d 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/clustering_example.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/clustering_example.cpp @@ -13,36 +13,40 @@ using Point_set = CGAL::Point_set_3; int main (int argc, char** argv) { + // Read input file std::ifstream ifile (argv[1], std::ios_base::binary); Point_set points; ifile >> points; + // Add a cluster map Point_set::Property_map cluster_map = points.add_property_map ("cluster", -1).first; + // Compute average spacing double spacing = CGAL::compute_average_spacing (points, 12); - std::cerr << "Spacing = " << spacing << std::endl; + // Adjacencies stored in vector std::vector > adjacencies; - + + // Compute clusters CGAL::Real_timer t; t.start(); std::size_t nb_clusters = CGAL::cluster_point_set (points, cluster_map, 0, points.parameters().neighbor_radius(spacing). - neighborhood (std::back_inserter (adjacencies))); + adjacencies (std::back_inserter (adjacencies))); t.stop(); std::cerr << "Found " << nb_clusters << " clusters with " << adjacencies.size() << " adjacencies in " << t.time() << " seconds" << std::endl; + // Output a colored PLY file Point_set::Property_map red = points.add_property_map("red", 0).first; Point_set::Property_map green = points.add_property_map("green", 0).first; Point_set::Property_map blue = points.add_property_map("blue", 0).first; - for (Point_set::Index idx : points) { + // One color per cluster CGAL::Random rand (cluster_map[idx]); - red[idx] = rand.get_int(64, 192); green[idx] = rand.get_int(64, 192); blue[idx] = rand.get_int(64, 192); diff --git a/Point_set_processing_3/include/CGAL/cluster_point_set.h b/Point_set_processing_3/include/CGAL/cluster_point_set.h index b1d30c8654f..ef344707637 100644 --- a/Point_set_processing_3/include/CGAL/cluster_point_set.h +++ b/Point_set_processing_3/include/CGAL/cluster_point_set.h @@ -37,13 +37,13 @@ namespace internal // `std::back_insert_iterator`) cannot be default constructed, which // makes the mechanism `choose_param(get_param(...),Default())` fails. template -OutputIterator get_neighborhood (const NamedParameters& np, OutputIterator*) +OutputIterator get_adjacencies (const NamedParameters& np, OutputIterator*) { - return CGAL::parameters::get_parameter(np, internal_np::neighborhood); + return CGAL::parameters::get_parameter(np, internal_np::adjacencies); } template -CGAL::Emptyset_iterator get_neighborhood (const NamedParameters&, CGAL::Emptyset_iterator*) +CGAL::Emptyset_iterator get_adjacencies (const NamedParameters&, CGAL::Emptyset_iterator*) { return CGAL::Emptyset_iterator(); } @@ -87,6 +87,16 @@ CGAL::Emptyset_iterator get_neighborhood (const NamedParameters&, CGAL::Emptyset `k` is used as a limit on the number of points returned by each spherical query (to avoid overly large number of points in high density areas). If no limit is wanted, use `k=0`.\cgalParamEnd + \cgalParamBegin{attraction_factor} used to compute adjacencies + between clusters. Adjacencies are computed using a nearest + neighbor graph built similarly to the one used for clustering, + using `attraction_factor * k` and `attraction_factor * + nearest_neighbors` as parameters. %Default value is `2`.\cgalParamEnd + \cgalParamBegin{adjacencies} model of `OutputIterator` that + accepts objects of type `std::pair`. Each pair contains the indices of two adjacent + clusters. If this parameter is not used, adjacencies are not + computed at all.\cgalParamEnd \cgalParamBegin{geom_traits} an instance of a geometric traits class, model of `Kernel`\cgalParamEnd \cgalNamedParamsEnd @@ -106,7 +116,7 @@ std::size_t cluster_point_set (PointRange& points, typedef typename iterator::value_type value_type; typedef typename Point_set_processing_3::GetPointMap::type PointMap; typedef typename Point_set_processing_3::GetK::Kernel Kernel; - typedef typename Point_set_processing_3::GetNeighborhood::type Neighborhood; + typedef typename Point_set_processing_3::GetAdjacencies::type Adjacencies; typedef typename GetSvdTraits::type SvdTraits; CGAL_static_assertion_msg(!(boost::is_same()); double callback_factor = 1.; - if (!std::is_same::Empty>::value) + if (!std::is_same::Empty>::value) callback_factor = 0.5; typedef typename Kernel::Point_3 Point; @@ -185,15 +195,15 @@ std::size_t cluster_point_set (PointRange& points, ++ nb_clusters; } - if (!std::is_same::Empty>::value) + if (!std::is_same::Empty>::value) { - Neighborhood neighborhood = Point_set_processing_3::internal::get_neighborhood(np, (Neighborhood*)(nullptr)); + Adjacencies adjacencies = Point_set_processing_3::internal::get_adjacencies(np, (Adjacencies*)(nullptr)); k *= factor; neighbor_radius *= factor; std::vector neighbors; - std::vector > adjacencies; + std::vector > adj; done = 0; for (const value_type& p : points) @@ -208,9 +218,9 @@ std::size_t cluster_point_set (PointRange& points, { std::size_t c1 = get (cluster_map, *it); if (c0 < c1) - adjacencies.push_back (std::make_pair (c0, c1)); + adj.push_back (std::make_pair (c0, c1)); else if (c0 > c1) - adjacencies.push_back (std::make_pair (c1, c0)); + adj.push_back (std::make_pair (c1, c0)); // else c0 == c1, ignore } @@ -218,9 +228,9 @@ std::size_t cluster_point_set (PointRange& points, if (callback && !callback (callback_factor + callback_factor * (done + 1) / double(size))) return nb_clusters; } - std::sort (adjacencies.begin(), adjacencies.end()); - auto last = std::unique (adjacencies.begin(), adjacencies.end()); - std::copy (adjacencies.begin(), last, neighborhood); + std::sort (adj.begin(), adj.end()); + auto last = std::unique (adj.begin(), adj.end()); + std::copy (adj.begin(), last, adjacencies); } return nb_clusters; From 80e8283706eddca4c15dd9a2d6cdaf3ae821da42 Mon Sep 17 00:00:00 2001 From: Simon Giraudot Date: Thu, 12 Mar 2020 09:59:49 +0100 Subject: [PATCH 06/23] Fix errors --- .../examples/Point_set_processing_3/clustering_example.cpp | 2 ++ Point_set_processing_3/include/CGAL/cluster_point_set.h | 3 +++ 2 files changed, 5 insertions(+) diff --git a/Point_set_processing_3/examples/Point_set_processing_3/clustering_example.cpp b/Point_set_processing_3/examples/Point_set_processing_3/clustering_example.cpp index 8ec3cc7e69d..737913b6521 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/clustering_example.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/clustering_example.cpp @@ -7,6 +7,8 @@ #include #include +#include + using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; using Point_3 = Kernel::Point_3; using Point_set = CGAL::Point_set_3; diff --git a/Point_set_processing_3/include/CGAL/cluster_point_set.h b/Point_set_processing_3/include/CGAL/cluster_point_set.h index ef344707637..a538dbccccd 100644 --- a/Point_set_processing_3/include/CGAL/cluster_point_set.h +++ b/Point_set_processing_3/include/CGAL/cluster_point_set.h @@ -19,6 +19,9 @@ #include #include +#include +#include + #include namespace CGAL From c4e62d70e8a7e5f726051c609c9c88354f23cff5 Mon Sep 17 00:00:00 2001 From: Simon Giraudot Date: Mon, 16 Mar 2020 16:32:38 +0100 Subject: [PATCH 07/23] Remove K parameter and update doc from reviews --- .../Point_set_processing_3.txt | 5 ++- .../clustering_example.cpp | 2 +- .../include/CGAL/cluster_point_set.h | 41 +++++++++++-------- .../Point_set/Point_set_clustering_plugin.cpp | 22 +++++----- STL_Extension/include/CGAL/iterator.h | 29 +++++++++++++ 5 files changed, 69 insertions(+), 30 deletions(-) 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 3bb77da6a41..5c3f2c50733 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 @@ -317,8 +317,9 @@ points in the domain. \section Point_set_processing_3Clustering Clustering If an input point set represents several objects which are spatially -separated, a clustering algorithm can be applied to identify simply -connected clusters on a nearest neighbors graph. +separated, a clustering algorithm can be applied to identify connected +components on a nearest neighbors graph built using a query sphere of +fixed radius centered on each point. The clustering is stored in a cluster map which associates each input point with the index of the cluster it belongs to: users can then use diff --git a/Point_set_processing_3/examples/Point_set_processing_3/clustering_example.cpp b/Point_set_processing_3/examples/Point_set_processing_3/clustering_example.cpp index 737913b6521..d6d2b406cde 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/clustering_example.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/clustering_example.cpp @@ -34,7 +34,7 @@ int main (int argc, char** argv) CGAL::Real_timer t; t.start(); std::size_t nb_clusters - = CGAL::cluster_point_set (points, cluster_map, 0, + = CGAL::cluster_point_set (points, cluster_map, points.parameters().neighbor_radius(spacing). adjacencies (std::back_inserter (adjacencies))); t.stop(); diff --git a/Point_set_processing_3/include/CGAL/cluster_point_set.h b/Point_set_processing_3/include/CGAL/cluster_point_set.h index a538dbccccd..ee13377a4ef 100644 --- a/Point_set_processing_3/include/CGAL/cluster_point_set.h +++ b/Point_set_processing_3/include/CGAL/cluster_point_set.h @@ -62,7 +62,8 @@ CGAL::Emptyset_iterator get_adjacencies (const NamedParameters&, CGAL::Emptyset_ /** \ingroup PkgPointSetProcessing3Algorithms - Identifies simply connected clusters on a nearest neighbors graph. + Identifies connected components on a nearest neighbors graph built + using a query sphere of fixed radius centered on each point. \tparam PointRange is a model of `Range`. The value type of its iterator is the key type of the named parameter `point_map`. @@ -71,7 +72,6 @@ CGAL::Emptyset_iterator get_adjacencies (const NamedParameters&, CGAL::Emptyset_ \param points input point range. \param cluster_map maps each point to the index of the cluster it belongs to. - \param k number of neighbors. \param np optional sequence of \ref psp_namedparameters "Named Parameters" among the ones listed below. \cgalNamedParamsBegin @@ -84,17 +84,14 @@ CGAL::Emptyset_iterator get_adjacencies (const NamedParameters&, CGAL::Emptyset_ algorithm continues its execution normally; if it returns `false`, the algorithm is stopped and the number of already computed clusters is returned.\cgalParamEnd - \cgalParamBegin{neighbor_radius} spherical neighborhood radius. If - provided, the neighborhood of a query point is computed with a fixed spherical - radius instead of a fixed number of neighbors. In that case, the parameter - `k` is used as a limit on the number of points returned by each spherical - query (to avoid overly large number of points in high density areas). If no - limit is wanted, use `k=0`.\cgalParamEnd + \cgalParamBegin{neighbor_radius} spherical neighborhood + radius. If no value is provided, the default value is 1% of the + bounding box diagonal.\cgalParamEnd \cgalParamBegin{attraction_factor} used to compute adjacencies between clusters. Adjacencies are computed using a nearest neighbor graph built similarly to the one used for clustering, - using `attraction_factor * k` and `attraction_factor * - nearest_neighbors` as parameters. %Default value is `2`.\cgalParamEnd + using `attraction_factor * neighbor_radius` as + parameter. %Default value is `2`.\cgalParamEnd \cgalParamBegin{adjacencies} model of `OutputIterator` that accepts objects of type `std::pair`. Each pair contains the indices of two adjacent @@ -108,7 +105,6 @@ CGAL::Emptyset_iterator get_adjacencies (const NamedParameters&, CGAL::Emptyset_ template std::size_t cluster_point_set (PointRange& points, ClusterMap cluster_map, - unsigned int k, const NamedParameters& np) { using parameters::choose_parameter; @@ -128,7 +124,7 @@ std::size_t cluster_point_set (PointRange& points, PointMap point_map = choose_parameter(get_parameter(np, internal_np::point_map), PointMap()); typename Kernel::FT neighbor_radius = choose_parameter(get_parameter(np, internal_np::neighbor_radius), - typename Kernel::FT(0)); + typename Kernel::FT(-1)); typename Kernel::FT factor = choose_parameter(get_parameter(np, internal_np::attraction_factor), typename Kernel::FT(2)); @@ -150,8 +146,20 @@ std::size_t cluster_point_set (PointRange& points, // but this is costly to check CGAL_point_set_processing_precondition(points.begin() != points.end()); - // precondition: at least 2 nearest neighbors - CGAL_point_set_processing_precondition(k >= 2); + // If no radius is given, init with 1% of bbox diagonal + std::cerr << neighbor_radius << std::endl; + if (neighbor_radius < 0) + { + CGAL::Bbox_3 bbox = CGAL::bbox_3 (CGAL::make_transform_iterator_from_property_map (points.begin(), point_map), + CGAL::make_transform_iterator_from_property_map (points.end(), point_map)); + + neighbor_radius = 0.01 * CGAL::approximate_sqrt + ((bbox.xmax() - bbox.xmin()) * (bbox.xmax() - bbox.xmin()) + + (bbox.ymax() - bbox.ymin()) * (bbox.ymax() - bbox.ymin()) + + (bbox.zmax() - bbox.zmin()) * (bbox.zmax() - bbox.zmin())); + + std::cerr << neighbor_radius << std::endl; + } // Init cluster map with -1 for (const value_type& p : points) @@ -189,7 +197,7 @@ std::size_t cluster_point_set (PointRange& points, if (callback && !callback (callback_factor * (done + 1) / double(size))) return (nb_clusters + 1); - neighbor_query.get_iterators (get (point_map, *current), k, neighbor_radius, + neighbor_query.get_iterators (get (point_map, *current), 0, neighbor_radius, boost::make_function_output_iterator ([&](const iterator& it) { todo.push(it); })); @@ -202,7 +210,6 @@ std::size_t cluster_point_set (PointRange& points, typename Point_set_processing_3::GetAdjacencies::Empty>::value) { Adjacencies adjacencies = Point_set_processing_3::internal::get_adjacencies(np, (Adjacencies*)(nullptr)); - k *= factor; neighbor_radius *= factor; std::vector neighbors; @@ -214,7 +221,7 @@ std::size_t cluster_point_set (PointRange& points, std::size_t c0 = get (cluster_map, p); neighbors.clear(); - neighbor_query.get_iterators (get (point_map, p), k, neighbor_radius, + neighbor_query.get_iterators (get (point_map, p), 0, neighbor_radius, std::back_inserter (neighbors)); for (const iterator& it : neighbors) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp index 565c7e7e572..17986cf4e8a 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp @@ -28,20 +28,19 @@ struct Clustering_functor { Point_set* points; Point_set::Property_map cluster_map; - const int nb_neighbors; const double neighbor_radius; boost::shared_ptr result; - Clustering_functor (Point_set* points, const int nb_neighbors, + Clustering_functor (Point_set* points, const double neighbor_radius, Point_set::Property_map cluster_map) : points (points), cluster_map (cluster_map), - nb_neighbors (nb_neighbors), neighbor_radius (neighbor_radius), + neighbor_radius (neighbor_radius), result (new std::size_t(0)) { } void operator()() { - *result = CGAL::cluster_point_set (*points, cluster_map, nb_neighbors, + *result = CGAL::cluster_point_set (*points, cluster_map, points->parameters().neighbor_radius(neighbor_radius). callback(*(this->callback()))); } @@ -99,10 +98,7 @@ void Polyhedron_demo_point_set_clustering_plugin::on_actionCluster_triggered() return; QMultipleInputDialog dialog ("Clustering", mw); - QSpinBox* nb_neighbors = dialog.add ("Number of neighbors (0 = use radius):"); - nb_neighbors->setRange (0, 10000000); - nb_neighbors->setValue (12); - QDoubleSpinBox* neighbor_radius = dialog.add ("Neighbor radius (0 = use number):"); + QDoubleSpinBox* neighbor_radius = dialog.add ("Neighbor radius (0 = automatic):"); neighbor_radius->setRange (0, 10000000); neighbor_radius->setValue (0); QSpinBox* min_nb = dialog.add ("Minimum number of points per cluster:"); @@ -132,13 +128,19 @@ void Polyhedron_demo_point_set_clustering_plugin::on_actionCluster_triggered() else // Use long name to avoid overwriting potentially existing map cluster_map = points->add_property_map ("cluster_point_set_property_map").first; + + // Default value + if (neighbor_radius->value() == 0) + { + neighbor_radius->setRange (-1, 10000000); + neighbor_radius->setValue(-1); + } // Computes average spacing - Clustering_functor functor (points, nb_neighbors->value(), neighbor_radius->value(), cluster_map); + Clustering_functor functor (points, neighbor_radius->value(), cluster_map); run_with_qprogressdialog (functor, "Clustering...", mw); std::size_t nb_clusters = *functor.result; - Scene_group_item* group; std::vector new_items; diff --git a/STL_Extension/include/CGAL/iterator.h b/STL_Extension/include/CGAL/iterator.h index 4875fb69aa5..35ec75f6c4f 100644 --- a/STL_Extension/include/CGAL/iterator.h +++ b/STL_Extension/include/CGAL/iterator.h @@ -1480,6 +1480,35 @@ struct Range_iterator_type { typedef typename RangeRef::iterato template struct Range_iterator_type { typedef typename RangeRef::const_iterator type; }; +// Syntaxic sugar for transform_iterator+pmap_to_unary_function +template +typename boost::transform_iterator, Iterator> +make_transform_iterator_from_property_map (Iterator it, Pmap pmap) +{ + return boost::make_transform_iterator (it, CGAL::Property_map_to_unary_function(pmap)); +} + +// Syntaxic sugar for make_range+transform_iterator+pmap_to_unary_function +template +CGAL::Iterator_range, + typename Range::const_iterator> > +make_transform_range_from_property_map (const Range& range, Pmap pmap) +{ + return CGAL::make_range + (make_transform_iterator_from_property_map (range.begin(), pmap), + make_transform_iterator_from_property_map (range.end(), pmap)); +} + +// Syntaxic sugar for make_range+transform_iterator+pmap_to_unary_function +template +CGAL::Iterator_range, + typename Range::iterator> > +make_transform_range_from_property_map (Range& range, Pmap pmap) +{ + return CGAL::make_range + (make_transform_iterator_from_property_map (range.begin(), pmap), + make_transform_iterator_from_property_map (range.end(), pmap)); +} } //namespace CGAL From b1966323e40c143aeb0666bdcdb7747fd777ea54 Mon Sep 17 00:00:00 2001 From: Simon Giraudot Date: Tue, 17 Mar 2020 09:20:07 +0100 Subject: [PATCH 08/23] Do not fallback on k=3 if sphere is empty for clustering --- .../internal/Neighbor_query.h | 17 ++++++++++------- .../include/CGAL/cluster_point_set.h | 4 ++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/Neighbor_query.h b/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/Neighbor_query.h index 1160fb3a3e5..eaa35bce79c 100644 --- a/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/Neighbor_query.h +++ b/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/Neighbor_query.h @@ -103,7 +103,7 @@ public: template void get_iterators (const Point_3& query, unsigned int k, FT neighbor_radius, - OutputIterator output) const + OutputIterator output, bool fallback_k_is_sphere_empty = true) const { if (neighbor_radius != FT(0)) { @@ -133,13 +133,16 @@ public: catch (const Maximum_points_reached_exception&) { } - // Fallback, if less than 3 points are return, search for the 3 - // first points - if (nb < 3) - k = 3; - // Else, no need to search for K nearest neighbors - else + if (fallback_k_is_sphere_empty) + { + // Fallback, if less than 3 points are return, search for the 3 + // first points + if (nb < 3) + k = 3; + // Else, no need to search for K nearest neighbors + else k = 0; + } } if (k != 0) diff --git a/Point_set_processing_3/include/CGAL/cluster_point_set.h b/Point_set_processing_3/include/CGAL/cluster_point_set.h index ee13377a4ef..c6008b8e4e8 100644 --- a/Point_set_processing_3/include/CGAL/cluster_point_set.h +++ b/Point_set_processing_3/include/CGAL/cluster_point_set.h @@ -199,7 +199,7 @@ std::size_t cluster_point_set (PointRange& points, neighbor_query.get_iterators (get (point_map, *current), 0, neighbor_radius, boost::make_function_output_iterator - ([&](const iterator& it) { todo.push(it); })); + ([&](const iterator& it) { todo.push(it); }), false); } @@ -222,7 +222,7 @@ std::size_t cluster_point_set (PointRange& points, neighbors.clear(); neighbor_query.get_iterators (get (point_map, p), 0, neighbor_radius, - std::back_inserter (neighbors)); + std::back_inserter (neighbors), false); for (const iterator& it : neighbors) { From 88e3fd47310be60bbe8a29bd70af7f380ccd8f7a Mon Sep 17 00:00:00 2001 From: Simon Giraudot Date: Tue, 17 Mar 2020 09:21:45 +0100 Subject: [PATCH 09/23] Remove garbage cerr --- Point_set_processing_3/include/CGAL/cluster_point_set.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/Point_set_processing_3/include/CGAL/cluster_point_set.h b/Point_set_processing_3/include/CGAL/cluster_point_set.h index c6008b8e4e8..fd6e27afe27 100644 --- a/Point_set_processing_3/include/CGAL/cluster_point_set.h +++ b/Point_set_processing_3/include/CGAL/cluster_point_set.h @@ -147,7 +147,6 @@ std::size_t cluster_point_set (PointRange& points, CGAL_point_set_processing_precondition(points.begin() != points.end()); // If no radius is given, init with 1% of bbox diagonal - std::cerr << neighbor_radius << std::endl; if (neighbor_radius < 0) { CGAL::Bbox_3 bbox = CGAL::bbox_3 (CGAL::make_transform_iterator_from_property_map (points.begin(), point_map), @@ -157,8 +156,6 @@ std::size_t cluster_point_set (PointRange& points, ((bbox.xmax() - bbox.xmin()) * (bbox.xmax() - bbox.xmin()) + (bbox.ymax() - bbox.ymin()) * (bbox.ymax() - bbox.ymin()) + (bbox.zmax() - bbox.zmin()) * (bbox.zmax() - bbox.zmin())); - - std::cerr << neighbor_radius << std::endl; } // Init cluster map with -1 From 0e67264624ae7b217538e975878646c4289cbbe6 Mon Sep 17 00:00:00 2001 From: Simon Giraudot Date: Tue, 17 Mar 2020 10:06:00 +0100 Subject: [PATCH 10/23] Update from review --- .../Point_set_processing_3.txt | 12 +++++++++++- .../Point_set_processing_3/fig/clustering.png | Bin 0 -> 145016 bytes .../internal/Neighbor_query.h | 17 +++++++---------- .../include/CGAL/cluster_point_set.h | 2 +- .../Point_set/Point_set_clustering_plugin.cpp | 2 +- 5 files changed, 20 insertions(+), 13 deletions(-) create mode 100644 Point_set_processing_3/doc/Point_set_processing_3/fig/clustering.png 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 5c3f2c50733..2de6c16e556 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 @@ -324,7 +324,17 @@ fixed radius centered on each point. The clustering is stored in a cluster map which associates each input point with the index of the cluster it belongs to: users can then use this map however they find it relevant to their use case, for example -segmenting the input point set into several (one per cluster). +segmenting the input point set into several (one per +cluster). \cgalFigureRef{Point_set_processing_3figclustering} shows different clustering +outputs. + +\cgalFigureBegin{Point_set_processing_3figclustering,clustering.png} +Point Set Clustering outputs (one color per cluster). Top: input point +set and clustering using a neighbor radius of 1.5 (147 clusters +extracted). Bottom: clustering with neighbor radius 3.0 (37 clusters +extracted), and with neighbor radius 6.0 (5 clusters extracted). +\cgalFigureEnd + \subsection Point_set_processing_3Example_clustering Example diff --git a/Point_set_processing_3/doc/Point_set_processing_3/fig/clustering.png b/Point_set_processing_3/doc/Point_set_processing_3/fig/clustering.png new file mode 100644 index 0000000000000000000000000000000000000000..e50b92a005934866901cc9ee070500ca81bc8d5e GIT binary patch literal 145016 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
Wk6&NAI!{<(|h%^7uSZlka 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 z z@(m)>T3FJRNBpu~Y(BR8r3f?ba+UHGR1`^lBVI6G<-V;wE6ZC+9)XMaVLUW>IA4M(Y0+G~`-j2nG0% zj&jVip7;z$*~}GFKVBu1&VuSm4@d=-3`mBhTi*nlywwA3XZg(vAZK3|D7&R+jHbi( zdKYY$2f6mN`(mw^TMG;mS0nZKK$g5p{qHp~^T&eBkp%8BOV1Z;rC0p56;FmOI;fX8LkZ9Wo_|=dLEsFy1M7v9=vPwr|M}X~8TnEH1{nA5u zg|szymoWF9N@^%%LAZw$YbwiM677F^+@$<`2g#aB0k`-BE!}2tj4GvOHa!T|RMEZ0 zv#`g~?3~bZCM|2-zC+|)`M8RZIn~$E*#oYB-9qwsy9@PrRw4GlZH35g>(aWp(x&Pu z?^e&Lwx*i(F6qNcRkg3DV zyy(7=Zn0$40dljV#RrI)bJ?v1n4A!^M)s`wE`m1OOp-UkRh?li@rY{LP=w}a&}8Bs z;VM4P@rlQ@Z)Y+XNt+CdiW$X^bN(GPC zNzgfSSCW|>p8Y~M?+e|oM$j!#?;*P~?X!D91VI2-nN~xV$R{?Y-2x6OnPRc4rSR)B z@*i`8Yv+uwV1mmvUmp+*duAk}CaC2bxT zjcFOBY^gnnEwHAVX@Wwou2eLhPXGXNT9ruha|HT@nk`i#Va3fXL%_k;)Z`Siu$ihv zo?-1t5%i5a2NHESBx6%9yHHL_7LoQ>8f$3;$F|V~KgELv(1ve1*w$K(BS2>x>ck)N z*wRvj?gf)--awcqP}ijje1s4H7y|Fw)K#>b=NZvEEM$)&+E8~)&Pt99!I~r?bvU(f zpCt1lsQkTjv;>OluPO-<3h`b$fq3V8$~iFZxHz;mLY<+@ESv>J+Z?zriC<}3ZbT@L z-9)W_ctNK#wNxF*Nf5^-<>zsrfR@18=)NB`<{1tGV+88Iqg{76u!aUwHJ&PbXxv;{ ztW%ac3i~kgq4X2LcI)37Gysamt|oF36BaUToz}83oELErrFCEUm@C?$SbO|7*2Xc- zdsj{3>5(p9`xR}$?n+mgDn=etmBD@yD=J7#2hQyQJ~UvLKE6))6yb6E0D>7Pgq9lY z5mA^$!8rpZZBtEidJ~9t(-U%>GlDDb`wPC#l(4n^zyHJMWjVpYg#M3Pnj17)1*&6X zbe+12;knl_Ii$3+h~PfdwE3v_PE%I1)(cg$54Nm>T~db+l@8obRE8`)wc3Rk@-kAr zIbA}5xA+uZs62zVzfl$i6kIeoG;GW}m&f|z!vp5QuZKflQ6prgwQNlTdr)gxh|=R5>Y6JXh zA%s9Q=);1y2=|=n1})8Uq9JKQ`GfbA*CUm!*5?t?9D!#h0?KE&SPc9^JGF>&{Y16- zAT~J+3+!R{^F)8wOl5@b2q2)|P>U5+kk--V*u9X02$dNXmLLu?O`oLCOs%ZbjTe-~ zo}c}u7flmBDBR{UuBC(XM!GL1VMpY-rpnZX&F2hmxNwjt<_PLhxvpDb#9Z}aaeFE* z^zpTB?8n=~CFa~%#iwlkLqbNnocr0QT zb^h!!XxgAIb_*67#g=Nkzk}i%58{F{TGYcdakaF4wI6|pR&GeXpYjz6)}Xkh+O;8o z7EJSw4wwT9!ya?BZcPxgvKk}?YW+njIjOm&Q27gvmmrb=iV{k_P(pa&z-tc8Ub$8n zsLME+ffx*Tc%{YT>5zMLAbM$&bo>Q51f3fPxJbz zTSiK4nYEe$eN(g0bvm)>rSrMIU`kNq)QW|7-7dUEe8lVRaR6+r%gT3A?X6|w)Gp`y zE}Xbpj&O==Nr+Ch`Tzgn-^xG!WATT7_@{sU$A8+StK2r7KeeTH`|+1w@Bk=qknOnH|I=gaR#82{;2R0oVXNYP^t#L1`F6DETzv&yPC94ZzYZ&N_fC)ku z1wE!00ViUbyQ!>)KEnZMog7eg3eok19!m@~Akd2gOH3*dwnK=*hmf_0*rhLypQ$?bc`H}* z>gCr5amF26&+xc9kLaO_e#}@y2gHgPx>1@QY%gpM5rmZAT5CZ2LEQ2ED>%d{sIlty zkeP@?AfL8)Xk79}$7^6pK#z~0={Ux2SJU3+$Y}%9hNA-9O&-@oA%fC z$j$B>uFo*zL87eTM45uC3~|YVkC^cIaNuHRCUwq{r63q3ZlIXQ!sLEsfg%O5(jI9l zPh4=#q-xRA6&_bn(9PoH%z!V`+Js{S_+6E^h#t3W;-^y>s^BuPTpnCT^l*eQcg#?@ z?Yf1HlFE@ip3+57wagy&8g(Tqt6#iYhkyIVERh+-3rN+zZJRQ5o{#W= zz*XF&0M~UFGrXX-y~jgG)L~;7lhyUXJT8;(Q}&fDd0#-fgciibg{=2p(kvx?x*SqR zW=$qiIk1Oz#@n5lKmHIJ49oomNq5}raJ`IG#YL7Bh6dxvIKX+!?CRL}%Q1^g1>S2M zxwm1&V-w@GaL(v^Vw-Cb=5^Gv;aZXztj;{q9tY==BfO*8^5}Fu7k#3NsnNlmZJ6&{t&*AIzqPl^lpUPxnd;#v+e*9We9^MG8jQu34llqSjv=#`C= ziTRmZjpb80Mb^~3(CfKAM$QLMeHkB>%G*wAtERB@U1e*^EJcvtk&~fzpi+#U+=-jY z%f;oMf(AwBtzl}4i4=hb?wxzstY&Pd+HLcLX4&39xo?!EG!}m~h#xpr+fiUdpB6iE zb`V~QV;r)?>v+`dTg?4tfxyp;UB?P@zHGPb_PtGl9zCu{)3h5t!YxjPJOAV^#XRfXzI+wFUXL#N z7x_Pa^Yyo{zV?5_mH+2I+U1*<-(0nId7B?ME`F@@MRN0dWIOYGF>F8-;On&CkITFI z9e(Gss;Pu3mm!2LnVkcw%@QA3DAXja@e(YTnSiSKTAD@LN7$p?hZA+zFAe}?l{MYW z6Y5Bf6q`Uw2d;tkJw?N9*L7^T$Rqbz>=#d~aaGfU&kCAr$J_eS12d=F^S~LzC-?D! zz}TK^rOV{wDU}O*CU@WZ(mSgt#IDyZJDncY(wW`Zvv&vx3(+I#lX23fi)f8F=wttG z3Ihnf^PrkQmIx|-k9MO|>z9YZ?WYH(+MJs>;S{fldqO<1R!kqi%TeXc`qSVhr!7g} zcv`E0YgZ7s=>ecct$4PVPS z%wylEZbn!aMbx0j^7L1g{nxKvz4-cfFNQzGgyMHW?hJUf3fz<9;mJc|`CNn}CabXAC*_TbQHjfYq-eLj%O^pHw&SRmqn)ns{b= zTspJ!s`a199KnY=v!4+aEbPI7m)cy$tm;s4S-KHo4tG209qx3dHh^#DOP%*FB(6DO z%f#yD_DKB@sQ9%m4Zx zUvVy#8|+P~%~UP9vJqTB%{78~JF6y4JvUG+2$YO&`x8Y$5*WQ-C}Uo|j9-8KD!q95 z^2IBE{r6Wd{`l>SSFc{-QQ_24=8rt?9apIt+SbtvM`MpUhZ-=*UuT?p?9={E?v|f7 zAUO40N7R1C)0CQ(8==O+%oF*(jOTokNfuKFb>9+n?CxZ#O1^YNHx--AQypU;P0gVw z-ywut1~5nN%waZZvbi_I`5HP9*cC{!>YVQ8rH@0OYc7Ij$2ob{)9i8J6Ky(!`P8!? z*tD{=m2oPCQ%D!39H1&UzU2H^u9+CZe81mCpC;?=T80wO;9icDhMjO>%G2bg0!i0Zg291PeAFkRiBj2Jc{<^^{(T<9zAspm0A1j`@k&L$a2=yamP#( zL27>gx6AMUA^qWRT{*?lZ?-G8uA4UaJAlaG26gG?Y3LihEk=*We$U{mCsM*VZY*y* zqw{altCugoHF~wzZ(h83_3g_S|8|_X{cY4K<(tIB;|g7PTi?%`lLX^3+Yq|lpV+yA z{}g-#@%um~djFg?=~;#*VimiUZ6c$>U8z6Gl?)!22Mo6}`7o)y<*4k=87Pr!xXP5E zEjZfcn%jk?s1v)V%f;I<`>O5$lrMR!x!&M3chlJSvs>0#r3VJzKCvh6XyE^t6rkvz zY1oOg+Sy%j=KvS>hPe+=!I*E0fgX274p?!TuPU4MqP2>-iWbphp_j*z&-b~P?*g{{ z`!Xb-z93i2zP`%OvKSlqcVs-aAIV7XeV#pgm2MAZW0r#hA4AVc@5#j0WEs@N;p5~% z4nfSf<*Q%*ru>gz{=qEC_I&bz)&n~y5MgmQb{W@kqc&YNhJ%J+cIC;^gbAJwUS#wy zU%GGei7BJVpd4!}3qV!Zf0V#;YCR^LUb#Y%Q9H*)cpgh*T@3gsEZU-vI z;7j}*0P-DD$f^9Jb=*#ZxZDT!h@*c>?eJso|9fg3l88$3N&cUa)0Rw5fytmmdMBi67&K#Zho&Liw zzXHfA?N?uY_1|EzgF|Y3?7C5^MYmn7uw)tm9E~qs)vz)#WXa9q-?u#_OSiI*CW!;1HN|b!{y$g)?J(j zR{x0a&LpdOm!*KM0lS`9zu*e8x+ynGcw-@K)C`=O?LZIv*f(Q6F)H9MU1^US{w+!N zhc&v?M|exif4{`fvWMHKq2P5x5A5Tmho|>i5qQlu+g6vjJuNaV@jJWEGgUo~HY;uo zfj>KWD)f%|2Vao8h!e~enMWOW!YvC>_=$^$Uk=mN$@N*!?rx;m+O^qR#r6b1-ucEt zJVOZF`M-dkr$79)H-BAz{nc0hheC01D0ut%!>YV}{ffB`%NqL7KVC*5MCQfdr7rfS@+x_LD40_Sl@VSHe~Qy*DuKzy6o;0tfj-?I>c+Y=m*uIlWE z4OehEE$fMWWS6en019kYy}Ip$bvD1_6xd7k)Ej~@RMdbISAC<#O6*}~wr{#dnk*1~ zcXh2EFGRQVBlLm0YdCXs-(d6Id%*kBKPGYbsSAROL9n}ToS3D6#;ouVwI8QUMzQwC zu3PTwp3mOZ2EK-y;TrCYxHfzk=LLA_0Q%^E{i|+f>-E3*V!!(R|DDUVYgq)fsZw@u zS1r}x@%r8^{2b|L-0t~zFTeiXEBl+5NBd3L|M#rOe!l^y)>+ zR>Ne<_UQ8C{MLth*aQejQ|ceQsq+J6DJ8+ zT_d2GK*`b)s)OW7R!xkvy^aU2%&eTc!dfg_nkG-wKIsAGy~`)HwPDh-S!%hycMCs9 zMOAM_=^oI^Ha!LU{lz zQ8&N(R~-M`_9xEdY^|d>USAT$4&MEe)8_yd4g9_RGy8~*+3kyO*o}2^6taoa@4m5m z>lqW=^4RzL(B2gr{&Ow}y^f}&NRz^a0yDPXs!vYvMYgP^hRN}reYwSs02$>JV(n7r z@qTP_KP_J6JhB5#wdtrgX;S3`SX#Qa&g+$h4tS;mM6NmBf9U4^GrrFPd$^8U4PY6A zP%)?ojJNb)MA})sDv$5BJ(`1jXfKfq|4A2wGCwku!%PP|gS-7dXbEvKS4 z{{x>cgf%YT%ooFcdT;{{2(TGpD0nEG{|{!NP18Ju+pOJD{){i*=Ifcy&xK;4IG1$h zoW@6Qhh~HX6zHee+T12!sJiCSyGpanFwytj3R9pMZVio|8-1@MFq~V&m`MRw-|t;C zLRLoLpC4=wEd%nQ6|U+p+zy@t+l91(%o426d>j4<`=mxxBEE@ai+HYjq_?PJX`g(v1} z-+b)tvvwbx10aX3GYH+q2AYKD{1FHcV(Y0l)w3nxI?@Ns10nw4irO8%cW+JQVoQ0{ z9Y(4y-NKQyJZ|Zd_twVlMgrfovm|9iIN=>O_8In=W3}V@?Z$fW(kRnp2};q`H5KS&By+JT>YsFl7MjzqHAwu>8vTWrtBw0P1$&T)4_NPXf!M1P0gBq z=rX*tKa={VHnCJmNw(cIsvnD4CG^|ke)ZL_$3Lj~|NYZal*AG>gE?lMIp2D~76CU~Sk?nTho1;w(Pw{-u3_jlychu)S+tR4DO|}(#*rUqt+T$23VOhPWhJ92R@YQ;surp+u5*GkwPdzSv`h zJ3+mci8KGpCIm@r(5GzDZCPOfCL>)W2R|D380;Koi-&LPKcE44ekoyX1PZZD#& z&xymQ1axi{eu_Pe=g0Q4t-=Ek zg&KG?M#~XGUvpFwzH9|V1HXg=>SMShm?U24v|Wvj_@FXJW0wxb5V%-*c$VIEbe zHD{+v6CaMje-1_Gs^}tvS?x$g`hGkn<=AY9Pw3mjz#cjT^8`hUCQr8{A~$n>Y5?(= zT~3qBA5nJxM?dh>rdd|<~vEWiHybz|X04_3p1X6)zLB{eonbx{M}+;Hy+ z@1D%$K0T(lA|-)eeq@~BA+y*Xrr3+auQO5Rc#q4YTfB0%P80Sn$^Zw@vDT{?{`lY; zTm6IhJG}TP!y4o4(|sjq2k6>)Z>kth0Z0*iZ)6w;j5>gqWNn_U$J^1nn%Z(C791v* zI@dzxzl!QMJF*m^v1>qan6FY)*mep*-43c#0A}4ZRu|pi{qbEm#G%Pb^wZ0rykobE z3wyXV)8&pH$C$EnoW~h%458UMp3;=60%-B3lbVvAtgUtXwx|)5hj8zqa`d!*5Pk=fbXYmZ`r=;Ey zd>bHfXN}?92S;Sh9u!OYZXFYS*t$FvupU*mt0dd5o#Sd0u-a&@7S^y!F?Y1>^;oyJ z^ge&^tIIfN-5z_p4NcW7-Kp%F!{t+DMuf);$Fu4fFMe(E(evru6qNv|rn|%?$H6ni z3>7=b)PC6f#eVZwt zQ#$LIDSA@&E@uREXY8hJS?~Cq9UAt~pr3A=sUCOVkC7kyu6KLc=6$u>W?dOu;B-7K z66|jFFxhULX84nlbimvyv*oi=t*RS)QpudyEK))Z|IV!q2iFiaBns)gn{$cac_#~i zJy#?j!;PkEiT_lb?mS;&q;#K*c#eE_!6q>Iv!N`zS%F(Q0GWZgJJIzZ~>t;D75H3lydy8; z2&qY`<+i`@6omVWzxi$X*T4JrgE=XJRr-?3>d6kX1xxR{(%@JZJtH&}t7XH3 zk8FA{lw8R01PBi&x19-Pj@DCfsijWTA@^m+5nn)+nc#C>J`l2rf3DI17~aleiMP%& z%fqn&naTAga>eUx4FZb}`_z3B1%m=|Cxq`Q5L3){aIvs5M=K>4!AtS7!(S-i}(1aNb81wcPNYYsMWnpA!uq; zIUqa@*1|9b@BQgMj_h&v8)q3fBC36|W=l%vk@z98cOJgLc>CG*AS<4z{mzuM+1+RK z!uOzIVxk7+&ue zAsRVNTCS!JI~N>`MGblG(@l2|x9%ialU~$1r3P92xO>Cm3!?~3fOj;*JKvbj=>~gy zU>nfuW@i}gu6EX+u!s%J$xS9->(x-rTZxy{(ry9opG?W*qNPed%+Kdseuz!gnd(xn z*NVTxKw4phKp%@2?!a6RBDG8KF0)O&sthn-~N|x%isLXchhDyP%E3% zR^00&ssrb7G$O+%&As^FCHkf0W-86XCaYz4cZDik<{=GgGWCKzUE0=%l?`u4>3vY- z>sGN$8cf*RoRw4&3B>T+Dg+ivX;C0at%UXPV#`eC;HKaKd;1{t&;^H&2G2Xbey$P4 z@U;6WzsZ&$Xw5iwU~5O%%^e<8?aOTmBgfNASht9qv8%hFbNNfYA~k0bKAZ0bpuNtB zyDXsVyyRoxJX@*n=Qxxs7#qqy09OG?4Q1G~_U4k)-L3mb(x6vj3{WEkb|-j^&7`wK z{Lo!lXUP`oz>W8#q=;dCuGb+mAQITAtFQ_=2!v;p9G-UkUzw-@j>{SV^0~6Gc}e+1P=gOJ%movs|4)X2oqI^$XYL+@i zhwqaCh%RhQU`AbUh!rffOJb4wOcO&5zz>t^jtYiHxff|Vdxl~-{^O98xubB@xs+9a zuMwJJY8%e7;k^rZnkns!>j0Zk{hAXwmfCLfRT3sQ*b6jMONhCGpXil)xo_27{1tq% zXdW!2+mbkFV(Ie$AlTQ1&mFaK>^0v&Cg+!X%#J@Yo}n4>oLe7cjlF6O`yy{l z4YZ<(!~mT5y-3^|nKa{XV+wPS=|~{nUd)LIWVOXvm(|~ww_%d3-fEcH8_~r~mMs>N za)&*Xaa>1IbC(K!C}#$@^cS^*T#&!}yAS^Pi6R=DR$TH-38kX;6<@y&?hNQ#=Vqg7 z3|F{tFl!pfNo0#5V{|2W7|3rsv9DxXbk~D+(;LC3W!}8J&XI`|?&yGLIwe1bD?)9@ z?4mc72G@dH!chXt;a(hr<^3A_BD(hyUltK5M~LZ9T3yBFz_iDkTTw98P6I| z-A;`^+GA~qmM$DWXNq_mXd%vxsTl^_C$@Z$b4UurEGgDZEOJ}OeA#1ja5+{(lLXbx z_^%gDd<;K>_n-`VBm2K$5~Wh2K;^=FvQA|N>dufi3scL zV`8#rq-v{mi2nQeuv~|e@?ZLbd=L3}{cP=-I|cFhuA8B!)^nU|3UI;U)CT8Sw>i{^ z+K0@#Mc>8PMudmm5FN)hFqf?cai>4US>i21O`ebW{NXBhvZ28wOcX)en` zj|I$Nxv6xpou|`66tPm`lG+J(UJ*q2(KD@xNRPL-qwD+GFUFo;cf##O<%zJnQlWK&w+hs*f;~FLuqjzR$x)A@9VeAre<{a|1J=2IAjS#gBZ5DWCeR)gT}tvG zbFc>@#TbO8Xn^j-#%SD9aCJQ1Uc}_N#;wt|0&c$BEO*u10bY%SV-^Hv%s?0(oaU%J zs~89w4pvBaGMXhCJkg~u^@8~7;2emOqXS~v^7#-aV@tikmB%sRp;NO4r04RElQI`& zfL9gU-SG{#mMvGNd9VHEpr|iSe3Etu7FCp#67HR(X7cL3-n<5X8*WF4nNQk*4S{)r zX(%b(BD`_e2ma}Fi^D6K5f=^!jCb8Ab$@;b0^X)v0dbc^z?<>#0GzQc>tsOM*D!Ef zhtGe4xxfFc3sOVWgZqVeeVl3LuBB!%6!tcMGrY zi()2|sd$QL06076e5(|^8LYq?a5K<;p9~APn$s0*r_$sPxNLT#s0*1zZ@HC?E30ny zb8h34kVr@aLf9Y~6M{g_;Ea)!1E!vUGH%Q%oXTG7z%|V^qmbcO{!%YU>2s#07h*AP zePX>~V+jcaZ*qjG_>!9ZVP_s|E0=K}jc@Z&#W)=iFiUoHrS~K?seK0Kzzi!xxqGXp zM{J-OW$Eqk_eK#NJ=TY6Tee(DNF}DpaCxk*3|wcndY%&boK&qDsj@5=%)kY#r#Ncl z-cW&+^gYK+;p&Ks;nmumR=V1h{cN-UQZGoJ!?BPgyXJ@$IWTAcanC`-H?CT&H!8iH z5_LL;t#obdtsfFMOK~}HhDkDL#d|C32s1vo(?iN!qZ@7RMPtN_r9$JAsvuI+`=JodEq!CYGJQfRlVf+=p)@}`AsyT*yfBmRRG4Mr z5?{{7gfDP}FZY5dOBb!nf#`|zAIh+h$iYbyA?^HT)0Maw=f zzi#_Z9}M6xSDkQVDr6&w`rY3;^9piVl9vw2ia*G!QCyI|r^H$z%kQ@uRtF|>(Tokl zSfn_|$q}xOHFE^`;8w`k9`K;d`OK_#jKt_ccA_DUH~czAmA^Ro_|yxZ{S-hT4p86T z=lSTb=l-34D*+<6yOQXSn5Gx$^mDi8V;Kn`so)+3|PmeB3lGWC#&>sZT7{!l?M* z%mIxY(ph#~jjiK^Jcy<|J!bq*8_WC?UXYO;RcEQ@7;dNKMoT_tn_py55CmPC-Vm0v zC~s;eT!Er8;4yv%(Z^rjeNQ+iy-^x9%Q{R(f#LlQr|5nvA+W*S&JXRdc{sc4u^Vvm9NwRe|=d!R_fi7*69li^Pr4qY)gLC@&5@0EBSk%8 z#{2coil)@r%Vjkx0qJaA0jQvUa!$!|I zD}iLkcZIiqTZ1k1OgrHSQ!lfXRFNwA{T)$UHVddbQW<4$Gpnr#DeFLpK`Em-FprY$ zc90ylw;?24gZ1#u#f zG&b}?@JrjdteW#avrJ~T=C~-J+-<2m+&Q1wk*hA}oewvHt?GEN^Nr!(Ji&%|!%e}= zJdIMaUFZqRvAkEDLu>`)rR27!4zy@C9bvNMm)l92HqDi6mCkNe1;{o6CjiYc!G-$< zQ-n3nGOVUY7sR7#Dyn;pNa7LQY*cZff}O;#G?p*@bR%fXRnWi)* zH~JVaajfwb%Ov_hURYzr#fc)G<$1Z-8FbTISPzgDPNO$cu(A`ItvSdxyjOX^Q>%dJ zBJ~8AkiCQgOh)5~4tcq7&!zx5CFd+XlJH1!` zaf%vWL^e*pwYA~Py&$~4I>yKnGg!p65vj3>$QH5WT65i#z2vz)aJT?Nucv|MiE*4M zNGTc;X0cP&uzMO1jFpQ_Gad65M~=WUaq?DXAkYOZLH2T2<+x_LANWc_=Tji)+J>FA z0=9`=i(^`AWr}J&g?Pr@Qk;dKP68J682~#o<+x2k(Q_Zt$~E#L({q#<%a7y5N#{AG zSgwfJXRg6Ubm!hcP^q@4DqMQlBT)%?ri-x;GqEhz$`KIrj%5W7yClM%lb~X=@w~|& zdVjRCm+5WQnml=K`NpNg)6cG~S%#6qm~mCOCz?2`37|IQ^6WjC9Mf9+c&w+qV_#u;!s)#5+D2Frv?-=v9t4dyo+3FU=m0SpywBfNnsr2GLtt^$X>a%$ZMqsIiR(7ccw7P3D}@S z;4{`F2H_w5E|18PfV-LFz^_6GT6ukDnY_X8An`eEp-u^x7oUoYW+@O%_m`!n#X6Sa zT4@BD!<8T9D^R0x$Fs@r&jyWn;GfSjx5JWyN?d;|xg*5cc5*CLY;Y`F6L*|xyA<3V z&3*7NU}Rdo$+x`<^iBvbZ1zoB4RODD8U#Ql z?hm1SNQun^|T$k=SJXfrsOIt;)jr z)seYmZqK*^HX#awGf-)(<6p_R8cYHYVVgEl?-`}{jj_GA%suKk#wq2^r3)|#K6kyb zlUkX?C?q=?B}?<=W^+Gj54iklWu^OogOHg#V5A_~ZLUal;iU4k6O-xY0FNBO=4T&j zJJ!>rO0bz5b(@$bDzfu&oka$-oMc!Ak@of66E|}=p*S1hiBu!pQ6;Ilj*LhJ09Z-h z#Nrz2QRs#DGH^drWgP#sA6?Gd78+a^c)nrfHp!`M?Yo*l|XZM)lP*bXsnu{l*=wxjyk1sRs}d{CVx zN}g&7TiW9Q92y;jTGrJo?87@Y3MlTe1pnkZf%LzI&@|bzd2J;SrjklI*86dg?*NF;BJ;xXLo~peSLwac<;h;M{VAx6>*a#V1 zX3_vZlW#Wm zJkHj#3ce94>?JW}WCb9|HO!UipfbhFIJkD9kzJ72Ted7WO=h->v`223V8t_y z!_VV)z7qic+sd6`!kN1%JbM{}I+BffjCA{KW1I~mzrPaq7PvFx3p|jF>@2dZX#%SD+=Ag7@o%2X zC^u?CiDF<=ZW~&E1h0=>kOcoSrUsD%d>NmEdvZ zoJium2uA8jxI+j68@nJ2*G$1|#?AG_$`knq?h$}4d``AIkS{x=_RfJM1%)DHP$k+Yekl7$fdKp40yr#0pAPO8K4{3?3k- z)GYhj$OexxuZa;}{&oG(SEeWm%vpKi&yfkPNjeQpe>kpnP10q$l>Hj#Q&<{vT3sCQ z*vAkjLnuIK1YiVe?W8iyEIr~i_gf=sFn*8_dExIc+k`tf9LzOy?|3RdomcL4Kg_XN zX%9+RkMIHP9VEZy;%Rm$(;oiA`+wNVc`n;skOE(hoXgnd>2D>9Uy6*S&G0(=Tsum2 z=sl2 zUfh49?gyx%&Pf(0Qo38W9u(5FR&8s08FM(Dw30Bhnmp9o2#e*r%vN*+phYekXA$ws zqz@5zVUxHHq`{U4F5ta{c$2{Y3B6pab?#Mem98B7MB(@^t}xrziQtQp{9K)#?BC*2 zo7XVw`nEkba{0jNP-)r)qvI$B7{?`&znR|qx-o;%UJuond{t>hy;7OGZtV=*g&(D2 z$L`DZKFRAj+rY6a!e5xXiUn?pDmxrx=zRbVsO^vM)W7$F+*AwW-p_V4q3_)&^vhQV zhxdSI>lnvNI|)42yRYKW>gQJ5 zS#x$&8yOj5ql&FB$@ha)$y!AozX5as&{4hfCwdL68o8bR%?@QLrUrH$ZgXslD$yTBPO3xtPb|xTJs=u@-}hYDjDau?1ay)6hVNj zft^#kix7Y<)QD^K6hI#g12>gUmFZf)ces zx%`MD`S)Isi+V_c8-!$lm6m#eQzl(puHX-pk6~`_B$9To z;d5gCFEAM66oF+ObxS=jh{nZ}g*AC*UuT{!bXUwLJ8+8q*k;F`F5iU>rhu0mm(t#G z`z?59h-_KtQ@ntM<94`Meo{B%{?*0DghcF@c7dP`rdTl zD7UgAW#Nx@B@Gj?aGCtSY~?l_i^EI8d`@2LfZbS*Qa7m$Z<-paL4X(-o6cE~QQ*_|~2>#X; zSBG1YU8%qS5Ifi|aySRSzRV{Rh!Bjn9gUQMz4KXfz=m=$>}8D1s^yw2c^=}f!UmW zWg(ZfqB0jMV|~EM;F19+fn*;wBFUX)$8O$6J4Ws`qHx@WgH>G081U;sMz)`XJw8Oz zgXRW}fui8L+6}zTLftoM|JxZg65!G#(;2to5Tmx#^Y{RTeO0`%{EgZ`mXRU;!Q(NxXK9Yw2V2gu5ff^DKryr5yKl)}3s(gS~i!t0KjWTYziNwzEmkxj&% z@`>~#kJRI4A(*08_XZhYr1&1>!y9znZG}$e{koqCC5$LPi`QKk;z?a$lHBTC&+@F- zlKtqZ9m9r9m00IqMS%fOme{8jsjAu$9v*^Z|Jk4oH1ou+q9ZxSF>R$GA z_oARf@~4QQ(qO4c;w+=SC^(B83>;p(3ryLwdK3x6s5bzkO{#2-D8z6?O38NXBI%d? zrtM0SaT8m=*ED_iGtT8_^CC~4`Cx;F<9l!Y0mPcxKyL*^J1^`@n0Pg$yMIpj;{#VC z1-hnnlo>x!(-i^WvuC?HXF38~3%M~D5PDe1_Hga!=cp4iN>vz?Fy5p$5**ph9FLCPx<~^~J2P8DRF{eW33n(D(l!eo zu`QxaY|EjdC<3*&RxHnVangTFug7f&l`x6z*3o<0M#DXy+!nmYpG&Ox&})<)&m#4? zc%u3OI|l%bm>%3X$6;iZEeE}EFGfJ%JYRaAH&_}eCWB6-9sqs*c4V%nnPlx5+!=1i zwA~W-V%hCfwr(%m&ZaFg`kOu2cFJwqA(i-ZIQ5SWOmWFiVWImP{F&Owbon`O#uJ=A zUvl&55Q%2>Ip5oXgS$EYoG-{O{hn-;no~GC%E$u^9f5{H6rq~ZAZi%2hhcl+v@#RI z?aF#jos%e1aUTNdAj{xzT>u3f`RtH3|j92wt{FC63$+kq$2#qZJ{vJMP_Cw%3!%eC&7$B zTX8IAU`AbJTsv8>4XWNP3|AS@%?+HCvruNnZDXPkxH?)#_ zWVli?s$u4X3!WDlf5MH)cZbL}ApG0Y&+mem`W(2>Akq}Hf9hI(fa%CG6(2LPJfZU+kK)4KD&9z`nbwgON@cHqlB3~N}GSaVPjnH^LwUH z`*>NX{e^hF?i_Ur3kNKGgQver2noL3bUTvUsk!Z@G7UabMN6gzDSku=<5aGW{U2+e z_UT}{cz0ZQz}7e*<{g*r?#R_%EN>P2+d$$Y^M=%1HzkcfC#3s55Gz;r52ViTNcOO} z4p_w11DU)52PL}HGOT|Eqi-C$=&pSk8w+btViMc~2#Fg&m_qavpv#R7y-et=M)VRd zi4Zy>%rqN?&O()_Pwm^ZujTnwVt+&Z*`)hjH?LFQ-2T8NYEc%kZ~%(J+zhrbi9hf9 z&L@!ZmCL*hJruPY5Yw|uY?b2TgUdnZ zg||88$a(5Xet=Ut6uJkUymQn*$qAe#KVObF_K32W@f5yek8NRBF6*|9qcQG6Nt4tj zDx3Q{Q4LGbFGmE}qt51>Ox^Y{%bp%1iZe+?)^ov6+)cLR>O@}YV);X`NB&tt+z(z5 zTdfvw=beXiTSY^mmh1)fKdtESv4=D46{~^=8=9NxxD~jW=upydt0W=9-bBl;6FuFQ zEb5kN$)FLMy6D**ed_^!z8T-OM`Jcb^mhu>4sM3UsVI+c9Mc9oj2lP8-RVKbX#*1 z)z3SJ`>B^37y1(#!@aggS0aFrl|Nuho6TzwjS%ldo;cjt^>|D&;2b#2oaxl!im30) zG>_wWN_Gg`PGpbMaa~mso1Q=+}3raPzf7-N+En$|A zXG44xUtL038pDL3>M0z;JwlD_iAb`bo)`;J0u1CTd+NA46U>rj0F&?Do++lf0&tMu&c9 zI*-`6>w#qkMoCJkXDOqoMmT1F2lp@Q<(ChrPG%CZ+c5KOGIqg zVa3clTX(cE0K()iD>AM&bbkCtR%8xQGw7hV`UcDPC5+zZL>^247XPO!J| zCR$v}2IzK?_Nw4Mwng{(^|%Pa9^0$T(#(=F2Xb9RoSs4!W|0yH*xX>)gUdF!(Mz#o zyshDqtx;*TWb&!^oC2BE^I?DXi-Naa%Yq4#3|01u3${oWZfokfufS|%gxoVYCJIi^ zSEe4RHoof{#L5@_n#i%3?;Zmfw4OC)v6Sk{@2jIL-CIoEV? zoJjU;-N8@q)HvprHlSqhSxcnz@(+^g&#$MynBY@&F{hX~X}ib_lk${#TUuJe%`#Tw zWLFU@>9r)RR=79ieVsT9!r6~ageiHMKsxMm8_(xn5JPpOiG9)Gc0uU4l+d^RBm*-P zd=Ii<=OR6(%$xTIs~SoY!_vf~*@Me*1mm8M`3I2?@QHe0xr4KuxF9klDj&a19Bsd_j={Y)F+S8NY%1K$4SUp<))EhPFqW6M zx1X5XQWkxoDsCE{^He{agB+Hr^4Pzvw+py*-$_~f@U)BYAivd1I_AiUB zKNtDFkPDI~$yC|yxsdUN5OqoNy6X8PzK{zthY~NYY|q(~FXPo`X|vj&?ePUcELh|2 m8Do7xT%@G>8Dsr0000 void get_iterators (const Point_3& query, unsigned int k, FT neighbor_radius, - OutputIterator output, bool fallback_k_is_sphere_empty = true) const + OutputIterator output, bool fallback_k_if_sphere_empty = true) const { if (neighbor_radius != FT(0)) { @@ -133,16 +133,13 @@ public: catch (const Maximum_points_reached_exception&) { } - if (fallback_k_is_sphere_empty) - { - // Fallback, if less than 3 points are return, search for the 3 - // first points - if (nb < 3) - k = 3; - // Else, no need to search for K nearest neighbors - else + // Fallback, if less than 3 points are return, search for the 3 + // first points + if (fallback_k_if_sphere_empty && nb < 3) + k = 3; + // Else, no need to search for K nearest neighbors + else k = 0; - } } if (k != 0) diff --git a/Point_set_processing_3/include/CGAL/cluster_point_set.h b/Point_set_processing_3/include/CGAL/cluster_point_set.h index fd6e27afe27..6d25eb54d2c 100644 --- a/Point_set_processing_3/include/CGAL/cluster_point_set.h +++ b/Point_set_processing_3/include/CGAL/cluster_point_set.h @@ -196,7 +196,7 @@ std::size_t cluster_point_set (PointRange& points, neighbor_query.get_iterators (get (point_map, *current), 0, neighbor_radius, boost::make_function_output_iterator - ([&](const iterator& it) { todo.push(it); }), false); + ([&](const iterator& it) { todo.push(it); }), true); } diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp index 17986cf4e8a..b4b11d6b1bd 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp @@ -193,7 +193,7 @@ void Polyhedron_demo_point_set_clustering_plugin::on_actionCluster_triggered() Point_set::Index iidx = *(colored->point_set()->insert (points->point(idx))); if (cluster_size[cluster_map[idx]] >= min_nb->value()) { - CGAL::Random rand(cluster_map[idx]); + CGAL::Random rand(cluster_map[idx] + 1); unsigned char r, g, b; r = static_cast(64 + rand.get_int(0, 192)); g = static_cast(64 + rand.get_int(0, 192)); From 8423de8b956ab50191752198f4f3f01a0cb44b12 Mon Sep 17 00:00:00 2001 From: Simon Giraudot Date: Wed, 18 Mar 2020 14:57:53 +0100 Subject: [PATCH 11/23] Slightly modify algo/structures to accept 2D points as well --- .../internal/Neighbor_query.h | 15 ++++- .../internal/bbox_diagonal.h | 59 +++++++++++++++++++ .../include/CGAL/cluster_point_set.h | 13 +--- 3 files changed, 73 insertions(+), 14 deletions(-) create mode 100644 Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/bbox_diagonal.h diff --git a/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/Neighbor_query.h b/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/Neighbor_query.h index a5cb067a5a0..f5758b40c85 100644 --- a/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/Neighbor_query.h +++ b/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/Neighbor_query.h @@ -14,6 +14,7 @@ #include +#include #include #include #include @@ -39,7 +40,12 @@ public: typedef PointMap Point_map; typedef typename Kernel::FT FT; + typedef typename boost::property_traits::value_type Point; + + typedef typename Kernel::Point_2 Point_2; typedef typename Kernel::Point_3 Point_3; + + typedef std::is_same Is_2d; typedef typename Range_iterator_type::type input_iterator; typedef typename input_iterator::value_type value_type; @@ -64,7 +70,10 @@ public: } }; - typedef CGAL::Search_traits_3 Tree_traits_base; + typedef typename std::conditional, + CGAL::Search_traits_3 >::type Tree_traits_base; + typedef CGAL::Search_traits_adapter Tree_traits; typedef CGAL::Sliding_midpoint Splitter; typedef CGAL::Distance_adapter > Distance; @@ -102,7 +111,7 @@ public: PointMap point_map() const { return m_point_map; } template - void get_iterators (const Point_3& query, unsigned int k, FT neighbor_radius, + void get_iterators (const Point& query, unsigned int k, FT neighbor_radius, OutputIterator output, bool fallback_k_if_sphere_empty = true) const { if (neighbor_radius != FT(0)) @@ -163,7 +172,7 @@ public: } template - void get_points (const Point_3& query, unsigned int k, FT neighbor_radius, + void get_points (const Point& query, unsigned int k, FT neighbor_radius, OutputIterator output) const { return get_iterators(query, k, neighbor_radius, diff --git a/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/bbox_diagonal.h b/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/bbox_diagonal.h new file mode 100644 index 00000000000..5f02c1c91fc --- /dev/null +++ b/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/bbox_diagonal.h @@ -0,0 +1,59 @@ +// Copyright (c) 2020 GeometryFactory (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Simon Giraudot + +#ifndef CGAL_PSP_INTERNAL_BBOX_DIAGONAL_H +#define CGAL_PSP_INTERNAL_BBOX_DIAGONAL_H + +#include + +namespace CGAL +{ +namespace Point_set_processing_3 +{ +namespace internal +{ + +template +double bbox_diagonal (const PointRange& points, PointMap point_map, const typename Kernel::Point_2&) +{ + CGAL::Bbox_2 bbox = CGAL::bbox_2 (CGAL::make_transform_iterator_from_property_map (points.begin(), point_map), + CGAL::make_transform_iterator_from_property_map (points.end(), point_map)); + + return CGAL::approximate_sqrt + ((bbox.xmax() - bbox.xmin()) * (bbox.xmax() - bbox.xmin()) + + (bbox.ymax() - bbox.ymin()) * (bbox.ymax() - bbox.ymin())); +} + +template +double bbox_diagonal (const PointRange& points, PointMap point_map, const typename Kernel::Point_3&) +{ + CGAL::Bbox_3 bbox = CGAL::bbox_3 (CGAL::make_transform_iterator_from_property_map (points.begin(), point_map), + CGAL::make_transform_iterator_from_property_map (points.end(), point_map)); + + return CGAL::approximate_sqrt + ((bbox.xmax() - bbox.xmin()) * (bbox.xmax() - bbox.xmin()) + + (bbox.ymax() - bbox.ymin()) * (bbox.ymax() - bbox.ymin()) + + (bbox.zmax() - bbox.zmin()) * (bbox.zmax() - bbox.zmin())); +} + +template +double bbox_diagonal (const PointRange& points, PointMap point_map) +{ + typedef typename boost::property_traits::value_type Point; + return bbox_diagonal::Kernel> (points, point_map, Point()); +} + +} // namespace internal +} // namespace Point_set_processing_3 +} // namespace CGAL + + +#endif // CGAL_PSP_INTERNAL_BBOX_DIAGONAL_H diff --git a/Point_set_processing_3/include/CGAL/cluster_point_set.h b/Point_set_processing_3/include/CGAL/cluster_point_set.h index 6d25eb54d2c..c06979fe106 100644 --- a/Point_set_processing_3/include/CGAL/cluster_point_set.h +++ b/Point_set_processing_3/include/CGAL/cluster_point_set.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -136,8 +137,6 @@ std::size_t cluster_point_set (PointRange& points, typename Point_set_processing_3::GetAdjacencies::Empty>::value) callback_factor = 0.5; - typedef typename Kernel::Point_3 Point; - // types for K nearest neighbors search structure typedef Point_set_processing_3::internal::Neighbor_query Neighbor_query; @@ -148,15 +147,7 @@ std::size_t cluster_point_set (PointRange& points, // If no radius is given, init with 1% of bbox diagonal if (neighbor_radius < 0) - { - CGAL::Bbox_3 bbox = CGAL::bbox_3 (CGAL::make_transform_iterator_from_property_map (points.begin(), point_map), - CGAL::make_transform_iterator_from_property_map (points.end(), point_map)); - - neighbor_radius = 0.01 * CGAL::approximate_sqrt - ((bbox.xmax() - bbox.xmin()) * (bbox.xmax() - bbox.xmin()) - + (bbox.ymax() - bbox.ymin()) * (bbox.ymax() - bbox.ymin()) - + (bbox.zmax() - bbox.zmin()) * (bbox.zmax() - bbox.zmin())); - } + neighbor_radius = 0.01 * Point_set_processing_3::internal::bbox_diagonal (points, point_map); // Init cluster map with -1 for (const value_type& p : points) From 42edead686b8699b7bd1705339da784d2c924aed Mon Sep 17 00:00:00 2001 From: Simon Giraudot Date: Wed, 8 Apr 2020 10:28:22 +0200 Subject: [PATCH 12/23] Fix text and trailing whitespaces --- .../Point_set_processing_3.txt | 8 ++++---- .../include/CGAL/cluster_point_set.h | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) 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 2de6c16e556..5d9c75a112b 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 @@ -318,15 +318,15 @@ points in the domain. If an input point set represents several objects which are spatially separated, a clustering algorithm can be applied to identify connected -components on a nearest neighbors graph built using a query sphere of +components on a nearest neighbor graph built using a query sphere of fixed radius centered on each point. The clustering is stored in a cluster map which associates each input point with the index of the cluster it belongs to: users can then use this map however they find it relevant to their use case, for example -segmenting the input point set into several (one per -cluster). \cgalFigureRef{Point_set_processing_3figclustering} shows different clustering -outputs. +segmenting the input point set into one point set per +cluster. \cgalFigureRef{Point_set_processing_3figclustering} shows +different clustering outputs. \cgalFigureBegin{Point_set_processing_3figclustering,clustering.png} Point Set Clustering outputs (one color per cluster). Top: input point diff --git a/Point_set_processing_3/include/CGAL/cluster_point_set.h b/Point_set_processing_3/include/CGAL/cluster_point_set.h index c06979fe106..16b394c482f 100644 --- a/Point_set_processing_3/include/CGAL/cluster_point_set.h +++ b/Point_set_processing_3/include/CGAL/cluster_point_set.h @@ -63,7 +63,7 @@ CGAL::Emptyset_iterator get_adjacencies (const NamedParameters&, CGAL::Emptyset_ /** \ingroup PkgPointSetProcessing3Algorithms - Identifies connected components on a nearest neighbors graph built + Identifies connected components on a nearest neighbor graph built using a query sphere of fixed radius centered on each point. \tparam PointRange is a model of `Range`. The value type of its @@ -110,7 +110,7 @@ std::size_t cluster_point_set (PointRange& points, { using parameters::choose_parameter; using parameters::get_parameter; - + // basic geometric types typedef typename PointRange::iterator iterator; typedef typename iterator::value_type value_type; @@ -161,11 +161,11 @@ std::size_t cluster_point_set (PointRange& points, // Flooding algorithm from each point std::size_t done = 0; std::size_t size = points.size(); - + for (iterator it = points.begin(); it != points.end(); ++ it) { const value_type& p = *it; - + if (get (cluster_map, p) != -1) continue; @@ -181,7 +181,7 @@ std::size_t cluster_point_set (PointRange& points, put (cluster_map, *current, nb_clusters); ++ done; - + if (callback && !callback (callback_factor * (done + 1) / double(size))) return (nb_clusters + 1); @@ -207,7 +207,7 @@ std::size_t cluster_point_set (PointRange& points, for (const value_type& p : points) { std::size_t c0 = get (cluster_map, p); - + neighbors.clear(); neighbor_query.get_iterators (get (point_map, p), 0, neighbor_radius, std::back_inserter (neighbors), false); @@ -230,7 +230,7 @@ std::size_t cluster_point_set (PointRange& points, auto last = std::unique (adj.begin(), adj.end()); std::copy (adj.begin(), last, adjacencies); } - + return nb_clusters; } From 4fb633a6bed348c835bc03be06c6e6c65c2df708 Mon Sep 17 00:00:00 2001 From: Mael Date: Thu, 16 Apr 2020 18:59:54 +0200 Subject: [PATCH 13/23] Add missing include --- STL_Extension/include/CGAL/iterator.h | 1 + 1 file changed, 1 insertion(+) diff --git a/STL_Extension/include/CGAL/iterator.h b/STL_Extension/include/CGAL/iterator.h index f9afdb2234e..6736bec9d72 100644 --- a/STL_Extension/include/CGAL/iterator.h +++ b/STL_Extension/include/CGAL/iterator.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include From cc0420a658ccd317a9ad6f4c0e776f42f012965f Mon Sep 17 00:00:00 2001 From: Mael Date: Thu, 16 Apr 2020 19:14:11 +0200 Subject: [PATCH 14/23] Fix namespace --- Point_set_processing_3/include/CGAL/cluster_point_set.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Point_set_processing_3/include/CGAL/cluster_point_set.h b/Point_set_processing_3/include/CGAL/cluster_point_set.h index 16b394c482f..7da96d2bfba 100644 --- a/Point_set_processing_3/include/CGAL/cluster_point_set.h +++ b/Point_set_processing_3/include/CGAL/cluster_point_set.h @@ -114,7 +114,7 @@ std::size_t cluster_point_set (PointRange& points, // basic geometric types typedef typename PointRange::iterator iterator; typedef typename iterator::value_type value_type; - typedef typename Point_set_processing_3::GetPointMap::type PointMap; + typedef typename CGAL::GetPointMap::type PointMap; typedef typename Point_set_processing_3::GetK::Kernel Kernel; typedef typename Point_set_processing_3::GetAdjacencies::type Adjacencies; typedef typename GetSvdTraits::type SvdTraits; From 73ea825b07d5233ed5d7a0d86254c4df456c8897 Mon Sep 17 00:00:00 2001 From: Mael Date: Thu, 16 Apr 2020 19:37:22 +0200 Subject: [PATCH 15/23] Fix unused warning --- .../examples/Point_set_processing_3/clustering_example.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Point_set_processing_3/examples/Point_set_processing_3/clustering_example.cpp b/Point_set_processing_3/examples/Point_set_processing_3/clustering_example.cpp index d6d2b406cde..08151c1cda6 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/clustering_example.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/clustering_example.cpp @@ -16,7 +16,7 @@ using Point_set = CGAL::Point_set_3; int main (int argc, char** argv) { // Read input file - std::ifstream ifile (argv[1], std::ios_base::binary); + std::ifstream ifile ((argc > 1) ? argv[1] : "data/hippo1.ply", std::ios_base::binary); Point_set points; ifile >> points; From 4c78812c02de417a12c7c7e34cb5a1f66d3b58c5 Mon Sep 17 00:00:00 2001 From: Mael Date: Thu, 16 Apr 2020 19:39:06 +0200 Subject: [PATCH 16/23] Fix clustering example not requiring Eigen in the CMakeLists.txt --- .../examples/Point_set_processing_3/CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 a91ee87aa08..85f5f79eed8 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 @@ -47,7 +47,6 @@ 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( "clustering_example.cpp" ) create_single_source_cgal_program( "read_ply_points_with_colors_example.cpp" ) create_single_source_cgal_program( "write_ply_points_example.cpp" ) @@ -70,7 +69,11 @@ if ( CGAL_FOUND ) create_single_source_cgal_program( "normal_estimation.cpp" ) CGAL_target_use_Eigen(normal_estimation) + create_single_source_cgal_program( "clustering_example.cpp" ) + CGAL_target_use_Eigen(clustering_example) + create_single_source_cgal_program( "edges_example.cpp" ) + CGAL_target_use_Eigen(edges_example) # Executables that require libpointmatcher find_package(libpointmatcher QUIET) @@ -102,8 +105,6 @@ if ( CGAL_FOUND ) message(STATUS "NOTICE : registration_with_opengr_pointmatcher_pipeline requires libpointmatcher and OpenGR, and will not be compiled.") endif() - CGAL_target_use_Eigen(edges_example) - create_single_source_cgal_program( "callback_example.cpp" ) CGAL_target_use_Eigen(callback_example) From bbd24f2946f68629e228a1b8b7db68178daa1aa8 Mon Sep 17 00:00:00 2001 From: Mael Date: Thu, 16 Apr 2020 19:46:09 +0200 Subject: [PATCH 17/23] Fix missing include --- STL_Extension/include/CGAL/iterator.h | 1 + 1 file changed, 1 insertion(+) diff --git a/STL_Extension/include/CGAL/iterator.h b/STL_Extension/include/CGAL/iterator.h index 6736bec9d72..14c76b745e5 100644 --- a/STL_Extension/include/CGAL/iterator.h +++ b/STL_Extension/include/CGAL/iterator.h @@ -22,6 +22,7 @@ #include #include +#include #include #include #include From 0e025c588a367b0ec886b0c636f10c7192e22c16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Fri, 17 Apr 2020 09:22:45 +0200 Subject: [PATCH 18/23] Fix unused typedef warning --- Point_set_processing_3/include/CGAL/cluster_point_set.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Point_set_processing_3/include/CGAL/cluster_point_set.h b/Point_set_processing_3/include/CGAL/cluster_point_set.h index 7da96d2bfba..cf38b4b5fa7 100644 --- a/Point_set_processing_3/include/CGAL/cluster_point_set.h +++ b/Point_set_processing_3/include/CGAL/cluster_point_set.h @@ -117,10 +117,9 @@ std::size_t cluster_point_set (PointRange& points, typedef typename CGAL::GetPointMap::type PointMap; typedef typename Point_set_processing_3::GetK::Kernel Kernel; typedef typename Point_set_processing_3::GetAdjacencies::type Adjacencies; - typedef typename GetSvdTraits::type SvdTraits; - CGAL_static_assertion_msg(!(boost::is_same::NoTraits>::value), + CGAL_static_assertion_msg(!(boost::is_same::type, + typename GetSvdTraits::NoTraits>::value), "Error: no SVD traits"); PointMap point_map = choose_parameter(get_parameter(np, internal_np::point_map), PointMap()); From d6cf97278f5e2811bfe9212564101f1171ae7b16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Fri, 17 Apr 2020 09:22:57 +0200 Subject: [PATCH 19/23] Fix trailing whitespace --- .../clustering_example.cpp | 2 +- .../internal/bbox_diagonal.h | 6 ++-- .../Point_set/Point_set_clustering_plugin.cpp | 32 +++++++++---------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Point_set_processing_3/examples/Point_set_processing_3/clustering_example.cpp b/Point_set_processing_3/examples/Point_set_processing_3/clustering_example.cpp index 08151c1cda6..934609dd152 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/clustering_example.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/clustering_example.cpp @@ -57,6 +57,6 @@ int main (int argc, char** argv) std::ofstream ofile ("out.ply", std::ios_base::binary); CGAL::set_binary_mode (ofile); ofile << points; - + return EXIT_SUCCESS; } diff --git a/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/bbox_diagonal.h b/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/bbox_diagonal.h index 5f02c1c91fc..84976b8f23c 100644 --- a/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/bbox_diagonal.h +++ b/Point_set_processing_3/include/CGAL/Point_set_processing_3/internal/bbox_diagonal.h @@ -26,7 +26,7 @@ double bbox_diagonal (const PointRange& points, PointMap point_map, const typena { CGAL::Bbox_2 bbox = CGAL::bbox_2 (CGAL::make_transform_iterator_from_property_map (points.begin(), point_map), CGAL::make_transform_iterator_from_property_map (points.end(), point_map)); - + return CGAL::approximate_sqrt ((bbox.xmax() - bbox.xmin()) * (bbox.xmax() - bbox.xmin()) + (bbox.ymax() - bbox.ymin()) * (bbox.ymax() - bbox.ymin())); @@ -37,10 +37,10 @@ double bbox_diagonal (const PointRange& points, PointMap point_map, const typena { CGAL::Bbox_3 bbox = CGAL::bbox_3 (CGAL::make_transform_iterator_from_property_map (points.begin(), point_map), CGAL::make_transform_iterator_from_property_map (points.end(), point_map)); - + return CGAL::approximate_sqrt ((bbox.xmax() - bbox.xmin()) * (bbox.xmax() - bbox.xmin()) - + (bbox.ymax() - bbox.ymin()) * (bbox.ymax() - bbox.ymin()) + + (bbox.ymax() - bbox.ymin()) * (bbox.ymax() - bbox.ymin()) + (bbox.zmax() - bbox.zmin()) * (bbox.zmax() - bbox.zmin())); } diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp index b4b11d6b1bd..9cfd1771a93 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp @@ -29,9 +29,9 @@ struct Clustering_functor Point_set* points; Point_set::Property_map cluster_map; const double neighbor_radius; - boost::shared_ptr result; + boost::shared_ptr result; - Clustering_functor (Point_set* points, + Clustering_functor (Point_set* points, const double neighbor_radius, Point_set::Property_map cluster_map) : points (points), cluster_map (cluster_map), @@ -81,7 +81,7 @@ public: public Q_SLOTS: void on_actionCluster_triggered(); -}; // end +}; // end void Polyhedron_demo_point_set_clustering_plugin::on_actionCluster_triggered() { @@ -107,16 +107,16 @@ void Polyhedron_demo_point_set_clustering_plugin::on_actionCluster_triggered() QCheckBox* add_property = dialog.add ("Add a \"cluster\" property to the input item"); add_property->setChecked (true); - + QCheckBox* gen_color = dialog.add ("Generate one colored point set"); gen_color->setChecked (true); - + QCheckBox* gen_sub = dialog.add ("Generate N point subsets"); gen_sub->setChecked (false); - + if (!dialog.exec()) return; - + QApplication::setOverrideCursor(Qt::BusyCursor); QApplication::processEvents(); CGAL::Real_timer task_timer; task_timer.start(); @@ -135,11 +135,11 @@ void Polyhedron_demo_point_set_clustering_plugin::on_actionCluster_triggered() neighbor_radius->setRange (-1, 10000000); neighbor_radius->setValue(-1); } - + // Computes average spacing Clustering_functor functor (points, neighbor_radius->value(), cluster_map); run_with_qprogressdialog (functor, "Clustering...", mw); - + std::size_t nb_clusters = *functor.result; Scene_group_item* group; @@ -177,17 +177,17 @@ void Polyhedron_demo_point_set_clustering_plugin::on_actionCluster_triggered() { Scene_points_with_normal_item* colored; Point_set::Property_map red, green, blue; - + colored = new Scene_points_with_normal_item; colored->setName (QString("%1 (clustering)").arg(item->name())); - + red = colored->point_set()->add_property_map("red", 0).first; green = colored->point_set()->add_property_map("green", 0).first; blue = colored->point_set()->add_property_map("blue", 0).first; colored->point_set()->check_colors(); - + colored->point_set()->reserve (points->size()); - + for (Point_set::Index idx : *points) { Point_set::Index iidx = *(colored->point_set()->insert (points->point(idx))); @@ -205,7 +205,7 @@ void Polyhedron_demo_point_set_clustering_plugin::on_actionCluster_triggered() } scene->addItem(colored); } - + if (gen_sub->isChecked()) { for (Scene_points_with_normal_item* new_item : new_items) @@ -219,10 +219,10 @@ void Polyhedron_demo_point_set_clustering_plugin::on_actionCluster_triggered() delete new_item; } } - + if (!add_property->isChecked()) points->remove_property_map (cluster_map); - + std::size_t memory = CGAL::Memory_sizer().virtual_size(); std::cerr << "Number of clusters = " << nb_clusters << " (" << task_timer.time() << " seconds, " From 90ad2cb613084d767b1e48f1fc9db9c2de2c95ee Mon Sep 17 00:00:00 2001 From: Simon Giraudot Date: Mon, 20 Apr 2020 08:46:59 +0200 Subject: [PATCH 20/23] Fix warnings and trailing whitespaces --- .../doc/Point_set_processing_3/NamedParameters.txt | 1 - Point_set_processing_3/include/CGAL/cluster_point_set.h | 4 ++-- .../Plugins/Point_set/Point_set_clustering_plugin.cpp | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) 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 50275f718f4..ba478f86b6f 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 @@ -273,7 +273,6 @@ is an output iterator used to store adjacencies.\n \b Type: a class model of `OutputIterator` that accepts objects of type `std::pair`. \n Default value: `CGAL::Emptyset_iterator`. - \cgalNPEnd \cgalNPTableEnd diff --git a/Point_set_processing_3/include/CGAL/cluster_point_set.h b/Point_set_processing_3/include/CGAL/cluster_point_set.h index cf38b4b5fa7..3098db06c97 100644 --- a/Point_set_processing_3/include/CGAL/cluster_point_set.h +++ b/Point_set_processing_3/include/CGAL/cluster_point_set.h @@ -165,7 +165,7 @@ std::size_t cluster_point_set (PointRange& points, { const value_type& p = *it; - if (get (cluster_map, p) != -1) + if (int(get (cluster_map, p)) != -1) continue; todo.push (it); @@ -175,7 +175,7 @@ std::size_t cluster_point_set (PointRange& points, iterator current = todo.front(); todo.pop(); - if (get (cluster_map, *current) != -1) + if (int(get (cluster_map, *current)) != -1) continue; put (cluster_map, *current, nb_clusters); diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp index 9cfd1771a93..61eaa766927 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp @@ -191,7 +191,7 @@ void Polyhedron_demo_point_set_clustering_plugin::on_actionCluster_triggered() for (Point_set::Index idx : *points) { Point_set::Index iidx = *(colored->point_set()->insert (points->point(idx))); - if (cluster_size[cluster_map[idx]] >= min_nb->value()) + if (cluster_size[cluster_map[idx]] >= std::size_t(min_nb->value())) { CGAL::Random rand(cluster_map[idx] + 1); unsigned char r, g, b; @@ -210,7 +210,7 @@ void Polyhedron_demo_point_set_clustering_plugin::on_actionCluster_triggered() { for (Scene_points_with_normal_item* new_item : new_items) { - if (new_item->point_set()->size() >= min_nb->value()) + if (new_item->point_set()->size() >= std::size_t(min_nb->value())) { scene->addItem(new_item); scene->changeGroup (new_item, group); From c1d8fb69b78c0a4e9769bc245cdee01eadca8478 Mon Sep 17 00:00:00 2001 From: Simon Giraudot Date: Wed, 22 Apr 2020 13:47:33 +0200 Subject: [PATCH 21/23] Move transform ranges in property_maps --- Property_map/include/CGAL/property_map.h | 34 ++++++++++++++++++++++++ STL_Extension/include/CGAL/iterator.h | 33 ----------------------- 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/Property_map/include/CGAL/property_map.h b/Property_map/include/CGAL/property_map.h index f58413b9755..988c7ab6583 100644 --- a/Property_map/include/CGAL/property_map.h +++ b/Property_map/include/CGAL/property_map.h @@ -27,6 +27,8 @@ #include // defines std::pair +#include +#include #include #include #include @@ -576,6 +578,38 @@ make_cartesian_converter_property_map(Vpm vpm) return Cartesian_converter_property_map(vpm); } +/// \cond SKIP_IN_MANUAL +// Syntaxic sugar for transform_iterator+pmap_to_unary_function +template +typename boost::transform_iterator, Iterator> +make_transform_iterator_from_property_map (Iterator it, Pmap pmap) +{ + return boost::make_transform_iterator (it, CGAL::Property_map_to_unary_function(pmap)); +} + +// Syntaxic sugar for make_range+transform_iterator+pmap_to_unary_function +template +CGAL::Iterator_range, + typename Range::const_iterator> > +make_transform_range_from_property_map (const Range& range, Pmap pmap) +{ + return CGAL::make_range + (make_transform_iterator_from_property_map (range.begin(), pmap), + make_transform_iterator_from_property_map (range.end(), pmap)); +} + +// Syntaxic sugar for make_range+transform_iterator+pmap_to_unary_function +template +CGAL::Iterator_range, + typename Range::iterator> > +make_transform_range_from_property_map (Range& range, Pmap pmap) +{ + return CGAL::make_range + (make_transform_iterator_from_property_map (range.begin(), pmap), + make_transform_iterator_from_property_map (range.end(), pmap)); +} +/// \endcond + } // namespace CGAL diff --git a/STL_Extension/include/CGAL/iterator.h b/STL_Extension/include/CGAL/iterator.h index 14c76b745e5..ea2b445b6f5 100644 --- a/STL_Extension/include/CGAL/iterator.h +++ b/STL_Extension/include/CGAL/iterator.h @@ -22,10 +22,8 @@ #include #include -#include #include #include -#include #include #include #include @@ -1482,37 +1480,6 @@ struct Range_iterator_type { typedef typename RangeRef::iterato template struct Range_iterator_type { typedef typename RangeRef::const_iterator type; }; -// Syntaxic sugar for transform_iterator+pmap_to_unary_function -template -typename boost::transform_iterator, Iterator> -make_transform_iterator_from_property_map (Iterator it, Pmap pmap) -{ - return boost::make_transform_iterator (it, CGAL::Property_map_to_unary_function(pmap)); -} - -// Syntaxic sugar for make_range+transform_iterator+pmap_to_unary_function -template -CGAL::Iterator_range, - typename Range::const_iterator> > -make_transform_range_from_property_map (const Range& range, Pmap pmap) -{ - return CGAL::make_range - (make_transform_iterator_from_property_map (range.begin(), pmap), - make_transform_iterator_from_property_map (range.end(), pmap)); -} - -// Syntaxic sugar for make_range+transform_iterator+pmap_to_unary_function -template -CGAL::Iterator_range, - typename Range::iterator> > -make_transform_range_from_property_map (Range& range, Pmap pmap) -{ - return CGAL::make_range - (make_transform_iterator_from_property_map (range.begin(), pmap), - make_transform_iterator_from_property_map (range.end(), pmap)); -} - - } //namespace CGAL #include From a94cb6bc1e9b230ad22067bad5e4edb88174dfea Mon Sep 17 00:00:00 2001 From: Simon Giraudot Date: Mon, 27 Apr 2020 10:06:16 +0200 Subject: [PATCH 22/23] Add missing dependency --- Point_set_3/package_info/Point_set_3/dependencies | 1 + 1 file changed, 1 insertion(+) diff --git a/Point_set_3/package_info/Point_set_3/dependencies b/Point_set_3/package_info/Point_set_3/dependencies index cab851ef527..6f0ec394588 100644 --- a/Point_set_3/package_info/Point_set_3/dependencies +++ b/Point_set_3/package_info/Point_set_3/dependencies @@ -1,5 +1,6 @@ Algebraic_foundations BGL +Circulator GraphicsView Installation Interval_support From 0df35c783610df51f622a2d81300940d590e8605 Mon Sep 17 00:00:00 2001 From: Simon Giraudot Date: Wed, 29 Apr 2020 13:34:26 +0200 Subject: [PATCH 23/23] Fix unused parameter warning --- .../Plugins/Point_set/Point_set_clustering_plugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp index 61eaa766927..c68a2ab3c07 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/Point_set_clustering_plugin.cpp @@ -73,7 +73,7 @@ public: return QList() << actionCluster; } - bool applicable(QAction* action) const { + bool applicable(QAction*) const { Scene_points_with_normal_item* item = qobject_cast(scene->item(scene->mainSelectionIndex())); return item; }