Polygon layout works pretty well now, add star shape

This commit is contained in:
jaseg 2025-12-12 15:04:24 +01:00
parent 5c2db13c3e
commit 299527fa72
3 changed files with 74 additions and 28 deletions

View file

@ -26,7 +26,7 @@ import math
import click
from gerbonara.layers import LayerStack
from .geometry import PlanarInductor, divisors, CircleShape, SectorShape
from .geometry import PlanarInductor, divisors, CircleShape, SectorShape, StarShape
from .kicad import footprint_to_board
from .svg import make_transparent_svg
@ -88,7 +88,8 @@ def cli(ctx, footprint_name, clipboard, single_layer, arc_tolerance, circle_segm
footprint = model.render_footprint(footprint_name, arc_tolerance, circle_segments)
except ValueError as e:
raise click.ClickException(*e.args)
#raise click.ClickException(*e.args)
raise
data = None
if format == 'kicad-footprint':
@ -179,3 +180,16 @@ def sector(ctx, outfile, angle, **kwargs):
angle = math.radians(angle)
shape = SectorShape(angle=angle, **kwargs)
ctx.obj['write'](shape, outfile)
@cli.command()
@click.option('--inner-diameter', type=float, default=25, help='Inner diameter [mm]')
@click.option('--outer-diameter', type=float, default=50, help='Outer diameter [mm]')
@click.option('--points', type=int, default=5, help='Number of points')
@click.option('--arc-tolerance', type=float, default=0.05, help='Tolerance for splitting arc into straight segments [mm] (default: 0.05 mm)')
@click.option('--annular-width', type=float, default=5, help='Width of the trace area on the outside of the shape [mm]')
@click.argument('outfile', required=False, type=click.Path(writable=True, dir_okay=False, path_type=Path))
@click.pass_context
def star(ctx, outfile, **kwargs):
shape = StarShape(**kwargs)
ctx.obj['write'](shape, outfile)

View file

@ -136,7 +136,7 @@ class CircleShape(Shape):
points.append((xn, yn))
dists.append(dist((xp, yp), (xn, yn)))
return points, sum(dists), None
return points, sum(dists), [None]*len(points)
def project_point(self, r, a, r_ref=None):
@ -182,7 +182,7 @@ class OffsetShape(Shape):
def project_point(self, r, a, r_ref=None):
# Skeletonator uses a t coordinate from 0 - 1 per revolution instead of a radian angle.
return self.sk.project_point(a/(2*pi), r, r_ref=r_ref)
return self.sk.project_point(a/(2*pi) % 1, r, r_ref=r_ref)
def offset_exterior(self, margin):
@ -240,6 +240,32 @@ class SectorShape(OffsetShape):
return f'{self.outer_diameter:.2f} x {self.inner_diameter:.2f} mm {degrees(self.angle):.0f} deg sector'
@dataclass
class StarShape(OffsetShape):
inner_diameter: float
outer_diameter: float
annular_width: float
points: int = 5
arc_tolerance: float = 0.05 # mm
polygon: list = field(init=False)
def __post_init__(self):
# center on y axis
pt = lambda r, a: (-r*sin(a), r*cos(a))
circle = lambda r, n, phase: [pt(r, (i + phase)*2*pi/n) for i in range(n)]
self.polygon = [x for pair in zip(circle(self.outer_diameter/2, self.points, 0), circle(self.inner_diameter/2, self.points, 0.5)) for x in pair]
super().__post_init__()
@property
def slug(self):
return f'star_{self.outer_diameter:.2f}x{self.inner_diameter:.2f}'
@property
def desc(self):
purpose = ', for demonic purposes' if self.points == 5 else ''
return f'{self.outer_diameter:.2f} x {self.inner_diameter:.2f} mm star shape{purpose}'
@dataclass
class PlanarInductor():
shape: Shape
@ -436,12 +462,14 @@ class PlanarInductor():
else:
# Add a straight connecting segment connecting the inner point to the outside of the spiral.
xq, yq = shape.project_point(shape.outer_radius, fold_angle)
ref = angle_refs_layer0[-1]
xq, yq = self.shape.project_point(self.shape.outer_radius, fold_angle, r_ref=ref)
angle_refs_layer1 = [ref, ref]
points_layer1 = [(xn, yn), (xq, yq)]
footprint.lines.append(kicad.make_line(xn, yn, xq, yq, self.trace_width, self.layer_pair[1]))
arms_layers[0].append(points_layer0)
arms_layers[1].append(points_layer1)
arms_layers[0].append((points_layer0, angle_refs_layer0))
arms_layers[1].append((points_layer1, angle_refs_layer1))
for i in range(self.twists):
start_angle = i*self.sector_angle
@ -454,11 +482,13 @@ class PlanarInductor():
if i%2 != 0:
r -= 2*self.via_offset
xv, yv = self.shape.project_point(r, fold_angle, r_ref=ref_n)
points_layer0, refs_layer0 = arms_layers[0][i]
points_layer1, refs_layer1 = arms_layers[1][i]
if self.via_offset:
footprint.lines.append(kicad.make_line(*arms_layers[0][i][-1], xv, yv, self.trace_width, self.layer_pair[0]))
footprint.lines.append(kicad.make_line(xv, yv, *arms_layers[1][i][0], self.trace_width, self.layer_pair[1]))
xv, yv = self.shape.project_point(r, fold_angle, r_ref=refs_layer0[-1])
footprint.lines.append(kicad.make_line(*points_layer0[-1], xv, yv, self.trace_width, self.layer_pair[0]))
footprint.lines.append(kicad.make_line(xv, yv, *points_layer1[0], self.trace_width, self.layer_pair[1]))
footprint.pads.append(kicad.make_via(xv, yv,
self.via_diameter, self.via_drill, self.clearance,
@ -466,19 +496,21 @@ class PlanarInductor():
# Handle outer via ring and process staggering if enabled unless we are at the start of the coil, where we will
# place pads below.
r = self.outer_via_ring_radius
if self.stagger_outer_vias:
if i%2 != 0:
r += 2*self.via_offset
points_layer0, refs_layer0 = arms_layers[0][i]
points_layer1, refs_layer1 = arms_layers[1][(i - self.turns) % self.twists]
xv, yv = self.shape.project_point(r, start_angle, r_ref=refs_layer0[0])
footprint.lines.append(kicad.make_line(*points_layer0[0], xv, yv, self.trace_width, self.layer_pair[0]))
footprint.lines.append(kicad.make_line(*points_layer1[-1], xv, yv, self.trace_width, self.layer_pair[1]))
if i > 0:
r = self.outer_via_ring_radius
if self.stagger_outer_vias:
if i%2 != 0:
r += 2*self.via_offset
xv, yv = self.shape.project_point(r, start_angle, r_ref=ref_0)
if self.via_offset:
footprint.lines.append(kicad.make_line(*arms_layers[0][i][0], xv, yv, self.trace_width, self.layer_pair[0]))
footprint.lines.append(kicad.make_line(*arms_layers[1][(i - self.turns) % self.twists][-1], xv, yv, self.trace_width, self.layer_pair[1]))
footprint.pads.append(kicad.make_via(xv, yv,
self.via_diameter, self.via_drill, self.clearance,
self.layer_pair))

View file

@ -122,8 +122,8 @@ class Skeletonator:
t_end = t_start + 1
r_outer = r_interpolate(t_start)
r_inner = r_interpolate(t_end)
oc_arcs, outer_circumference = self.map_circumference(r_outer)
ic_arcs, inner_circumference = self.map_circumference(r_inner)
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
circumference_angles = []
@ -142,8 +142,8 @@ class Skeletonator:
_arc, p2_proj = self.project_arc(p2, rp2)
if approx_in_range(t1, tp1, tp2):
yield interpolate(p1_proj, p2_proj, t1, tp1, tp2), r_inner
yield interpolate(p1_proj, p2_proj, t1, tp1, tp2), r_ref
if approx_in_range(t2, tp1, tp2):
yield interpolate(p1_proj, p2_proj, t2, tp1, tp2), r_inner
yield interpolate(p1_proj, p2_proj, t2, tp1, tp2), r_ref
elif approx_in_range(tp2, t1, t2):
yield p2_proj, r_inner
yield p2_proj, r_ref