This commit is contained in:
Alexander Munch-Hansen 2018-09-20 14:50:16 +02:00
parent 4f7c4a5699
commit 69037ec771
2 changed files with 125 additions and 9 deletions

View File

@ -2,7 +2,7 @@
import random import random
from collections import namedtuple from collections import namedtuple
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from math import atan2, degrees, tau, pi, acos, sqrt from math import acos, sqrt
Point = namedtuple('Point', 'x y') Point = namedtuple('Point', 'x y')
Vector = namedtuple('Vector', 'x y') Vector = namedtuple('Vector', 'x y')
@ -18,8 +18,19 @@ def gen_point():
return p_i return p_i
def calc_angle(v: Vector, upside_down=False) -> float: def calc_angle(v1: Vector, v2: Vector) -> float:
return (90 - degrees(atan2(v.y, v.x)) - 180*upside_down) % 360 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)
# pls
hmmmmmmm = round(tmp, 6)
return acos(hmmmmmmm)
def calc_vector(p1: Point, p2: Point) -> Vector: def calc_vector(p1: Point, p2: Point) -> Vector:
@ -41,16 +52,15 @@ def display(points, hull):
def rapper(points: set): def rapper(points: set):
min_pt = min(points) min_pt = min(points)
max_pt = max(points)
hull = [min_pt] hull = [min_pt]
upside_down = False comp_vec = Vector(0, 1)
while True: while True:
hull.append(min(points - {hull[-1]}, hull.append(min(points - {hull[-1]},
key=lambda p: calc_angle(calc_vector(hull[-1], p), upside_down))) key=lambda p: calc_angle(comp_vec, calc_vector(hull[-1], p))))
if hull[-1].x == max_pt.x: comp_vec = calc_vector(hull[-2], hull[-1])
upside_down = True
display(points, hull) display(points, hull)
if hull[-1] == min_pt: if hull[-1] == min_pt:
break break

106
h2/quick_hull.py Normal file
View File

@ -0,0 +1,106 @@
import random
from collections import namedtuple
import matplotlib.pyplot as plt
from enum import Enum, auto
from math import atan2, degrees, tau, pi, acos, sqrt
Point = namedtuple('Point', 'x y')
class Side(Enum):
ON = auto()
ABOVE = auto()
BELOW = auto()
def display(points, hull):
x = [point.x for point in points]
y = [point.y for point in points]
h_x = [point.x for point in hull]
h_y = [point.y for point in hull]
plt.plot(h_x, h_y, 'ro-')
plt.scatter(x, y)
plt.show()
def gen_point():
a = random.uniform(1, 5)
b = random.uniform(1, 5)
x_i = random.uniform(1, 5)
p_i = Point(x_i, a * x_i + b)
return p_i
def sidedness(p1, p2, p3, eps=0.0000001):
y = p1.y * (p3.x - p2.x)
x = p1.x
a = (p3.y - p2.y)
b = p3.y * (p3.x - p2.x) - a * p3.x
line = a*x+b
if y - eps < line < y + eps:
return [Side.ON, y - line]
elif y > line:
return [Side.ABOVE, y - line]
return [Side.BELOW, y - line]
def prune_below_find_highest(points: set, line_segment, side = Side.BELOW, lel=max):
min_pt, max_pt = line_segment
above_pts = []
for point in points:
sidedness_res = sidedness(min_pt, max_pt, point)
if sidedness_res[0] != side:
above_pts.append(point)
highest_above = lel(above_pts, key=lambda pt: pt[1])
return above_pts, highest_above
def find_line_segment(points):
min_pt = min(points, key=lambda pt: pt.x)
max_pt = max(points, key=lambda pt: pt.x)
return min_pt, max_pt
def quick_hull(points, side=Side.BELOW, lel=max, hull=None):
if hull is None:
hull = set()
print(len(points))
if len(points) <= 2:
return hull
min_pt, max_pt = find_line_segment(points)
above_points, max_dist_pt = prune_below_find_highest(points, (min_pt, max_pt), side, lel)
left_side = prune_below_find_highest(points, (min_pt, max_dist_pt), side, lel)[0]
right_side = prune_below_find_highest(points, (max_dist_pt, max_pt), side, lel)[0]
hull.add(max_dist_pt)
quick_hull(left_side, side, lel, hull=hull).union(quick_hull(right_side, side, lel, hull=hull))
return hull
points = {Point(2, 5), Point(3, 1), Point(3, 4), Point(9, 4), Point(1, 5), Point(5, 7)}
#points = {gen_point() for _ in range(10)}
line_segment = find_line_segment(points)
uh_hull = quick_hull(points)
lh_hull = quick_hull(points, Side.ABOVE, min)
hull = uh_hull.union(lh_hull)
display(points, hull)