diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/simplify_polyline.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/simplify_polyline.h new file mode 100644 index 00000000000..a7755d71f6d --- /dev/null +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/simplify_polyline.h @@ -0,0 +1,195 @@ +// Copyright (c) 2020 GeometryFactory (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Sébastien Loriot + +#ifndef CGAL_POLYGON_MESH_PROCESSING_SIMPLIFY_POLYLINE_H +#define CGAL_POLYGON_MESH_PROCESSING_SIMPLIFY_POLYLINE_H + +#include + +#include +#include + +#include + + +namespace CGAL { +namespace Polygon_mesh_processing { +namespace experimental { + +enum Polyline_simplification_algorithms { DOUGLAS_PEUCKER, ITERATIVE }; + +template +void simplify_polyline(const PointRangeIn& input, + PointRangeOut& output, + const double max_squared_frechet_distance, + const NamedParametersIn& np_in, + const NamedParametersOut& np_out) +{ + using parameters::choose_parameter; + using parameters::get_parameter; + + static_assert(std::is_same< + typename std::iterator_traits::value_type, + typename std::iterator_traits::value_type >::value ); + + typedef typename GetPointMap::type Point_map_in; + typedef typename GetPointMap::type Point_map_out; + typedef typename Point_set_processing_3::GetK::Kernel Kernel; + + Point_map_in in_pm = choose_parameter(get_parameter(np_in, internal_np::point_map)); + Point_map_out out_pm = choose_parameter(get_parameter(np_out, internal_np::point_map)); + + const Polyline_simplification_algorithms algorithm = + choose_parameter(get_parameter(np_in, internal_np::algorithm), DOUGLAS_PEUCKER); + + switch(algorithm) + { + case ITERATIVE: + { + // skip points in the input range that do not contains any information + if (input.size()<=2) + { + output.reserve(input.size()); + for (const auto& p : input) + { + output.push_back(p); + put(out_pm, output.back(), get(in_pm, p)); + } + return; + } + + auto is_valid_approx = [&input, &in_pm, max_squared_frechet_distance]( + std::size_t b, std::size_t e, + const typename Kernel::Line_3& line) + { + typename Kernel::Compare_squared_distance_3 compare_squared_distance; + for (std::size_t i=b+1; i=input.size()) break; + } + output.push_back(input.back()); + put(out_pm, output.back(), get(in_pm, input.back())); + return; + } + case DOUGLAS_PEUCKER: + { + const bool is_closed = input.front()==input.back(); + + std::size_t nb_points = is_closed ? input.size()-1 : input.size(); + + if (nb_points<=2) + { + output.reserve(input.size()); + for (const auto& p : input) + { + output.push_back(p); + put(out_pm, output.back(), get(in_pm, p)); + } + return; + } + + std::vector< std::pair > ranges; + ranges.push_back(std::make_pair(0, nb_points-1)); + + std::vector kept(input.size(), false); + if (is_closed) kept[nb_points]=true; + while( !ranges.empty() ) + { + std::size_t rb, re; + std::tie(rb, re) = ranges.back(); + ranges.pop_back(); + kept[rb]=true; + kept[re]=true; + if (rb+1==re) continue; + + typename Kernel::Line_3 line(get(in_pm, input[rb]), get(in_pm, input[re])); + double max_d = max_squared_frechet_distance; + std::size_t max_i = 0; + for (std::size_t i=rb; i max_d) + { + max_d = d; + max_i = i; + } + } + if (max_i != 0) + { + ranges.push_back( std::make_pair(max_i, re) ); + ranges.push_back( std::make_pair(rb, max_i) ); + } + } + std::size_t nb_kept=0; + for (std::size_t i=0; i +void simplify_polyline(const PointRangeIn& input, + PointRangeOut& output, + const double max_squared_frechet_distance) +{ + simplify_polyline(input, output, max_squared_frechet_distance, + parameters::all_default(), parameters::all_default()); +} + +template +void simplify_polyline(const PointRangeIn& input, + PointRangeOut& output, + const double max_squared_frechet_distance, + const NamedParametersIn& np_in) +{ + simplify_polyline(input, output, max_squared_frechet_distance, np_in, parameters::all_default()); +} + +} } } // end of CGAL::Polygon_mesh_processing::experimental namespace + + +#endif // CGAL_POLYGON_MESH_PROCESSING_SIMPLIFY_POLYLINE_H \ No newline at end of file diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt index c1612e65f9d..9805d8fa7da 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt @@ -103,6 +103,7 @@ endif() create_single_source_cgal_program("test_pmp_manifoldness.cpp") create_single_source_cgal_program("test_mesh_smoothing.cpp") create_single_source_cgal_program("test_remove_caps_needles.cpp") + create_single_source_cgal_program("test_simplify_polylines_pmp.cpp") # create_single_source_cgal_program("test_pmp_repair_self_intersections.cpp") if( TARGET CGAL::TBB_support ) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_simplify_polylines_pmp.cmd b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_simplify_polylines_pmp.cmd new file mode 100644 index 00000000000..815749de91a --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_simplify_polylines_pmp.cmd @@ -0,0 +1 @@ +data/U.polylines.txt 0.000001 diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_simplify_polylines_pmp.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_simplify_polylines_pmp.cpp new file mode 100644 index 00000000000..dbdc503a979 --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_simplify_polylines_pmp.cpp @@ -0,0 +1,60 @@ +#include +#include + +#include +#include +#include + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef K::Point_3 Point_3; + +namespace PMP_exp=CGAL::Polygon_mesh_processing::experimental; + +int main(int argc, char **argv) +{ + if (argc!=3) + { + std::cerr << "Usage: " << argv[0] << " in.polylines.txt max_squared_frechet_distance\n"; + return 1; + } + + std::ifstream in(argv[1]); + const double max_squared_frechet_distance = atof(argv[2]); + + //TODO read more than 1 poly or use CGAL I/O function + int n; + in >> n; + + std::vector polyline(n); + for(int i=0; i> polyline[i]; + + // first run + std::vector simplified_polyline; + PMP_exp::simplify_polyline(polyline, simplified_polyline, max_squared_frechet_distance, + CGAL::parameters::algorithm(PMP_exp::ITERATIVE)); + + std::cout << "Polyline of " << n << " points simplified into a polyline of " << simplified_polyline.size() << " points\n"; + + std::ofstream out("out_iterative.polylines.txt"); + out << simplified_polyline.size(); + for (auto p : simplified_polyline) + out << " " << p; + out << "\n"; + out.close(); + + // second run + simplified_polyline.clear(); + PMP_exp::simplify_polyline(polyline, simplified_polyline, max_squared_frechet_distance, + CGAL::parameters::algorithm(PMP_exp::DOUGLAS_PEUCKER)); + + std::cout << "Polyline of " << n << " points simplified into a polyline of " << simplified_polyline.size() << " points\n"; + + out.open("out_dp.polylines.txt"); + out << simplified_polyline.size(); + for (auto p : simplified_polyline) + out << " " << p; + out << "\n"; + + return 0; +}