文件
phonerail/.claude/r2/opt_legs.py
T

114 行
5.6 KiB
Python
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# -*- coding: utf-8 -*-
u"""按**手定的拓扑**分段 A*:每段 = 一个网从点 A 到点 B(点给坐标 + 层),A* 只管段内细节(2026-09-15)。
DCDC_ROUTE_SERVER=1 pypy3 .claude/r2/opt_legs.py <spec.json>
spec:{"base": "_blade_routes.xxx.json", "out": "_blade_routes.legs%d.json",
"variants": [[[网, [x, y, 层], [x, y, 层]], ...], ...]}
层 1 = L1 顶、2 = L4 底。点通常是已有的同网孔 / 焊盘中心:起止格点取点周围 0.4 mm 内的空格点,
再用一段直线从精确点接到格点(净距交给 router_check 复核)。整个网的铜同网可穿,所以后一段可以接在前一段的铜上。
为什么不用 route_groups:它按「最近焊盘」贪心连树,SCL/SDA 这种金手指 → 上拉电阻 → U3 → U4 的总线会被连成绕到板西边的树。
"""
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)
if not os.environ.get('DCDC_ROUTE_SERVER'):
sys.exit(u'⛔ 搜索只在 10.5.5.13 上跑(CLAUDE.md)')
import router as RT # noqa: E402
W = 0.15
def grid_now(pads, segs, vias, halfw):
board_vias = [(v[0], v[1], v[2], RT.VIA_D) for v in vias]
g = RT.build_grid(pads, halfw, board_vias)
for (l, a, b, c, d, ww, nn) in segs:
RT.block_track(g, l, a, b, c, d, halfw + ww / 2.0, nn,
extra=max(0.0, RT.VIA_D / 2.0 + RT.CLR - halfw), exact_v=True)
for (vx, vy, nn) in vias:
rv = RT.VIA_D / 2.0 + RT.CLR + halfw
ev = max(0.0, RT.VIA_D / 2.0 - halfw)
for l in RT.LAY:
g.block_both(l, vx - rv, vy - rv, vx + rv, vy + rv, ev, net=nn, exact_v=True)
return g
def nodes_near(g, x, y, lay, net, r=0.4, own_r=0.3):
u"""⚠ 孔 / 焊盘自己那一圈格点常被判成「两个网共占」(255:旁边别网孔的膨胀框压进来了),A* 起不了步。
点本身就是本网的铜 ⇒ 0.3 mm 以内的格点强制记成本网(两张图都改),出了这一圈照常判。"""
k_ = g.nid(net)
kk = int(math.ceil(own_r / RT.G))
for di in range(-kk, kk + 1):
for dj in range(-kk, kk + 1):
i, j = RT.gx(x) + di, RT.gy(y) + dj
if math.hypot(RT.wx(i) - x, RT.wy(j) - y) <= own_r + 1e-9 and 0 <= i < RT.NX and 0 <= j < RT.NY:
g.own[lay][g.idx(i, j)] = k_
g.ownv[lay][g.idx(i, j)] = k_
out = []
i0, j0 = RT.gx(x), RT.gy(y)
k = int(math.ceil(r / RT.G))
for di in range(-k, k + 1):
for dj in range(-k, k + 1):
i, j = i0 + di, j0 + dj
if math.hypot(RT.wx(i) - x, RT.wy(j) - y) <= r + 1e-9 and g.free(lay, i, j, net):
out.append((lay, i, j))
return out
def main():
spec = json.load(io.open(sys.argv[1], encoding='utf-8'))
base = json.load(io.open(os.path.join(HERE, spec['base']), encoding='utf-8'))
pads = RT.load_pads()
halfw = W / 2.0 + RT.CLR
for k, legs in enumerate(spec['variants']):
segs = [tuple(s) for s in base['segs']]
vias = [tuple(v) for v in base['vias']]
L, bad = {}, []
for net, a, b in legs:
g = grid_now(pads, segs, vias, halfw)
st = nodes_near(g, a[0], a[1], a[2], net)
gl = nodes_near(g, b[0], b[1], b[2], net)
path = RT.astar(g, st, gl, net, halfw) if st and gl else None
if not path:
inv = {v: kk for kk, v in g._id.items()}
def who(p):
return sorted(set(str(inv.get(g.own[p[2]][g.idx(RT.gx(p[0]) + di, RT.gy(p[1]) + dj)], '·'))
for di in (-1, 0, 1) for dj in (-1, 0, 1)))
bad.append(u'%s %s→%s(起点 %d %s / 终点 %d %s 个格点)' % (net, a, b, len(st), who(a), len(gl), who(b)))
if spec.get('debug'):
kn = g.nid(net)
for lay in RT.LAY:
print(u'--- %s L%d 周围(.=空 o=本网 #=别网,行=y 从大到小)' % (net, lay))
for j in range(RT.gy(a[1]) + 14, RT.gy(a[1]) - 8, -1):
row = ''
for i in range(RT.gx(a[0]) - 16, RT.gx(a[0]) + 16):
v = g.own[lay][g.idx(i, j)]
vv = g.ownv[lay][g.idx(i, j)]
row += '.' if v == 0 else ('o' if v == kn else '#')
row += ',' if vv == 0 else ('o' if vv == kn else '#')
print('%6.2f %s' % (RT.wy(j), row))
continue
sg, vv = RT.to_segments(path)
new = [(l, x0, y0, x1, y1, W, net) for (l, x0, y0, x1, y1) in sg]
p0, p1 = path[0], path[-1]
for (pt, nd) in ((a, p0), (b, p1)):
if math.hypot(RT.wx(nd[1]) - pt[0], RT.wy(nd[2]) - pt[1]) > 1e-6:
new.append((nd[0], pt[0], pt[1], RT.wx(nd[1]), RT.wy(nd[2]), W, net))
segs += new
vias += [(x, y, net) for (x, y) in vv]
L[net] = L.get(net, 0.0) + sum(math.hypot(s[3] - s[1], s[4] - s[2]) for s in new)
print(u'[%d] %s 未通 %s' % (k, ' '.join('%s=%.1f' % kv for kv in sorted(L.items())), bad))
json.dump({'segs': [list(s) for s in segs], 'vias': [list(v) for v in vias], 'logs': bad},
io.open(os.path.join(HERE, spec['out'] % k), 'w', encoding='utf-8'), ensure_ascii=False)
if __name__ == '__main__':
main()