This commit is contained in:
Casper 2018-09-20 15:44:39 +02:00
parent 69037ec771
commit 3cead7083e

View File

@ -1,5 +1,7 @@
import random import random
from collections import namedtuple from collections import namedtuple
from typing import Set
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from enum import Enum, auto from enum import Enum, auto
from math import atan2, degrees, tau, pi, acos, sqrt from math import atan2, degrees, tau, pi, acos, sqrt
@ -37,70 +39,63 @@ def gen_point():
return p_i return p_i
def sidedness(p1, p2, p3, eps=0.0000001): def distance(a, b, c):
y = p1.y * (p3.x - p2.x) try:
x = p1.x nom = abs((b.y - a.y) * c.x - (b.x - a.x) * c.y + b.x * a.y - b.y * a.x)
a = (p3.y - p2.y) den = sqrt((b.y - a.y) ** 2 + (b.x - a.x) ** 2)
b = p3.y * (p3.x - p2.x) - a * p3.x return nom / den
except ZeroDivisionError:
line = a*x+b return 0
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): def is_left(a: Point, b: Point, c: Point):
min_pt, max_pt = line_segment return ((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)) > 0
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): def quick_hull(points: Set[Point]):
min_pt = min(points, key=lambda pt: pt.x) assert len(points) >= 2
max_pt = max(points, key=lambda pt: pt.x)
return min_pt, max_pt
left = min(points)
right = max(points)
def quick_hull(points, side=Side.BELOW, lel=max, hull=None): hull = {left, right}
if hull is None:
hull = set()
print(len(points)) find_hull({p for p in points if not is_left(left, right, p)},
if len(points) <= 2: left,
return hull right,
hull)
min_pt, max_pt = find_line_segment(points) find_hull({p for p in points if is_left(left, right, p)},
right,
above_points, max_dist_pt = prune_below_find_highest(points, (min_pt, max_pt), side, lel) left,
hull)
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 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) def find_hull(points: Set[Point], p: Point, q: Point, hull: Set[Point]):
uh_hull = quick_hull(points) if not points:
lh_hull = quick_hull(points, Side.ABOVE, min) return
hull = uh_hull.union(lh_hull) farthest = max(points, key=lambda point: distance(p, q, point))
hull.add(farthest)
s1 = {point
for point in points
if not is_left(p, farthest, point)}
print("--")
print(s1)
s2 = {point
for point in points
if not is_left(farthest, q, point)}
print(s2)
find_hull(s1, p, farthest, hull)
find_hull(s2, farthest, q, hull)
points = {gen_point() for _ in range(10)}
hull = quick_hull(points)
display(points, hull) display(points, hull)