Fix rollover problem

This commit is contained in:
jaseg 2025-12-17 12:18:28 +01:00
parent 82d107fc82
commit b0b942431e
3 changed files with 51 additions and 18 deletions

View file

@ -166,11 +166,11 @@ class OffsetShape(Shape):
return f'polygonal (n={len(self.polygon)} point, r={self.radius:.2f} mm radius)'
def compute_spiral(self, a1, a2, fn=None):
def compute_spiral(self, a1, a2, fn=None, debug=False):
# Skeletonator uses a t coordinate from 0 - 1 per revolution instead of a radian angle.
points = []
angle_refs = []
for point, angle_ref in self.sk.do_spiral(a1/(2*pi), a2/(2*pi), self.outer_radius, self.inner_radius):
for point, angle_ref in self.sk.do_spiral(a1/(2*pi), a2/(2*pi), self.outer_radius, self.inner_radius, debug=debug):
points.append(point)
angle_refs.append(angle_ref)
if a2 < a1:
@ -519,7 +519,7 @@ class PlanarInductor():
end_angle = fold_angle + self.sweeping_angle
# Handle the spiral arm
points_layer0, arm_length, angle_refs_layer0 = self.shape.compute_spiral(a1=start_angle, a2=fold_angle, fn=circle_segments)
points_layer0, arm_length, angle_refs_layer0 = self.shape.compute_spiral(a1=start_angle, a2=fold_angle, fn=circle_segments, debug=True)
x0, y0 = points_layer0[0]
xn, yn = points_layer0[-1]
if angle_refs_layer0:
@ -534,7 +534,7 @@ class PlanarInductor():
if self.layers > 1:
# Handle the returning arm on the bottom layer
points_layer1, _, angle_refs_layer1 = self.shape.compute_spiral(a1=end_angle, a2=fold_angle, fn=circle_segments)
points_layer1, _, angle_refs_layer1 = self.shape.compute_spiral(a1=end_angle, a2=fold_angle, fn=circle_segments, debug=True)
points_layer1 = points_layer1[::-1]
if self.approximate_arcs and isinstance(self.shape, CircleShape):
footprint.arcs.extend(arc_approximate(points_layer1, self.trace_width, self.layer_pair[1], arc_tolerance))
@ -611,5 +611,6 @@ class PlanarInductor():
keepout=ZoneKeepout(copperpour_allowed=False),
polygon=ZonePolygon(pts=[XYCoord(x=x, y=y) for x, y in pts])))
self.shape.sk.dump_to_pdf('/tmp/test.pdf')
return footprint

View file

@ -189,7 +189,7 @@ class Skeletonator:
for x, y in poly:
p = (round(x, 6), round(y, 6))
self.node_map[(x, y)] = coord_map[p]
self.dump_to_pdf('/tmp/test.pdf')
self.debug_arms = []
def iter_arcs(self, p):
i = 0
@ -246,13 +246,15 @@ class Skeletonator:
points.append(pt)
return arcs, points
def do_spiral(self, t1, t2, r1=None, r2=None):
def do_spiral(self, t1, t2, r1=None, r2=None, debug=False):
if r1 is None:
r1 = self.radius
if r2 is None:
r2 = self.min_radius
direction = True
if t2 < t1:
direction = False
t1, t2 = t2, t1
r1, r2 = r2, r1
@ -260,15 +262,17 @@ class Skeletonator:
t = max(t1, min(t2, t)) # Clip to start/end of spiral
f = (t - t1) / (t2 - t1)
return r1 + (r2 - r1) * f
for t_start in range(math.floor(t1), math.ceil(t2)):
debug_arm = []
for t_start in range(math.ceil(t2-t1)):
t_start += t1
t_end = t_start + 1
r_outer = r_interpolate(t_start)
r_inner = r_interpolate(t_end)
r_ref = min(r_inner, r_outer) # Handle outward spirals where the radii are swapped
_ic_arcs, inner_circumference = self.map_circumference(r_ref)
angle = t_start
angle = math.floor(t_start)
circumference_angles = []
inner_circumference_sum = sum(math.dist(p1, p2) for p1, p2 in edge_cycle(inner_circumference))
point_angles = []
@ -276,9 +280,9 @@ class Skeletonator:
edge_angle = math.dist(p1, p2) / inner_circumference_sum
point_angles.append(angle)
angle += edge_angle
point_angles.append(t_end)
point_angles += [a+1 for a in point_angles]
for (p1, p2), (tp1, tp2) in zip(self.poly_edges, itertools.pairwise(point_angles)):
for (p1, p2), (tp1, tp2) in zip(self.poly_edges * 2, itertools.pairwise(point_angles)):
rp1 = r_interpolate(tp1)
rp2 = r_interpolate(tp2)
_arc, p1_proj = self.project_arc(p1, rp1)
@ -286,26 +290,35 @@ class Skeletonator:
if approx_in_range(t1, tp1, tp2):
_arc, p2_proj_r1 = self.project_arc(p2, r1)
yield interpolate(p1_proj, p2_proj_r1, t1, tp1, tp2), r_ref
p_out = interpolate(p1_proj, p2_proj_r1, t1, tp1, tp2)
debug_arm.append(p_out)
yield p_out, r_ref
if approx_in_range(t2, tp1, tp2):
_arc, p1_proj_r2 = self.project_arc(p1, r2)
yield interpolate(p1_proj_r2, p2_proj, t2, tp1, tp2), r_ref
p_out = interpolate(p1_proj_r2, p2_proj, t2, tp1, tp2)
debug_arm.append(p_out)
yield p_out, r_ref
elif approx_in_range(tp2, t1, t2):
debug_arm.append(p2_proj)
yield p2_proj, r_ref
if debug:
self.debug_arms.append((debug_arm, direction, t1, t2))
def dump_to_pdf(self, filename):
with PdfPages(filename) as pdf:
fig, ax = plt.subplots(figsize=(10, 10))
fig, ax = plt.subplots(figsize=(20, 20))
# 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, 'b-', linewidth=2, label='Polygon')
ax.plot(poly_x, poly_y, 'bo', markersize=4)
ax.plot(poly_x, poly_y, '-', color='black', linewidth=.5, label='Polygon')
ax.plot(poly_x, poly_y, 'o', color='black', markersize=4)
# skeleton edges
for node1, node2 in self.skeleton_edges:
ax.plot([node1.x, node2.x], [node1.y, node2.y], 'r-', linewidth=1, alpha=0.7)
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:
@ -316,6 +329,25 @@ class Skeletonator:
else:
ax.plot(n.x, n.y, 'o', color='magenta', markersize=6)
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')
print(f'{count[direction]:03d}/{'A' if direction else 'B'}: {t1:.3f} {t2:.3f}')
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')
ax.set_aspect('equal', adjustable='box')
ax.grid(True, alpha=0.3)
ax.legend()

2
uv.lock generated
View file

@ -569,7 +569,7 @@ wheels = [
[[package]]
name = "kicoil"
version = "0.9.0"
version = "0.10.0"
source = { editable = "." }
dependencies = [
{ name = "beautifulsoup4" },