diff --git a/src/utils/polygon.cpp b/src/utils/polygon.cpp index 853104aada..e41ee79bc3 100644 --- a/src/utils/polygon.cpp +++ b/src/utils/polygon.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -104,43 +105,52 @@ Polygons Polygons::approxConvexHull(int extra_outset) void Polygons::makeConvex() { + if (this->paths.empty()) + { + // early out if there are no polygons + return; + } + + // convex hulls don't have holes so resize paths to only contain the outer path + this->paths.resize(1); + 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); } }