main
John Lauer Component record for ltc2945cms-pbf: files batch 3 16c3b96 23h ago
#!/usr/bin/env python3
"""Put every pin of an adom-symbol 1.2.6 two-column .kicad_sym on the 2.54 mm grid (1.2.6 starts each column at
y = -2.00 and spaces groups 8.35 mm apart, so most pins land off grid; a known bug).

Per column (pins sharing an x), keeping the tool's order and grouping (a gap > 2.6 mm starts a new group):
first pin at y = -2.54, pins in a group 2.54 mm apart, 7.62 mm from the last pin of a group to the first of the next
(the Adom three-grid-step group gap). Each group caption keeps its 2.00 mm offset above the group's first pin. The
body bottom becomes 2.54 mm below the lowest pin; the Value field and the hidden field column below the body move
by the same amount. Value is set to the exact MPN and Footprint to the page's library footprint.
usage: snap_symbol_groups.py in.kicad_sym out.kicad_sym MPN LIB:FOOTPRINT"""
import re, sys, json
src, out, mpn, fp = sys.argv[1:5]
s = open(src).read()
G = 2.54
pin_re = re.compile(r"(\(pin \w+ line )\(at (-?[\d.]+) (-?[\d.]+) (\d+)\)")
cols = {}
for m in pin_re.finditer(s):
    cols.setdefault(m.group(2), []).append(float(m.group(3)))
newy = {}; captions = {}
for x, ys in cols.items():
    ys = sorted(set(ys), reverse=True)
    y_new = -G; prev = None
    for y in ys:
        if prev is not None:
            if prev - y > 2.6:
                y_new -= 3 * G
                captions[(x, round(y, 2))] = y_new
            else:
                y_new -= G
        else:
            captions[(x, round(y, 2))] = y_new
        newy[(x, round(y, 2))] = round(y_new, 2); prev = y
s = pin_re.sub(lambda m: f"{m.group(1)}(at {m.group(2)} {newy[(m.group(2), round(float(m.group(3)), 2))]:.2f} {m.group(4)})", s)
first = {}
for (x, y), ny in captions.items():
    first[(1 if float(x) > 0 else -1, round(y + 2.0, 2))] = round(ny + 2.0, 2)
def cap(m):
    x, y = float(m.group(2)), float(m.group(3)); key = (1 if x > 0 else -1, round(y, 2))
    return f"{m.group(1)}(at {m.group(2)} {first.get(key, y):.2f} 0)"
s = re.sub(r'(\(text "[^"]+" )\(at (-?[\d.]+) (-?[\d.]+) 0\)', cap, s)
low = min(newy.values())
rm = re.search(r"\(rectangle \(start (-?[\d.]+) (-?[\d.]+)\) \(end (-?[\d.]+) (-?[\d.]+)\)", s)
old_bottom = float(rm.group(4)); new_bottom = round(low - G, 2); shift = round(new_bottom - old_bottom, 2)
s = s[:rm.start()] + f"(rectangle (start {rm.group(1)} {rm.group(2)}) (end {rm.group(3)} {new_bottom:.2f})" + s[rm.end():]
def prop(m):
    name, val, ws, x, y = m.group(1), m.group(2), m.group(3), m.group(4), float(m.group(5))
    if name == "Reference": return m.group(0)
    return f'(property "{name}" "{val}"{ws}(at {x} {y + shift:.2f}'
s = re.sub(r'\(property "([^"]+)" "([^"]*)"(\s*)\(at (\S+) (-?[\d.]+)', prop, s)
s = re.sub(r'\(property "Value" "[^"]*"', f'(property "Value" "{mpn}"', s, count=1)
s = re.sub(r'\(property "Footprint" "[^"]*"', f'(property "Footprint" "{fp}"', s, count=1)
open(out, "w").write(s)
print(json.dumps({"pin_moves": {f"{k[0]},{k[1]}": v for k, v in newy.items()}, "body_bottom": [old_bottom, new_bottom]}, indent=1))