diff --git a/package.py b/package.py index a11a9da..b20ff43 100644 --- a/package.py +++ b/package.py @@ -111,7 +111,7 @@ def do_release(dry_run): if not dry_run: 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) print(f'Created commit {res.stdout.strip()}') print(f'Creating and signing version tag v{version}') diff --git a/pyproject.toml b/pyproject.toml index 9f964ab..82c2c92 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "kicoil" -version = "0.9.0" +version = "0.10.0" description = "Planar Inductor Generator" readme = "README.rst" license = "Apache-2.0" diff --git a/src/kicoil/geometry.py b/src/kicoil/geometry.py index 7d17d55..dbabb1d 100644 --- a/src/kicoil/geometry.py +++ b/src/kicoil/geometry.py @@ -142,6 +142,8 @@ class CircleShape(Shape): def project_point(self, r, a, r_ref=None): return cos(a) * r, sin(a) * r + def map_angle(self, a): + return a def offset_exterior(self, margin): r = self.outer_radius + margin @@ -180,6 +182,12 @@ class OffsetShape(Shape): 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): # 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) @@ -518,6 +526,7 @@ class PlanarInductor(): fold_angle = start_angle + self.sweeping_angle end_angle = fold_angle + self.sweeping_angle + print(f'### TWIST {i} INWARD ###') # Handle the spiral arm 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] @@ -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:])) if self.layers > 1: + print(f'### TWIST {i} OUTWARD ###') # 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 = points_layer1[::-1] @@ -568,8 +578,8 @@ class PlanarInductor(): 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.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, @@ -586,7 +596,7 @@ class PlanarInductor(): 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]) + 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_layer1[-1], xv, yv, self.trace_width, self.layer_pair[1])) diff --git a/src/kicoil/skeletonator.py b/src/kicoil/skeletonator.py index cb56db1..2ee8ee1 100644 --- a/src/kicoil/skeletonator.py +++ b/src/kicoil/skeletonator.py @@ -31,6 +31,14 @@ def interpolate(p1, p2, t, t_start=0, t_end=1): 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): """ Approximate range check """ if math.isclose(value, lower) or math.isclose(value, upper): @@ -245,8 +253,43 @@ class Skeletonator: arcs.append(arc) points.append(pt) 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): + print(f' {t1=:.5f} {t2=:.5f} {r1=:.2f} {r2=:.2f}') if r1 is None: r1 = self.radius if r2 is None: @@ -256,48 +299,38 @@ class Skeletonator: t1, t2 = t2, t1 r1, r2 = r2, r1 - def r_interpolate(t): - t = max(t1, min(t2, t)) # Clip to start/end of spiral - f = (t - t1) / (t2 - t1) - return r1 + (r2 - r1) * f + angle_map = [] + circumferences = [] + n = 100 + 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)): - 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 - 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 = 0 + point_angles = [0] + for p1, p2 in edge_cycle(circumference): + edge_angle = math.dist(p1, p2) / circumference_sum 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)): - rp1 = r_interpolate(tp1) - rp2 = r_interpolate(tp2) - _arc, p1_proj = self.project_arc(p1, rp1) - _arc, p2_proj = self.project_arc(p2, rp2) + for r, t, point_angles in zip(radius_steps, angle_steps, angle_map): + for (p1, p2), (tp1, tp2) in zip(self.poly_edges, itertools.pairwise(point_angles + point_angles[:1])): + _arc, p1_proj = self.project_arc(p1, r) + _arc, p2_proj = self.project_arc(p2, r) - 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 - 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 + if approx_in_range(t%1, tp1, tp2): + yield interpolate(p1_proj, p2_proj, t%1, tp1, tp2), r def dump_to_pdf(self, filename): with PdfPages(filename) as pdf: 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_y = [p[1] for p in self.poly] + [self.poly[0][1]] ax.plot(poly_x, poly_y, 'b-', linewidth=2, label='Polygon') diff --git a/uv.lock b/uv.lock index c28bbe4..c2048c5 100644 --- a/uv.lock +++ b/uv.lock @@ -569,7 +569,7 @@ wheels = [ [[package]] name = "kicoil" -version = "0.9.0" +version = "0.10.0" source = { editable = "." } dependencies = [ { name = "beautifulsoup4" },