Skip to content

Commit

Permalink
Add point ordering to convex hull algorithm
Browse files Browse the repository at this point in the history
This is the Andrew’s Monotone Chain Convex Hull Algorithm.
  • Loading branch information
casperlamboo authored and wawanbreton committed Jan 18, 2024
1 parent 2f87df7 commit 0fef7f0
Showing 1 changed file with 29 additions and 26 deletions.
55 changes: 29 additions & 26 deletions src/utils/polygon.cpp
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -16,6 +16,8 @@
#include <range/v3/view/join.hpp>
#include <range/v3/view/transform.hpp>
#include <range/v3/view/zip.hpp>
#include <range/v3/view/sliding.hpp>


#include "utils/ListPolyIt.h"
#include "utils/PolylineStitcher.h"
Expand Down Expand Up @@ -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);
}
}

Expand Down

0 comments on commit 0fef7f0

Please sign in to comment.