From 0fef7f0128f38705962d660b0b2cbf14a0bf096e Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 11 Jan 2024 10:56:52 +0100 Subject: [PATCH] Add point ordering to convex hull algorithm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the Andrew’s Monotone Chain Convex Hull Algorithm. --- src/utils/polygon.cpp | 55 +++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/src/utils/polygon.cpp b/src/utils/polygon.cpp index 853104aada..64685470b7 100644 --- a/src/utils/polygon.cpp +++ b/src/utils/polygon.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2022 Ultimaker B.V. +// Copyright (c) 2023 UltiMaker // CuraEngine is released under the terms of the AGPLv3 or higher. #include "utils/polygon.h" @@ -16,6 +16,8 @@ #include #include #include +#include + #include "utils/ListPolyIt.h" #include "utils/PolylineStitcher.h" @@ -104,43 +106,44 @@ Polygons Polygons::approxConvexHull(int extra_outset) void Polygons::makeConvex() { + // Andrew’s Monotone Chain Convex Hull Algorithm for (PolygonRef poly : *this) { if (poly.size() <= 3) { - continue; // Already convex. + // Already convex. + continue; } Polygon convexified; - - // Start from a vertex that is known to be on the convex hull: The one with the lowest X. - const size_t start_index = std::min_element( - poly.begin(), - poly.end(), - [](Point2LL a, Point2LL b) - { - return a.X == b.X ? a.Y < b.Y : a.X < b.X; - }) - - poly.begin(); - convexified.path->push_back(poly[start_index]); - - for (size_t i = 1; i <= poly.size(); ++i) + auto makeSortedPolyConvex = [&convexified](PolygonRef& poly) { - const Point2LL& current = poly[(start_index + i) % poly.size()]; + convexified.path->push_back(poly[0]); - // Track backwards to make sure we haven't been in a concave pocket for multiple vertices already. - while (convexified.size() >= 2 - && (LinearAlg2D::pointIsLeftOfLine(convexified.path->back(), (*convexified.path)[convexified.size() - 2], current) >= 0 - || LinearAlg2D::pointIsLeftOfLine(convexified.path->back(), (*convexified.path)[convexified.size() - 2], convexified.path->front()) > 0)) + for (const auto window : poly | ranges::views::sliding(2)) { - convexified.path->pop_back(); + const Point2LL& current = window[0]; + const Point2LL& after = window[1]; + + if (LinearAlg2D::pointIsLeftOfLine(current, convexified.path->back(), after) < 0) + { + //Track backwards to make sure we haven't been in a concave pocket for multiple vertices already. + while(convexified.size() >= 2 && LinearAlg2D::pointIsLeftOfLine(convexified.path->back(), (*convexified.path)[convexified.size() - 2], current) > 0) + { + convexified.path->pop_back(); + } + convexified.path->push_back(current); + } } - convexified.path->push_back(current); - } - // remove last vertex as the starting vertex is added in the last iteration of the loop - convexified.path->pop_back(); + }; + + std::sort(poly.begin(), poly.end(), [](Point2LL a, Point2LL b) { return a.X == b.X ? a.Y < b.Y : a.X < b.X; }); + makeSortedPolyConvex(poly); + std::reverse(poly.begin(), poly.end()); + makeSortedPolyConvex(poly); - poly.path->swap(*convexified.path); // Due to vector's implementation, this is constant time. + // Due to vector's implementation, this is constant time + poly.path->swap(*convexified.path); } }