Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016X4CAujMuib2tDqt9JRg6X
64 lines
3.2 KiB
Python
64 lines
3.2 KiB
Python
"""Lee un .kicad_pcb (KiCad 8/9/10) sin KiCad y guarda en JSON lo que hace falta para dibujarlo:
|
|
pistas, arcos, vías, pads (con su red y su capa), contorno y zonas rellenadas.
|
|
Uso: python3 leer_pcb.py tarjeta.kicad_pcb pcb_v56.json"""
|
|
import re, json, math, sys
|
|
src = open(sys.argv[1], encoding='utf-8').read()
|
|
tok_re = re.compile(r'\(|\)|"(?:[^"\\]|\\.)*"|[^\s()"]+')
|
|
def parse(s):
|
|
stack=[[]]
|
|
for m in tok_re.finditer(s):
|
|
t=m.group(0)
|
|
if t=='(':
|
|
stack.append([])
|
|
elif t==')':
|
|
x=stack.pop(); stack[-1].append(x)
|
|
else:
|
|
if t.startswith('"'): t=t[1:-1]
|
|
stack[-1].append(t)
|
|
return stack[0][0]
|
|
root=parse(src)
|
|
def find(node, key):
|
|
return [c for c in node if isinstance(c,list) and c and c[0]==key]
|
|
def one(node,key):
|
|
r=find(node,key); return r[0] if r else None
|
|
out={'segments':[], 'vias':[], 'pads':[], 'edges':[], 'zones':[], 'arcs':[]}
|
|
for seg in find(root,'segment'):
|
|
s=one(seg,'start'); e=one(seg,'end'); w=one(seg,'width'); l=one(seg,'layer'); n=one(seg,'net')
|
|
out['segments'].append([float(s[1]),float(s[2]),float(e[1]),float(e[2]),float(w[1]),l[1],n[1] if n else ''])
|
|
for a in find(root,'arc'):
|
|
s=one(a,'start'); m=one(a,'mid'); e=one(a,'end'); w=one(a,'width'); l=one(a,'layer'); n=one(a,'net')
|
|
out['arcs'].append([float(s[1]),float(s[2]),float(m[1]),float(m[2]),float(e[1]),float(e[2]),float(w[1]),l[1],n[1] if n else ''])
|
|
for v in find(root,'via'):
|
|
at=one(v,'at'); sz=one(v,'size'); n=one(v,'net')
|
|
out['vias'].append([float(at[1]),float(at[2]),float(sz[1]),n[1] if n else ''])
|
|
for fp in find(root,'footprint'):
|
|
at=one(fp,'at'); fx,fy=float(at[1]),float(at[2]); frot=float(at[3]) if len(at)>3 else 0.0
|
|
layer=one(fp,'layer')[1]
|
|
ref=''
|
|
for p in find(fp,'property'):
|
|
if len(p)>2 and p[1]=='Reference': ref=p[2]
|
|
for pad in find(fp,'pad'):
|
|
pat=one(pad,'at'); px,py=float(pat[1]),float(pat[2]); prot=float(pat[3]) if len(pat)>3 else 0.0
|
|
size=one(pad,'size'); w,h=float(size[1]),float(size[2])
|
|
shape=pad[3] if len(pad)>3 else 'rect'
|
|
typ=pad[2] if len(pad)>2 else 'smd'
|
|
n=one(pad,'net'); net=n[1] if n and len(n)>1 else ''
|
|
# KiCad: pad position rotated by footprint rotation (clockwise in screen coords since y down)
|
|
r=math.radians(-frot)
|
|
ax=fx+px*math.cos(r)-py*math.sin(r); ay=fy+px*math.sin(r)+py*math.cos(r)
|
|
lay=one(pad,'layers'); lays=lay[1:] if lay else []
|
|
out['pads'].append([round(ax,3),round(ay,3),w,h,prot,shape,typ,net,ref,layer,lays])
|
|
for g in root:
|
|
if isinstance(g,list) and g and g[0] in ('gr_line','gr_rect','gr_arc','gr_circle','gr_poly'):
|
|
l=one(g,'layer')
|
|
if l and l[1]=='Edge.Cuts':
|
|
out['edges'].append(g)
|
|
for z in find(root,'zone'):
|
|
n=one(z,'net'); net=n[1] if n and len(n)>1 else ''
|
|
for fpoly in find(z,'filled_polygon'):
|
|
l=one(fpoly,'layer'); pts=one(fpoly,'pts')
|
|
xy=[(float(p[1]),float(p[2])) for p in pts[1:] if isinstance(p,list) and p[0]=='xy']
|
|
out['zones'].append([l[1] if l else '', net, xy])
|
|
json.dump(out, open(sys.argv[2] if len(sys.argv) > 2 else 'pcb.json', 'w'))
|
|
print({k: len(v) for k, v in out.items()})
|