Compare commits
6 commits
main
...
experiment
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82eccbad1d | ||
|
|
87033c116f | ||
|
|
556707dc35 | ||
|
|
3e6c7d6f57 | ||
|
|
5bdf4d3274 | ||
|
|
7bfaabc839 |
5 changed files with 83 additions and 40 deletions
|
|
@ -111,7 +111,7 @@ def do_release(dry_run):
|
||||||
|
|
||||||
if not dry_run:
|
if not dry_run:
|
||||||
print('Create git commit')
|
print('Create git commit')
|
||||||
subprocess.run(['git', 'commit', '-m', f'Version {version}', '--no-edit'], check=True, capture_output=True)
|
subprocess.run(['git', 'commit', '-m', f'KiCad package version {version}', '--no-edit'], check=True, capture_output=True)
|
||||||
res = subprocess.run('git rev-parse --short HEAD'.split(), check=True, capture_output=True, text=True)
|
res = subprocess.run('git rev-parse --short HEAD'.split(), check=True, capture_output=True, text=True)
|
||||||
print(f'Created commit {res.stdout.strip()}')
|
print(f'Created commit {res.stdout.strip()}')
|
||||||
print(f'Creating and signing version tag v{version}')
|
print(f'Creating and signing version tag v{version}')
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[project]
|
[project]
|
||||||
name = "kicoil"
|
name = "kicoil"
|
||||||
version = "0.9.0"
|
version = "0.10.0"
|
||||||
description = "Planar Inductor Generator"
|
description = "Planar Inductor Generator"
|
||||||
readme = "README.rst"
|
readme = "README.rst"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,8 @@ class CircleShape(Shape):
|
||||||
def project_point(self, r, a, r_ref=None):
|
def project_point(self, r, a, r_ref=None):
|
||||||
return cos(a) * r, sin(a) * r
|
return cos(a) * r, sin(a) * r
|
||||||
|
|
||||||
|
def map_angle(self, a):
|
||||||
|
return a
|
||||||
|
|
||||||
def offset_exterior(self, margin):
|
def offset_exterior(self, margin):
|
||||||
r = self.outer_radius + margin
|
r = self.outer_radius + margin
|
||||||
|
|
@ -180,6 +182,12 @@ class OffsetShape(Shape):
|
||||||
return points, arm_length, angle_refs
|
return points, arm_length, angle_refs
|
||||||
|
|
||||||
|
|
||||||
|
def map_angle(self, a):
|
||||||
|
a_new = self.sk.map_angle(a / (2*pi), self.outer_radius, self.inner_radius)
|
||||||
|
print(f'NEW MAPPED {a:.3f} to {a_new:.3f}')
|
||||||
|
return a_new * 2 * pi
|
||||||
|
|
||||||
|
|
||||||
def project_point(self, r, a, r_ref=None):
|
def project_point(self, r, a, r_ref=None):
|
||||||
# Skeletonator uses a t coordinate from 0 - 1 per revolution instead of a radian angle.
|
# Skeletonator uses a t coordinate from 0 - 1 per revolution instead of a radian angle.
|
||||||
return self.sk.project_point(a/(2*pi) % 1, r, r_ref=r_ref)
|
return self.sk.project_point(a/(2*pi) % 1, r, r_ref=r_ref)
|
||||||
|
|
@ -518,6 +526,7 @@ class PlanarInductor():
|
||||||
fold_angle = start_angle + self.sweeping_angle
|
fold_angle = start_angle + self.sweeping_angle
|
||||||
end_angle = fold_angle + self.sweeping_angle
|
end_angle = fold_angle + self.sweeping_angle
|
||||||
|
|
||||||
|
print(f'### TWIST {i} INWARD ###')
|
||||||
# Handle the spiral arm
|
# 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)
|
||||||
x0, y0 = points_layer0[0]
|
x0, y0 = points_layer0[0]
|
||||||
|
|
@ -533,6 +542,7 @@ class PlanarInductor():
|
||||||
footprint.lines.extend(kicad.make_line(*p1, *p2, self.trace_width, self.layer_pair[0]) for p1, p2 in zip(points_layer0, points_layer0[1:]))
|
footprint.lines.extend(kicad.make_line(*p1, *p2, self.trace_width, self.layer_pair[0]) for p1, p2 in zip(points_layer0, points_layer0[1:]))
|
||||||
|
|
||||||
if self.layers > 1:
|
if self.layers > 1:
|
||||||
|
print(f'### TWIST {i} OUTWARD ###')
|
||||||
# Handle the returning arm on the bottom layer
|
# 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)
|
||||||
points_layer1 = points_layer1[::-1]
|
points_layer1 = points_layer1[::-1]
|
||||||
|
|
@ -568,8 +578,8 @@ class PlanarInductor():
|
||||||
|
|
||||||
xv, yv = self.shape.project_point(r, fold_angle, r_ref=refs_layer0[-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(*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.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,
|
footprint.pads.append(kicad.make_via(xv, yv,
|
||||||
self.via_diameter, self.via_drill, self.clearance,
|
self.via_diameter, self.via_drill, self.clearance,
|
||||||
|
|
@ -586,7 +596,7 @@ class PlanarInductor():
|
||||||
points_layer0, refs_layer0 = arms_layers[0][i]
|
points_layer0, refs_layer0 = arms_layers[0][i]
|
||||||
points_layer1, refs_layer1 = arms_layers[1][(i - self.turns) % self.twists]
|
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])
|
xv, yv = self.shape.project_point(r, self.shape.map_angle(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_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]))
|
footprint.lines.append(kicad.make_line(*points_layer1[-1], xv, yv, self.trace_width, self.layer_pair[1]))
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,14 @@ def interpolate(p1, p2, t, t_start=0, t_end=1):
|
||||||
return (x1 + t*dx, y1 + t*dy)
|
return (x1 + t*dx, y1 + t*dy)
|
||||||
|
|
||||||
|
|
||||||
|
def interpolate_1d(a, b, t, t_start=0, t_end=1):
|
||||||
|
if math.isclose(t_start, t_end):
|
||||||
|
return a
|
||||||
|
t_range = t_end - t_start
|
||||||
|
t = (t - t_start) / t_range
|
||||||
|
return a + (b-a) * t
|
||||||
|
|
||||||
|
|
||||||
def approx_in_range(value, lower, upper):
|
def approx_in_range(value, lower, upper):
|
||||||
""" Approximate range check """
|
""" Approximate range check """
|
||||||
if math.isclose(value, lower) or math.isclose(value, upper):
|
if math.isclose(value, lower) or math.isclose(value, upper):
|
||||||
|
|
@ -245,8 +253,43 @@ class Skeletonator:
|
||||||
arcs.append(arc)
|
arcs.append(arc)
|
||||||
points.append(pt)
|
points.append(pt)
|
||||||
return arcs, points
|
return arcs, points
|
||||||
|
|
||||||
|
def map_angle(self, t, r1, r2):
|
||||||
|
r_ref = min(r1, r2)
|
||||||
|
_ic_arcs, inner_circumference = self.map_circumference(r_ref)
|
||||||
|
inner_circumference_sum = sum(math.dist(p1, p2) for p1, p2 in edge_cycle(inner_circumference))
|
||||||
|
|
||||||
|
angle = 0
|
||||||
|
point_angles = [0]
|
||||||
|
for p1, p2 in edge_cycle(inner_circumference):
|
||||||
|
edge_angle = math.dist(p1, p2) / inner_circumference_sum
|
||||||
|
angle += edge_angle
|
||||||
|
point_angles.append(angle)
|
||||||
|
|
||||||
|
_oc_arcs, outer_circumference = self.map_circumference(max(r1, r2))
|
||||||
|
outer_circumference_sum = sum(math.dist(p1, p2) for p1, p2 in edge_cycle(outer_circumference))
|
||||||
|
|
||||||
|
angle = 0
|
||||||
|
point_angles_outer = [0]
|
||||||
|
for p1, p2 in edge_cycle(outer_circumference):
|
||||||
|
edge_angle = math.dist(p1, p2) / outer_circumference_sum
|
||||||
|
angle += edge_angle
|
||||||
|
point_angles_outer.append(angle)
|
||||||
|
|
||||||
|
t_map_int = math.floor(t)
|
||||||
|
t %= 1.0
|
||||||
|
|
||||||
|
for ia1, ia2, oa1, oa2 in zip(point_angles, point_angles[1:] + [1], point_angles_outer, point_angles_outer[1:] + [1]):
|
||||||
|
|
||||||
|
if approx_in_range(t, oa1, oa2):
|
||||||
|
if oa1 == oa2:
|
||||||
|
return t_map_int + ia2
|
||||||
|
else:
|
||||||
|
return t_map_int + ia1 + (ia2 - ia1) * ((t - oa1) / (oa2 - oa1))
|
||||||
|
|
||||||
|
|
||||||
def do_spiral(self, t1, t2, r1=None, r2=None):
|
def do_spiral(self, t1, t2, r1=None, r2=None):
|
||||||
|
print(f' {t1=:.5f} {t2=:.5f} {r1=:.2f} {r2=:.2f}')
|
||||||
if r1 is None:
|
if r1 is None:
|
||||||
r1 = self.radius
|
r1 = self.radius
|
||||||
if r2 is None:
|
if r2 is None:
|
||||||
|
|
@ -256,48 +299,38 @@ class Skeletonator:
|
||||||
t1, t2 = t2, t1
|
t1, t2 = t2, t1
|
||||||
r1, r2 = r2, r1
|
r1, r2 = r2, r1
|
||||||
|
|
||||||
def r_interpolate(t):
|
angle_map = []
|
||||||
t = max(t1, min(t2, t)) # Clip to start/end of spiral
|
circumferences = []
|
||||||
f = (t - t1) / (t2 - t1)
|
n = 100
|
||||||
return r1 + (r2 - r1) * f
|
radius_steps = [r1 + (r2 - r1) * i/(n-1) for i in range(n)]
|
||||||
|
angle_steps = [t1 + (t2 - t1) * i/(n-1) for i in range(n)]
|
||||||
|
for r in radius_steps:
|
||||||
|
_ic_arcs, circumference = self.map_circumference(r)
|
||||||
|
circumference_sum = sum(math.dist(p1, p2) for p1, p2 in edge_cycle(circumference))
|
||||||
|
circumferences.append(circumference_sum)
|
||||||
|
|
||||||
for t_start in range(math.floor(t1), math.ceil(t2)):
|
angle = 0
|
||||||
t_end = t_start + 1
|
point_angles = [0]
|
||||||
r_outer = r_interpolate(t_start)
|
for p1, p2 in edge_cycle(circumference):
|
||||||
r_inner = r_interpolate(t_end)
|
edge_angle = math.dist(p1, p2) / circumference_sum
|
||||||
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 = []
|
|
||||||
inner_circumference_sum = sum(math.dist(p1, p2) for p1, p2 in edge_cycle(inner_circumference))
|
|
||||||
point_angles = []
|
|
||||||
for p1, p2 in edge_cycle(inner_circumference):
|
|
||||||
edge_angle = math.dist(p1, p2) / inner_circumference_sum
|
|
||||||
point_angles.append(angle)
|
|
||||||
angle += edge_angle
|
angle += edge_angle
|
||||||
point_angles.append(t_end)
|
point_angles.append(angle)
|
||||||
|
|
||||||
|
angle_map.append(point_angles)
|
||||||
|
|
||||||
for (p1, p2), (tp1, tp2) in zip(self.poly_edges, itertools.pairwise(point_angles)):
|
for r, t, point_angles in zip(radius_steps, angle_steps, angle_map):
|
||||||
rp1 = r_interpolate(tp1)
|
for (p1, p2), (tp1, tp2) in zip(self.poly_edges, itertools.pairwise(point_angles + point_angles[:1])):
|
||||||
rp2 = r_interpolate(tp2)
|
_arc, p1_proj = self.project_arc(p1, r)
|
||||||
_arc, p1_proj = self.project_arc(p1, rp1)
|
_arc, p2_proj = self.project_arc(p2, r)
|
||||||
_arc, p2_proj = self.project_arc(p2, rp2)
|
|
||||||
|
|
||||||
if approx_in_range(t1, tp1, tp2):
|
if approx_in_range(t%1, tp1, tp2):
|
||||||
_arc, p2_proj_r1 = self.project_arc(p2, r1)
|
yield interpolate(p1_proj, p2_proj, t%1, tp1, tp2), r
|
||||||
yield interpolate(p1_proj, p2_proj_r1, t1, tp1, tp2), 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
|
|
||||||
elif approx_in_range(tp2, t1, t2):
|
|
||||||
yield p2_proj, r_ref
|
|
||||||
|
|
||||||
def dump_to_pdf(self, filename):
|
def dump_to_pdf(self, filename):
|
||||||
with PdfPages(filename) as pdf:
|
with PdfPages(filename) as pdf:
|
||||||
fig, ax = plt.subplots(figsize=(10, 10))
|
fig, ax = plt.subplots(figsize=(10, 10))
|
||||||
|
|
||||||
# polygon outline
|
# polygon outliner
|
||||||
poly_x = [p[0] for p in self.poly] + [self.poly[0][0]]
|
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]]
|
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, 'b-', linewidth=2, label='Polygon')
|
||||||
|
|
|
||||||
2
uv.lock
generated
2
uv.lock
generated
|
|
@ -569,7 +569,7 @@ wheels = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kicoil"
|
name = "kicoil"
|
||||||
version = "0.9.0"
|
version = "0.10.0"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "beautifulsoup4" },
|
{ name = "beautifulsoup4" },
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue