2018-10-18 16:41:18 +00:00
|
|
|
from profile import Profiler
|
2018-10-09 17:26:55 +00:00
|
|
|
|
2018-10-18 16:41:18 +00:00
|
|
|
from util import gen_point, Side, display
|
2018-10-09 17:26:55 +00:00
|
|
|
|
|
|
|
|
2018-10-18 16:41:18 +00:00
|
|
|
@Profiler("calculating sidedness")
|
|
|
|
def sidedness(p1, p2, p3, eps=0.0000001):
|
2018-10-09 17:26:55 +00:00
|
|
|
# Find line from p1 to p2, ask where p3 is in relation to this
|
|
|
|
|
|
|
|
y = p3.y * (p2.x - p1.x)
|
|
|
|
x = p3.x
|
|
|
|
|
|
|
|
a = (p2.y - p1.y)
|
|
|
|
b = p2.y * (p2.x - p1.x) - a * p2.x
|
|
|
|
|
|
|
|
if y - eps < a * x + b < y + eps:
|
|
|
|
return Side.ON
|
|
|
|
elif y > a * x + b:
|
|
|
|
return Side.ABOVE
|
|
|
|
return Side.BELOW
|
|
|
|
|
|
|
|
|
2018-10-18 16:41:18 +00:00
|
|
|
@Profiler("graham_scan")
|
2018-10-09 17:26:55 +00:00
|
|
|
def graham_scan(points):
|
|
|
|
# A funky issue where both a and b become negative in the sidedness test causes us to have to use
|
|
|
|
# Side.ABOVE for both tests, regardless of UH or LH.
|
2018-10-18 16:41:18 +00:00
|
|
|
with Profiler("sorting points"):
|
|
|
|
sorted_points = sorted(points)
|
2018-10-09 17:26:55 +00:00
|
|
|
|
|
|
|
UH = sorted_points[:2]
|
2018-10-21 16:08:38 +00:00
|
|
|
with Profiler("iterating points", excluded=("calculating sidedness",)):
|
2018-10-18 16:41:18 +00:00
|
|
|
for s in sorted_points[2:]:
|
|
|
|
while len(UH) > 1 and (sidedness(UH[-2], UH[-1], s) != Side.ABOVE):
|
|
|
|
del UH[-1]
|
|
|
|
UH.append(s)
|
|
|
|
|
2018-10-21 16:08:38 +00:00
|
|
|
reversed_list = list(reversed(sorted_points))
|
|
|
|
reversed_list.append(UH[0])
|
2018-10-09 17:26:55 +00:00
|
|
|
LH = reversed_list[:2]
|
|
|
|
|
2018-10-21 16:08:38 +00:00
|
|
|
with Profiler("iterating points", excluded=("calculating sidedness",)):
|
2018-10-18 16:41:18 +00:00
|
|
|
for s in reversed_list[2:]:
|
|
|
|
while len(LH) > 1 and (sidedness(LH[-2], LH[-1], s) != Side.ABOVE):
|
|
|
|
del LH[-1]
|
|
|
|
LH.append(s)
|
2018-10-09 17:26:55 +00:00
|
|
|
|
2018-10-11 12:38:59 +00:00
|
|
|
return UH + LH
|
2018-10-09 17:26:55 +00:00
|
|
|
|
|
|
|
|
2018-10-11 12:38:59 +00:00
|
|
|
if __name__ == '__main__':
|
|
|
|
p = [gen_point() for _ in range(30)]
|
|
|
|
hull = graham_scan(p)
|
2018-10-09 17:26:55 +00:00
|
|
|
|
2018-10-11 12:38:59 +00:00
|
|
|
display(p, hull)
|