From ecd26ebc043803af569a4624976a8aaee0cd1579 Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Mon, 22 Jan 2024 16:54:15 +0100 Subject: [PATCH 01/38] lfs-v1 --- .../Point_set_processing_3.txt | 12 + .../doc/Point_set_processing_3/examples.txt | 1 + .../Point_set_processing_3/lfs_example.cpp | 49 ++ .../include/CGAL/estimate_lfs.h | 691 ++++++++++++++++++ 4 files changed, 753 insertions(+) create mode 100644 Point_set_processing_3/examples/Point_set_processing_3/lfs_example.cpp create mode 100644 Point_set_processing_3/include/CGAL/estimate_lfs.h 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 7d62380935d3..5f94d8f7614b 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 @@ -926,6 +926,18 @@ The following example reads a point set from a file, estimates the points that are on sharp edges: \cgalExample{Point_set_processing_3/edges_example.cpp} +\section Point_set_processing_3LFSEstimation Local Feature Size Estimation + +The function `estimate_local_feature_size()` can estimate the local feature size for each point by giving a raw point set. + +The function will estimate the curvature and shape diameter. Then, the local feature size will be the minimum of the curvature radius and half of the shape diameter. + +\subsection Point_set_processing_3Example_LFS Example + +The following example reads a point set from a file, estimates the +local feature size for each point: +\cgalExample{Point_set_processing_3/lfs_example.cpp} + \section Point_set_processing_3Structuring Structuring 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 ea670af1b623..aa41d39531f5 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 @@ -21,6 +21,7 @@ \example Point_set_processing_3/bilateral_smooth_point_set_example.cpp \example Point_set_processing_3/edge_aware_upsample_point_set_example.cpp \example Point_set_processing_3/edges_example.cpp +\example Point_set_processing_3/lfs_example.cpp \example Point_set_processing_3/structuring_example.cpp \example Point_set_processing_3/callback_example.cpp */ diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example.cpp new file mode 100644 index 000000000000..890fcaf9be3a --- /dev/null +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example.cpp @@ -0,0 +1,49 @@ +#include +#include +#include + +#include +#include // defines std::pair + + +// types +typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; +typedef Kernel::FT FT; +typedef Kernel::Point_3 Point; +typedef Kernel::Vector_3 Vector; + +// Point with normal vector stored in a std::pair. +typedef std::pair Point_with_normal; + +// Concurrency +typedef CGAL::Parallel_if_available_tag Concurrency_tag; + + +int main(void) +{ + + // read xyz + const std::string filename = "./frog.xyz"; + + std::vector points; + if(!CGAL::IO::read_points(filename, + std::back_inserter(points), + CGAL::parameters::point_map(CGAL::First_of_pair_property_map()))) + { + std::cerr << "Error: cannot read file " << filename<< std::endl; + return EXIT_FAILURE; + } + + unsigned int jet_k = 24; + std::size_t N_rays = 60; + FT apex_angle = 30; + std::vector lfses = CGAL::estimate_local_feature_size(points, jet_k, N_rays, apex_angle, + CGAL::parameters::point_map(CGAL::First_of_pair_property_map()) + .normal_map(CGAL::Second_of_pair_property_map())); + + for (const auto &lfs : lfses) + std::cerr << lfs << "\n"; + + + return EXIT_SUCCESS; +} diff --git a/Point_set_processing_3/include/CGAL/estimate_lfs.h b/Point_set_processing_3/include/CGAL/estimate_lfs.h new file mode 100644 index 000000000000..79754e7040b1 --- /dev/null +++ b/Point_set_processing_3/include/CGAL/estimate_lfs.h @@ -0,0 +1,691 @@ +// Copyright (c) 2007-09 INRIA Sophia-Antipolis (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) : Rao Fu + +#ifndef CGAL_ESTIMATE_LFS_H +#define CGAL_ESTIMATE_LFS_H + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace CGAL { + +// ---------------------------------------------------------------------------- +// Private section +// ---------------------------------------------------------------------------- +/// \cond SKIP_IN_MANUAL +namespace internal { + +template +typename Geom_traits::Sphere_3 +calculate_loose_bsphere(const PointRange& points, const PointMap point_map, + const typename Geom_traits::FT loose_ratio=0.1) +{ + typedef typename Geom_traits::FT FT; + typedef typename Geom_traits::Point_3 Point; + typedef typename Geom_traits::Sphere_3 Sphere; + + CGAL_precondition(points.begin() != points.end()); + + Point center = CGAL::centroid(CGAL::make_transform_iterator_from_property_map + (points.begin(), point_map), + CGAL::make_transform_iterator_from_property_map + (points.end(), point_map)); + + FT max_distance = std::numeric_limits::min(); + for (const auto& pt : points) + { + const Point& point = get(point_map, pt); + FT distance = CGAL::squared_distance(center, point); + if (distance > max_distance) { + max_distance = distance; + } + } + + return Sphere(center, max_distance*(1.0 + loose_ratio)); +} + +template +typename NeighborQuery::FT +classical_point_dist_func(const typename NeighborQuery::Point_3& query, ///< point + const NeighborQuery& neighbor_query) +{ + // basic geometric types + typedef typename NeighborQuery::Kernel Kernel; + typedef typename NeighborQuery::FT FT; + typedef typename Kernel::Point_3 Point; + std::vector points; + // find the nearest neighbor + neighbor_query.get_points(query, 1, FT(0), std::back_inserter(points)); + + const FT dsq = CGAL::squared_distance(query, points[0]); + + return std::sqrt(dsq); +} + +template +typename NeighborQuery::FT +av_sqrt_sum_sq_distance_to_knn(const typename NeighborQuery::Point_3& query, ///< point + const NeighborQuery& neighbor_query, + const int knn) +{ + // basic geometric types + typedef typename NeighborQuery::Kernel Kernel; + typedef typename NeighborQuery::FT FT; + typedef typename Kernel::Point_3 Point; + std::vector points; + // find the nearest neighbor + neighbor_query.get_points(query, knn, FT(0), std::back_inserter(points)); + + // get distances + FT sum_sq_distances = FT(0.0); + for (const auto& point : points) + { + sum_sq_distances += CGAL::squared_distance(query, point); + } + + std::size_t sz = points.size(); + + if (sz == 0) + return 0.0; + else + return std::sqrt(sum_sq_distances / sz); +} + +template +typename NeighborQuery::FT +classical_point_dist_func_epsilon_band(const PointRange &points, + const NeighborQuery& neighbor_query, + const PointMap point_map, + const int band_knn=12) +{ + // basic geometric types + typedef typename NeighborQuery::Kernel Kernel; + typedef typename NeighborQuery::FT FT; + typedef typename Kernel::Point_3 Point; + + CGAL_precondition(points.begin() != points.end()); + CGAL_precondition(band_knn > 0); + + FT l_min = std::numeric_limits::infinity(); + // for (typename PointRange::const_iterator pwn_it = pwns.begin(); + // pwn_it != pwns.end(); pwn_it++) + for (const auto& pt : points) + { + const Point &query = get(point_map, pt); + + FT l = av_sqrt_sum_sq_distance_to_knn(query, neighbor_query, band_knn); + + if (l == FT(0)) continue; + + if (l < l_min) + { + l_min = l; + } + } + + return l_min; +} + +template +Point calculate_ray_sphere_intersection(const Point &p, const Vector &n, const Sphere &sphere) +{ + + Vector d = p - sphere.center(); + + auto d_len = std::sqrt(d.squared_length()); + + auto cos_theta = -1.0 * (d * n) / d_len / std::sqrt(n.squared_length()); + + auto l = d_len * cos_theta + + std::sqrt(sphere.squared_radius() - d.squared_length() * (1.0 - cos_theta * cos_theta)); + + Point t = p + n * l; + + return t; +} + +template +Point local2world(const Point &local, const Point &o, + const Vector &u_basis, const Vector &v_basis, const Vector &w_basis) +{ + Vector v = local.x() * u_basis + local.y() * v_basis + local.z() * w_basis; + Point world = o + v; + + return world; +} + + +template +void random_dual_cone_search_segs(const typename Geom_traits::Point_3 &p, + const typename Geom_traits::Vector_3 &n, + const typename Geom_traits::Sphere_3 &bsphere, + std::vector &segs, + const typename Geom_traits::FT half_apex_angle, std::size_t N) +{ + typedef typename Geom_traits::FT FT; + typedef typename Geom_traits::Point_3 Point; + typedef typename Geom_traits::Vector_3 Vector; + typedef typename Geom_traits::Segment_3 Segment; + + FT half_apex_radian = half_apex_angle * CGAL_PI / 180.0; + + // add the normal directions + const Point s = calculate_ray_sphere_intersection(p, n, bsphere); + const Point t = calculate_ray_sphere_intersection(p, -1.0 * n, bsphere); + segs.push_back(Segment(s, t)); + + // add rays within the cone + + // define local coordinate system + // normal should be normalized + FT eps = 1e-5; + assert(std::abs(n.squared_length() - 1.0) <= eps); + const Vector w_basis = n; + + Vector v_vec(0.0, 0.0, 0.0); + if (std::abs(n.x()) >= std::abs(n.y())) + v_vec = Vector(n.z(), 0.0, -1.0 * n.x()); + else + v_vec = Vector(0.0, n.z(), -1.0 * n.y()); + // normalize v_vec + FT v_vec_norm = v_vec.squared_length(); + v_vec_norm = v_vec_norm > eps ? v_vec_norm : eps; + const Vector v_basis = v_vec / std::sqrt(v_vec_norm); + + const Vector u_vec = CGAL::cross_product(v_basis, w_basis); + // normalize u_vec + FT u_vec_norm = u_vec.squared_length(); + u_vec_norm = u_vec_norm > eps ? u_vec_norm : eps; + const Vector u_basis = u_vec / std::sqrt(u_vec_norm); + + FT w = std::cos(half_apex_radian); + assert(w <= 1.0); + + FT R = std::sqrt(1.0 - w * w); + + for (int i = 0; i < N; i++) + { + FT rd1 = rand() / (double)RAND_MAX; + FT rd2 = rand() / (double)RAND_MAX; + + FT random_r = R * std::sqrt(rd1); + FT random_phi = 2.0 * CGAL_PI * rd2; + + FT u = random_r * std::cos(random_phi); + FT v = random_r * std::sin(random_phi); + + // local to world + const Point world = local2world(Point(u, v, w), p, u_basis, v_basis, w_basis); + const Vector ray_direction = world - p; + const Point s = calculate_ray_sphere_intersection(p, ray_direction, bsphere); + const Point t = calculate_ray_sphere_intersection(p, -1.0 * ray_direction, bsphere); + segs.push_back(Segment(s, t)); + } +} + +template +bool recursive_dichotomic_search_base(const typename NeighborQuery::Kernel::Segment_3 &e, + const typename NeighborQuery::Kernel::Point_2 &s, + const typename NeighborQuery::Kernel::Point_2 &t, + const NeighborQuery& neighbor_query, + const typename NeighborQuery::FT epsilon_band, + std::vector& intersections, + typename NeighborQuery::FT lipschitz, + typename NeighborQuery::FT eps) +{ + // basic geometric types + typedef typename NeighborQuery::Kernel Kernel; + typedef typename NeighborQuery::FT FT; + typedef typename Kernel::Point_3 Point; + typedef typename Kernel::Point_2 Point_2; + typedef typename Kernel::Vector_3 Vector; + + CGAL_precondition(t.x() + eps >= s.x() - eps); + CGAL_precondition(lipschitz >= 0.0); + + Point ps = e.source(); + Point pt = e.target(); + Vector v = pt - ps; + FT len = std::sqrt(v.squared_length()); + + // should satisfy lipschitz continuity -- keep it? + FT abs_dy = std::abs(t.y() - s.y()); + FT abs_dx = std::abs(t.x() - s.x()); + // it is better to abs_dy / abs_dx, when abs_dx is small + if (abs_dy / abs_dx > lipschitz + eps) + { + return false; + } + + FT lip_lbx = (s.x() + t.x()) * 0.5 - (t.y() - s.y()) * 0.5 / lipschitz; + FT lip_lby = s.y() - lipschitz * (lip_lbx - s.x()); + + CGAL_precondition(lip_lbx + eps >= s.x() - eps); + CGAL_precondition(lip_lbx - eps <= t.x() + eps); + + FT lip_ubx = ((t.y() - s.y()) / lipschitz + t.x() + s.x()) * 0.5; + FT lip_uby = s.y() + lipschitz * (lip_ubx - s.x()); + + CGAL_precondition(lip_ubx + eps >= s.x() - eps); + CGAL_precondition(lip_ubx - eps <= t.x() + eps); + + // stop + if (lip_lby >= epsilon_band || lip_uby <= epsilon_band) + { + return true; + } + + const bool s_sign = (s.y() > epsilon_band); + const bool t_sign = (t.y() > epsilon_band); + const FT s_slope = (s_sign) ? -lipschitz : lipschitz; + const FT t_slope = (t_sign) ? lipschitz : -lipschitz; + + FT ms_x = s.x() + (epsilon_band - s.y()) / s_slope; + const Point pms = ps + ms_x / len * v; + FT ms_y = classical_point_dist_func(pms, neighbor_query); + const Point_2 ms(ms_x, ms_y); + CGAL_precondition(ms_x + eps >= s.x() - eps); + + FT mt_x = t.x() + (epsilon_band - t.y()) / t_slope; + const Point pmt = ps + mt_x / len * v; + FT mt_y = classical_point_dist_func(pmt, neighbor_query); + const Point_2 mt(mt_x, mt_y); + CGAL_precondition(mt_x - eps <= t.x() + eps); + CGAL_precondition(mt_x + eps >= ms_x - eps); + + const FT mm_x = (ms_x + mt_x) / 2; + const Point pmm = ps + mm_x / len * v; + FT mm_y = classical_point_dist_func(pmm, neighbor_query); + Point_2 mm(mm_x, mm_y); + + if (std::abs(mm_y - epsilon_band) <= eps && (mt_x - ms_x) <= epsilon_band / lipschitz) + { + if (s_sign == t_sign) + { + intersections.push_back(pmm); + intersections.push_back(pmm); + } + else + { + intersections.push_back(pmm); + } + + return true; + } + + bool fl = recursive_dichotomic_search_base(e, ms, mm, neighbor_query, epsilon_band, + intersections, lipschitz, eps); + bool fr = recursive_dichotomic_search_base(e, mm, mt, neighbor_query, epsilon_band, + intersections, lipschitz, eps); + + return (fl && fr); +} + +template +bool recursive_dichotomic_search(const typename NeighborQuery::Kernel::Segment_3 &e, + const typename NeighborQuery::Kernel::Point_2 &s, + const typename NeighborQuery::Kernel::Point_2 &t, + const NeighborQuery &neighbor_query, + const typename NeighborQuery::FT epsilon_band, + std::vector& intersections, + typename NeighborQuery::FT lipschitz, + typename NeighborQuery::FT eps=1e-5) +{ + // basic geometric types + typedef typename NeighborQuery::Kernel Kernel; + typedef typename Kernel::Point_3 Point; + + CGAL_precondition(intersections.empty()); + bool flag = recursive_dichotomic_search_base(e, s, t, neighbor_query, epsilon_band, + intersections, lipschitz, eps); + + if (flag == false) // should not false + { + ; + } + else if (intersections.size() % 2 != 0) + { + const bool s_sign = (s.y() > epsilon_band); + const bool t_sign = (t.y() > epsilon_band); + + if ((!s_sign) && t_sign) + { + std::vector intersections_; + intersections_.push_back(e.source()); + for (std::size_t i = 1; i < intersections.size(); i = i + 2) + { + const Point p0 = intersections[i]; + const Point p1 = intersections[i + 1]; + const Point p = p1 + (p0 - p1) / 2; + intersections_.push_back(p); + } + intersections = intersections_; + } + + if ((!t_sign) && s_sign) + { + std::vector intersections_; + for (std::size_t i = 0; i < intersections.size() - 1; i = i + 2) + { + const Point p0 = intersections[i]; + const Point p1 = intersections[i + 1]; + const Point p = p1 + (p0 - p1) / 2; + intersections_.push_back(p); + } + intersections_.push_back(e.target()); + intersections = intersections_; + } + + if (s_sign && t_sign) + { + flag = false; + } + + // should not happen + if ((!s_sign) && (!t_sign)) + { + flag = false; + } + } + else + { + std::vector intersections_; + for (std::size_t i = 0; i < intersections.size(); i = i + 2) + { + const Point p0 = intersections[i]; + const Point p1 = intersections[i + 1]; + const Point p = p1 + (p0 - p1) / 2; + intersections_.push_back(p); + } + intersections = intersections_; + } + + return flag; +} + +template +typename NeighborQuery::FT +estimate_shape_diameter(const typename NeighborQuery::Point_3& query, ///< point + const typename NeighborQuery::Kernel::Vector_3& normal, + const NeighborQuery& neighbor_query, ///< KD-tree + const typename NeighborQuery::Kernel::Sphere_3& bsphere, + const typename NeighborQuery::FT epsilon_band, + const typename NeighborQuery::FT apex_angle, + std::size_t N_rays, + unsigned int reject_knn=6) +{ + // basic geometric types + typedef typename NeighborQuery::Kernel Kernel; + typedef typename NeighborQuery::FT FT; + typedef typename Kernel::Point_3 Point; + typedef typename Kernel::Point_2 Point_2; + typedef typename Kernel::Segment_3 Segment; + + std::vector points; + neighbor_query.get_points (query, reject_knn, FT(0), std::back_inserter(points)); + FT project_raidus = reject_knn == 0 ? 0.0 : std::sqrt(CGAL::squared_distance(query, points[reject_knn-1])); + + // dual cone search segments + FT half_apex_angle = apex_angle / 2.0; + std::vector segs; + random_dual_cone_search_segs(query, normal, bsphere, segs, + half_apex_angle, N_rays); + + std::vector antipodal_point_dsqs; + for (const auto seg : segs) + { + const Point ps = seg.source(); + const Point pt = seg.target(); + FT l = std::sqrt(seg.squared_length()); + + FT ls = classical_point_dist_func(ps, neighbor_query); + FT lt = classical_point_dist_func(pt, neighbor_query); + FT lipschitz = 1.1; // slightly enlarge the lipschitz + std::vector intersections; + bool flag = recursive_dichotomic_search(seg, Point_2(0.0, ls), Point_2(l, lt), neighbor_query, + epsilon_band, intersections, lipschitz); + + if (flag && intersections.size() >= 2) + { + std::vector dsqs; + for (std::size_t i = 0; i < intersections.size(); i++) + { + const auto intersection = intersections[i]; + + FT dsq = (query - intersection).squared_length(); + dsqs.push_back(dsq); + } + std::sort(dsqs.begin(), dsqs.end(), std::less()); + // dist should be larger than epsilon_band + auto it = upper_bound(dsqs.begin(), dsqs.end(), project_raidus*project_raidus); + + if (it != dsqs.end()) + { + FT dsq_min = *it; + antipodal_point_dsqs.push_back(dsq_min); + } + } + } + + FT dsq_upper_bound = 4.0 * bsphere.squared_radius(); + + if(antipodal_point_dsqs.empty()) + antipodal_point_dsqs.push_back(dsq_upper_bound); + + // robust distance function here + FT sum_squared_distances = std::accumulate(antipodal_point_dsqs.begin(), antipodal_point_dsqs.end(), 0.0); + std::size_t sz = antipodal_point_dsqs.size(); + if (sz == 0) + return 0.0; + else + return std::sqrt(sum_squared_distances / sz); +} + +template +typename Monge_form::FT +min_curvature_radius(Monge_form &monge_form) +{ + // basic geometric types + typedef typename Monge_form::FT FT; + + FT max_principal_curvature = monge_form.principal_curvatures(0); + FT min_principal_curvature = monge_form.principal_curvatures(1); + + FT r1 = 1.0 / std::abs(max_principal_curvature); + FT r2 = 1.0 / std::abs(min_principal_curvature); + FT r = std::min(r1, r2); + + return r; +} + +template +typename NeighborQuery::FT +estimate_local_feature_size(const typename NeighborQuery::Point_3& query, ///< point + typename NeighborQuery::Kernel::Vector_3& normal, + const NeighborQuery& neighbor_query, ///< KD-tree + const typename NeighborQuery::Kernel::Sphere_3& bsphere, ///< bounding sphere + unsigned int jet_k, ///< number of neighbors + typename NeighborQuery::FT neighbor_radius, + unsigned int degree_fitting, + unsigned int degree_monge, + typename NeighborQuery::FT epsilon_band, + typename NeighborQuery::FT apex_angle, + std::size_t N_rays + ) +{ + // basic geometric types + typedef typename NeighborQuery::Kernel Kernel; + typedef typename Kernel::Point_3 Point; + typedef typename Kernel::Vector_3 Vector; + typedef typename Kernel::FT FT; + + // types for jet fitting + typedef Monge_via_jet_fitting< Kernel> Monge_jet_fitting; + typedef typename Monge_jet_fitting::Monge_form Monge_form; + + std::vector points; + // query using as fallback minimum requires nb points for jet fitting (d+1)*(d+2)/2 + neighbor_query.get_points (query, jet_k, neighbor_radius, std::back_inserter(points), + (degree_fitting + 1) * (degree_fitting + 2) / 2); + + // estimate jet + Monge_jet_fitting monge_fit; + Monge_form monge_form = monge_fit(points.begin(), points.end(), degree_fitting, degree_monge); + + if (normal * normal == 0.0) + normal = monge_form.normal_direction(); // already normalized in monge_form + else + monge_form.comply_wrt_given_normal(normal); // orient jet with valid given normal + + FT abs_curvature_r = min_curvature_radius(monge_form); + + // estimate shape diameter + FT shape_diameter = estimate_shape_diameter(query, normal, neighbor_query, bsphere, epsilon_band, apex_angle, N_rays); + FT half_shape_diameter = 0.5 * shape_diameter; + + FT lfs = std::min(abs_curvature_r, half_shape_diameter); + + return lfs; +} + +} /* namespace internal */ +/// \endcond + +template +std::vector::FT> +estimate_local_feature_size(PointRange& points, + unsigned int jet_k, + std::size_t N_rays, + typename Point_set_processing_3_np_helper::Geom_traits::FT apex_angle, + const NamedParameters& np = parameters::default_values()) +{ + std::cerr << "estimate lfs" << std::endl; + + using parameters::choose_parameter; + using parameters::get_parameter; + + CGAL_TRACE_STREAM << "Calls estimate_local_feature_size()\n"; + + // basic geometric types + typedef Point_set_processing_3_np_helper NP_helper; + typedef typename NP_helper::Point_map PointMap; + typedef typename NP_helper::Normal_map NormalMap; + typedef typename NP_helper::Geom_traits Kernel; + typedef typename Kernel::FT FT; + typedef typename Kernel::Point_3 Point; + typedef typename Kernel::Vector_3 Vector; + typedef typename Kernel::Sphere_3 Sphere; + + CGAL_assertion_msg(NP_helper::has_normal_map(points, np), "Error: no normal map"); + PointMap point_map = NP_helper::get_point_map(points, np); + NormalMap normal_map = NP_helper::get_normal_map(points, np); + unsigned int degree_fitting = choose_parameter(get_parameter(np, internal_np::degree_fitting), 2); + unsigned int degree_monge = choose_parameter(get_parameter(np, internal_np::degree_monge), 2); + FT neighbor_radius = choose_parameter(get_parameter(np, internal_np::neighbor_radius), FT(0.0)); + + const std::function& callback = choose_parameter(get_parameter(np, internal_np::callback), + std::function()); + + // types for K nearest neighbors search structure + typedef Point_set_processing_3::internal::Neighbor_query Neighbor_query; + + CGAL_precondition(points.begin() != points.end()); + + // precondition: at least 2 nearest neighbors + CGAL_precondition(k >= 2); + + std::size_t memory = CGAL::Memory_sizer().virtual_size(); + CGAL_TRACE_STREAM << (memory >> 20) << " Mb allocated\n"; + CGAL_TRACE_STREAM << " Creates KD-tree\n"; + + Neighbor_query neighbor_query(points, point_map); + + + // Input points types + typedef typename PointRange::iterator iterator; + typedef typename iterator::value_type value_type; + + + std::size_t nb_points = points.size(); + + // calculate a loose bounding shpere + Sphere bsphere = CGAL::internal::calculate_loose_bsphere(points, point_map); + // estimate an epsilon band for the classical distance function + FT epsilon_band = CGAL::internal::classical_point_dist_func_epsilon_band(points, neighbor_query, point_map); + + // point_lfses.resize(nb_points); + // point_curv_radii.resize(nb_points); + // point_half_shape_diameters.resize(nb_points); + // point_monge_forms.resize(nb_points); + std::vector lfses(nb_points); + std::map index_map; + for (std::size_t i = 0; i < nb_points; i++) index_map[get(point_map, points[i])] = i; + + Point_set_processing_3::internal::Callback_wrapper + callback_wrapper (callback, nb_points); + + CGAL::for_each + (points, + [&](value_type& vt) + { + if (callback_wrapper.interrupted()) + return false; + + const Point& point = get(point_map, vt); + Vector normal = get(normal_map, vt); + + bool need_jet_normal = false; + + if (normal * normal == 0.0) + need_jet_normal = true; + + FT lfs = CGAL::internal::estimate_local_feature_size + (point, normal, neighbor_query, bsphere, + jet_k, neighbor_radius, degree_fitting, degree_monge, + epsilon_band, apex_angle, N_rays + ); + lfses[index_map[point]] = lfs; + + if (need_jet_normal) + put(normal_map, vt, normal); + + ++ callback_wrapper.advancement(); + + return true; + }); + + return lfses; +} + +} //namespace CGAL + +#endif // CGAL_ESTIMATE_LFS_H \ No newline at end of file From c5ce18efbbf2b610eb50d5b13165ab544711d5da Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Mon, 29 Jan 2024 10:26:27 +0100 Subject: [PATCH 02/38] remove comments --- Point_set_processing_3/include/CGAL/estimate_lfs.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Point_set_processing_3/include/CGAL/estimate_lfs.h b/Point_set_processing_3/include/CGAL/estimate_lfs.h index 79754e7040b1..95028c072303 100644 --- a/Point_set_processing_3/include/CGAL/estimate_lfs.h +++ b/Point_set_processing_3/include/CGAL/estimate_lfs.h @@ -642,10 +642,6 @@ estimate_local_feature_size(PointRange& points, // estimate an epsilon band for the classical distance function FT epsilon_band = CGAL::internal::classical_point_dist_func_epsilon_band(points, neighbor_query, point_map); - // point_lfses.resize(nb_points); - // point_curv_radii.resize(nb_points); - // point_half_shape_diameters.resize(nb_points); - // point_monge_forms.resize(nb_points); std::vector lfses(nb_points); std::map index_map; for (std::size_t i = 0; i < nb_points; i++) index_map[get(point_map, points[i])] = i; From ed49e417e9c342dedca49b58182dbe42a61e797f Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Mon, 29 Jan 2024 14:48:22 +0100 Subject: [PATCH 03/38] cmakelists for lfs_example --- .../examples/Point_set_processing_3/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 ac5e71b1aa11..d845435bce7e 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 @@ -49,7 +49,8 @@ foreach( edge_aware_upsample_point_set_example structuring_example read_ply_points_with_colors_example - write_ply_points_example) + write_ply_points_example + lfs_example) create_single_source_cgal_program("${target}.cpp") target_link_libraries(${target} PRIVATE ${CGAL_libs}) endforeach() From 772f36ff867911c82f4456cbf2c1bcf41053c759 Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Mon, 29 Jan 2024 15:30:21 +0100 Subject: [PATCH 04/38] code style --- .../include/CGAL/estimate_lfs.h | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Point_set_processing_3/include/CGAL/estimate_lfs.h b/Point_set_processing_3/include/CGAL/estimate_lfs.h index 95028c072303..497920c35478 100644 --- a/Point_set_processing_3/include/CGAL/estimate_lfs.h +++ b/Point_set_processing_3/include/CGAL/estimate_lfs.h @@ -267,7 +267,7 @@ bool recursive_dichotomic_search_base(const typename NeighborQuery::Kernel::Segm typedef typename Kernel::Point_2 Point_2; typedef typename Kernel::Vector_3 Vector; - CGAL_precondition(t.x() + eps >= s.x() - eps); + assert(t.x() + eps >= s.x() - eps); CGAL_precondition(lipschitz >= 0.0); Point ps = e.source(); @@ -287,14 +287,14 @@ bool recursive_dichotomic_search_base(const typename NeighborQuery::Kernel::Segm FT lip_lbx = (s.x() + t.x()) * 0.5 - (t.y() - s.y()) * 0.5 / lipschitz; FT lip_lby = s.y() - lipschitz * (lip_lbx - s.x()); - CGAL_precondition(lip_lbx + eps >= s.x() - eps); - CGAL_precondition(lip_lbx - eps <= t.x() + eps); + assert(lip_lbx + eps >= s.x() - eps); + assert(lip_lbx - eps <= t.x() + eps); FT lip_ubx = ((t.y() - s.y()) / lipschitz + t.x() + s.x()) * 0.5; FT lip_uby = s.y() + lipschitz * (lip_ubx - s.x()); - CGAL_precondition(lip_ubx + eps >= s.x() - eps); - CGAL_precondition(lip_ubx - eps <= t.x() + eps); + assert(lip_ubx + eps >= s.x() - eps); + assert(lip_ubx - eps <= t.x() + eps); // stop if (lip_lby >= epsilon_band || lip_uby <= epsilon_band) @@ -311,14 +311,14 @@ bool recursive_dichotomic_search_base(const typename NeighborQuery::Kernel::Segm const Point pms = ps + ms_x / len * v; FT ms_y = classical_point_dist_func(pms, neighbor_query); const Point_2 ms(ms_x, ms_y); - CGAL_precondition(ms_x + eps >= s.x() - eps); + assert(ms_x + eps >= s.x() - eps); FT mt_x = t.x() + (epsilon_band - t.y()) / t_slope; const Point pmt = ps + mt_x / len * v; FT mt_y = classical_point_dist_func(pmt, neighbor_query); const Point_2 mt(mt_x, mt_y); - CGAL_precondition(mt_x - eps <= t.x() + eps); - CGAL_precondition(mt_x + eps >= ms_x - eps); + assert(mt_x - eps <= t.x() + eps); + assert(mt_x + eps >= ms_x - eps); const FT mm_x = (ms_x + mt_x) / 2; const Point pmm = ps + mm_x / len * v; @@ -450,7 +450,7 @@ estimate_shape_diameter(const typename NeighborQuery::Point_3& query, ///< point std::vector points; neighbor_query.get_points (query, reject_knn, FT(0), std::back_inserter(points)); - FT project_raidus = reject_knn == 0 ? 0.0 : std::sqrt(CGAL::squared_distance(query, points[reject_knn-1])); + FT project_radius = reject_knn == 0 ? 0.0 : std::sqrt(CGAL::squared_distance(query, points[reject_knn-1])); // dual cone search segments FT half_apex_angle = apex_angle / 2.0; @@ -484,7 +484,7 @@ estimate_shape_diameter(const typename NeighborQuery::Point_3& query, ///< point } std::sort(dsqs.begin(), dsqs.end(), std::less()); // dist should be larger than epsilon_band - auto it = upper_bound(dsqs.begin(), dsqs.end(), project_raidus*project_raidus); + auto it = upper_bound(dsqs.begin(), dsqs.end(), project_radius*project_radius); if (it != dsqs.end()) { @@ -520,7 +520,7 @@ min_curvature_radius(Monge_form &monge_form) FT r1 = 1.0 / std::abs(max_principal_curvature); FT r2 = 1.0 / std::abs(min_principal_curvature); - FT r = std::min(r1, r2); + FT r = (std::min)(r1, r2); return r; } @@ -570,7 +570,7 @@ estimate_local_feature_size(const typename NeighborQuery::Point_3& query, ///< p FT shape_diameter = estimate_shape_diameter(query, normal, neighbor_query, bsphere, epsilon_band, apex_angle, N_rays); FT half_shape_diameter = 0.5 * shape_diameter; - FT lfs = std::min(abs_curvature_r, half_shape_diameter); + FT lfs = (std::min)(abs_curvature_r, half_shape_diameter); return lfs; } From 7240a15c513b29377432b9a9c3b49d094e805a00 Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Mon, 29 Jan 2024 16:10:33 +0100 Subject: [PATCH 05/38] code style-2 --- .../include/CGAL/estimate_lfs.h | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/Point_set_processing_3/include/CGAL/estimate_lfs.h b/Point_set_processing_3/include/CGAL/estimate_lfs.h index 497920c35478..0765c67883c8 100644 --- a/Point_set_processing_3/include/CGAL/estimate_lfs.h +++ b/Point_set_processing_3/include/CGAL/estimate_lfs.h @@ -93,7 +93,7 @@ template typename NeighborQuery::FT av_sqrt_sum_sq_distance_to_knn(const typename NeighborQuery::Point_3& query, ///< point const NeighborQuery& neighbor_query, - const int knn) + const int knn) { // basic geometric types typedef typename NeighborQuery::Kernel Kernel; @@ -121,7 +121,7 @@ av_sqrt_sum_sq_distance_to_knn(const typename NeighborQuery::Point_3& query, /// template typename NeighborQuery::FT classical_point_dist_func_epsilon_band(const PointRange &points, - const NeighborQuery& neighbor_query, + const NeighborQuery& neighbor_query, const PointMap point_map, const int band_knn=12) { @@ -183,7 +183,7 @@ Point local2world(const Point &local, const Point &o, template -void random_dual_cone_search_segs(const typename Geom_traits::Point_3 &p, +void random_dual_cone_search_segs(const typename Geom_traits::Point_3 &p, const typename Geom_traits::Vector_3 &n, const typename Geom_traits::Sphere_3 &bsphere, std::vector &segs, @@ -251,7 +251,7 @@ void random_dual_cone_search_segs(const typename Geom_traits::Point_3 &p, } template -bool recursive_dichotomic_search_base(const typename NeighborQuery::Kernel::Segment_3 &e, +bool recursive_dichotomic_search_base(const typename NeighborQuery::Kernel::Segment_3 &e, const typename NeighborQuery::Kernel::Point_2 &s, const typename NeighborQuery::Kernel::Point_2 &t, const NeighborQuery& neighbor_query, @@ -349,7 +349,7 @@ bool recursive_dichotomic_search_base(const typename NeighborQuery::Kernel::Segm } template -bool recursive_dichotomic_search(const typename NeighborQuery::Kernel::Segment_3 &e, +bool recursive_dichotomic_search(const typename NeighborQuery::Kernel::Segment_3 &e, const typename NeighborQuery::Kernel::Point_2 &s, const typename NeighborQuery::Kernel::Point_2 &t, const NeighborQuery &neighbor_query, @@ -363,14 +363,10 @@ bool recursive_dichotomic_search(const typename NeighborQuery::Kernel::Segment_3 typedef typename Kernel::Point_3 Point; CGAL_precondition(intersections.empty()); - bool flag = recursive_dichotomic_search_base(e, s, t, neighbor_query, epsilon_band, + bool flag = recursive_dichotomic_search_base(e, s, t, neighbor_query, epsilon_band, intersections, lipschitz, eps); - if (flag == false) // should not false - { - ; - } - else if (intersections.size() % 2 != 0) + if (intersections.size() % 2 != 0) { const bool s_sign = (s.y() > epsilon_band); const bool t_sign = (t.y() > epsilon_band); @@ -378,6 +374,7 @@ bool recursive_dichotomic_search(const typename NeighborQuery::Kernel::Segment_3 if ((!s_sign) && t_sign) { std::vector intersections_; + intersections_.reserve(intersections.size()); intersections_.push_back(e.source()); for (std::size_t i = 1; i < intersections.size(); i = i + 2) { @@ -386,12 +383,13 @@ bool recursive_dichotomic_search(const typename NeighborQuery::Kernel::Segment_3 const Point p = p1 + (p0 - p1) / 2; intersections_.push_back(p); } - intersections = intersections_; + std::swap(intersections, intersections_); } if ((!t_sign) && s_sign) { std::vector intersections_; + intersections_.reserve(intersections.size()); for (std::size_t i = 0; i < intersections.size() - 1; i = i + 2) { const Point p0 = intersections[i]; @@ -400,10 +398,10 @@ bool recursive_dichotomic_search(const typename NeighborQuery::Kernel::Segment_3 intersections_.push_back(p); } intersections_.push_back(e.target()); - intersections = intersections_; + std::swap(intersections, intersections_); } - if (s_sign && t_sign) + if (s_sign && t_sign) { flag = false; } @@ -527,7 +525,7 @@ min_curvature_radius(Monge_form &monge_form) template typename NeighborQuery::FT -estimate_local_feature_size(const typename NeighborQuery::Point_3& query, ///< point +estimate_local_feature_size(const typename NeighborQuery::Point_3& query, ///< point typename NeighborQuery::Kernel::Vector_3& normal, const NeighborQuery& neighbor_query, ///< KD-tree const typename NeighborQuery::Kernel::Sphere_3& bsphere, ///< bounding sphere @@ -684,4 +682,4 @@ estimate_local_feature_size(PointRange& points, } //namespace CGAL -#endif // CGAL_ESTIMATE_LFS_H \ No newline at end of file +#endif // CGAL_ESTIMATE_LFS_H From bf2c0ed949bc41d6fc4e3f1b58078863788d2eb9 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 30 Jan 2024 13:28:08 +0000 Subject: [PATCH 06/38] fixes so that it compiles --- .../Point_set_processing_3/CMakeLists.txt | 4 +- .../include/CGAL/estimate_lfs.h | 63 ++++++++++--------- 2 files changed, 34 insertions(+), 33 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 d845435bce7e..ec4dc13337a6 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 @@ -49,8 +49,7 @@ foreach( edge_aware_upsample_point_set_example structuring_example read_ply_points_with_colors_example - write_ply_points_example - lfs_example) + write_ply_points_example) create_single_source_cgal_program("${target}.cpp") target_link_libraries(${target} PRIVATE ${CGAL_libs}) endforeach() @@ -73,6 +72,7 @@ if(TARGET CGAL::Eigen3_support) # Executables that require Eigen foreach( target + lfs_example jet_smoothing_example normal_estimation clustering_example diff --git a/Point_set_processing_3/include/CGAL/estimate_lfs.h b/Point_set_processing_3/include/CGAL/estimate_lfs.h index 0765c67883c8..b6657d88fcbf 100644 --- a/Point_set_processing_3/include/CGAL/estimate_lfs.h +++ b/Point_set_processing_3/include/CGAL/estimate_lfs.h @@ -44,7 +44,7 @@ namespace internal { template typename Geom_traits::Sphere_3 -calculate_loose_bsphere(const PointRange& points, const PointMap point_map, +calculate_loose_bsphere(const PointRange& points, const PointMap point_map, const typename Geom_traits::FT loose_ratio=0.1) { typedef typename Geom_traits::FT FT; @@ -58,7 +58,7 @@ calculate_loose_bsphere(const PointRange& points, const PointMap point_map, CGAL::make_transform_iterator_from_property_map (points.end(), point_map)); - FT max_distance = std::numeric_limits::min(); + FT max_distance = (std::numeric_limits::min)(); for (const auto& pt : points) { const Point& point = get(point_map, pt); @@ -74,7 +74,7 @@ calculate_loose_bsphere(const PointRange& points, const PointMap point_map, template typename NeighborQuery::FT classical_point_dist_func(const typename NeighborQuery::Point_3& query, ///< point - const NeighborQuery& neighbor_query) + const NeighborQuery& neighbor_query) { // basic geometric types typedef typename NeighborQuery::Kernel Kernel; @@ -143,7 +143,7 @@ classical_point_dist_func_epsilon_band(const PointRange &points, FT l = av_sqrt_sum_sq_distance_to_knn(query, neighbor_query, band_knn); if (l == FT(0)) continue; - + if (l < l_min) { l_min = l; @@ -207,7 +207,7 @@ void random_dual_cone_search_segs(const typename Geom_traits::Point_3 &p, // normal should be normalized FT eps = 1e-5; assert(std::abs(n.squared_length() - 1.0) <= eps); - const Vector w_basis = n; + const Vector w_basis = n; Vector v_vec(0.0, 0.0, 0.0); if (std::abs(n.x()) >= std::abs(n.y())) @@ -252,12 +252,12 @@ void random_dual_cone_search_segs(const typename Geom_traits::Point_3 &p, template bool recursive_dichotomic_search_base(const typename NeighborQuery::Kernel::Segment_3 &e, - const typename NeighborQuery::Kernel::Point_2 &s, + const typename NeighborQuery::Kernel::Point_2 &s, const typename NeighborQuery::Kernel::Point_2 &t, const NeighborQuery& neighbor_query, - const typename NeighborQuery::FT epsilon_band, + const typename NeighborQuery::FT epsilon_band, std::vector& intersections, - typename NeighborQuery::FT lipschitz, + typename NeighborQuery::FT lipschitz, typename NeighborQuery::FT eps) { // basic geometric types @@ -279,7 +279,7 @@ bool recursive_dichotomic_search_base(const typename NeighborQuery::Kernel::Segm FT abs_dy = std::abs(t.y() - s.y()); FT abs_dx = std::abs(t.x() - s.x()); // it is better to abs_dy / abs_dx, when abs_dx is small - if (abs_dy / abs_dx > lipschitz + eps) + if (abs_dy / abs_dx > lipschitz + eps) { return false; } @@ -336,13 +336,13 @@ bool recursive_dichotomic_search_base(const typename NeighborQuery::Kernel::Segm { intersections.push_back(pmm); } - + return true; } - bool fl = recursive_dichotomic_search_base(e, ms, mm, neighbor_query, epsilon_band, + bool fl = recursive_dichotomic_search_base(e, ms, mm, neighbor_query, epsilon_band, intersections, lipschitz, eps); - bool fr = recursive_dichotomic_search_base(e, mm, mt, neighbor_query, epsilon_band, + bool fr = recursive_dichotomic_search_base(e, mm, mt, neighbor_query, epsilon_band, intersections, lipschitz, eps); return (fl && fr); @@ -350,12 +350,12 @@ bool recursive_dichotomic_search_base(const typename NeighborQuery::Kernel::Segm template bool recursive_dichotomic_search(const typename NeighborQuery::Kernel::Segment_3 &e, - const typename NeighborQuery::Kernel::Point_2 &s, + const typename NeighborQuery::Kernel::Point_2 &s, const typename NeighborQuery::Kernel::Point_2 &t, const NeighborQuery &neighbor_query, - const typename NeighborQuery::FT epsilon_band, + const typename NeighborQuery::FT epsilon_band, std::vector& intersections, - typename NeighborQuery::FT lipschitz, + typename NeighborQuery::FT lipschitz, typename NeighborQuery::FT eps=1e-5) { // basic geometric types @@ -404,13 +404,13 @@ bool recursive_dichotomic_search(const typename NeighborQuery::Kernel::Segment_3 if (s_sign && t_sign) { flag = false; - } + } // should not happen - if ((!s_sign) && (!t_sign)) + if ((!s_sign) && (!t_sign)) { flag = false; - } + } } else { @@ -435,7 +435,7 @@ estimate_shape_diameter(const typename NeighborQuery::Point_3& query, ///< point const NeighborQuery& neighbor_query, ///< KD-tree const typename NeighborQuery::Kernel::Sphere_3& bsphere, const typename NeighborQuery::FT epsilon_band, - const typename NeighborQuery::FT apex_angle, + const typename NeighborQuery::FT apex_angle, std::size_t N_rays, unsigned int reject_knn=6) { @@ -445,7 +445,7 @@ estimate_shape_diameter(const typename NeighborQuery::Point_3& query, ///< point typedef typename Kernel::Point_3 Point; typedef typename Kernel::Point_2 Point_2; typedef typename Kernel::Segment_3 Segment; - + std::vector points; neighbor_query.get_points (query, reject_knn, FT(0), std::back_inserter(points)); FT project_radius = reject_knn == 0 ? 0.0 : std::sqrt(CGAL::squared_distance(query, points[reject_knn-1])); @@ -453,7 +453,7 @@ estimate_shape_diameter(const typename NeighborQuery::Point_3& query, ///< point // dual cone search segments FT half_apex_angle = apex_angle / 2.0; std::vector segs; - random_dual_cone_search_segs(query, normal, bsphere, segs, + random_dual_cone_search_segs(query, normal, bsphere, segs, half_apex_angle, N_rays); std::vector antipodal_point_dsqs; @@ -493,7 +493,7 @@ estimate_shape_diameter(const typename NeighborQuery::Point_3& query, ///< point } FT dsq_upper_bound = 4.0 * bsphere.squared_radius(); - + if(antipodal_point_dsqs.empty()) antipodal_point_dsqs.push_back(dsq_upper_bound); @@ -512,7 +512,7 @@ min_curvature_radius(Monge_form &monge_form) { // basic geometric types typedef typename Monge_form::FT FT; - + FT max_principal_curvature = monge_form.principal_curvatures(0); FT min_principal_curvature = monge_form.principal_curvatures(1); @@ -545,7 +545,7 @@ estimate_local_feature_size(const typename NeighborQuery::Point_3& query, ///< p typedef typename Kernel::FT FT; // types for jet fitting - typedef Monge_via_jet_fitting< Kernel> Monge_jet_fitting; + typedef Monge_via_jet_fitting< Kernel > Monge_jet_fitting; typedef typename Monge_jet_fitting::Monge_form Monge_form; std::vector points; @@ -619,7 +619,8 @@ estimate_local_feature_size(PointRange& points, CGAL_precondition(points.begin() != points.end()); // precondition: at least 2 nearest neighbors - CGAL_precondition(k >= 2); + // @todo fix this k as it does not exist + // CGAL_precondition(k >= 2); std::size_t memory = CGAL::Memory_sizer().virtual_size(); CGAL_TRACE_STREAM << (memory >> 20) << " Mb allocated\n"; @@ -631,7 +632,7 @@ estimate_local_feature_size(PointRange& points, // Input points types typedef typename PointRange::iterator iterator; typedef typename iterator::value_type value_type; - + std::size_t nb_points = points.size(); @@ -659,9 +660,9 @@ estimate_local_feature_size(PointRange& points, bool need_jet_normal = false; - if (normal * normal == 0.0) + if (normal * normal == 0.0) need_jet_normal = true; - + FT lfs = CGAL::internal::estimate_local_feature_size (point, normal, neighbor_query, bsphere, jet_k, neighbor_radius, degree_fitting, degree_monge, @@ -669,14 +670,14 @@ estimate_local_feature_size(PointRange& points, ); lfses[index_map[point]] = lfs; - if (need_jet_normal) + if (need_jet_normal) put(normal_map, vt, normal); - + ++ callback_wrapper.advancement(); return true; }); - + return lfses; } From aa9f1cb568698b40ec767d56d54a1a694e698e19 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Mon, 5 Feb 2024 13:33:55 +0000 Subject: [PATCH 07/38] Pass property map as parameter --- .../Point_set_processing_3/lfs_example.cpp | 25 +++++++++++-------- .../include/CGAL/estimate_lfs.h | 25 ++++++++----------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example.cpp index 890fcaf9be3a..819b3f7a5fa3 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example.cpp @@ -4,7 +4,7 @@ #include #include // defines std::pair - +#include // types typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; @@ -13,7 +13,7 @@ typedef Kernel::Point_3 Point; typedef Kernel::Vector_3 Vector; // Point with normal vector stored in a std::pair. -typedef std::pair Point_with_normal; +typedef std::tuple Point_with_normal_and_lfs; // Concurrency typedef CGAL::Parallel_if_available_tag Concurrency_tag; @@ -25,25 +25,30 @@ int main(void) // read xyz const std::string filename = "./frog.xyz"; - std::vector points; + std::vector points; if(!CGAL::IO::read_points(filename, std::back_inserter(points), - CGAL::parameters::point_map(CGAL::First_of_pair_property_map()))) + CGAL::parameters::point_map(CGAL::Nth_of_tuple_property_map<0, Point_with_normal_and_lfs>()))) { std::cerr << "Error: cannot read file " << filename<< std::endl; return EXIT_FAILURE; } - + unsigned int jet_k = 24; std::size_t N_rays = 60; FT apex_angle = 30; - std::vector lfses = CGAL::estimate_local_feature_size(points, jet_k, N_rays, apex_angle, - CGAL::parameters::point_map(CGAL::First_of_pair_property_map()) - .normal_map(CGAL::Second_of_pair_property_map())); - for (const auto &lfs : lfses) - std::cerr << lfs << "\n"; + CGAL::estimate_local_feature_size(points, + jet_k, + N_rays, + apex_angle, + CGAL::Nth_of_tuple_property_map<2,Point_with_normal_and_lfs>(), + CGAL::parameters::point_map(CGAL::Nth_of_tuple_property_map<0, Point_with_normal_and_lfs>()) + .normal_map(CGAL::Nth_of_tuple_property_map<1, Point_with_normal_and_lfs>())); + for (const auto &p : points){ + std::cerr << std::get<2>(p) << "\n"; + } return EXIT_SUCCESS; } diff --git a/Point_set_processing_3/include/CGAL/estimate_lfs.h b/Point_set_processing_3/include/CGAL/estimate_lfs.h index b6657d88fcbf..53646cf05dea 100644 --- a/Point_set_processing_3/include/CGAL/estimate_lfs.h +++ b/Point_set_processing_3/include/CGAL/estimate_lfs.h @@ -578,13 +578,15 @@ estimate_local_feature_size(const typename NeighborQuery::Point_3& query, ///< p template -std::vector::FT> +void estimate_local_feature_size(PointRange& points, - unsigned int jet_k, - std::size_t N_rays, - typename Point_set_processing_3_np_helper::Geom_traits::FT apex_angle, - const NamedParameters& np = parameters::default_values()) + const unsigned int jet_k, + const std::size_t N_rays, + const typename Point_set_processing_3_np_helper::Geom_traits::FT apex_angle, + LfsMap lfs_map, + const NamedParameters& np = parameters::default_values()) { std::cerr << "estimate lfs" << std::endl; @@ -641,10 +643,6 @@ estimate_local_feature_size(PointRange& points, // estimate an epsilon band for the classical distance function FT epsilon_band = CGAL::internal::classical_point_dist_func_epsilon_band(points, neighbor_query, point_map); - std::vector lfses(nb_points); - std::map index_map; - for (std::size_t i = 0; i < nb_points; i++) index_map[get(point_map, points[i])] = i; - Point_set_processing_3::internal::Callback_wrapper callback_wrapper (callback, nb_points); @@ -666,19 +664,16 @@ estimate_local_feature_size(PointRange& points, FT lfs = CGAL::internal::estimate_local_feature_size (point, normal, neighbor_query, bsphere, jet_k, neighbor_radius, degree_fitting, degree_monge, - epsilon_band, apex_angle, N_rays - ); - lfses[index_map[point]] = lfs; + epsilon_band, apex_angle, N_rays); + put(lfs_map, vt, lfs); if (need_jet_normal) put(normal_map, vt, normal); - ++ callback_wrapper.advancement(); + ++ callback_wrapper.advancement(); return true; }); - - return lfses; } } //namespace CGAL From c885e08321f3e833458d2f27bb4c3120a2d37d23 Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Mon, 5 Feb 2024 15:39:18 +0100 Subject: [PATCH 08/38] lfs_map v2 --- .../{lfs_example.cpp => lfs_example_vector.cpp} | 7 ++++--- Point_set_processing_3/include/CGAL/estimate_lfs.h | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) rename Point_set_processing_3/examples/Point_set_processing_3/{lfs_example.cpp => lfs_example_vector.cpp} (88%) diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_vector.cpp similarity index 88% rename from Point_set_processing_3/examples/Point_set_processing_3/lfs_example.cpp rename to Point_set_processing_3/examples/Point_set_processing_3/lfs_example_vector.cpp index 819b3f7a5fa3..26e0637c9f09 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_vector.cpp @@ -23,7 +23,7 @@ int main(void) { // read xyz - const std::string filename = "./frog.xyz"; + const std::string filename = "/home/daozi/CGAL/LFS-example/frog.xyz"; std::vector points; if(!CGAL::IO::read_points(filename, @@ -37,12 +37,13 @@ int main(void) unsigned int jet_k = 24; std::size_t N_rays = 60; FT apex_angle = 30; - + + auto lfs_map = CGAL::Nth_of_tuple_property_map<2, Point_with_normal_and_lfs>(); CGAL::estimate_local_feature_size(points, + lfs_map, jet_k, N_rays, apex_angle, - CGAL::Nth_of_tuple_property_map<2,Point_with_normal_and_lfs>(), CGAL::parameters::point_map(CGAL::Nth_of_tuple_property_map<0, Point_with_normal_and_lfs>()) .normal_map(CGAL::Nth_of_tuple_property_map<1, Point_with_normal_and_lfs>())); diff --git a/Point_set_processing_3/include/CGAL/estimate_lfs.h b/Point_set_processing_3/include/CGAL/estimate_lfs.h index 53646cf05dea..34e686751a11 100644 --- a/Point_set_processing_3/include/CGAL/estimate_lfs.h +++ b/Point_set_processing_3/include/CGAL/estimate_lfs.h @@ -582,10 +582,10 @@ template void estimate_local_feature_size(PointRange& points, + LfsMap lfs_map, const unsigned int jet_k, const std::size_t N_rays, const typename Point_set_processing_3_np_helper::Geom_traits::FT apex_angle, - LfsMap lfs_map, const NamedParameters& np = parameters::default_values()) { std::cerr << "estimate lfs" << std::endl; @@ -665,6 +665,7 @@ estimate_local_feature_size(PointRange& points, (point, normal, neighbor_query, bsphere, jet_k, neighbor_radius, degree_fitting, degree_monge, epsilon_band, apex_angle, N_rays); + put(lfs_map, vt, lfs); if (need_jet_normal) From 7d4d04078a7d3fda4c50c6dc150fd74e364c108f Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Mon, 5 Feb 2024 15:58:35 +0100 Subject: [PATCH 09/38] reference --- Point_set_processing_3/include/CGAL/estimate_lfs.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Point_set_processing_3/include/CGAL/estimate_lfs.h b/Point_set_processing_3/include/CGAL/estimate_lfs.h index 34e686751a11..c0e5da0e6ea9 100644 --- a/Point_set_processing_3/include/CGAL/estimate_lfs.h +++ b/Point_set_processing_3/include/CGAL/estimate_lfs.h @@ -582,7 +582,7 @@ template void estimate_local_feature_size(PointRange& points, - LfsMap lfs_map, + LfsMap& lfs_map, const unsigned int jet_k, const std::size_t N_rays, const typename Point_set_processing_3_np_helper::Geom_traits::FT apex_angle, @@ -670,6 +670,7 @@ estimate_local_feature_size(PointRange& points, if (need_jet_normal) put(normal_map, vt, normal); + ; ++ callback_wrapper.advancement(); From 9b1788d16e10ca6b3e6ea8f764d4c0a0ab6eae3c Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Mon, 5 Feb 2024 16:43:17 +0100 Subject: [PATCH 10/38] doxygen & point_set example --- .../lfs-example-pointset.cpp | 68 +++++++++++++++++++ ...ample_vector.cpp => lfs_example_tuple.cpp} | 0 .../include/CGAL/estimate_lfs.h | 36 ++++++++++ 3 files changed, 104 insertions(+) create mode 100644 Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp rename Point_set_processing_3/examples/Point_set_processing_3/{lfs_example_vector.cpp => lfs_example_tuple.cpp} (100%) diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp new file mode 100644 index 000000000000..7d5bee7cf332 --- /dev/null +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include +#include // defines std::pair + + +// types +typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; +typedef Kernel::FT FT; +typedef Kernel::Point_3 Point; +typedef Kernel::Vector_3 Vector; + +typedef CGAL::Point_set_3 Point_set; +typedef Point_set::Property_map Point_map; +typedef Point_set::Property_map FT_map; + +// types for K nearest neighbors search structure +typedef CGAL::Point_set_processing_3::internal::Neighbor_query Neighbor_query; + +// Concurrency +typedef CGAL::Parallel_if_available_tag Concurrency_tag; +//typedef CGAL::Parallel_tag Concurrency_tag; +typedef CGAL::Sequential_tag Concurrency_tag; + +int main(void) +{ + + // read xyz + const std::string filename = "/home/daozi/CGAL/LFS-example/frog.xyz"; + // const std::string filename = "/home/daozi/CGAL/LFS-example/frog-pca-normal.pwn"; + + Point_set point_set; + point_set.add_normal_map(); + + FT_map lfs_map; + boost::tie (lfs_map, boost::tuples::ignore) = point_set.add_property_map ("LFS", 0.); + + if (!CGAL::IO::read_points(filename, point_set.index_back_inserter(), + CGAL::parameters::point_map(point_set.point_push_map()) + .normal_map(point_set.normal_push_map())) + ) + { + std::cerr << "Error: cannot read file " << filename<< std::endl; + return EXIT_FAILURE; + } + + + unsigned int jet_k = 24; + std::size_t N_rays = 60; + FT apex_angle = 30; + CGAL::estimate_local_feature_size(point_set, lfs_map, jet_k, N_rays, apex_angle, + CGAL::parameters::point_map(point_set.point_push_map()) + .normal_map(point_set.normal_push_map())); + // print + for (Point_set::iterator it = point_set.begin(); it != point_set.end(); ++ it) + { + Point p = point_set.point(*it); + Vector n = point_set.normal(*it); + FT lfs = lfs_map[*it]; + std::cerr << "point:" << p << " normal:" << n << " lfs:" << lfs << std::endl; + } + + std::cerr << "this is a test!" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_vector.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp similarity index 100% rename from Point_set_processing_3/examples/Point_set_processing_3/lfs_example_vector.cpp rename to Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp diff --git a/Point_set_processing_3/include/CGAL/estimate_lfs.h b/Point_set_processing_3/include/CGAL/estimate_lfs.h index c0e5da0e6ea9..fdd55363a38d 100644 --- a/Point_set_processing_3/include/CGAL/estimate_lfs.h +++ b/Point_set_processing_3/include/CGAL/estimate_lfs.h @@ -576,6 +576,42 @@ estimate_local_feature_size(const typename NeighborQuery::Point_3& query, ///< p } /* namespace internal */ /// \endcond +/** + \ingroup PkgPointSetProcessing3Algorithms + + Estimates the local feature size (LFS) for the input 3D point cloud. The function only works for 3D point cloud. + If the input 3D point cloud has no normals, the function will also estimate the normals using jet-fitting. + + + \tparam PointRange is a model of `Range`. The value type of + its iterator is the key type of the named parameter `point_map`. + + \param points input point range + \param LfsMap the map to store the LFS value + \param jet_k number of neighbors for jet-fitting + \param N_rays number of rays for dual cone search + \param apex_angle angle for dual cone search + \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below + + \cgalNamedParamsBegin + \cgalParamNBegin{point_map} + \cgalParamDescription{a property map associating points to the elements of the point set `points`} + \cgalParamType{a model of `ReadablePropertyMap` whose key type is the value type + of the iterator of `PointRange` and whose value type is `geom_traits::Point_3`} + \cgalParamDefault{`CGAL::Identity_property_map`} + \cgalParamNEnd + + \cgalParamNBegin{geom_traits} + \cgalParamDescription{an instance of a geometric traits class} + \cgalParamType{a model of `Kernel`} + \cgalParamDefault{a \cgal Kernel deduced from the point type, using `CGAL::Kernel_traits`} + \cgalParamNEnd + \cgalNamedParamsEnd + + \note This function accepts both 2D and 3D points. + + \return The estimated lfs will be stored in the LfsMap. +*/ template Date: Mon, 5 Feb 2024 16:45:28 +0100 Subject: [PATCH 11/38] change reference back to copy --- Point_set_processing_3/include/CGAL/estimate_lfs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Point_set_processing_3/include/CGAL/estimate_lfs.h b/Point_set_processing_3/include/CGAL/estimate_lfs.h index fdd55363a38d..f55f26f1a8d3 100644 --- a/Point_set_processing_3/include/CGAL/estimate_lfs.h +++ b/Point_set_processing_3/include/CGAL/estimate_lfs.h @@ -618,7 +618,7 @@ template void estimate_local_feature_size(PointRange& points, - LfsMap& lfs_map, + LfsMap lfs_map, const unsigned int jet_k, const std::size_t N_rays, const typename Point_set_processing_3_np_helper::Geom_traits::FT apex_angle, From f79f71ad6fe40a50c35853b490c8ae969a2ecb9d Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Mon, 5 Feb 2024 16:50:30 +0100 Subject: [PATCH 12/38] put lfs_map in the back --- .../examples/Point_set_processing_3/lfs-example-pointset.cpp | 2 +- .../examples/Point_set_processing_3/lfs_example_tuple.cpp | 2 +- Point_set_processing_3/include/CGAL/estimate_lfs.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp index 7d5bee7cf332..ac5d8fd8500b 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp @@ -50,7 +50,7 @@ int main(void) unsigned int jet_k = 24; std::size_t N_rays = 60; FT apex_angle = 30; - CGAL::estimate_local_feature_size(point_set, lfs_map, jet_k, N_rays, apex_angle, + CGAL::estimate_local_feature_size(point_set, jet_k, N_rays, apex_angle, lfs_map, CGAL::parameters::point_map(point_set.point_push_map()) .normal_map(point_set.normal_push_map())); // print diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp index 26e0637c9f09..bd99231bd6d2 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp @@ -40,10 +40,10 @@ int main(void) auto lfs_map = CGAL::Nth_of_tuple_property_map<2, Point_with_normal_and_lfs>(); CGAL::estimate_local_feature_size(points, - lfs_map, jet_k, N_rays, apex_angle, + lfs_map, CGAL::parameters::point_map(CGAL::Nth_of_tuple_property_map<0, Point_with_normal_and_lfs>()) .normal_map(CGAL::Nth_of_tuple_property_map<1, Point_with_normal_and_lfs>())); diff --git a/Point_set_processing_3/include/CGAL/estimate_lfs.h b/Point_set_processing_3/include/CGAL/estimate_lfs.h index f55f26f1a8d3..f68c37d53fe6 100644 --- a/Point_set_processing_3/include/CGAL/estimate_lfs.h +++ b/Point_set_processing_3/include/CGAL/estimate_lfs.h @@ -587,10 +587,10 @@ estimate_local_feature_size(const typename NeighborQuery::Point_3& query, ///< p its iterator is the key type of the named parameter `point_map`. \param points input point range - \param LfsMap the map to store the LFS value \param jet_k number of neighbors for jet-fitting \param N_rays number of rays for dual cone search \param apex_angle angle for dual cone search + \param LfsMap the map to store the LFS value \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below \cgalNamedParamsBegin @@ -618,10 +618,10 @@ template void estimate_local_feature_size(PointRange& points, - LfsMap lfs_map, const unsigned int jet_k, const std::size_t N_rays, const typename Point_set_processing_3_np_helper::Geom_traits::FT apex_angle, + LfsMap lfs_map, const NamedParameters& np = parameters::default_values()) { std::cerr << "estimate lfs" << std::endl; From e7220972d51e063718400256c2827c44466ae539 Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Mon, 5 Feb 2024 17:05:51 +0100 Subject: [PATCH 13/38] CGAL::data_file_path(points_3/kitten.xyz --- .../examples/Point_set_processing_3/lfs-example-pointset.cpp | 4 +--- .../examples/Point_set_processing_3/lfs_example_tuple.cpp | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp index ac5d8fd8500b..dea763ea34a0 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp @@ -26,10 +26,8 @@ typedef CGAL::Sequential_tag Concurrency_tag; int main(void) { - // read xyz - const std::string filename = "/home/daozi/CGAL/LFS-example/frog.xyz"; - // const std::string filename = "/home/daozi/CGAL/LFS-example/frog-pca-normal.pwn"; + const std::string filename = = CGAL::data_file_path("points_3/kitten.xyz"); Point_set point_set; point_set.add_normal_map(); diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp index bd99231bd6d2..e9711061f857 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp @@ -23,7 +23,7 @@ int main(void) { // read xyz - const std::string filename = "/home/daozi/CGAL/LFS-example/frog.xyz"; + const std::string filename = CGAL::data_file_path("points_3/kitten.xyz"); std::vector points; if(!CGAL::IO::read_points(filename, From d9d84af409e899740dfcc7b9a1642c5348de1bc5 Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Mon, 5 Feb 2024 17:07:08 +0100 Subject: [PATCH 14/38] Update Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp Co-authored-by: Andreas Fabri --- .../examples/Point_set_processing_3/lfs-example-pointset.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp index dea763ea34a0..be98b4f74e63 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp @@ -49,7 +49,7 @@ int main(void) std::size_t N_rays = 60; FT apex_angle = 30; CGAL::estimate_local_feature_size(point_set, jet_k, N_rays, apex_angle, lfs_map, - CGAL::parameters::point_map(point_set.point_push_map()) + CGAL::parameters::point_map(point_set.point_map()) .normal_map(point_set.normal_push_map())); // print for (Point_set::iterator it = point_set.begin(); it != point_set.end(); ++ it) From 3607da4f9429f445779dbbee1e52ff17b4d5ad39 Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Mon, 5 Feb 2024 17:07:16 +0100 Subject: [PATCH 15/38] Update Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp Co-authored-by: Andreas Fabri --- .../examples/Point_set_processing_3/lfs-example-pointset.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp index be98b4f74e63..f1f5a6e95ee6 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp @@ -50,7 +50,7 @@ int main(void) FT apex_angle = 30; CGAL::estimate_local_feature_size(point_set, jet_k, N_rays, apex_angle, lfs_map, CGAL::parameters::point_map(point_set.point_map()) - .normal_map(point_set.normal_push_map())); + .normal_map(point_set.normal_map())); // print for (Point_set::iterator it = point_set.begin(); it != point_set.end(); ++ it) { From b28da76a661cb23d11428dd115efbac231136db1 Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Mon, 5 Feb 2024 17:18:46 +0100 Subject: [PATCH 16/38] doxygen --- .../doc/Point_set_processing_3/PackageDescription.txt | 1 + .../doc/Point_set_processing_3/Point_set_processing_3.txt | 4 ++-- .../doc/Point_set_processing_3/examples.txt | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) 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 f9e3c5b663e1..68381494f766 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 @@ -78,6 +78,7 @@ format. - `CGAL::vcm_estimate_normals()` - `CGAL::vcm_is_on_feature_edge()` - `CGAL::structure_point_set()` +- `CGAL::estimate_local_feature_size()` \cgalCRPSection{I/O (All Formats)} 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 5f94d8f7614b..ff7880e8abc3 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 @@ -928,7 +928,7 @@ points that are on sharp edges: \section Point_set_processing_3LFSEstimation Local Feature Size Estimation -The function `estimate_local_feature_size()` can estimate the local feature size for each point by giving a raw point set. +The function `estimate_local_feature_size()` can estimate the local feature size for each point by giving a raw point set. If the point has no normals, the function can also estimate normals using jet-fitting. The function will estimate the curvature and shape diameter. Then, the local feature size will be the minimum of the curvature radius and half of the shape diameter. @@ -936,7 +936,7 @@ The function will estimate the curvature and shape diameter. Then, the local fea The following example reads a point set from a file, estimates the local feature size for each point: -\cgalExample{Point_set_processing_3/lfs_example.cpp} +\cgalExample{Point_set_processing_3/lfs_example_tuple.cpp} \section Point_set_processing_3Structuring Structuring 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 aa41d39531f5..1847286a243f 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 @@ -21,7 +21,8 @@ \example Point_set_processing_3/bilateral_smooth_point_set_example.cpp \example Point_set_processing_3/edge_aware_upsample_point_set_example.cpp \example Point_set_processing_3/edges_example.cpp -\example Point_set_processing_3/lfs_example.cpp +\example Point_set_processing_3/lfs_example_tuple.cpp +\example Point_set_processing_3/lfs_example_pointset.cpp \example Point_set_processing_3/structuring_example.cpp \example Point_set_processing_3/callback_example.cpp */ From c2afd8c51bf9bc6488a2c03ec94d07c9611762fa Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Mon, 5 Feb 2024 17:28:32 +0100 Subject: [PATCH 17/38] cmakelists for examples --- .../examples/Point_set_processing_3/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 ec4dc13337a6..338a6336ef82 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 @@ -72,7 +72,8 @@ if(TARGET CGAL::Eigen3_support) # Executables that require Eigen foreach( target - lfs_example + lfs_example_tuple + lfs_example_pointset jet_smoothing_example normal_estimation clustering_example From 962d3b143676f853596aae680202c543452c0652 Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Mon, 5 Feb 2024 17:34:42 +0100 Subject: [PATCH 18/38] cmakelists & doxygen --- .../{lfs-example-pointset.cpp => lfs_example_pointset.cpp} | 0 Point_set_processing_3/include/CGAL/estimate_lfs.h | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename Point_set_processing_3/examples/Point_set_processing_3/{lfs-example-pointset.cpp => lfs_example_pointset.cpp} (100%) diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp similarity index 100% rename from Point_set_processing_3/examples/Point_set_processing_3/lfs-example-pointset.cpp rename to Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp diff --git a/Point_set_processing_3/include/CGAL/estimate_lfs.h b/Point_set_processing_3/include/CGAL/estimate_lfs.h index f68c37d53fe6..f3e291338a80 100644 --- a/Point_set_processing_3/include/CGAL/estimate_lfs.h +++ b/Point_set_processing_3/include/CGAL/estimate_lfs.h @@ -608,7 +608,7 @@ estimate_local_feature_size(const typename NeighborQuery::Point_3& query, ///< p \cgalParamNEnd \cgalNamedParamsEnd - \note This function accepts both 2D and 3D points. + \note This function only accepts 3D points. \return The estimated lfs will be stored in the LfsMap. */ From 5afb5af334ae28fc3757cc58db08c7e9ade2cb13 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Mon, 5 Feb 2024 16:44:38 +0000 Subject: [PATCH 19/38] trivial fixes --- .../Point_set_processing_3/lfs_example_pointset.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp index f1f5a6e95ee6..6feff5a1e09b 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp @@ -27,7 +27,7 @@ typedef CGAL::Sequential_tag Concurrency_tag; int main(void) { // read xyz - const std::string filename = = CGAL::data_file_path("points_3/kitten.xyz"); + const std::string filename = CGAL::data_file_path("points_3/kitten.xyz"); Point_set point_set; point_set.add_normal_map(); @@ -36,8 +36,8 @@ int main(void) boost::tie (lfs_map, boost::tuples::ignore) = point_set.add_property_map ("LFS", 0.); if (!CGAL::IO::read_points(filename, point_set.index_back_inserter(), - CGAL::parameters::point_map(point_set.point_push_map()) - .normal_map(point_set.normal_push_map())) + CGAL::parameters::point_map(point_set.point_map()) + .normal_map(point_set.normal_map())) ) { std::cerr << "Error: cannot read file " << filename<< std::endl; From 4da4046e58b3cd0dc8a2f9d127965cc0f1923166 Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Mon, 5 Feb 2024 22:56:02 +0100 Subject: [PATCH 20/38] point_push_map instead of point_map, otherwise, read points will not read points correctly --- .../Point_set_processing_3/lfs_example_pointset.cpp | 9 +++------ .../Point_set_processing_3/lfs_example_tuple.cpp | 9 +++++++-- Point_set_processing_3/include/CGAL/estimate_lfs.h | 10 ++++++++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp index 6feff5a1e09b..477f7eebdcd4 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp @@ -21,8 +21,6 @@ typedef CGAL::Point_set_processing_3::internal::Neighbor_query ("LFS", 0.); if (!CGAL::IO::read_points(filename, point_set.index_back_inserter(), - CGAL::parameters::point_map(point_set.point_map()) - .normal_map(point_set.normal_map())) + CGAL::parameters::point_map(point_set.point_push_map()) + .normal_map(point_set.normal_push_map())) ) { std::cerr << "Error: cannot read file " << filename<< std::endl; return EXIT_FAILURE; } - - + unsigned int jet_k = 24; std::size_t N_rays = 60; FT apex_angle = 30; diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp index e9711061f857..8d8747339d88 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp @@ -16,8 +16,8 @@ typedef Kernel::Vector_3 Vector; typedef std::tuple Point_with_normal_and_lfs; // Concurrency -typedef CGAL::Parallel_if_available_tag Concurrency_tag; - +//typedef CGAL::Parallel_if_available_tag Concurrency_tag; +typedef CGAL::Sequential_tag Concurrency_tag; int main(void) { @@ -34,6 +34,11 @@ int main(void) return EXIT_FAILURE; } + for (const auto& pts : points) + { + std::cerr << std::get<0>(pts) << ", " << std::get<1>(pts) << ", " << std::get<2>(pts) << std::endl; + } + unsigned int jet_k = 24; std::size_t N_rays = 60; FT apex_angle = 30; diff --git a/Point_set_processing_3/include/CGAL/estimate_lfs.h b/Point_set_processing_3/include/CGAL/estimate_lfs.h index f3e291338a80..5029a81d4785 100644 --- a/Point_set_processing_3/include/CGAL/estimate_lfs.h +++ b/Point_set_processing_3/include/CGAL/estimate_lfs.h @@ -694,8 +694,15 @@ estimate_local_feature_size(PointRange& points, bool need_jet_normal = false; - if (normal * normal == 0.0) + FT squared_length = std::sqrt(normal.squared_length()); + if (squared_length == 0.0) + { need_jet_normal = true; + } + else if (squared_length != 1.0) // normalize to unit vector + { + normal = normal / std::sqrt(squared_length); + } FT lfs = CGAL::internal::estimate_local_feature_size (point, normal, neighbor_query, bsphere, @@ -706,7 +713,6 @@ estimate_local_feature_size(PointRange& points, if (need_jet_normal) put(normal_map, vt, normal); - ; ++ callback_wrapper.advancement(); From d20626de8106fae21ba051bcb9cc0a0e086748bd Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 6 Feb 2024 07:35:56 +0000 Subject: [PATCH 21/38] Use CGAL::IO::read_point_set() --- .../Point_set_processing_3/lfs_example_pointset.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp index 6feff5a1e09b..13961a33312b 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp @@ -2,8 +2,10 @@ #include #include #include +#include + #include -#include // defines std::pair + // types @@ -35,16 +37,13 @@ int main(void) FT_map lfs_map; boost::tie (lfs_map, boost::tuples::ignore) = point_set.add_property_map ("LFS", 0.); - if (!CGAL::IO::read_points(filename, point_set.index_back_inserter(), - CGAL::parameters::point_map(point_set.point_map()) - .normal_map(point_set.normal_map())) - ) + if (!CGAL::IO::read_point_set(filename, point_set)) { std::cerr << "Error: cannot read file " << filename<< std::endl; return EXIT_FAILURE; } - - + + unsigned int jet_k = 24; std::size_t N_rays = 60; FT apex_angle = 30; From 6b3af0decf3c1e1b3cc62abfb4ef7364d52d5277 Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Tue, 6 Feb 2024 11:36:03 +0100 Subject: [PATCH 22/38] fix lfs_example_tuple --- .../Point_set_processing_3/lfs_example_tuple.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp index 8d8747339d88..032df8d10f13 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp @@ -28,17 +28,13 @@ int main(void) std::vector points; if(!CGAL::IO::read_points(filename, std::back_inserter(points), - CGAL::parameters::point_map(CGAL::Nth_of_tuple_property_map<0, Point_with_normal_and_lfs>()))) + CGAL::parameters::point_map(CGAL::Nth_of_tuple_property_map<0, Point_with_normal_and_lfs>()) + .normal_map(CGAL::Nth_of_tuple_property_map<1, Point_with_normal_and_lfs>()))) { std::cerr << "Error: cannot read file " << filename<< std::endl; return EXIT_FAILURE; } - for (const auto& pts : points) - { - std::cerr << std::get<0>(pts) << ", " << std::get<1>(pts) << ", " << std::get<2>(pts) << std::endl; - } - unsigned int jet_k = 24; std::size_t N_rays = 60; FT apex_angle = 30; @@ -52,8 +48,8 @@ int main(void) CGAL::parameters::point_map(CGAL::Nth_of_tuple_property_map<0, Point_with_normal_and_lfs>()) .normal_map(CGAL::Nth_of_tuple_property_map<1, Point_with_normal_and_lfs>())); - for (const auto &p : points){ - std::cerr << std::get<2>(p) << "\n"; + for (const auto &pts : points){ + std::cerr << std::get<2>(pts) << std::endl; } return EXIT_SUCCESS; From d76a89404caa2587ff0600fa89049339216ef82d Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Tue, 6 Feb 2024 11:45:22 +0100 Subject: [PATCH 23/38] remove trailing whitespace --- .../examples/Point_set_processing_3/lfs_example_tuple.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp index 032df8d10f13..67a149362004 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp @@ -3,7 +3,7 @@ #include #include -#include // defines std::pair +#include #include // types @@ -12,7 +12,6 @@ typedef Kernel::FT FT; typedef Kernel::Point_3 Point; typedef Kernel::Vector_3 Vector; -// Point with normal vector stored in a std::pair. typedef std::tuple Point_with_normal_and_lfs; // Concurrency From e4b015248fb0c18b3260b2ab235c22e312cedf28 Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Tue, 6 Feb 2024 11:49:55 +0100 Subject: [PATCH 24/38] remove trailing whitespace --- .../examples/Point_set_processing_3/lfs_example_tuple.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp index 67a149362004..198308a677fa 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp @@ -15,8 +15,7 @@ typedef Kernel::Vector_3 Vector; typedef std::tuple Point_with_normal_and_lfs; // Concurrency -//typedef CGAL::Parallel_if_available_tag Concurrency_tag; -typedef CGAL::Sequential_tag Concurrency_tag; +typedef CGAL::Parallel_if_available_tag Concurrency_tag; int main(void) { From 73fffec1dee136e066462e74e07230e1e3dda20b Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Tue, 6 Feb 2024 11:52:20 +0100 Subject: [PATCH 25/38] remove trailing whitespace --- .../examples/Point_set_processing_3/lfs_example_tuple.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp index 198308a677fa..1c9e37466b1b 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp @@ -36,7 +36,6 @@ int main(void) unsigned int jet_k = 24; std::size_t N_rays = 60; FT apex_angle = 30; - auto lfs_map = CGAL::Nth_of_tuple_property_map<2, Point_with_normal_and_lfs>(); CGAL::estimate_local_feature_size(points, jet_k, From 8733f9b7b60fefff427dbc0656d409cc05ac058f Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Tue, 6 Feb 2024 11:53:20 +0100 Subject: [PATCH 26/38] remove trailing whitespace --- .../examples/Point_set_processing_3/lfs_example_tuple.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp index 1c9e37466b1b..0616ccd8d977 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp @@ -19,7 +19,6 @@ typedef CGAL::Parallel_if_available_tag Concurrency_tag; int main(void) { - // read xyz const std::string filename = CGAL::data_file_path("points_3/kitten.xyz"); From 78499bb9c6af23099e72ae9ab460c0203d9aebf5 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 6 Feb 2024 11:43:57 +0000 Subject: [PATCH 27/38] read_point_set() adds the property --- .../examples/Point_set_processing_3/lfs_example_pointset.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp index 02993b29795f..e3a838811a2d 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp @@ -30,7 +30,6 @@ int main(void) const std::string filename = CGAL::data_file_path("points_3/kitten.xyz"); Point_set point_set; - point_set.add_normal_map(); FT_map lfs_map; boost::tie (lfs_map, boost::tuples::ignore) = point_set.add_property_map ("LFS", 0.); From 0358747dd7d8acf68f56ec5e5e92f5415607aa52 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Tue, 6 Feb 2024 12:05:55 +0000 Subject: [PATCH 28/38] doc fixes --- Point_set_processing_3/include/CGAL/estimate_lfs.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Point_set_processing_3/include/CGAL/estimate_lfs.h b/Point_set_processing_3/include/CGAL/estimate_lfs.h index 64d8c2e2d69e..3433e0756571 100644 --- a/Point_set_processing_3/include/CGAL/estimate_lfs.h +++ b/Point_set_processing_3/include/CGAL/estimate_lfs.h @@ -579,8 +579,8 @@ estimate_local_feature_size(const typename NeighborQuery::Point_3& query, ///< p /** \ingroup PkgPointSetProcessing3Algorithms - Estimates the local feature size (LFS) for the input 3D point cloud. The function only works for 3D point cloud. - If the input 3D point cloud has no normals, the function will also estimate the normals using jet-fitting. + estimates the local feature size (LFS) for the input 3D point cloud. The function only works for 3D point cloud. + If the input 3D point cloud has no normals, the function will also estimate the normals using jet fitting. \tparam PointRange is a model of `Range`. The value type of @@ -604,13 +604,13 @@ estimate_local_feature_size(const typename NeighborQuery::Point_3& query, ///< p \cgalParamNBegin{geom_traits} \cgalParamDescription{an instance of a geometric traits class} \cgalParamType{a model of `Kernel`} - \cgalParamDefault{a \cgal Kernel deduced from the point type, using `CGAL::Kernel_traits`} + \cgalParamDefault{a \cgal kernel deduced from the point type, using `CGAL::Kernel_traits`} \cgalParamNEnd \cgalNamedParamsEnd \note This function only accepts 3D points. - \return The estimated lfs will be stored in the LfsMap. + \return The estimated local feature size is stored in `lfs_map`. */ template Date: Tue, 6 Feb 2024 13:19:50 +0100 Subject: [PATCH 29/38] Update Point_set_processing_3/doc/Point_set_processing_3/Point_set_processing_3.txt Co-authored-by: Andreas Fabri --- .../doc/Point_set_processing_3/Point_set_processing_3.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ff7880e8abc3..5cab603f4e5e 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 @@ -928,7 +928,7 @@ points that are on sharp edges: \section Point_set_processing_3LFSEstimation Local Feature Size Estimation -The function `estimate_local_feature_size()` can estimate the local feature size for each point by giving a raw point set. If the point has no normals, the function can also estimate normals using jet-fitting. +The function `estimate_local_feature_size()` can estimate the local feature size for each point by giving a raw point set. If the points have no normals, the function also estimates normals using jet fitting. The function will estimate the curvature and shape diameter. Then, the local feature size will be the minimum of the curvature radius and half of the shape diameter. From 3cb7ed6e19f65dd18a4ce79a74aaf6faa9da70c8 Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Tue, 6 Feb 2024 13:20:28 +0100 Subject: [PATCH 30/38] Update Point_set_processing_3/include/CGAL/estimate_lfs.h Co-authored-by: Andreas Fabri --- Point_set_processing_3/include/CGAL/estimate_lfs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Point_set_processing_3/include/CGAL/estimate_lfs.h b/Point_set_processing_3/include/CGAL/estimate_lfs.h index 3433e0756571..6558bab93aa7 100644 --- a/Point_set_processing_3/include/CGAL/estimate_lfs.h +++ b/Point_set_processing_3/include/CGAL/estimate_lfs.h @@ -587,7 +587,7 @@ estimate_local_feature_size(const typename NeighborQuery::Point_3& query, ///< p its iterator is the key type of the named parameter `point_map`. \param points input point range - \param jet_k number of neighbors for jet-fitting + \param jet_k number of neighbors for jet fitting \param N_rays number of rays for dual cone search \param apex_angle angle for dual cone search \param LfsMap the map to store the LFS value From 9c7cb50407fbe8be5ecab934af0f7880e33a4f63 Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Tue, 6 Feb 2024 13:23:26 +0100 Subject: [PATCH 31/38] rename header file --- .../examples/Point_set_processing_3/lfs_example_pointset.cpp | 2 +- .../examples/Point_set_processing_3/lfs_example_tuple.cpp | 2 +- .../CGAL/{estimate_lfs.h => estimate_local_feature_size.h} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename Point_set_processing_3/include/CGAL/{estimate_lfs.h => estimate_local_feature_size.h} (100%) diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp index e3a838811a2d..55eada88f491 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp index 0616ccd8d977..767735f09ea6 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include diff --git a/Point_set_processing_3/include/CGAL/estimate_lfs.h b/Point_set_processing_3/include/CGAL/estimate_local_feature_size.h similarity index 100% rename from Point_set_processing_3/include/CGAL/estimate_lfs.h rename to Point_set_processing_3/include/CGAL/estimate_local_feature_size.h From 9a8f017ba3a437784916f5d522a422a35a910c20 Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Tue, 6 Feb 2024 13:36:53 +0100 Subject: [PATCH 32/38] fix doc --- .../include/CGAL/estimate_local_feature_size.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/Point_set_processing_3/include/CGAL/estimate_local_feature_size.h b/Point_set_processing_3/include/CGAL/estimate_local_feature_size.h index 6558bab93aa7..75aade5a7e0f 100644 --- a/Point_set_processing_3/include/CGAL/estimate_local_feature_size.h +++ b/Point_set_processing_3/include/CGAL/estimate_local_feature_size.h @@ -608,8 +608,6 @@ estimate_local_feature_size(const typename NeighborQuery::Point_3& query, ///< p \cgalParamNEnd \cgalNamedParamsEnd - \note This function only accepts 3D points. - \return The estimated local feature size is stored in `lfs_map`. */ template Date: Thu, 29 Feb 2024 21:54:11 +0100 Subject: [PATCH 33/38] smooth --- .../lfs_example_pointset.cpp | 31 +- .../lfs_example_tuple.cpp | 18 +- .../CGAL/estimate_local_feature_size.h | 264 +++++++++++++++++- 3 files changed, 298 insertions(+), 15 deletions(-) diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp index 55eada88f491..4a0674316c6a 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp @@ -6,8 +6,6 @@ #include - - // types typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; typedef Kernel::FT FT; @@ -47,14 +45,31 @@ int main(void) CGAL::estimate_local_feature_size(point_set, jet_k, N_rays, apex_angle, lfs_map, CGAL::parameters::point_map(point_set.point_map()) .normal_map(point_set.normal_map())); + + // optionally, smooth the raw LFS values for other purposes + unsigned int median_filter_k = 11, median_filter_T = 5; + CGAL::median_filter_smoothing_lfs(point_set, median_filter_k, median_filter_T, lfs_map, + CGAL::parameters::point_map(point_set.point_map())); + + unsigned int lipschitz_continuity_smoothing_k = 11; + FT lipschitz_continuity_lipschitz = 1.0; // 1-lipschitz_continuity for LFS function + CGAL::lipschitz_continuity_smoothing_lfs(point_set, lfs_map, lipschitz_continuity_smoothing_k, lipschitz_continuity_lipschitz, + CGAL::parameters::point_map(point_set.point_map())); + + unsigned int laplacian_smoothing_k = 11, laplacian_smoothing_T = 5; + FT laplacian_smoothing_lambda = 1.0; + CGAL::laplacian_smoothing_lfs(point_set, lfs_map, + laplacian_smoothing_k, laplacian_smoothing_T, laplacian_smoothing_lambda, + CGAL::parameters::point_map(point_set.point_map())); + // print for (Point_set::iterator it = point_set.begin(); it != point_set.end(); ++ it) - { - // Point p = point_set.point(*it); - // Vector n = point_set.normal(*it); - FT lfs = lfs_map[*it]; - std::cerr << lfs << std::endl; - } + { + // Point p = point_set.point(*it); + // Vector n = point_set.normal(*it); + FT lfs = lfs_map[*it]; + std::cout << lfs << std::endl; + } return EXIT_SUCCESS; } diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp index 767735f09ea6..63d9c927efae 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp @@ -44,8 +44,24 @@ int main(void) CGAL::parameters::point_map(CGAL::Nth_of_tuple_property_map<0, Point_with_normal_and_lfs>()) .normal_map(CGAL::Nth_of_tuple_property_map<1, Point_with_normal_and_lfs>())); + // optionally, smooth the raw LFS values for other purposes + unsigned int median_filter_k = 11, median_filter_T = 5; + CGAL::median_filter_smoothing_lfs(points, median_filter_k, median_filter_T, lfs_map, + CGAL::parameters::point_map(CGAL::Nth_of_tuple_property_map<0, Point_with_normal_and_lfs>())); + + unsigned int lipschitz_continuity_smoothing_k = 11; + FT lipschitz_continuity_lipschitz = 1.0; // 1 lipschitz_continuity for LFS function + CGAL::lipschitz_continuity_smoothing_lfs(points, lfs_map, lipschitz_continuity_smoothing_k, lipschitz_continuity_lipschitz, + CGAL::parameters::point_map(CGAL::Nth_of_tuple_property_map<0, Point_with_normal_and_lfs>())); + + unsigned int laplacian_smoothing_k = 11, laplacian_smoothing_T = 5; + FT laplacian_smoothing_lambda = 1.0; + CGAL::laplacian_smoothing_lfs(points, lfs_map, + laplacian_smoothing_k, laplacian_smoothing_T, laplacian_smoothing_lambda, + CGAL::parameters::point_map(CGAL::Nth_of_tuple_property_map<0, Point_with_normal_and_lfs>())); + for (const auto &pts : points){ - std::cerr << std::get<2>(pts) << std::endl; + std::cout << std::get<2>(pts) << std::endl; } return EXIT_SUCCESS; diff --git a/Point_set_processing_3/include/CGAL/estimate_local_feature_size.h b/Point_set_processing_3/include/CGAL/estimate_local_feature_size.h index 75aade5a7e0f..92fb6c20cf33 100644 --- a/Point_set_processing_3/include/CGAL/estimate_local_feature_size.h +++ b/Point_set_processing_3/include/CGAL/estimate_local_feature_size.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -30,9 +31,13 @@ #include #include #include +#include #include #include +#include +#include +#include namespace CGAL { @@ -134,8 +139,6 @@ classical_point_dist_func_epsilon_band(const PointRange &points, CGAL_precondition(band_knn > 0); FT l_min = std::numeric_limits::infinity(); - // for (typename PointRange::const_iterator pwn_it = pwns.begin(); - // pwn_it != pwns.end(); pwn_it++) for (const auto& pt : points) { const Point &query = get(point_map, pt); @@ -573,6 +576,66 @@ estimate_local_feature_size(const typename NeighborQuery::Point_3& query, ///< p return lfs; } +template +void +construct_knn_graph(PointRange& points, + const int knn, + std::vector>& knn_graph, + const NamedParameters& np = parameters::default_values()) +{ + using parameters::choose_parameter; + using parameters::get_parameter; + // basic geometric types + typedef Point_set_processing_3_np_helper NP_helper; + typedef typename NP_helper::Point_map PointMap; + typedef typename NP_helper::Geom_traits Kernel; + typedef typename Kernel::FT FT; + typedef typename Kernel::Point_3 Point; + + // types for K nearest neighbors search structure + typedef typename PointRange::iterator iterator; + typedef Point_set_processing_3::internal::Neighbor_query Neighbor_query; + // Property map typename PointRange::iterator -> index + typedef Index_property_map IndexMap; + + PointMap point_map = NP_helper::get_point_map(points, np); + + const std::function& callback = choose_parameter(get_parameter(np, internal_np::callback), + std::function()); + + CGAL_precondition(points.begin() != points.end()); + + std::size_t memory = CGAL::Memory_sizer().virtual_size(); + CGAL_TRACE_STREAM << (memory >> 20) << " Mb allocated\n"; + CGAL_TRACE_STREAM << " Creates KD-tree\n"; + + Neighbor_query neighbor_query(points, point_map); + + std::size_t nb_points = points.size(); + knn_graph.clear(); + knn_graph.resize(nb_points); + + Point_set_processing_3::internal::Callback_wrapper + callback_wrapper (callback, nb_points); + typedef boost::zip_iterator>::iterator> > Zip_iterator; + CGAL::for_each + (CGAL::make_range (boost::make_zip_iterator (boost::make_tuple (points.begin(), knn_graph.begin())), + boost::make_zip_iterator (boost::make_tuple (points.end(), knn_graph.end()))), + [&](const typename Zip_iterator::reference& t) + { + if (callback_wrapper.interrupted()) + return false; + + const Point& point = get(point_map, get<0>(t)); + neighbor_query.get_iterators(point, knn, FT(0), std::back_inserter(get<1>(t))); + + ++ callback_wrapper.advancement(); + return true; + }); +} + } /* namespace internal */ /// \endcond @@ -622,8 +685,6 @@ estimate_local_feature_size(PointRange& points, LfsMap lfs_map, const NamedParameters& np = parameters::default_values()) { - std::cerr << "estimate lfs" << std::endl; - using parameters::choose_parameter; using parameters::get_parameter; @@ -664,12 +725,10 @@ estimate_local_feature_size(PointRange& points, Neighbor_query neighbor_query(points, point_map); - // Input points types typedef typename PointRange::iterator iterator; typedef typename iterator::value_type value_type; - std::size_t nb_points = points.size(); // calculate a loose bounding shpere @@ -718,6 +777,199 @@ estimate_local_feature_size(PointRange& points, }); } +template +void +median_filter_smoothing_lfs(PointRange& points, + const unsigned int knn, + const unsigned int T, + LfsMap lfs_map, + const NamedParameters& np = parameters::default_values()) +{ + // basic geometric types + typedef Point_set_processing_3_np_helper NP_helper; + typedef typename NP_helper::Geom_traits Kernel; + typedef typename Kernel::FT FT; + typedef typename PointRange::iterator iterator; + typedef typename iterator::value_type value_type; + + CGAL_precondition(points.begin() != points.end()); + + std::vector> knn_graph; + CGAL::internal::construct_knn_graph(points, knn, knn_graph, np); + + std::size_t nb_points = points.size(); + + assert(nb_points == knn_graph.size()); + + auto begin = boost::make_zip_iterator (boost::make_tuple (points.begin(), knn_graph.begin())); + auto end = boost::make_zip_iterator (boost::make_tuple (points.end(), knn_graph.end())); + + for (int t = 0; t < T; t++) + { + for(auto it = begin; it != end; it++) + { + value_type& vt = get<0>(*it); + FT lfs = get(lfs_map, vt); + const std::vector& iterators = get<1>(*it); + + std::vector knn_lfs_vals; + for (const auto& iterator : iterators) knn_lfs_vals.push_back(get(lfs_map, *iterator)); + const auto median = knn_lfs_vals.begin() + knn_lfs_vals.size() / 2; + std::nth_element(knn_lfs_vals.begin(), median, knn_lfs_vals.end()); + + put(lfs_map, vt, *median); + } + } +} + +template +void +lipschitz_continuity_smoothing_lfs(PointRange& points, + LfsMap lfs_map, + const unsigned int knn, + const typename Point_set_processing_3_np_helper::Geom_traits::FT lipschitz=1.0, + const NamedParameters& np = parameters::default_values()) +{ + // basic geometric types + typedef Point_set_processing_3_np_helper NP_helper; + typedef typename NP_helper::Point_map PointMap; + typedef typename NP_helper::Geom_traits Kernel; + typedef typename Kernel::FT FT; + typedef typename PointRange::iterator iterator; + typedef typename iterator::value_type value_type; + + CGAL_precondition(points.begin() != points.end()); + + std::vector> knn_graph; + CGAL::internal::construct_knn_graph(points, knn, knn_graph, np); + + std::size_t nb_points = points.size(); + + assert(nb_points == knn_graph.size()); + + PointMap point_map = NP_helper::get_point_map(points, np); + + // Property map typename PointRange::iterator -> index + typedef Index_property_map IndexMap; + IndexMap index_map(points.begin(), points.end()); + + // starting from the minimum of the func_vals + auto min_iterator = std::min_element(points.begin(), points.end(), + [&](const value_type& vt1, const value_type& vt2) { + return get(lfs_map, vt1) < get(lfs_map, vt2); + } + ); + std::size_t min_lfs_index = std::distance(std::begin(points), min_iterator); + + std::unordered_map visited_map; + visited_map[min_lfs_index] = 1; + + std::function cmp = + [&](std::size_t ind1, std::size_t ind2) + { return get(lfs_map, *(points.begin() + ind1)) > get(lfs_map, *(points.begin() + ind2)); }; + std::priority_queue, decltype(cmp)> min_top_queue(cmp); + + for (iterator it = points.begin(); it != points.end(); it++) min_top_queue.push(get(index_map, it)); + + while (!min_top_queue.empty()) + { + std::size_t cur = min_top_queue.top(); + min_top_queue.pop(); + + // check if the current node satisfying lipschitz continuity + const std::vector& iterators = *(knn_graph.begin() + cur); + auto iterator_cur = points.begin() + cur; + FT cur_lfs = get(lfs_map, *iterator_cur); + for (const auto& iterator : iterators) + { + std::size_t nei = get(index_map, iterator); + auto iterator_nei = points.begin() + nei; + + if (nei == cur) continue; + + if (visited_map.find(nei) != visited_map.end()) + continue; + else + visited_map[nei] = 1; + + FT dist = CGAL::squared_distance(get(point_map, *iterator_cur), get(point_map, *iterator_nei)); + dist = std::sqrt(dist); + + FT nei_lfs = get(lfs_map, *iterator_nei); + + if (nei_lfs > cur_lfs) + { + if (std::abs(nei_lfs - cur_lfs) > lipschitz * dist) + nei_lfs = cur_lfs + dist; + put(lfs_map, *iterator_nei, nei_lfs); + } + else + { + min_top_queue.push(cur); + break; + } + } + } +} + +template +void +laplacian_smoothing_lfs(PointRange& points, + LfsMap lfs_map, + const unsigned int knn, + const unsigned int T, + const typename Point_set_processing_3_np_helper::Geom_traits::FT lambda, + const NamedParameters& np = parameters::default_values()) +{ + // basic geometric types + typedef Point_set_processing_3_np_helper NP_helper; + typedef typename NP_helper::Geom_traits Kernel; + typedef typename Kernel::FT FT; + typedef typename PointRange::iterator iterator; + typedef typename iterator::value_type value_type; + + CGAL_precondition(points.begin() != points.end()); + + std::vector> knn_graph; + CGAL::internal::construct_knn_graph(points, knn, knn_graph, np); + + std::size_t nb_points = points.size(); + + assert(nb_points == knn_graph.size()); + + auto begin = boost::make_zip_iterator (boost::make_tuple (points.begin(), knn_graph.begin())); + auto end = boost::make_zip_iterator (boost::make_tuple (points.end(), knn_graph.end())); + + LfsMap smoothed_lfs_map = lfs_map; + for (int t = 0; t < T; t++) + { + for(auto it = begin; it != end; it++) + { + value_type& vt = get<0>(*it); + FT lfs = get(lfs_map, vt); + const std::vector& iterators = get<1>(*it); + + FT avg_lfs = 0.0; + for (const auto& iterator : iterators) avg_lfs += get(lfs_map, *iterator); + avg_lfs /= iterators.size(); + + FT smoothed_lfs = lfs + lambda * (avg_lfs - lfs); + put(smoothed_lfs_map, vt, smoothed_lfs); + } + } + + std::swap(lfs_map, smoothed_lfs_map); +} + } //namespace CGAL #endif // CGAL_ESTIMATE_LFS_H From d935d2b8dcb787d4f8fe0d94229523ee623a4611 Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Thu, 29 Feb 2024 22:52:19 +0100 Subject: [PATCH 34/38] doxygen --- .../Point_set_processing_3.txt | 18 ++- .../lfs_example_pointset.cpp | 7 +- .../lfs_example_tuple.cpp | 8 +- .../CGAL/estimate_local_feature_size.h | 104 +++++++++++++++++- 4 files changed, 120 insertions(+), 17 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 5cab603f4e5e..658e06bd3582 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 @@ -928,14 +928,22 @@ points that are on sharp edges: \section Point_set_processing_3LFSEstimation Local Feature Size Estimation -The function `estimate_local_feature_size()` can estimate the local feature size for each point by giving a raw point set. If the points have no normals, the function also estimates normals using jet fitting. +Function `estimate_local_feature_size()` can estimate the local feature size for each point by giving a raw point set. If the points have no normals, the function also estimates normals using jet fitting. The function works by calculating the curvature and shape diameter. Then, the local feature size will be the minimum of the curvature radius and half of the shape diameter. -The function will estimate the curvature and shape diameter. Then, the local feature size will be the minimum of the curvature radius and half of the shape diameter. +Function `median_filter_smoothing_lfs()` performs the median filter to smooth the estimated local feature size. -\subsection Point_set_processing_3Example_LFS Example +Function `lipschitz_continuity_smoothing_lfs()` smooth the local feature size based on the property of 1-Lipschitz continuity of the local feature size function. -The following example reads a point set from a file, estimates the -local feature size for each point: +Function `laplacian_smoothing_lfs()` performs the laplacian smoothing to smooth the estimated local feature size. + +\subsection Point_set_processing_3Example_LFS_point_set Point_set_3 Example + +The following example uses the `CGAL::Point_set_3` to store a point cloud and estimates the local feature size for each point: +\cgalExample{Point_set_processing_3/lfs_example_pointset.cpp} + +\subsection Point_set_processing_3Example_LFS_tuple std::vector Example + +The following example uses the `std::vector` to store a point cloud and estimates the local feature size for each point: \cgalExample{Point_set_processing_3/lfs_example_tuple.cpp} diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp index 4a0674316c6a..df5d2c072d00 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp @@ -52,14 +52,13 @@ int main(void) CGAL::parameters::point_map(point_set.point_map())); unsigned int lipschitz_continuity_smoothing_k = 11; - FT lipschitz_continuity_lipschitz = 1.0; // 1-lipschitz_continuity for LFS function - CGAL::lipschitz_continuity_smoothing_lfs(point_set, lfs_map, lipschitz_continuity_smoothing_k, lipschitz_continuity_lipschitz, + FT lipschitz = 1.0; + CGAL::lipschitz_continuity_smoothing_lfs(point_set, lipschitz_continuity_smoothing_k, lipschitz, lfs_map, CGAL::parameters::point_map(point_set.point_map())); unsigned int laplacian_smoothing_k = 11, laplacian_smoothing_T = 5; FT laplacian_smoothing_lambda = 1.0; - CGAL::laplacian_smoothing_lfs(point_set, lfs_map, - laplacian_smoothing_k, laplacian_smoothing_T, laplacian_smoothing_lambda, + CGAL::laplacian_smoothing_lfs(point_set, laplacian_smoothing_k, laplacian_smoothing_T, laplacian_smoothing_lambda, lfs_map, CGAL::parameters::point_map(point_set.point_map())); // print diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp index 63d9c927efae..d0c4df60867b 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp @@ -50,14 +50,14 @@ int main(void) CGAL::parameters::point_map(CGAL::Nth_of_tuple_property_map<0, Point_with_normal_and_lfs>())); unsigned int lipschitz_continuity_smoothing_k = 11; - FT lipschitz_continuity_lipschitz = 1.0; // 1 lipschitz_continuity for LFS function - CGAL::lipschitz_continuity_smoothing_lfs(points, lfs_map, lipschitz_continuity_smoothing_k, lipschitz_continuity_lipschitz, + FT lipschitz = 1.0; // 1 lipschitz_continuity for LFS function + CGAL::lipschitz_continuity_smoothing_lfs(points, lipschitz_continuity_smoothing_k, lipschitz, lfs_map, CGAL::parameters::point_map(CGAL::Nth_of_tuple_property_map<0, Point_with_normal_and_lfs>())); unsigned int laplacian_smoothing_k = 11, laplacian_smoothing_T = 5; FT laplacian_smoothing_lambda = 1.0; - CGAL::laplacian_smoothing_lfs(points, lfs_map, - laplacian_smoothing_k, laplacian_smoothing_T, laplacian_smoothing_lambda, + CGAL::laplacian_smoothing_lfs(points, + laplacian_smoothing_k, laplacian_smoothing_T, laplacian_smoothing_lambda, lfs_map, CGAL::parameters::point_map(CGAL::Nth_of_tuple_property_map<0, Point_with_normal_and_lfs>())); for (const auto &pts : points){ diff --git a/Point_set_processing_3/include/CGAL/estimate_local_feature_size.h b/Point_set_processing_3/include/CGAL/estimate_local_feature_size.h index 92fb6c20cf33..4f398efc52d4 100644 --- a/Point_set_processing_3/include/CGAL/estimate_local_feature_size.h +++ b/Point_set_processing_3/include/CGAL/estimate_local_feature_size.h @@ -642,7 +642,7 @@ construct_knn_graph(PointRange& points, /** \ingroup PkgPointSetProcessing3Algorithms - estimates the local feature size (LFS) for the input 3D point cloud. The function only works for 3D point cloud. + Estimate the local feature size (LFS) for the input 3D point cloud. The function only works for 3D point cloud. If the input 3D point cloud has no normals, the function will also estimate the normals using jet fitting. @@ -777,6 +777,38 @@ estimate_local_feature_size(PointRange& points, }); } +/** + \ingroup PkgPointSetProcessing3Algorithms + + Smooth the local feature size using median filter. + + + \tparam PointRange is a model of `Range`. The value type of + its iterator is the key type of the named parameter `point_map`. + + \param points input point range + \param knn number of neighbors for median filter + \param T number of iterations + \param LfsMap the map for the LFS value + \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below + + \cgalNamedParamsBegin + \cgalParamNBegin{point_map} + \cgalParamDescription{a property map associating points to the elements of the point set `points`} + \cgalParamType{a model of `ReadablePropertyMap` whose key type is the value type + of the iterator of `PointRange` and whose value type is `geom_traits::Point_3`} + \cgalParamDefault{`CGAL::Identity_property_map`} + \cgalParamNEnd + + \cgalParamNBegin{geom_traits} + \cgalParamDescription{an instance of a geometric traits class} + \cgalParamType{a model of `Kernel`} + \cgalParamDefault{a \cgal kernel deduced from the point type, using `CGAL::Kernel_traits`} + \cgalParamNEnd + \cgalNamedParamsEnd + + \return The function will smooth the local feature size values directly in `lfs_map`. +*/ template `} + \cgalParamNEnd + + \cgalParamNBegin{geom_traits} + \cgalParamDescription{an instance of a geometric traits class} + \cgalParamType{a model of `Kernel`} + \cgalParamDefault{a \cgal kernel deduced from the point type, using `CGAL::Kernel_traits`} + \cgalParamNEnd + \cgalNamedParamsEnd + + \return The function will smooth the local feature size values directly in `lfs_map`. +*/ template void lipschitz_continuity_smoothing_lfs(PointRange& points, - LfsMap lfs_map, const unsigned int knn, - const typename Point_set_processing_3_np_helper::Geom_traits::FT lipschitz=1.0, + const typename Point_set_processing_3_np_helper::Geom_traits::FT lipschitz, + LfsMap lfs_map, const NamedParameters& np = parameters::default_values()) { // basic geometric types @@ -918,16 +981,49 @@ lipschitz_continuity_smoothing_lfs(PointRange& points, } } +/** + \ingroup PkgPointSetProcessing3Algorithms + + Smooth the local feature size using median filter. + + + \tparam PointRange is a model of `Range`. The value type of + its iterator is the key type of the named parameter `point_map`. + + \param points input point range + \param knn number of neighbors for laplacian smoothing + \param T number of iterations + \param lambda lambda value for laplacian smoothing + \param LfsMap the map for the LFS value + \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below + + \cgalNamedParamsBegin + \cgalParamNBegin{point_map} + \cgalParamDescription{a property map associating points to the elements of the point set `points`} + \cgalParamType{a model of `ReadablePropertyMap` whose key type is the value type + of the iterator of `PointRange` and whose value type is `geom_traits::Point_3`} + \cgalParamDefault{`CGAL::Identity_property_map`} + \cgalParamNEnd + + \cgalParamNBegin{geom_traits} + \cgalParamDescription{an instance of a geometric traits class} + \cgalParamType{a model of `Kernel`} + \cgalParamDefault{a \cgal kernel deduced from the point type, using `CGAL::Kernel_traits`} + \cgalParamNEnd + \cgalNamedParamsEnd + + \return The function will smooth the local feature size values directly in `lfs_map`. +*/ template void laplacian_smoothing_lfs(PointRange& points, - LfsMap lfs_map, const unsigned int knn, const unsigned int T, const typename Point_set_processing_3_np_helper::Geom_traits::FT lambda, + LfsMap lfs_map, const NamedParameters& np = parameters::default_values()) { // basic geometric types From 7207ebc2aa538065be6da612394c0c79a044bcdf Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Thu, 29 Feb 2024 23:57:24 +0100 Subject: [PATCH 35/38] test --- .../lfs_example_pointset.cpp | 4 - .../lfs_example_tuple.cpp | 9 ++- .../CGAL/estimate_local_feature_size.h | 12 +-- .../Point_set_processing_3/CMakeLists.txt | 3 + .../lfs_estimation_test.cpp | 78 +++++++++++++++++++ 5 files changed, 94 insertions(+), 12 deletions(-) create mode 100644 Point_set_processing_3/test/Point_set_processing_3/lfs_estimation_test.cpp diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp index df5d2c072d00..4735cdb36d42 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_pointset.cpp @@ -4,8 +4,6 @@ #include #include -#include - // types typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; typedef Kernel::FT FT; @@ -16,8 +14,6 @@ typedef CGAL::Point_set_3 Point_set; typedef Point_set::Property_map Point_map; typedef Point_set::Property_map FT_map; -// types for K nearest neighbors search structure -typedef CGAL::Point_set_processing_3::internal::Neighbor_query Neighbor_query; // Concurrency typedef CGAL::Parallel_if_available_tag Concurrency_tag; diff --git a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp index d0c4df60867b..bde51ad07f7d 100644 --- a/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp +++ b/Point_set_processing_3/examples/Point_set_processing_3/lfs_example_tuple.cpp @@ -60,8 +60,13 @@ int main(void) laplacian_smoothing_k, laplacian_smoothing_T, laplacian_smoothing_lambda, lfs_map, CGAL::parameters::point_map(CGAL::Nth_of_tuple_property_map<0, Point_with_normal_and_lfs>())); - for (const auto &pts : points){ - std::cout << std::get<2>(pts) << std::endl; + // print + for (const auto &pts : points) + { + // Point p = std::get<0>(pts); + // Vector n = std::get<1>(pts); + FT lfs = std::get<2>(pts); + std::cout << lfs << std::endl; } return EXIT_SUCCESS; diff --git a/Point_set_processing_3/include/CGAL/estimate_local_feature_size.h b/Point_set_processing_3/include/CGAL/estimate_local_feature_size.h index 4f398efc52d4..f9ba979ddd6c 100644 --- a/Point_set_processing_3/include/CGAL/estimate_local_feature_size.h +++ b/Point_set_processing_3/include/CGAL/estimate_local_feature_size.h @@ -628,8 +628,8 @@ construct_knn_graph(PointRange& points, if (callback_wrapper.interrupted()) return false; - const Point& point = get(point_map, get<0>(t)); - neighbor_query.get_iterators(point, knn, FT(0), std::back_inserter(get<1>(t))); + const Point& point = get(point_map, boost::get<0>(t)); + neighbor_query.get_iterators(point, knn, FT(0), std::back_inserter(boost::get<1>(t))); ++ callback_wrapper.advancement(); return true; @@ -843,9 +843,9 @@ median_filter_smoothing_lfs(PointRange& points, { for(auto it = begin; it != end; it++) { - value_type& vt = get<0>(*it); + value_type& vt = boost::get<0>(*it); FT lfs = get(lfs_map, vt); - const std::vector& iterators = get<1>(*it); + const std::vector& iterators = boost::get<1>(*it); std::vector knn_lfs_vals; for (const auto& iterator : iterators) knn_lfs_vals.push_back(get(lfs_map, *iterator)); @@ -1050,9 +1050,9 @@ laplacian_smoothing_lfs(PointRange& points, { for(auto it = begin; it != end; it++) { - value_type& vt = get<0>(*it); + value_type& vt = boost::get<0>(*it); FT lfs = get(lfs_map, vt); - const std::vector& iterators = get<1>(*it); + const std::vector& iterators = boost::get<1>(*it); FT avg_lfs = 0.0; for (const auto& iterator : iterators) avg_lfs += get(lfs_map, *iterator); diff --git a/Point_set_processing_3/test/Point_set_processing_3/CMakeLists.txt b/Point_set_processing_3/test/Point_set_processing_3/CMakeLists.txt index cbd77792290f..239710909f87 100644 --- a/Point_set_processing_3/test/Point_set_processing_3/CMakeLists.txt +++ b/Point_set_processing_3/test/Point_set_processing_3/CMakeLists.txt @@ -71,6 +71,9 @@ if(TARGET CGAL::Eigen3_support) create_single_source_cgal_program("psp_jet_includes.cpp") target_link_libraries(psp_jet_includes PUBLIC CGAL::Eigen3_support) + + create_single_source_cgal_program("lfs_estimation_test.cpp") + target_link_libraries(lfs_estimation_test PUBLIC CGAL::Eigen3_support) else() message(STATUS "NOTICE: Some tests require Eigen 3.1 (or greater), and will not be compiled.") endif() diff --git a/Point_set_processing_3/test/Point_set_processing_3/lfs_estimation_test.cpp b/Point_set_processing_3/test/Point_set_processing_3/lfs_estimation_test.cpp new file mode 100644 index 000000000000..5ded7ec6451c --- /dev/null +++ b/Point_set_processing_3/test/Point_set_processing_3/lfs_estimation_test.cpp @@ -0,0 +1,78 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +// types +typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; +typedef Kernel::FT FT; +typedef Kernel::Point_3 Point; +typedef Kernel::Vector_3 Vector; +typedef CGAL::Random_points_on_sphere_3 Random_points_on_sphere_3; + +typedef std::tuple Point_with_normal_and_lfs; + + +// Concurrency +typedef CGAL::Parallel_if_available_tag Concurrency_tag; + +void test (std::vector& input, + std::ptrdiff_t result0 = 1, int result1 = 1, int result2 = 1, int result3 = 1, int result4 = 1) +{ + ; +} + + +int main(void) +{ + const int num_points = 1000; // number of sampled points + FT r = 1.0; // unit sphere + + CGAL::Random rand = CGAL::Random(23); + + // std::vector point_coordinates; + // point_coordinates.reserve(num_points); + // std::copy_n(Random_points_on_sphere_3(r, rand), num_points, std::back_inserter(point_coordinates)); + + std::vector points; + std::generate_n(std::back_inserter(points), + num_points, + [&]() -> Point_with_normal_and_lfs { + Point point = *CGAL::Random_points_on_sphere_3(r, rand); + Vector normal = point - CGAL::ORIGIN; + return std::make_tuple(point, normal, FT(0)); + }); + + + unsigned int jet_k = 24; + std::size_t N_rays = 60; + FT apex_angle = 30; + auto lfs_map = CGAL::Nth_of_tuple_property_map<2, Point_with_normal_and_lfs>(); + CGAL::estimate_local_feature_size(points, + jet_k, + N_rays, + apex_angle, + lfs_map, + CGAL::parameters::point_map(CGAL::Nth_of_tuple_property_map<0, Point_with_normal_and_lfs>()) + .normal_map(CGAL::Nth_of_tuple_property_map<1, Point_with_normal_and_lfs>())); + + // evalute the LFS on the sphere + // the GT should be the radius + FT mse = 0.0; + for (const auto &pts : points) + { + FT lfs = std::get<2>(pts); + mse += std::pow((lfs - r), 2); + } + mse /= num_points; + + std::cout << "Mean Squared Error (MSE): " << mse << std::endl; + + return EXIT_SUCCESS; +} + From 71ceedc63e0df32e84daa4395bba6edaa7b69d60 Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Fri, 1 Mar 2024 00:12:18 +0100 Subject: [PATCH 36/38] remove trailing whitespaces --- .../Point_set_processing_3.txt | 138 +++++++++--------- 1 file changed, 69 insertions(+), 69 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 658e06bd3582..532d62f3a884 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 @@ -52,12 +52,12 @@ typedef CGAL::First_of_pair_property_map Point_map; typedef CGAL::Second_of_pair_property_map Vector_map; CGAL::jet_estimate_normals - // concurrency tag - (points, // input range of points - 12, // parameter: number of neighbors - CGAL::parameters:: // named parameters: - point_map (Point_map()). // * point map - normal_map (Vector_map())); // * normal map + // concurrency tag + (points, // input range of points + 12, // parameter: number of neighbors + CGAL::parameters:: // named parameters: + point_map (Point_map()). // * point map + normal_map (Vector_map())); // * normal map \endcode This API was introduced in \cgal 4.12. Please refer to the @@ -116,10 +116,10 @@ std::vector points; // Old pre-CGAL 4.12 API CGAL::jet_estimate_normals - (points.begin(), points.end(), - CGAL::First_of_pair_property_map(), - CGAL::Second_of_pair_property_map(), - 12); // Number of neighbors + (points.begin(), points.end(), + CGAL::First_of_pair_property_map(), + CGAL::Second_of_pair_property_map(), + 12); // Number of neighbors \endcode The pair of iterators is replaced by a range and the optional @@ -133,10 +133,10 @@ std::vector points; // New API CGAL::jet_estimate_normals - (points, - 12, // Number of neighbors - CGAL::parameters::point_map (CGAL::First_of_pair_property_map()). - normal_map (CGAL::Second_of_pair_property_map())); + (points, + 12, // Number of neighbors + CGAL::parameters::point_map (CGAL::First_of_pair_property_map()). + normal_map (CGAL::Second_of_pair_property_map())); \endcode Please refer to the [Reference Manual](@ref PkgPointSetProcessing3Ref) for @@ -524,66 +524,66 @@ above-mentioned example was used. \cgalFigureAnchor{Point_set_processing_3tableRegistrationRegistration_visualization_table} - - - + + + - - - + + + - - - + + + - - - + + + - - - + + +
Scan 1 Scan 1 (possibly transformed, green) and Scan 2 (the reference, red) Scan 1 Scan 1 (possibly transformed, green) and Scan 2 (the reference, red)
Unregistered - \cgalFigureBegin{Point_set_processing_3figRegistrationUnregistered_hippo2_view1, registration_view1_hippo2_unregistered.png} - \cgalFigureEnd - - \cgalFigureBegin{Point_set_processing_3figRegistrationUnregistered_hippo1_hippo2_view1, registration_view1_hippo1_hippo2_unregistered.png} - \cgalFigureEnd - Unregistered + \cgalFigureBegin{Point_set_processing_3figRegistrationUnregistered_hippo2_view1, registration_view1_hippo2_unregistered.png} + \cgalFigureEnd + + \cgalFigureBegin{Point_set_processing_3figRegistrationUnregistered_hippo1_hippo2_view1, registration_view1_hippo1_hippo2_unregistered.png} + \cgalFigureEnd +
- Registered - using - %OpenGR - - \cgalFigureBegin{Point_set_processing_3figRegistrationOpenGR_hippo2_view1, registration_view1_hippo2_opengr.png} - \cgalFigureEnd - - \cgalFigureBegin{Point_set_processing_3figRegistrationOpenGR_hippo1_hippo2_view1, registration_view1_hippo1_hippo2_opengr.png} - \cgalFigureEnd - + Registered + using + %OpenGR + + \cgalFigureBegin{Point_set_processing_3figRegistrationOpenGR_hippo2_view1, registration_view1_hippo2_opengr.png} + \cgalFigureEnd + + \cgalFigureBegin{Point_set_processing_3figRegistrationOpenGR_hippo1_hippo2_view1, registration_view1_hippo1_hippo2_opengr.png} + \cgalFigureEnd +
- Registered - using - PointMatcher - - \cgalFigureBegin{Point_set_processing_3figRegistrationPointMatcher_hippo2_view1, registration_view1_hippo2_pointmatcher.png} - \cgalFigureEnd - - \cgalFigureBegin{Point_set_processing_3figRegistrationPointMatcher_hippo1_hippo2_view1, registration_view1_hippo1_hippo2_pointmatcher.png} - \cgalFigureEnd - + Registered + using + PointMatcher + + \cgalFigureBegin{Point_set_processing_3figRegistrationPointMatcher_hippo2_view1, registration_view1_hippo2_pointmatcher.png} + \cgalFigureEnd + + \cgalFigureBegin{Point_set_processing_3figRegistrationPointMatcher_hippo1_hippo2_view1, registration_view1_hippo1_hippo2_pointmatcher.png} + \cgalFigureEnd +
- Registered - using - OpenGR+PointMatcher - Pipeline - - \cgalFigureBegin{Point_set_processing_3figRegistrationPipeline_hippo2_view1, registration_view1_hippo2_opengr_pointmatcher_pipeline.png} - \cgalFigureEnd - - \cgalFigureBegin{Point_set_processing_3figRegistrationPipeline_hippo1_hippo2_view1, registration_view1_hippo1_hippo2_opengr_pointmatcher_pipeline.png} - \cgalFigureEnd - + Registered + using + OpenGR+PointMatcher + Pipeline + + \cgalFigureBegin{Point_set_processing_3figRegistrationPipeline_hippo2_view1, registration_view1_hippo2_opengr_pointmatcher_pipeline.png} + \cgalFigureEnd + + \cgalFigureBegin{Point_set_processing_3figRegistrationPipeline_hippo1_hippo2_view1, registration_view1_hippo1_hippo2_opengr_pointmatcher_pipeline.png} + \cgalFigureEnd +
\cgalFigureCaptionBegin{Point_set_processing_3tableRegistrationRegistration_visualization_table} @@ -698,7 +698,7 @@ in two until each cluster's size is less than the parameter `size`. \cgalFigureBegin{Point_set_processing_3figHierarchy_simplification_size, hierarchical_clustering_size.jpg} Input point set and hierarchy simplification with different `size` parameter: \f$10\f$, \f$100\f$ and \f$1000\f$. In the 3 cases, -`var_max`\f$=1/3\f$. \cgalFigureEnd +`var_max`\f$=1/3\f$. \cgalFigureEnd \subsubsection Point_set_processing_3Hierarchy_simplification_parameter_var_max Parameter: var_max @@ -706,7 +706,7 @@ In addition to the size parameter, a variation parameter allows to increase simplification in monotonous regions. For each cluster, a surface variation measure is computed using the sorted eigenvalues of the covariance matrix: \f[ \sigma(p) = \frac{\lambda_0}{\lambda_0 + - \lambda_1 + \lambda_2}. \f] + \lambda_1 + \lambda_2}. \f] This function goes from \f$0\f$ if the cluster is coplanar to \f$1/3\f$ if it is fully isotropic. If a cluster's variation is above @@ -717,7 +717,7 @@ point set. \cgalFigureBegin{Point_set_processing_3figHierarchical_clustering_var_max, hierarchical_clustering_var_max.jpg} Input point set and hierarchy simplification with different `var_max` parameter: \f$0.00001\f$, \f$0.001\f$ and \f$0.1\f$. In the 3 cases, -`size`\f$=1000\f$. \cgalFigureEnd +`size`\f$=1000\f$. \cgalFigureEnd \subsection Point_set_processing_3Example_wlop WLOP Simplification Example From 03b3d6cac1ca78dcf3de181759b942a650d7ca3e Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Fri, 1 Mar 2024 00:19:55 +0100 Subject: [PATCH 37/38] remove trailing whilespace --- .../doc/Point_set_processing_3/Point_set_processing_3.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 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 532d62f3a884..d0ade050cf6d 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 @@ -930,11 +930,11 @@ points that are on sharp edges: Function `estimate_local_feature_size()` can estimate the local feature size for each point by giving a raw point set. If the points have no normals, the function also estimates normals using jet fitting. The function works by calculating the curvature and shape diameter. Then, the local feature size will be the minimum of the curvature radius and half of the shape diameter. -Function `median_filter_smoothing_lfs()` performs the median filter to smooth the estimated local feature size. +Function `median_filter_smoothing_lfs()` performs the median filter to smooth the estimated local feature size. Function `lipschitz_continuity_smoothing_lfs()` smooth the local feature size based on the property of 1-Lipschitz continuity of the local feature size function. -Function `laplacian_smoothing_lfs()` performs the laplacian smoothing to smooth the estimated local feature size. +Function `laplacian_smoothing_lfs()` performs the laplacian smoothing to smooth the estimated local feature size. \subsection Point_set_processing_3Example_LFS_point_set Point_set_3 Example From a81bfbf5f6e07e218ece570cdfea785a8c770c38 Mon Sep 17 00:00:00 2001 From: bizerfr <16448481+bizerfr@users.noreply.github.com> Date: Fri, 1 Mar 2024 00:23:27 +0100 Subject: [PATCH 38/38] remove trailing whilespace --- .../doc/Point_set_processing_3/Point_set_processing_3.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 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 d0ade050cf6d..af5f3d6df2d9 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 @@ -928,11 +928,15 @@ points that are on sharp edges: \section Point_set_processing_3LFSEstimation Local Feature Size Estimation -Function `estimate_local_feature_size()` can estimate the local feature size for each point by giving a raw point set. If the points have no normals, the function also estimates normals using jet fitting. The function works by calculating the curvature and shape diameter. Then, the local feature size will be the minimum of the curvature radius and half of the shape diameter. +Function `estimate_local_feature_size()` can estimate the local feature size for each point +by giving a raw point set. If the points have no normals, the function also estimates normals using jet fitting. +The function works by calculating the curvature and shape diameter. +Then, the local feature size will be the minimum of the curvature radius and half of the shape diameter. Function `median_filter_smoothing_lfs()` performs the median filter to smooth the estimated local feature size. -Function `lipschitz_continuity_smoothing_lfs()` smooth the local feature size based on the property of 1-Lipschitz continuity of the local feature size function. +Function `lipschitz_continuity_smoothing_lfs()` smooth the local feature size +based on the property of 1-Lipschitz continuity of the local feature size function. Function `laplacian_smoothing_lfs()` performs the laplacian smoothing to smooth the estimated local feature size.