文件

147 行
5.9 KiB
Python

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# -*- 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'))