Skip to content

Commit

Permalink
Add find_convex_hull using Graham scan
Browse files Browse the repository at this point in the history
  • Loading branch information
arrufat committed Nov 10, 2023
1 parent f7d99ae commit 7a6a206
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 0 deletions.
63 changes: 63 additions & 0 deletions dlib/geometry/vector.h
Original file line number Diff line number Diff line change
Expand Up @@ -1308,6 +1308,69 @@ namespace dlib
return (s0>0&&s1>0&&s2>0&&s3>0) || (s0<0&&s1<0&&s2<0&&s3<0);
}

// ----------------------------------------------------------------------------------------

namespace impl
{
enum class points_orientation
{
collinear,
clockwise,
couter_clockwise,
};

template <typename T>
std::enable_if_t<std::is_same<T, point>::value || std::is_same<T, dpoint>::value, points_orientation>
find_points_orientation(const T& a, const T& b, const T& c)
{
const auto v = a.x() * (b.y() - c.y()) + b.x() * (c.y() - a.y()) + c.x() * (a.y() - b.y());
if (v < 0) return points_orientation::clockwise;
if (v > 0) return points_orientation::couter_clockwise;
return points_orientation::collinear;
}
}

template <typename T>
std::enable_if_t<std::is_same<T, point>::value || std::is_same<T, dpoint>::value, std::vector<T>>
find_convex_hull(std::vector<T>& points)
{
if (points.size() < 3)
return points;

// find the point with the lowest y coordinate, and the left-most in case of ties.
const auto p0 = *std::min_element(points.begin(), points.end(), [](const auto& a, const auto& b){
return std::make_pair(a.y(), a.x()) < std::make_pair(b.y(), b.x());
});

// sort the points by polar angle in clockwise order
std::sort(points.begin(), points.end(), [&p0](const auto& a, const auto& b){
switch (impl::find_points_orientation(p0, a, b))
{
case impl::points_orientation::clockwise:
return true;
case impl::points_orientation::couter_clockwise:
return false;
case impl::points_orientation::collinear:
return (p0.x() - a.x()) * (p0.x() - a.x()) + (p0.y() - a.y()) * (p0.y() - a.y()) <
(p0.x() - b.x()) * (p0.x() - b.x()) + (p0.y() - b.y()) * (p0.y() - b.y());
default:
throw std::logic_error("unreachable: invalid points_orientation");
}
});

std::vector<T> hull;
for (const auto& p : points)
{
while (hull.size() > 1 &&
impl::find_points_orientation(hull.at(hull.size() - 2), hull.back(), p) != impl::points_orientation::clockwise)
{
hull.pop_back();
}
hull.push_back(p);
}
return hull;
}

// ----------------------------------------------------------------------------------------

template <
Expand Down
15 changes: 15 additions & 0 deletions dlib/geometry/vector_abstract.h
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,21 @@ namespace dlib
false if not.
!*/

// ----------------------------------------------------------------------------------------

template <typename T>
std::enable_if_t<std::is_same<T, point>::value || std::is_same<T, dpoint>::value, std::vector<T>>
find_convex_hull(
std::vector<T>& points
);
/*!
requires
- points is a vector of point or dpoint with at least three elements in it.
ensures
- Finds the convex hull of points using the Graham scan algorithm. That is,
the smallest convex shape that contains all points.
!*/

// ----------------------------------------------------------------------------------------

template <
Expand Down
23 changes: 23 additions & 0 deletions dlib/test/geometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,28 @@ namespace

}

// ----------------------------------------------------------------------------------------

void test_find_convex_hull()
{
print_spinner();
std::vector<dpoint> points{
{ 0.0, 0.0 },
{ 1.0, 1.0 },
{ 2.0, 2.0 },
{ 3.0, 1.0 },
{ 4.0, 0.0 },
{ 2.0, 4.0 },
{ 1.0, 3.0 },
};
const auto hull = find_convex_hull(points);
DLIB_TEST(hull.size() == 4);
DLIB_TEST(hull[0] == dpoint(0, 0));
DLIB_TEST(hull[1] == dpoint(1, 3));
DLIB_TEST(hull[2] == dpoint(2, 4));
DLIB_TEST(hull[3] == dpoint(4, 0));
}

// ----------------------------------------------------------------------------------------

void test_border_enumerator()
Expand Down Expand Up @@ -956,6 +978,7 @@ namespace
test_find_similarity_transform2<float>();
test_line();
test_polygon();
test_find_convex_hull();
}
} a;

Expand Down

0 comments on commit 7a6a206

Please sign in to comment.