# -*- coding: utf-8 -*- u"""D16:把一个网里孤立的小块接到本网**已有的铜**上(不必落在某只焊盘上)—— 服务器跑。 DCDC_ROUTE_SERVER=1 /root/pypy/bin/pypy3 .claude/r2/d16_tap.py NET [--extra 结果文件 ...] [--w 0.2] route_nets 每块只挑一只焊盘当代表,大块挑中的那只脚出不来就整网失败(2026-09-12 SDA:U13.4 出不来)。 这里小块照旧用焊盘;大块改用本网已有线段上每 0.5 mm 取的点当虚拟焊盘,按离小块由近到远逐个试,接上即停。 --extra:本机还没下发的结果(如刚布好的 SCL)一并当障碍。写 _d16_tap.out.json。 """ import io import json import math import os import sys if os.environ.get('DCDC_ROUTE_SERVER') != '1': sys.exit(u'!! 布线运算只在服务器上跑(DCDC_ROUTE_SERVER=1),见 CLAUDE.md') if '--grid' not in sys.argv: sys.argv += ['--grid', '0.125'] HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, os.path.dirname(HERE)) sys.path.insert(0, HERE) import route_nets as N # noqa: E402 R = N.R TRIES = 30 def main(): skip = {k + 1 for k, a in enumerate(sys.argv) if a in ('--extra', '--w', '--grid')} net = [a for k, a in enumerate(sys.argv) if k >= 1 and k not in skip and not a.startswith('--')][0] w = float(sys.argv[sys.argv.index('--w') + 1]) if '--w' in sys.argv else 0.2 b = json.load(io.open(os.path.join(HERE, '_board_for_nets.json'), encoding='utf-8')) segs = [tuple(s[:7]) for s in b['segs']] vias = [tuple(v[:3]) for v in b['vias']] for k, a in enumerate(sys.argv): if a == '--extra': e = json.load(io.open(os.path.join(HERE, sys.argv[k + 1]), encoding='utf-8')) segs += [tuple(s[:7]) for s in e['segs']] vias += [tuple(v[:3]) for v in e['vias']] pads = [p for p in R.load_pads() if not str(p[1]).endswith(R.TH_SUFFIX)] groups = sorted(N.components(net, pads, segs, vias), key=len) print(u'%s:%d 块,小块 %s' % (net, len(groups), ['%s.%s' % (q[0], q[1]) for q in groups[0]])) if len(groups) < 2: return sp = groups[0][0] cand = [] for s in segs: if s[6] != net or s[0] not in (1, 2): continue n = max(1, int(math.hypot(s[3] - s[1], s[4] - s[2]) / 0.5)) for k in range(n + 1): x, y = s[1] + (s[3] - s[1]) * k / n, s[2] + (s[4] - s[2]) * k / n cand.append((math.hypot(x - sp[2], y - sp[3]), s[0], round(x, 3), round(y, 3), s[5])) # 本网的过孔也当落点(两个外层各一块 0.6 焊盘):大块有的地方只靠内层线连着(EasyEDA 自动布线留在 L2/L3 的 SDA), # 外层上一根本网的线都没有,只有孔 —— 2026-09-12 SDA 第一版只取外层线段上的点,候选全在 45 mm 外、全不通 for v in vias: if v[2] == net: for l in (1, 2): cand.append((math.hypot(v[0] - sp[2], v[1] - sp[3]), l, round(v[0], 3), round(v[1], 3), 0.6)) cand.sort() picked = [] for c in cand: if all(math.hypot(c[2] - p[2], c[3] - p[3]) > 1.0 or c[1] != p[1] for p in picked): picked.append(c) if len(picked) >= TRIES: break out = {'net': net, 'segs': [], 'vias': [], 'tries': [], 'logs': []} for d, l, x, y, ww in picked: vp = ['#tap', '1', x, y, max(ww, 0.2), max(ww, 0.2), l, net, 'RECT'] N.INNER[:] = [s for s in segs if s[0] in (15, 16)] obst = [s for s in segs if s[0] in (1, 2)] lg = [] s2, v2 = R.route_one(net, [sp, vp], w, pads + [vp], obst, vias, [], lg) after = N.components(net, pads, segs + [tuple(q) for q in s2], vias + [tuple(q) for q in v2]) ok = len(after) < len(groups) out['tries'].append([round(d, 2), l, x, y, ok, len(s2), len(v2)]) print(u' 试 L%s (%.2f,%.2f) 距 %.1f:%s' % (l, x, y, d, u'接上(%d 段 / %d 孔)' % (len(s2), len(v2)) if ok else u'不通')) if ok: out['segs'] = [list(q) for q in s2] out['vias'] = [list(q) for q in v2] out['logs'] = lg break io.open(os.path.join(HERE, '_d16_tap.out.json'), 'w', encoding='utf-8').write(json.dumps(out, ensure_ascii=False)) if __name__ == '__main__': main()