Improve CLI
This commit is contained in:
parent
1140c5bca3
commit
6666e665e2
3 changed files with 70 additions and 57 deletions
|
|
@ -52,7 +52,8 @@ int main()
|
|||
}
|
||||
|
||||
if (!poly.is_counterclockwise_oriented()) {
|
||||
poly.reverse_orientation();
|
||||
std::cerr << "Error: Polygon must be counter-clockwise" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
SsPtr ss = CGAL::create_interior_straight_skeleton_2(poly.vertices_begin(), poly.vertices_end(), K());
|
||||
|
|
|
|||
|
|
@ -94,6 +94,15 @@ def arc_approximate(points, trace_width, layer, tolerance=0.02, level=0):
|
|||
yield kicad.make_arc(x2, y2, x0, y0, x1, y1, trace_width, layer)
|
||||
|
||||
|
||||
def polygon_is_clockwise(coords):
|
||||
# https://en.wikipedia.org/wiki/Curve_orientation
|
||||
xb, yb, i = min([(x, y, i) for i, (x, y) in enumerate(coords)])
|
||||
xa, ya = coords[(i-1) % len(coords)]
|
||||
xc, yc = coords[(i+1) % len(coords)]
|
||||
det = (xa*yb + xb*yc + xc*ya) - (xa*yc + xb * ya + xc * yb)
|
||||
return det < 0
|
||||
|
||||
|
||||
class Shape:
|
||||
pass
|
||||
|
||||
|
|
@ -329,17 +338,22 @@ class SVGShape(OffsetShape):
|
|||
d = path.attrs['d']
|
||||
d = d.strip('MmZ ').replace(',', 'L')
|
||||
coord_pairs = d.split('L')
|
||||
coords = list(reversed([tuple(map(float, pair.split())) for pair in coord_pairs]))
|
||||
# Calculate bounding box
|
||||
min_x = min(x for x, _y in coords)
|
||||
min_y = max(x for x, _y in coords)
|
||||
max_x = min(y for _x, y in coords)
|
||||
max_y = max(y for _x, y in coords)
|
||||
if max_x < 0 or max_y < 0 or min_x > 0 or min_y > 0:
|
||||
# (0, 0) is not within the polygon's axis-aligned bounding box, recenter.
|
||||
ox, oy = skeletonator.polygon_center_of_mass(coords)
|
||||
warnings.warn(f'Polygon looks not centered, bounds are ({min_x:.2f}, {min_y:.2f}), ({max_x:.2f}, {max_y:.2f}). Aligning (0, 0) with polygon centroid at ({ox:.2f}, {oy:.2f})')
|
||||
coords = [(x-ox, y-oy) for x, y in coords]
|
||||
coords = [tuple(map(float, pair.split())) for pair in coord_pairs]
|
||||
|
||||
if polygon_is_clockwise(coords):
|
||||
coords = coords[::-1]
|
||||
|
||||
# Calculate bounding box
|
||||
min_x = min(x for x, _y in coords)
|
||||
min_y = max(x for x, _y in coords)
|
||||
max_x = min(y for _x, y in coords)
|
||||
max_y = max(y for _x, y in coords)
|
||||
if max_x < 0 or max_y < 0 or min_x > 0 or min_y > 0:
|
||||
# (0, 0) is not within the polygon's axis-aligned bounding box, recenter.
|
||||
ox, oy = skeletonator.polygon_center_of_mass(coords)
|
||||
warnings.warn(f'Polygon looks not centered, bounds are ({min_x:.2f}, {min_y:.2f}), ({max_x:.2f}, {max_y:.2f}). Aligning (0, 0) with polygon centroid at ({ox:.2f}, {oy:.2f})')
|
||||
coords = [(x-ox, y-oy) for x, y in coords]
|
||||
|
||||
self.polygon = coords
|
||||
super().__post_init__()
|
||||
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ class WasmApp:
|
|||
self.app.exports(store)["_start"](store)
|
||||
except wasmtime.ExitTrap as trap:
|
||||
if trap.code != 0:
|
||||
raise
|
||||
raise RuntimeError('Error computing straight skeleton.')
|
||||
return 0, stdout_f.read()
|
||||
|
||||
|
||||
|
|
@ -308,53 +308,51 @@ class Skeletonator:
|
|||
|
||||
def dump_to_pdf(self, filename):
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.backends.backend_pdf import PdfPages
|
||||
|
||||
with PdfPages(filename) as pdf:
|
||||
fig, ax = plt.subplots(figsize=(10, 10))
|
||||
|
||||
fig, ax = plt.subplots(figsize=(10, 10))
|
||||
|
||||
# polygon outline
|
||||
poly_x = [p[0] for p in self.poly] + [self.poly[0][0]]
|
||||
poly_y = [p[1] for p in self.poly] + [self.poly[0][1]]
|
||||
ax.plot(poly_x, poly_y, '-', color='black', linewidth=.5, label='Polygon')
|
||||
# polygon outline
|
||||
poly_x = [p[0] for p in self.poly] + [self.poly[0][0]]
|
||||
poly_y = [p[1] for p in self.poly] + [self.poly[0][1]]
|
||||
ax.plot(poly_x, poly_y, '-', color='black', linewidth=.5, label='Polygon')
|
||||
|
||||
# skeleton edges
|
||||
for node1, node2 in self.skeleton_edges:
|
||||
ax.plot([node1.x, node2.x], [node1.y, node2.y], '-', color='gray', linewidth=.5, alpha=0.7)
|
||||
# skeleton edges
|
||||
for node1, node2 in self.skeleton_edges:
|
||||
ax.plot([node1.x, node2.x], [node1.y, node2.y], '-', color='gray', linewidth=.5, alpha=0.7)
|
||||
|
||||
# skeleton nodes
|
||||
for n in self.skeleton_nodes:
|
||||
if n in self.divergent:
|
||||
ax.plot(n.x, n.y, 'o', markerfacecolor='none', markeredgecolor='green', markersize=4)
|
||||
elif n in self.arc_map:
|
||||
ax.plot(n.x, n.y, 'o', color='black', markersize=3, alpha=0.5)
|
||||
else:
|
||||
ax.plot(n.x, n.y, 'o', markerfacecolor='none', markeredgecolor='magenta', markersize=4)
|
||||
# skeleton nodes
|
||||
for n in self.skeleton_nodes:
|
||||
if n in self.divergent:
|
||||
ax.plot(n.x, n.y, 'o', markerfacecolor='none', markeredgecolor='green', markersize=4)
|
||||
elif n in self.arc_map:
|
||||
ax.plot(n.x, n.y, 'o', color='black', markersize=3, alpha=0.5)
|
||||
else:
|
||||
ax.plot(n.x, n.y, 'o', markerfacecolor='none', markeredgecolor='magenta', markersize=4)
|
||||
|
||||
count = {True: 0, False: 0}
|
||||
for arm, direction, t1, t2 in self.debug_arms:
|
||||
xs = [x for x, y in arm]
|
||||
ys = [y for x, y in arm]
|
||||
ax.plot(xs, ys, linewidth=.2, color='red' if direction else 'blue')
|
||||
align = 'left' if direction else 'right'
|
||||
ax.text(xs[-1], ys[-1], f'{count[direction]}', size=3, horizontalalignment=align)
|
||||
ax.text(xs[0], ys[0], f'{count[direction]}', size=3, horizontalalignment=align, color='gray')
|
||||
count[direction] += 1
|
||||
count = {True: 0, False: 0}
|
||||
for arm, direction, t1, t2 in self.debug_arms:
|
||||
xs = [x for x, y in arm]
|
||||
ys = [y for x, y in arm]
|
||||
ax.plot(xs, ys, linewidth=.2, color='red' if direction else 'blue')
|
||||
align = 'left' if direction else 'right'
|
||||
ax.text(xs[-1], ys[-1], f'{count[direction]}', size=3, horizontalalignment=align)
|
||||
ax.text(xs[0], ys[0], f'{count[direction]}', size=3, horizontalalignment=align, color='gray')
|
||||
count[direction] += 1
|
||||
|
||||
xs, ys = [], []
|
||||
for i in range(100):
|
||||
r = self.radius - (i/99) * self.min_radius
|
||||
arc, (px, py) = self.project_arc(self.poly[0], r)
|
||||
xs.append(px)
|
||||
ys.append(py)
|
||||
ax.plot(xs, ys, '--', linewidth=.5, color='black')
|
||||
xs, ys = [], []
|
||||
for i in range(100):
|
||||
r = self.radius - (i/99) * self.min_radius
|
||||
arc, (px, py) = self.project_arc(self.poly[0], r)
|
||||
xs.append(px)
|
||||
ys.append(py)
|
||||
ax.plot(xs, ys, '--', linewidth=.5, color='black')
|
||||
|
||||
ax.set_aspect('equal', adjustable='box')
|
||||
ax.grid(True, alpha=0.3)
|
||||
ax.legend()
|
||||
ax.set_title(f'Polygon Skeleton (radius: {self.radius:.3f}, min_radius: {self.min_radius:.3f})')
|
||||
ax.set_xlabel('X')
|
||||
ax.set_ylabel('Y')
|
||||
ax.invert_yaxis()
|
||||
pdf.savefig(fig, bbox_inches='tight')
|
||||
plt.close(fig)
|
||||
ax.set_aspect('equal', adjustable='box')
|
||||
ax.grid(True, alpha=0.3)
|
||||
ax.legend()
|
||||
ax.set_title(f'Polygon Skeleton (radius: {self.radius:.3f}, min_radius: {self.min_radius:.3f})')
|
||||
ax.set_xlabel('X')
|
||||
ax.set_ylabel('Y')
|
||||
ax.invert_yaxis()
|
||||
fig.savefig(filename, bbox_inches='tight', dpi=600)
|
||||
plt.close(fig)
|
||||
Loading…
Add table
Add a link
Reference in a new issue