Skip to content

Commit

Permalink
Fix convex hull in the case where the convex hull consist of islands
Browse files Browse the repository at this point in the history
  • Loading branch information
casperlamboo committed Jan 16, 2024
1 parent 0265e74 commit e64d381
Showing 1 changed file with 34 additions and 39 deletions.
73 changes: 34 additions & 39 deletions src/utils/polygon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <range/v3/view/sliding.hpp>
#include <range/v3/view/transform.hpp>
#include <range/v3/view/zip.hpp>
#include <spdlog/spdlog.h>

#include "utils/ListPolyIt.h"
#include "utils/PolylineStitcher.h"
Expand Down Expand Up @@ -111,56 +112,50 @@ void Polygons::makeConvex()
return;
}

// convex hulls cannot have holes, so remove all paths except the outerpath
this->paths.resize(1);

// Andrew’s Monotone Chain Convex Hull Algorithm
for (PolygonRef poly : *this)
std::vector<Point2LL> points;

for (const auto& poly : this->paths)
{
if (poly.size() <= 3)
{
// Already convex.
continue;
}
points.insert(points.end(), poly.begin(), poly.end());
}

ClipperLib::Path convexified;
auto make_sorted_poly_convex = [&convexified](std::vector<Point2LL>& poly)
{
convexified.push_back(poly[0]);

Polygon convexified;
auto makeSortedPolyConvex = [&convexified](PolygonRef& poly)
for (const auto window : poly | ranges::views::sliding(2))
{
convexified.path->push_back(poly[0]);
const Point2LL& current = window[0];
const Point2LL& after = window[1];

for (const auto window : poly | ranges::views::sliding(2))
if (LinearAlg2D::pointIsLeftOfLine(current, convexified.back(), after) < 0)
{
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.back(), convexified[convexified.size() - 2], current) >= 0
|| LinearAlg2D::pointIsLeftOfLine(convexified.back(), convexified[convexified.size() - 2], convexified.front()) > 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))
{
convexified.path->pop_back();
}
convexified.path->push_back(current);
convexified.pop_back();
}
convexified.push_back(current);
}
};
}
};

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);
std::sort(
points.begin(),
points.end(),
[](Point2LL a, Point2LL b)
{
return a.X == b.X ? a.Y < b.Y : a.X < b.X;
});
make_sorted_poly_convex(points);
std::reverse(points.begin(), points.end());
make_sorted_poly_convex(points);

// Due to vector's implementation, this is constant time
poly.path->swap(*convexified.path);
}
this->paths = { convexified };
}

size_t Polygons::pointCount() const
Expand Down

0 comments on commit e64d381

Please sign in to comment.