# -*- coding: utf-8 -*- u"""按坐标**拆掉窗口内待布网的旧铜**(下发新布线之前的第一步)。 /c/Python314/python .claude/r2/fan_rip.py # 只列清单,不动板 /c/Python314/python .claude/r2/fan_rip.py --push # 真删 ⛔ 2026-09-12 实测:**删外层段会连带清掉过孔和它在内层的延续** —— 点名删 6 段,读回发现线少 12 段、 孔少 8 个,多掉的全在 L15/L16。⇒ 拆前后都要比对**全板**线 / 孔总数,不能只数自己点名的那几段。 这里每次都先读回全板、删完再读回,逐项打印差值。 判据与 `repair_window` 同口径:`_base_window.json` 的窗口 + 网名,**两个端点都在窗口内**才算窗口内的铜 (跨窗边界的段是主干接口,必须留着)。 """ import io import json import math import os import sys sys.stdout.reconfigure(encoding='utf-8') HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, os.path.dirname(HERE)) sys.path.insert(0, HERE) from eda import run # noqa: E402 import pcb_base as B # noqa: E402 MIL = 0.0254 CFG = json.load(io.open(os.path.join(HERE, '_base_window.json'), encoding='utf-8')) WIN = CFG['window'] NETS = set(n.replace('@', '$') for n in CFG['nets']) OPEN = ("await eda.dmt_EditorControl.openDocument('%s');" "await new Promise(r=>setTimeout(r,2500));" % B.PCB) READ = OPEN + """ const L=(await eda.pcb_PrimitiveLine.getAll()||[]).filter(l=>[1,2,15,16].includes(l.layer)) .map(l=>[l.primitiveId,l.net,l.layer,l.startX,l.startY,l.endX,l.endY,l.lineWidth]); const V=(await eda.pcb_PrimitiveVia.getAll()||[]).map(v=>[v.primitiveId,v.net,v.x,v.y]); return {L, V}; """ def inw(x, y): return WIN[0] <= x <= WIN[2] and WIN[1] <= y <= WIN[3] def read(): r = run(READ, timeout=300) assert r.get('success'), json.dumps(r, ensure_ascii=False)[:400] return r['result'] def main(): d = read() L, V = d['L'], d['V'] outer = [l for l in L if l[2] in (1, 2)] inner = [l for l in L if l[2] in (15, 16)] print(u'板上:外层 %d 段 / 内层 %d 段 / 过孔 %d' % (len(outer), len(inner), len(V))) kill = [] for pid, net, lay, x1, y1, x2, y2, w in outer: if net not in NETS: continue if inw(x1 * MIL, y1 * MIL) and inw(x2 * MIL, y2 * MIL): kill.append((pid, net, lay, x1 * MIL, y1 * MIL, x2 * MIL, y2 * MIL)) killv = [(v[0], v[1]) for v in V if v[1] in NETS and inw(v[2] * MIL, v[3] * MIL)] print(u'要拆:%d 段 / %d 孔' % (len(kill), len(killv))) by = {} for k in kill: by[k[1]] = by.get(k[1], 0) + 1 for n in sorted(by): print(u' %-18s %d 段' % (n, by[n])) io.open(os.path.join(HERE, '_base_fan_ripped.json'), 'w', encoding='utf-8').write( json.dumps({'segs': [[k[2], k[3], k[4], k[5], k[6], 0, k[1]] for k in kill], 'vias': []}, ensure_ascii=False)) if '--push' not in sys.argv: print(u'(只列清单;加 --push 才真删)') return ids = [k[0] for k in kill] + [v[0] for v in killv] r = run(OPEN + """ const ids = %s; let n = 0; for (const id of ids) { try { await eda.pcb_PrimitiveLine.delete(id); n++; } catch (e) { try { await eda.pcb_PrimitiveVia.delete(id); n++; } catch (e2) {} } } await eda.pcb_Document.save(); return n; """ % json.dumps(ids), timeout=600) print(u'删除返回:%s' % json.dumps(r.get('result'), ensure_ascii=False)) d2 = read() o2 = [l for l in d2['L'] if l[2] in (1, 2)] i2 = [l for l in d2['L'] if l[2] in (15, 16)] print(u'拆后:外层 %d 段(−%d,点名 %d)/ 内层 %d 段(−%d)/ 过孔 %d(−%d)' % (len(o2), len(outer) - len(o2), len(kill), len(i2), len(inner) - len(i2), len(d2['V']), len(V) - len(d2['V']))) if len(outer) - len(o2) != len(kill) or len(inner) != len(i2): print(u'⛔ 连带删除!实际掉的和点名的对不上,检查内层 / 过孔') if __name__ == '__main__': main()