-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgraham_scan.py
More file actions
59 lines (42 loc) · 1.2 KB
/
graham_scan.py
File metadata and controls
59 lines (42 loc) · 1.2 KB
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
r"""The Graham scan algorithm for computing the convex hull.
Computes the convex hull of a set of $n$ points in the plane. It runs in time
$O(n \log n)$, which is optimal.
"""
from dataclasses import dataclass
@dataclass(frozen=True, eq=True)
class Point:
x: float
y: float
idx: float
@property
def tuple(self):
return self.x, self.y
def __lt__(self, other):
return (self.x, self.y) < (
other.x,
other.y,
)
def __sub__(self, other):
return Point(self.x - other.x, self.y - other.y, -1)
@classmethod
def from_str(c, s):
return Point(*map(float, s.split()))
def cross(o, a, b):
return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x)
def right(a, b, c):
return cross(a, b, c) <= 0
def graham(points):
points = sorted(set(points))
S, hull = [], [] # S is a stack of points
for p in points:
while len(S) >= 2 and right(S[-2], S[-1], p):
S.pop()
S.append(p)
hull += S
S = []
for p in reversed(points):
while len(S) >= 2 and right(S[-2], S[-1], p):
S.pop()
S.append(p)
hull += S[1:-1] # ignore endpoints
return hull