2018-09-17 15:06:20 +00:00
|
|
|
# Use atan2 instead of acos to calc angle; atan2(x,y) of the point we potentially want to add
|
2018-09-20 12:50:16 +00:00
|
|
|
from math import acos, sqrt
|
2018-09-17 15:06:20 +00:00
|
|
|
|
2018-10-18 16:41:18 +00:00
|
|
|
from profile import Profiler
|
2018-10-11 13:54:52 +00:00
|
|
|
from util import Vector, Point, gen_point
|
2018-09-17 15:06:20 +00:00
|
|
|
|
|
|
|
|
2018-10-18 16:41:18 +00:00
|
|
|
@Profiler("calculating angle")
|
2018-09-20 12:50:16 +00:00
|
|
|
def calc_angle(v1: Vector, v2: Vector) -> float:
|
|
|
|
dot = (v1.x * v2.x) + (v1.y * v2.y)
|
|
|
|
len_1 = sqrt(v1.x**2 + v1.y**2)
|
|
|
|
len_2 = sqrt(v2.x**2 + v2.y**2)
|
|
|
|
|
|
|
|
tmp = dot / (len_1 * len_2)
|
2018-10-11 13:54:52 +00:00
|
|
|
return acos(max(min(tmp, 1), -1)) # acos is only defined in [-1,1]
|
2018-09-20 12:50:16 +00:00
|
|
|
|
|
|
|
|
2018-10-18 16:41:18 +00:00
|
|
|
@Profiler("calculating vector")
|
2018-09-17 15:06:20 +00:00
|
|
|
def calc_vector(p1: Point, p2: Point) -> Vector:
|
2018-10-18 16:41:18 +00:00
|
|
|
return Vector((p2.x - p1.x), (p2. y - p1.y))
|
2018-09-17 15:06:20 +00:00
|
|
|
|
2018-09-18 09:54:11 +00:00
|
|
|
|
2018-10-18 16:41:18 +00:00
|
|
|
@Profiler("gift_wrapper")
|
2018-09-17 15:06:20 +00:00
|
|
|
def rapper(points: set):
|
|
|
|
min_pt = min(points)
|
|
|
|
hull = [min_pt]
|
2018-09-20 12:50:16 +00:00
|
|
|
comp_vec = Vector(0, 1)
|
2018-10-18 16:41:18 +00:00
|
|
|
with Profiler("iterating points", excluded=("calculating angle", "calculating vector")):
|
|
|
|
while True:
|
|
|
|
hull.append(min(points - {hull[-1]},
|
|
|
|
key=lambda p: calc_angle(comp_vec, calc_vector(hull[-1], p))))
|
|
|
|
|
|
|
|
comp_vec = calc_vector(hull[-2], hull[-1])
|
|
|
|
|
|
|
|
if hull[-1] == min_pt:
|
|
|
|
return hull
|
2018-09-17 15:06:20 +00:00
|
|
|
|
|
|
|
|
2018-10-11 12:38:59 +00:00
|
|
|
if __name__ == '__main__':
|
|
|
|
points = {gen_point() for _ in range(30)}
|
|
|
|
hull = rapper(points)
|