# -*- coding: utf-8 -*- u"""按**连线闭包**整块删掉一页上的一个子电路(元件 + 与之相连的导线 / 旗 / 端口)。 /c/Python314/python .claude/sch_cut.py Q103 Q104 ... # 干跑:打印闭包 /c/Python314/python .claude/sch_cut.py Q103 Q104 ... --push # 真删并存盘 判据(skill §5 / §10):导线按「端点对」解析;一段导线和一个引脚相连 = 引脚落在该段的 端点或中段;两个导线图元相连 = 一方的某个端点落在另一方的某一段上。 从待删元件的全部引脚出发做闭包,闭包里的导线段上如果站着**不在删除清单里的元件引脚**, 直接终止 —— 说明这块电路和别的电路是连着的,不能整块删。 旗 / 端口的引脚只要落在闭包导线的任意一段上就一起删(它们本来就只是这段线的名字)。 2026-09-06 为「74HC595 那一窝挪到载板」写的:P2×5 的按键 MOS 块、P3B 的 U21/U22、P6 的 U33。 """ import json import os import sys HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HERE) from eda import run # noqa: E402 JS_READ = u""" await eda.dmt_EditorControl.openDocument('%s'); await new Promise(r => setTimeout(r, 1200)); const cur = (await eda.dmt_Schematic.getCurrentSchematicPageInfo()).name; const parts = [], flags = []; for (const c of await eda.sch_PrimitiveComponent.getAll()) { const t = c.getState_ComponentType(); if (t === 'part') { const ps = await c.getAllPins(); parts.push([c.getState_PrimitiveId(), c.getState_Designator(), ps.map(p => [p.getState_PinNumber(), Math.round(p.getState_X()), Math.round(p.getState_Y())])]); } else if (t === 'netport' || t === 'netflag') { flags.push([c.getState_PrimitiveId(), t, c.getState_Net(), Math.round(c.getState_X()), Math.round(c.getState_Y())]); } } const wires = (await eda.sch_PrimitiveWire.getAll()).map(w => [w.getState_PrimitiveId(), w.getState_Line().map(Math.round)]); return {cur, parts, flags, wires}; """ JS_DEL = u""" await eda.dmt_EditorControl.openDocument('%s'); await new Promise(r => setTimeout(r, 1200)); const C = %s, W = %s; if (C.length) await eda.sch_PrimitiveComponent.delete(C); if (W.length) await eda.sch_PrimitiveWire.delete(W); await eda.sch_Document.save(); await new Promise(r => setTimeout(r, 800)); return {c: C.length, w: W.length}; """ def on_seg(p, a, b): u"""点 p 是否落在线段 ab 上(横平竖直,含端点)。""" (x, y), (x1, y1), (x2, y2) = p, a, b if x1 == x2 == x: return min(y1, y2) <= y <= max(y1, y2) if y1 == y2 == y: return min(x1, x2) <= x <= max(x1, x2) return False def segs_of(line): pts = list(zip(line[0::2], line[1::2])) return [(pts[i], pts[i + 1]) for i in range(0, len(pts) - 1, 2)] def read(page): r = run(JS_READ % page, timeout=300) assert r.get('success'), json.dumps(r, ensure_ascii=False)[:500] return r['result'] def closure(st, victims): parts = {d: (pid, pins) for pid, d, pins in st['parts']} missing = [d for d in victims if d not in parts] if missing: raise SystemExit(u'页 %s 上找不到元件: %s' % (st['cur'], missing)) seed = [(x, y) for d in victims for _n, x, y in parts[d][1]] wires = {pid: segs_of(line) for pid, line in st['wires']} keep = set() frontier = list(seed) while frontier: pts, frontier = frontier, [] for pid, segs in wires.items(): if pid in keep: continue if any(on_seg(p, a, b) for p in pts for a, b in segs): keep.add(pid) frontier += [q for a, b in segs for q in (a, b)] segs_all = [s for pid in keep for s in wires[pid]] # 闭包导线上不许站着别的元件的引脚 foreign = [] for d, (pid, pins) in parts.items(): if d in victims: continue for n, x, y in pins: if any(on_seg((x, y), a, b) for a, b in segs_all): foreign.append(u'%s.%s@(%d,%d)' % (d, n, x, y)) # 落在闭包导线上的旗 / 端口 fl = [f for f in st['flags'] if any(on_seg((f[3], f[4]), a, b) for a, b in segs_all)] return {'wires': sorted(keep), 'flags': fl, 'foreign': foreign, 'parts': [parts[d][0] for d in victims], 'nseg': len(segs_all)} def main(): args = [a for a in sys.argv[1:] if not a.startswith('--')] page, victims = args[0], args[1:] push = '--push' in sys.argv st = read(page) c = closure(st, victims) print(u'页 %s:删元件 %d / 导线图元 %d(%d 段)/ 旗端口 %d' % (st['cur'], len(c['parts']), len(c['wires']), c['nseg'], len(c['flags']))) for f in c['flags']: print(u' %-8s %-12s (%d,%d)' % (f[1], f[2], f[3], f[4])) if c['foreign']: raise SystemExit(u'⛔ 闭包碰到了别的元件的引脚,不能整块删:%s' % c['foreign']) if not push: print(u'(未加 --push,没有改动)') return ids = c['parts'] + [f[0] for f in c['flags']] r = run(JS_DEL % (page, json.dumps(ids), json.dumps(c['wires'])), timeout=300) assert r.get('success'), json.dumps(r, ensure_ascii=False)[:500] print(u' 已删:元件+旗端口 %d,导线 %d,已存盘' % (r['result']['c'], r['result']['w'])) if __name__ == '__main__': main()