From 225d52b084a7975097bd03b6741f6b3727b14818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 6 Jun 2024 17:30:58 +0200 Subject: [PATCH] Generalize the implementation of the Douglas Peucker algorithm to allow using a custom predicate to decide if points between the anchor and the floater should be removed. --- src/libslic3r/GCode/SeamPerimeters.cpp | 5 +---- src/libslic3r/MultiPoint.hpp | 28 +++++++++++++++++++------- tests/libslic3r/test_polyline.cpp | 4 ++-- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/GCode/SeamPerimeters.cpp b/src/libslic3r/GCode/SeamPerimeters.cpp index 214073878c..7971cdf6d8 100644 --- a/src/libslic3r/GCode/SeamPerimeters.cpp +++ b/src/libslic3r/GCode/SeamPerimeters.cpp @@ -58,10 +58,7 @@ std::pair, std::vector> remove_redundant_points( const std::int64_t index{std::distance(points.begin(), iterator)}; if (next(iterator) == points.end() || point_types[index] != point_types[index + 1]) { std::vector simplification_result; - douglas_peucker( - range_start, next(iterator), std::back_inserter(simplification_result), tolerance, - [](const Vec2d &point) { return point; } - ); + douglas_peucker(range_start, next(iterator), std::back_inserter(simplification_result), tolerance); points_result.insert( points_result.end(), simplification_result.begin(), simplification_result.end() diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index 64bda5b554..39353e989d 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -33,8 +33,8 @@ class BoundingBox3; // Reduces polyline in the -inline OutputIterator douglas_peucker(InputIterator begin, InputIterator end, OutputIterator out, const double tolerance, PointGetter point_getter) +template +inline OutputIterator douglas_peucker(InputIterator begin, InputIterator end, OutputIterator out, TakeFloaterPredicate take_floater_predicate, PointGetter point_getter) { using InputIteratorCategory = typename std::iterator_traits::iterator_category; static_assert(std::is_base_of_v); @@ -50,7 +50,6 @@ inline OutputIterator douglas_peucker(InputIterator begin, InputIterator end, Ou // Two points input. *out ++ = std::move(*next); } else { - const auto tolerance_sq = SquareLengthType(sqr(tolerance)); InputIterator anchor = begin; InputIterator floater = std::prev(end); std::vector dpStack; @@ -108,8 +107,8 @@ inline OutputIterator douglas_peucker(InputIterator begin, InputIterator end, Ou assert(max_dist_sq.has_value()); - // remove point if less than tolerance - take_floater = max_dist_sq <= tolerance_sq; + // Remove points between the anchor and the floater when the predicate is satisfied. + take_floater = take_floater_predicate(anchor, floater, *max_dist_sq); } if (take_floater) { @@ -138,14 +137,29 @@ inline OutputIterator douglas_peucker(InputIterator begin, InputIterator end, Ou return out; } -// Reduces polyline in the +inline OutputIterator douglas_peucker(InputIterator begin, InputIterator end, OutputIterator out, const double tolerance, PointGetter point_getter) { + const auto tolerance_sq = static_cast(sqr(tolerance)); + + const auto take_floater_predicate = [&tolerance_sq](InputIterator, InputIterator, const SquareLengthType max_dist_sq) -> bool { + return max_dist_sq <= tolerance_sq; + }; + + return douglas_peucker(begin, end, out, take_floater_predicate, point_getter); +} + template inline OutputIterator douglas_peucker(Points::const_iterator begin, Points::const_iterator end, OutputIterator out, const double tolerance) { return douglas_peucker(begin, end, out, tolerance, [](const Point &p) { return p; }); } +template +inline OutputIterator douglas_peucker(Pointfs::const_iterator begin, Pointfs::const_iterator end, OutputIterator out, const double tolerance) +{ + return douglas_peucker(begin, end, out, tolerance, [](const Vec2d &p) { return p; }); +} + inline Points douglas_peucker(const Points &src, const double tolerance) { Points out; diff --git a/tests/libslic3r/test_polyline.cpp b/tests/libslic3r/test_polyline.cpp index 3a51b78dd8..d519741bf4 100644 --- a/tests/libslic3r/test_polyline.cpp +++ b/tests/libslic3r/test_polyline.cpp @@ -81,14 +81,14 @@ SCENARIO("Simplify polyne, template", "[Polyline]") Points polyline{ {0,0}, {1000,0}, {2000,0}, {2000,1000}, {2000,2000}, {1000,2000}, {0,2000}, {0,1000}, {0,0} }; WHEN("simplified with Douglas-Peucker with back inserter") { Points out; - douglas_peucker(polyline.begin(), polyline.end(), std::back_inserter(out), 10, [](const Point &p) { return p; }); + douglas_peucker(polyline.begin(), polyline.end(), std::back_inserter(out), 10., [](const Point &p) { return p; }); THEN("simplified correctly") { REQUIRE(out == Points{ {0,0}, {2000,0}, {2000,2000}, {0,2000}, {0,0} }); } } WHEN("simplified with Douglas-Peucker in place") { Points out{ polyline }; - out.erase(douglas_peucker(out.begin(), out.end(), out.begin(), 10, [](const Point &p) { return p; }), out.end()); + out.erase(douglas_peucker(out.begin(), out.end(), out.begin(), 10., [](const Point &p) { return p; }), out.end()); THEN("simplified correctly") { REQUIRE(out == Points{ {0,0}, {2000,0}, {2000,2000}, {0,2000}, {0,0} }); }