main
John Lauer Component record TPS16850VMAR: files batch 1 6db00ed 18h ago
#!/usr/bin/env python3
"""Put every pin of an adom-symbol 1.2.6 .kicad_sym on the 2.54 mm grid (known adom-symbol bug: the first pin of
each column sits at y = -2.00 and group gaps are 8.35 mm, so every pin after it is off grid).

Per column (left x < 0, right x > 0), pins are walked top to bottom: the first goes to the nearest grid point, each
next one keeps its distance to the previous one rounded to whole grid steps (2.54 -> 1 step, the 8.35 group gap ->
3 steps). Group captions move with the pin they label, the body rectangle's bottom becomes the lowest pin - 2.54,
and properties drawn below the body move with that bottom. Footprint and Value are set to the page's values.
usage: snap_symbol.py in.kicad_sym out.kicad_sym MPN LIB:FOOTPRINT"""
import re, sys
src, out, mpn, fp = sys.argv[1:5]
s = open(src).read()
G = 2.54
PIN = r"(\(pin \w+ line )\(at (-?[\d.]+) (-?[\d.]+) (\d+)\)"
pins = [(float(m.group(2)), float(m.group(3))) for m in re.finditer(PIN, s)]
newy = {}
for right in (False, True):
    col = sorted([p for p in pins if (p[0] > 0) == right], key=lambda p: -p[1])
    prev_old = prev_new = None
    for x, y in col:
        ny = round(y / G) * G if prev_old is None else prev_new - max(1, round((prev_old - y) / G)) * G
        newy[(x, y)] = round(ny, 2); prev_old, prev_new = y, ny
s2 = re.sub(PIN, lambda m: f"{m.group(1)}(at {m.group(2)} {newy[(float(m.group(2)), float(m.group(3)))]:.2f} {m.group(4)})", s)
def cap_sub(m):
    x, y = float(m.group(2)), float(m.group(3))
    py, ny = max((py, ny) for (px, py), ny in newy.items() if (px > 0) == (x > 0) and py < y)
    return f'{m.group(1)}(at {m.group(2)} {ny + (y - py):.2f} 0)'
s2 = re.sub(r'(\(text "[^"]+" )\(at (-?[\d.]+) (-?[\d.]+) 0\)', cap_sub, s2)
rm = re.search(r"\(rectangle \(start (-?[\d.]+) (-?[\d.]+)\) \(end (-?[\d.]+) (-?[\d.]+)\)", s2)
old_bot = float(rm.group(4)); new_bot = round(min(newy.values()) - G, 2); d = new_bot - old_bot
s2 = s2[:rm.start()] + f"(rectangle (start {rm.group(1)} {rm.group(2)}) (end {rm.group(3)} {new_bot:.2f})" + s2[rm.end():]
def prop_sub(m):
    y = float(m.group(3))
    return f'{m.group(1)}(at {m.group(2)} {y + d:.2f}' if y < old_bot else m.group(0)
s2 = re.sub(r'(\(property "[^"]+" "[^"]*"\s*)\(at (-?[\d.]+) (-?[\d.]+)', prop_sub, s2)
s2 = re.sub(r'\(property "Value" "[^"]*"', f'(property "Value" "{mpn}"', s2, count=1)
s2 = re.sub(r'\(property "Footprint" "[^"]*"', f'(property "Footprint" "{fp}"', s2, count=1)
open(out, "w").write(s2)
off = [(k, v) for k, v in newy.items() if abs(v / G - round(v / G)) > 1e-6]
print("moved", sum(1 for k, v in newy.items() if abs(k[1] - v) > 1e-6), "of", len(newy), "pins; off grid after:", off,
      "; body bottom", old_bot, "->", new_bot, "; x on grid:", all(abs(k[0] / G - round(k[0] / G)) < 1e-6 for k in newy))