forked from mozman/ezdxf
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconvexhull.py
98 lines (76 loc) · 2.59 KB
/
convexhull.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
# Copyright (c) 2022, Manfred Moitzi
# License: MIT License
import random
import time
import pathlib
import ezdxf
from ezdxf.math import Vec2, convex_hull_2d, is_point_left_of_line
CWD = pathlib.Path("~/Desktop/Outbox").expanduser()
if not CWD.exists():
CWD = pathlib.Path(".")
SIZE = 100
ROUNDS = 2000
def old_convex_hull_2d(points):
"""Returns 2D convex hull for `points`.
Args:
points: iterable of points as :class:`Vec3` compatible objects,
z-axis is ignored
"""
def _convexhull(hull):
while len(hull) > 2:
# the last three points
start_point, check_point, destination_point = hull[-3:]
# curve not turns right
if not is_point_left_of_line(
check_point, start_point, destination_point
):
# remove the penultimate point
del hull[-2]
else:
break
return hull
points = sorted(set(Vec2.generate(points))) # remove duplicate points
if len(points) < 3:
raise ValueError(
"Convex hull calculation requires 3 or more unique points."
)
upper_hull = points[:2] # first two points
for next_point in points[2:]:
upper_hull.append(next_point)
upper_hull = _convexhull(upper_hull)
lower_hull = [points[-1], points[-2]] # last two points
for next_point in reversed(points[:-2]):
lower_hull.append(next_point)
lower_hull = _convexhull(lower_hull)
upper_hull.extend(lower_hull[1:-1])
return upper_hull
def random_points(n: int):
return [
Vec2(random.random() * SIZE, random.random() * SIZE) for _ in range(n)
]
def profile(func, points) -> float:
t0 = time.perf_counter()
for _ in range(ROUNDS):
func(points)
t1 = time.perf_counter()
return t1 - t0
def export_dxf(points):
doc = ezdxf.new()
msp = doc.modelspace()
for p in points:
msp.add_point(p, dxfattribs={"color": 1, "layer": "points"})
hull = old_convex_hull_2d(points)
msp.add_lwpolyline(hull, dxfattribs={"color": 2, "layer": "old_hull"})
hull = convex_hull_2d(points)
msp.add_lwpolyline(hull, dxfattribs={"color": 6, "layer": "new_hull"})
doc.saveas(CWD / "convexhull.dxf")
def main():
points = random_points(200)
old = profile(old_convex_hull_2d, points)
print(f"old convex hull function: {old:.3f}s")
new = profile(convex_hull_2d, points)
print(f"new convex hull function: {new:.3f}s")
print(f"ratio old/new: {old/new:.3f}")
export_dxf(points)
if __name__ == "__main__":
main()