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

120 行
6.0 KiB
Python
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# -*- coding: utf-8 -*-
u"""服务器上挪几颗「戳在内层线上」的过孔(2026-09-11 混合布线收尾):路径不重布,只把孔挪到附近合法的点,
原来接在孔上的线段改从它们各自的远端接到新孔(线宽不变)。
DCDC_ROUTE_SERVER=1 /root/pypy/bin/pypy3 .claude/r2/via_nudge.py NET@x,y [NET@x,y ...]
读 _board_for_nets.json,写 _via_nudge.out.json
候选点:原孔周围 1.5 mm、0.0625 网格。条件:新孔离四层所有异网线 / 孔 / 焊盘 ≥ CLR;改接的每段线在它那一层离异网铜 ≥ CLR;
新孔也不许落进任何贴片焊盘(同网也不行)。取改接线总长最短。
"""
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')
HERE = os.path.dirname(os.path.abspath(__file__))
CLR = 0.16 # 规则 0.15,留 0.01
VR = 0.305 # 0.61 孔
L = lambda s: math.hypot(s[3] - s[1], s[4] - s[2]) # noqa: E731
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))
def rd(q, x, y):
return math.hypot(max(q[2] - q[4] / 2 - x, x - q[2] - q[4] / 2, 0), max(q[3] - q[5] / 2 - y, y - q[3] - q[5] / 2, 0))
def main():
b = json.load(io.open(os.path.join(HERE, '_board_for_nets.json'), encoding='utf-8'))
pads = [p for p in json.load(io.open(os.path.join(HERE, '_base_pads.json'), encoding='utf-8')) if p[4] > 0.05]
segs = [list(s[:7]) for s in b['segs']]
vias = [list(v[:3]) for v in b['vias']]
out = []
for spec in [a for a in sys.argv[1:] if '@' in a]:
net, xy = spec.split('@')
ox, oy = (float(t) for t in xy.split(','))
old_v = [v for v in vias if v[2] == net and math.hypot(v[0] - ox, v[1] - oy) < 0.05]
att = [s for s in segs if s[6] == net and (math.hypot(s[1] - ox, s[2] - oy) < 0.05 or math.hypot(s[3] - ox, s[4] - oy) < 0.05)]
if not old_v:
print(u'%s:找不到这颗孔' % spec)
continue
far = []
for s in att:
fx, fy = (s[3], s[4]) if math.hypot(s[1] - ox, s[2] - oy) < 0.05 else (s[1], s[2])
far.append((s[0], fx, fy, s[5]))
rest_s = [s for s in segs if not any(s is a for a in att)]
rest_v = [v for v in vias if not any(v is o for o in old_v)]
# 附近的障碍先筛一遍
box = (ox - 5, oy - 5, ox + 5, oy + 5)
nb_s = [s for s in rest_s if s[6] != net and not (max(s[1], s[3]) < box[0] or min(s[1], s[3]) > box[2]
or max(s[2], s[4]) < box[1] or min(s[2], s[4]) > box[3])]
nb_v = [v for v in rest_v if box[0] <= v[0] <= box[2] and box[1] <= v[1] <= box[3]]
nb_p = [q for q in pads if box[0] - 2 <= q[2] <= box[2] + 2 and box[1] - 2 <= q[3] <= box[3] + 2]
def via_ok(x, y):
if any(pd(x, y, s) - VR - s[5] / 2 < CLR for s in nb_s):
return False
if any(math.hypot(v[0] - x, v[1] - y) < 2 * VR + CLR for v in nb_v if v[2] != net):
return False
if any(math.hypot(v[0] - x, v[1] - y) < 2 * VR + 0.3 for v in nb_v if v[2] == net):
return False # 同网孔也别叠(钻孔间距)
for q in nb_p:
if q[6] in (1, 2) and rd(q, x, y) < VR + (0.0 if q[7] == net else CLR) + (0.05 if q[7] == net else 0):
return False # 贴片焊盘里不落孔(同网也不行)
if q[6] == 12 and q[7] != net and rd(q, x, y) < VR + CLR:
return False
return True
def seg_ok(lay, a, c, w):
n = max(2, int(math.hypot(c[0] - a[0], c[1] - a[1]) / 0.03))
pts = [(a[0] + (c[0] - a[0]) * k / n, a[1] + (c[1] - a[1]) * k / n) for k in range(n + 1)]
for s in nb_s:
if s[0] == lay and min(pd(x, y, s) for x, y in pts) - w / 2 - s[5] / 2 < CLR:
return False
for v in nb_v:
if v[2] != net and min(math.hypot(v[0] - x, v[1] - y) for x, y in pts) - w / 2 - VR < CLR:
return False
for q in nb_p:
if q[7] != net and q[6] in (lay, 12) and min(rd(q, x, y) for x, y in pts) - w / 2 < CLR:
return False
return True
best = None
for i in range(-24, 25):
for j in range(-24, 25):
x, y = round(ox + i * 0.0625, 4), round(oy + j * 0.0625, 4)
if math.hypot(x - ox, y - oy) > 1.5 or not via_ok(x, y):
continue
if not all(seg_ok(lay, (fx, fy), (x, y), w) for lay, fx, fy, w in far):
continue
cost = sum(math.hypot(x - fx, y - fy) for _l, fx, fy, _w in far)
if best is None or cost < best[0]:
best = (cost, x, y)
if best is None:
print(u'%s:1.5 mm 内找不到合法的新孔位' % spec)
out.append({'spec': spec, 'ok': False})
continue
_c, nx, ny = best
new_s = [[lay, fx, fy, nx, ny, w, net] for lay, fx, fy, w in far]
old_len = sum(L(s) for s in att)
print(u'%s:→ (%.4f, %.4f),挪 %.2f mm;改接 %d 段(%.2f → %.2f mm)'
% (spec, nx, ny, math.hypot(nx - ox, ny - oy), len(new_s), old_len, sum(L(s) for s in new_s)))
out.append({'spec': spec, 'ok': True, 'old_via': old_v[0], 'old_segs': att, 'new_via': [nx, ny, net], 'new_segs': new_s})
# 下一颗要看到这颗的新位置
segs = rest_s + new_s
vias = rest_v + [[nx, ny, net]]
io.open(os.path.join(HERE, '_via_nudge.out.json'), 'w', encoding='utf-8').write(json.dumps(out, ensure_ascii=False))
if __name__ == '__main__':
main()