component
AS5047P-ATSM
Public Made by Adomby adom
AMS Position Sensors, TSSOP-14. Pinout extracted from the datasheet, KiCad symbol/footprint/3D.
main
John Lauer
Hero billboard re-rendered from the new model; symbol chip outline redrawn from the new model's etched mark (KiCad symb…
56f2c72
15d ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
#!/usr/bin/env python3
"""Swap the chip outline baked in an adom-symbol .kicad_sym body for a new outline SVG.
adom-symbol bakes `<mpn>-3d-outline-*-named.svg` as 0.025 mm polylines in unit _0_1. This finds the icon
box the old drawing occupies (fitting the old SVG to the baked polylines: one scale, Y flipped), fits
the new drawing into the same box (centred, aspect kept), simplifies each stroke (Douglas-Peucker,
`--tol` mm, the baked art's own density), and replaces the polylines in place. Nothing else changes.
Without --old-svg the symbol has no outline yet: --box x0,y0,x1,y1 (mm) places it and the polylines
are added at the end of unit _0_1.
usage: sym_outline.py sym.kicad_sym new.svg [--old-svg old.svg | --box x0,y0,x1,y1] [--tol 0.004] [--out f]"""
import re, sys, argparse
import numpy as np
ap = argparse.ArgumentParser()
ap.add_argument("sym"); ap.add_argument("svg"); ap.add_argument("--old-svg"); ap.add_argument("--box")
ap.add_argument("--tol", type=float, default=0.004); ap.add_argument("--out")
A = ap.parse_args()
PL = re.compile(r"[ \t]*\(polyline \(pts (.*?)\)\) \(stroke \(width 0\.025\) \(type default\)\) \(fill \(type none\)\)\)\n")
def svg_polys(path):
return [np.array([tuple(map(float, q.split(","))) for q in m.split()]) for m in re.findall(r'points="([^"]+)"', open(path).read())]
def rdp(p, tol):
if len(p) < 3:
return p
a, b = p[0], p[-1]; ab = b - a; n = np.linalg.norm(ab)
d = np.abs(ab[0] * (p[:, 1] - a[1]) - ab[1] * (p[:, 0] - a[0])) / n if n > 1e-12 else np.linalg.norm(p - a, axis=1)
i = int(np.argmax(d))
if d[i] <= tol:
return np.array([a, b])
return np.vstack([rdp(p[:i + 1], tol)[:-1], rdp(p[i:], tol)])
sym = open(A.sym).read()
old = PL.findall(sym)
if A.old_svg:
yp = np.vstack([np.array([tuple(map(float, q)) for q in re.findall(r"\(xy ([-\d.]+) ([-\d.]+)\)", m)]) for m in old])
box = (yp[:, 0].min(), yp[:, 1].min(), yp[:, 0].max(), yp[:, 1].max())
else:
box = tuple(float(v) for v in A.box.split(","))
new = svg_polys(A.svg); P = np.vstack(new)
w, h = P[:, 0].max() - P[:, 0].min(), P[:, 1].max() - P[:, 1].min()
s = min((box[2] - box[0]) / w, (box[3] - box[1]) / h)
cx, cy = (box[0] + box[2]) / 2, (box[1] + box[3]) / 2
mx, my = (P[:, 0].max() + P[:, 0].min()) / 2, (P[:, 1].max() + P[:, 1].min()) / 2
lines = []
for p in new:
q = np.c_[cx + s * (p[:, 0] - mx), cy - s * (p[:, 1] - my)]
q = rdp(q, A.tol)
lines.append(" (polyline (pts " + " ".join("(xy %.3f %.3f)" % tuple(v) for v in q)
+ ") (stroke (width 0.025) (type default)) (fill (type none)))\n")
if old:
first = PL.search(sym).start()
body = PL.sub("", sym)
out = body[:first] + "".join(lines) + body[first:]
else:
u = re.search(r'\(symbol "[^"]+_0_1"', sym)
# end of the _0_1 unit: its closing paren at four-space indent
end = sym.index("\n )\n", u.end())
out = sym[:end + 1] + "".join(lines) + sym[end + 1:]
open(A.out or A.sym, "w").write(out)
print(f"OK: {A.out or A.sym}: {len(old)} old -> {len(lines)} new polylines, {sum(l.count('(xy') for l in lines)} points, "
f"box {tuple(round(v, 3) for v in box)}, {s:.5f} mm/px")