-
Notifications
You must be signed in to change notification settings - Fork 76
/
Copy pathperimeter.py
91 lines (76 loc) · 2.88 KB
/
perimeter.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
import numpy as np
from . import polygon_repair
def repaired_lines_to_voxels(line_list, pixels, plane_shape):
if not line_list:
return
segments = [[tuple(pt.tolist())[:2] for pt in seg] for seg in line_list]
wq = polygon_repair.PolygonRepair(segments, plane_shape)
wq.repair_all()
new_line_list = []
for polyline in wq.loops:
for i in range(len(polyline) - 1):
new_line_list.append((polyline[i], polyline[i+1]))
lines_to_voxels(new_line_list, pixels)
def lines_to_voxels(line_list, pixels):
current_line_indices = set()
x = 0
i = 0
events = generate_line_events(line_list)
while i < len(events):
event_x, status, line_ind = events[i]
if event_x > x:
# If the events are ahead of our current x, paint lines
lines = [line_list[ind] for ind in current_line_indices]
paint_y_axis(lines, pixels, x)
x += 1
elif event_x <= x and status == 'begin':
# If the events are behind our current x, process them
assert line_ind not in current_line_indices
current_line_indices.add(line_ind)
i += 1
elif event_x <= x and status == 'end':
# Process end statuses so that vertical lines are not given to paint_y_axis
assert line_ind in current_line_indices
current_line_indices.remove(line_ind)
i += 1
def generate_y(p1, p2, x):
x1, y1 = p1[:2]
x2, y2 = p2[:2]
assert x1 != x2
dy = (y2 - y1)
dx = (x2 - x1)
y = dy * (x - x1) / dx + y1
inside_change = 0
if x1 > x2:
inside_change = -1
elif x1 < x2:
inside_change = 1
return y, inside_change
def paint_y_axis(lines, pixels, x):
# Counting the number of times we enter the inside of a part helps properly handle parts with multiple shells
# If we enter twice, we will continue to be "inside" until we exit twice.
inside = 0
target_ys = [generate_y(line[0], line[1], x) for line in lines]
target_ys.sort()
assert len(target_ys) % 2 == 0
yi = 0
for target_y, inside_change in target_ys:
target_y = int(np.ceil(target_y))
assert target_y >= 0
if inside > 0:
# Bulk assign all pixels between yi -> target_y
pixels[yi:target_y, int(x)] = True
inside += inside_change
yi = target_y
assert inside == 0, 'an error has occured at x%s inside:%s lines:%s' % (x, inside, lines)
def generate_line_events(line_list):
events = []
for i, line in enumerate(line_list):
first, second = sorted(line, key=lambda pt: pt[0])
if first[0] == second[0]:
# Ignore vertical lines
continue
events.append((first[0], 'begin', i))
events.append((second[0], 'end', i))
# Sorting by x value, then put all begin events before end events
return sorted(events)