-
Notifications
You must be signed in to change notification settings - Fork 3
/
convex_hull.h
68 lines (58 loc) · 1.54 KB
/
convex_hull.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#pragma once
#include "types.h"
#include <vector>
#include <algorithm>
namespace geometry
{
[[nodiscard]] static inline std::vector<ivec2>
convex_hull_construct(std::vector<ivec2> points, bool strictly_convex = true)
{
// lowest y-coordinate and leftmost point
ivec2 pivot = *std::min_element(points.begin(), points.end());
std::sort(
points.begin(),
points.end(),
[&pivot](const ivec2& a, const ivec2& b)
{
auto area = (a - pivot) * (b - pivot);
if (area == 0)
{
auto pivot_a = a - pivot;
auto pivot_b = b - pivot;
return pivot_a.dot(pivot_a) < pivot_b.dot(pivot_b);
}
else
{
return area > 0;
}
}
);
if (!strictly_convex)
{
int reverse_index = (int)points.size() - 2;
while (reverse_index > 0 && (points.back() - pivot) * (points[reverse_index] - pivot) == 0)
reverse_index--;
std::reverse(points.begin() + reverse_index + 1, points.end());
}
std::vector<ivec2> polygon;
polygon.reserve(points.size());
for (size_t i = 0; i < points.size(); ++i)
{
while (polygon.size() >= 2)
{
auto a = polygon.end()[-2];
auto b = polygon.end()[-1];
auto area = (b - a) * (points[i] - a);
if (area > 0 || (area == 0 && !strictly_convex))
break;
// remove last point if it results in a clockwise orientation or if the points are collinear
polygon.pop_back();
}
polygon.push_back(points[i]);
}
if (strictly_convex && polygon.size() == 2 && polygon[0] == polygon[1])
polygon.pop_back();
polygon.shrink_to_fit();
return polygon;
}
}