# -*- coding: utf-8 -*- # 两版底板布线缓存对比 + 离线预判 DRC(线到异网孔 < 0.15、孔到异网孔 < 0.75)。 # /c/Python314/python .claude/r2/route_compare.py [新缓存] [旧缓存] # 2026-09-11:拿第 17 轮验过,与 EasyEDA DRC 的「导线到过孔 / 过孔到过孔」一条不差。比较版本看信号网数 + 预判 DRC,别看总条数。 import json, math, collections, sys R = 'D:/DCDC/.claude/r2/' new_f = sys.argv[1] if len(sys.argv) > 1 else R + '_base_routes.json' old_f = sys.argv[2] if len(sys.argv) > 2 else R + '_base_routes.r17-144.json' load = lambda f: json.load(open(f, encoding='utf-8')) new, old = load(new_f), load(old_f) def by_net(d): c = collections.defaultdict(list) for m in d['logs']: s = str(m) if s.startswith('[连通性]'): continue n, _, r = s.partition(':') c[n.strip()].append(r.strip()) return c N, O = by_net(new), by_net(old) sig = lambda c: {k for k in c if k not in ('GND', '+3V3')} for tag, d, c in (('new', new, N), ('old', old, O)): print('%s total %d GND %d +3V3 %d signal nets %d' % (tag, len(d['logs']), len(c.get('GND', [])), len(c.get('+3V3', [])), len(sig(c)))) print('新坏:', ' '.join(sorted(sig(N) - sig(O)))) print('新好:', ' '.join(sorted(sig(O) - sig(N)))) print('--- new signal detail') for k in sorted(sig(N)): print(' %-12s %d | %s' % (k, len(N[k]), ' ; '.join(N[k][:2]))) def segpt(s, px, py): x1, y1, x2, y2 = s[1], s[2], s[3], s[4] dx, dy = x2 - x1, y2 - y1 L2 = dx * dx + dy * dy t = 0 if L2 == 0 else max(0, min(1, ((px - x1) * dx + (py - y1) * dy) / L2)) return math.hypot(px - (x1 + t * dx), py - (y1 + t * dy)) lv, vv = [], [] V = new['vias'] for s in new['segs']: for v in V: if v[2] == s[6] or abs(v[0] - (s[1] + s[3]) / 2) > abs(s[1] - s[3]) / 2 + 1.5 or \ abs(v[1] - (s[2] + s[4]) / 2) > abs(s[2] - s[4]) / 2 + 1.5: continue g = segpt(s, v[0], v[1]) - s[5] / 2 - 0.3 if g < 0.15 - 1e-6: lv.append((round(g, 3), s[6], s[5], v)) for i, a in enumerate(V): for b in V[i + 1:]: if a[2] != b[2] and math.hypot(a[0] - b[0], a[1] - b[1]) < 0.75 - 1e-6: vv.append((round(math.hypot(a[0] - b[0], a[1] - b[1]), 3), a, b)) print('预判 DRC:线到异网孔 < 0.15:%d 处;孔到异网孔 < 0.75:%d 处' % (len(lv), len(vv))) for x in sorted(lv)[:10]: print(' ', x) for x in sorted(vv)[:10]: print(' ', x) # ── 离线铜连通(2026-09-11):布线器自己的 [连通性] 不可靠(穿盘而过的线、端点不在盘心都会误报), # 这里按真实铜的重叠算:线-线 中心线距 < 半宽和;线-孔、线-盘、孔-盘 有重叠即连。平面网(GND / +12V)靠铺铜,不判。 def seg_dist(a, b): def pd(px, py, s): x1, y1, x2, y2 = s[1], s[2], s[3], s[4] dx, dy = x2 - x1, y2 - y1 L2 = dx * dx + dy * dy t = 0 if L2 == 0 else max(0, min(1, ((px - x1) * dx + (py - y1) * dy) / L2)) return math.hypot(px - (x1 + t * dx), py - (y1 + t * dy)) return min(pd(a[1], a[2], b), pd(a[3], a[4], b), pd(b[1], b[2], a), pd(b[3], b[4], a)) def rect_pt(q, px, py): return math.hypot(max(q[2] - q[4] / 2 - px, px - q[2] - q[4] / 2, 0), max(q[3] - q[5] / 2 - py, py - q[3] - q[5] / 2, 0)) def rect_seg(q, s): n = max(2, int(math.hypot(s[3] - s[1], s[4] - s[2]) / 0.05)) return min(rect_pt(q, s[1] + (s[3] - s[1]) * k / n, s[2] + (s[4] - s[2]) * k / n) for k in range(n + 1)) PADS = json.load(open(R + '_base_pads.json', encoding='utf-8')) if new.get('u20_remap'): # U20 引脚重分配的试算缓存:焊盘网名按 route_base 同一张表换,否则那几只脚判错 sys.path.insert(0, R) import route_base as _RB _m = _RB.U20_REMAPS[new['u20_remap']] for _q in PADS: if _q[0] == 'U20' and str(_q[1]) in _m: _q[7] = _m[str(_q[1])] print('(新缓存是 U20 重分配试算 %s:焊盘网名已按表换)' % new['u20_remap']) def components(d, net): ps = [('p', q) for q in PADS if q[7] == net] ss = [('s', s) for s in d['segs'] if s[6] == net] vs = [('v', v) for v in d['vias'] if v[2] == net] items = ps + ss + vs par = list(range(len(items))) def find(i): while par[i] != i: par[i] = par[par[i]] i = par[i] return i def touch(a, b): (ka, x), (kb, y) = a, b if ka == 'p' and kb == 'p': return False if ka == 'v' and kb == 'v': return math.hypot(x[0] - y[0], x[1] - y[1]) < 0.6 if ka == 's' and kb == 's': return x[0] == y[0] and seg_dist(x, y) < (x[5] + y[5]) / 2 if kb == 's': (ka, x), (kb, y) = b, a if ka == 's' and kb == 'v': return seg_dist(x, (0, y[0], y[1], y[0], y[1])) < x[5] / 2 + 0.3 if ka == 's' and kb == 'p': return y[6] in (x[0], 12) and rect_seg(y, x) < x[5] / 2 q, v = (x, y) if ka == 'p' else (y, x) # 孔-盘 return rect_pt(q, v[0], v[1]) < 0.3 for i in range(len(items)): for j in range(i + 1, len(items)): if find(i) != find(j) and touch(items[i], items[j]): par[find(i)] = find(j) groups = collections.defaultdict(list) for i, it in enumerate(items): groups[find(i)].append(it) return [g for g in groups.values()] nets = sorted({q[7] for q in PADS if q[7] and q[7] not in ('GND', '+12V')}) split = [] for net in nets: gs = [g for g in components(new, net) if any(k == 'p' for k, _ in g)] if len(gs) > 1: split.append((net, gs)) print('离线铜连通:%d 个网断开(平面网 GND / +12V 不判):%s' % (len(split), ' '.join(n for n, _ in split))) for net, gs in split: if net == '+3V3': for g in sorted(gs, key=len): print(' +3V3 一块:%s' % ' '.join('%s.%s' % (q[0], q[1]) for k, q in g if k == 'p'))