-
Notifications
You must be signed in to change notification settings - Fork 0
/
graham_scan.py
executable file
·101 lines (76 loc) · 3.11 KB
/
graham_scan.py
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#!/usr/bin/env python3
__author__ = 'Samuel Painter'
import functools
@functools.total_ordering
class Point:
"""A point class for use in the Graham Scan."""
def __init__(self, key, x, y):
self.key = key
self.x = x
self.y = y
def get_key(self):
"""Returns the key"""
return self.key
def get_x(self):
"""Returns the X Coordinate"""
return self.x
def get_y(self):
"""Returns the Y Coordinate"""
return self.y
def __str__(self):
"""Returns a string containing instance information."""
f = lambda n: int(n) if n.is_integer() else n
return str(self.key) + ',\t' + str(f(self.x)) + ',\t' + str(f(self.y))
__lt__ = lambda self, other: self.y < other.y if (self.x == other.x) else self.x < other.x
__eq__ = lambda self, other: self.x == other.x and self.y == other.y
def read_input_file(file):
"""Returns an array of points from the input file.
Assumes that points will be floats.
"""
points_array = []
with open(file, 'r') as f:
for line in f:
key, x, y = ''.join(line.split()).split(',')
points_array.append(Point(key, float(x), float(y)))
return points_array
def print_out_file(point_array, outfile):
"""Prints the output file containing the keys of the points in the convex hull."""
with open(outfile, 'wb') as f:
out = ",".join((x.get_key() for x in point_array))
f.write(bytes(out, "utf-8"))
def sort_points(point_array):
"""Return point_array sorted by leftmost first, then by slope, ascending."""
def slope(y):
"""returns the slope of the 2 points."""
x = point_array[0]
return (x.get_y() - y.get_y()) / \
(x.get_x() - y.get_x())
point_array.sort() # put leftmost first
point_array = point_array[:1] + sorted(point_array[1:], key=slope)
return point_array
def graham_scan(point_array):
"""Takes an array of points to be scanned.
Returns an array of points that make up the convex hull surrounding the points passed in in point_array.
"""
def cross_product_orientation(a, b, c):
"""Returns the orientation of the set of points.
>0 if x,y,z are clockwise, <0 if counterclockwise, 0 if co-linear.
"""
return (b.get_y() - a.get_y()) * \
(c.get_x() - a.get_x()) - \
(b.get_x() - a.get_x()) * \
(c.get_y() - a.get_y())
# convex_hull is a stack of points beginning with the leftmost point.
convex_hull = []
sorted_points = sort_points(point_array)
for p in sorted_points:
# if we turn clockwise to reach this point, pop the last point from the stack, else, append this point to it.
while len(convex_hull) > 1 and cross_product_orientation(convex_hull[-2], convex_hull[-1], p) >= 0:
convex_hull.pop()
convex_hull.append(p)
# the stack is now a representation of the convex hull, return it.
return convex_hull
if __name__ == '__main__':
points = read_input_file('input.txt')
hull = graham_scan(points)
print_out_file(hull, 'output.txt')