# -*- coding: utf-8 -*- u"""按**实际铜**算某几个网络的连通性(不看 EDA 的 DRC,自己算)。 /c/Python314/python .claude/pcb2_net_conn.py [网络名 ...] # 省略 = 22 个差分网络 DRC 面板的「连接性错误」是按对象列的、还分页,查起来费劲; 这个脚本直接把一个网络的**焊盘 / 过孔 / 导线**当图的节点, 两两相触就连边,最后数连通域 —— 1 个就是通的,>1 就指出每个碎片里有什么。 判据(都按铜的实际外形,不留净距): 线-线:段距 <= (w1+w2)/2 且同层 线-焊盘:段到矩形 <= w/2(焊盘 layer 12 = 两个信号层都算) 线-过孔:点到段 <= w/2 + r(过孔穿所有层) """ import json import math import os import sys HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HERE) from pcb2_route_dp import geom, seg_pt, seg_seg, seg_rect NETS = (['USB%d_%s' % (n, s) for n in range(1, 6) for s in ('DP', 'DM', 'DPJ', 'DMJ')] + ['UP_DP', 'UP_DM']) EPS = 1e-6 def layers_of(lay): return (1, 2) if lay == 12 else (lay,) def main(): want = [a for a in sys.argv[1:] if not a.startswith('--')] or NETS pads, vias, lines = geom() bad = 0 for net in want: items = [] for p in pads: if p['net'] == net: items.append(('pad', set(layers_of(p['lay'])), p)) for v in vias: if v['net'] == net: items.append(('via', set((1, 2)), v)) for l in lines: if l['net'] == net: items.append(('line', set((l['lay'],)), l)) n = len(items) par = list(range(n)) def find(i): while par[i] != i: par[i] = par[par[i]] i = par[i] return i def touch(a, b): (ka, la, oa), (kb, lb, ob) = a, b if not (la & lb): return False if ka == 'line' and kb == 'line': return seg_seg((oa['x0'], oa['y0']), (oa['x1'], oa['y1']), (ob['x0'], ob['y0']), (ob['x1'], ob['y1'])) <= (oa['w'] + ob['w']) / 2 + EPS if ka == 'line' and kb == 'pad': return seg_rect((oa['x0'], oa['y0']), (oa['x1'], oa['y1']), ob['x'], ob['y'], ob['hw'], ob['hh']) <= oa['w'] / 2 + EPS if ka == 'line' and kb == 'via': return seg_pt(oa['x0'], oa['y0'], oa['x1'], oa['y1'], ob['x'], ob['y']) <= oa['w'] / 2 + ob['r'] + EPS if ka == 'via' and kb == 'via': return math.hypot(oa['x'] - ob['x'], oa['y'] - ob['y']) <= oa['r'] + ob['r'] + EPS if ka == 'via' and kb == 'pad': return seg_rect((oa['x'], oa['y']), (oa['x'], oa['y']), ob['x'], ob['y'], ob['hw'], ob['hh']) <= oa['r'] + EPS if ka == 'pad' and kb == 'pad': return (abs(oa['x'] - ob['x']) <= oa['hw'] + ob['hw'] + EPS and abs(oa['y'] - ob['y']) <= oa['hh'] + ob['hh'] + EPS) return touch(b, a) for i in range(n): for j in range(i + 1, n): if touch(items[i], items[j]): a, b = find(i), find(j) if a != b: par[a] = b groups = {} for i in range(n): groups.setdefault(find(i), []).append(i) npads = sum(1 for k, _, _ in items if k == 'pad') ok = len(groups) == 1 and npads >= 2 bad += 0 if ok else 1 print(u' %-11s %2d 焊盘 / %2d 过孔 / %3d 线 -> %d 个连通域 %s' % (net, npads, sum(1 for k, _, _ in items if k == 'via'), sum(1 for k, _, _ in items if k == 'line'), len(groups), u'OK' if ok else u'!!')) if not ok: for g in sorted(groups.values(), key=len, reverse=True): desc = [] for i in g: k, _, o = items[i] if k == 'pad': desc.append(u'焊盘 %s.%s' % (o['ref'], o['num'])) elif k == 'via': desc.append(u'过孔(%.2f,%.2f)' % (o['x'], o['y'])) print(u' 碎片 %2d 个对象: %s' % (len(g), u' / '.join(desc) if desc else u'(只有导线)')) print(u'%d / %d 个网络不连通' % (bad, len(want))) return 1 if bad else 0 if __name__ == '__main__': sys.exit(main())