sync: HP_BOWEI 2026-09-09
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
"""dump_kicad_vec.py — 把 KiCad 板的离散几何导成 JSON,供 cmp_vec.py 比对。必须用 KiCad python 跑。
|
||||
|
||||
输出(单位一律 mm,KiCad 原生坐标系:y 向下):
|
||||
vias.json [{x, y, drill, top, bot, net}]
|
||||
pads.json [{x, y, layer, net, ref, pad, drill}]
|
||||
tracks.json [{x1, y1, x2, y2, width, layer, net}]
|
||||
meta.json {counts, layers}
|
||||
|
||||
用法(KiCad python 路径见 ~/skills_env.md 的 KICAD_PYTHON):
|
||||
D:\\KiCad\\10.0\\bin\\python.exe dump_kicad_vec.py \\
|
||||
--board E:\\Kicad_design\\FOO\\FOO.kicad_pcb \\
|
||||
--out E:\\Kicad_design\\FOO\\_migration\\gerber\\kicad_vec
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser(description="Dump KiCad board discrete geometry to JSON (mm)")
|
||||
ap.add_argument("--board", required=True)
|
||||
ap.add_argument("--out", required=True)
|
||||
args = ap.parse_args()
|
||||
|
||||
try:
|
||||
import pcbnew
|
||||
except ImportError:
|
||||
print("[ERROR] 导不到 pcbnew —— 用 KiCad 自带 python 跑(~\\skills_env.md 的 KICAD_PYTHON)。")
|
||||
return 1
|
||||
|
||||
out = Path(args.out)
|
||||
out.mkdir(parents=True, exist_ok=True)
|
||||
board = pcbnew.LoadBoard(args.board)
|
||||
mm = pcbnew.ToMM
|
||||
|
||||
def via_size(v) -> float:
|
||||
"""KiCad 10 起 PCB_VIA::GetWidth() 必须带层参数,不带会断言中止进程。
|
||||
老版本又只接受无参形式,所以两种都试。size 只是给人看的,取不到就算了。"""
|
||||
for call in (lambda: v.GetWidth(v.TopLayer()), lambda: v.GetWidth()):
|
||||
try:
|
||||
return mm(call())
|
||||
except Exception:
|
||||
continue
|
||||
return 0.0
|
||||
|
||||
vias, tracks = [], []
|
||||
for t in board.GetTracks():
|
||||
if isinstance(t, pcbnew.PCB_VIA):
|
||||
vias.append({
|
||||
"x": mm(t.GetPosition().x), "y": mm(t.GetPosition().y),
|
||||
"drill": mm(t.GetDrill()), "size": via_size(t),
|
||||
"top": board.GetLayerName(t.TopLayer()),
|
||||
"bot": board.GetLayerName(t.BottomLayer()),
|
||||
"net": t.GetNetname(),
|
||||
})
|
||||
else:
|
||||
tracks.append({
|
||||
"x1": mm(t.GetStart().x), "y1": mm(t.GetStart().y),
|
||||
"x2": mm(t.GetEnd().x), "y2": mm(t.GetEnd().y),
|
||||
"width": mm(t.GetWidth()),
|
||||
"layer": board.GetLayerName(t.GetLayer()),
|
||||
"net": t.GetNetname(),
|
||||
})
|
||||
|
||||
pads = []
|
||||
for fp in board.GetFootprints():
|
||||
ref = fp.GetReference()
|
||||
for p in fp.Pads():
|
||||
pads.append({
|
||||
"x": mm(p.GetPosition().x), "y": mm(p.GetPosition().y),
|
||||
"layer": board.GetLayerName(p.GetLayer()),
|
||||
"net": p.GetNetname(), "ref": ref, "pad": p.GetNumber(),
|
||||
"drill": mm(p.GetDrillSizeX()),
|
||||
})
|
||||
|
||||
layers = sorted({t["layer"] for t in tracks})
|
||||
for name, data in (("vias", vias), ("tracks", tracks), ("pads", pads)):
|
||||
(out / f"{name}.json").write_text(json.dumps(data, ensure_ascii=False), encoding="utf-8")
|
||||
(out / "meta.json").write_text(json.dumps({
|
||||
"units": "mm", "y_axis": "down (KiCad native)",
|
||||
"counts": {"vias": len(vias), "tracks": len(tracks), "pads": len(pads)},
|
||||
"copper_layers_with_tracks": layers,
|
||||
}, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
|
||||
print(f"[dump] vias={len(vias)} tracks={len(tracks)} pads={len(pads)} -> {out}")
|
||||
print(f"[layers] 有走线的铜层:{', '.join(layers)}")
|
||||
print("[next] Altium 侧用 eda-agent 导同名 JSON 到 altium_vec/(格式见 "
|
||||
"references\\vector-compare.md),然后跑 cmp_vec.py。")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
except Exception:
|
||||
pass
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user