R2:底板 / 刀片布局布线脚本、快照、截图与规划文档更新(含外壳、打印件、布线归档)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
zhou yang
2026-10-04 19:24:45 +08:00
共同撰写人 Claude Opus 5.5
父节点 cd01640c40
当前提交 cf7cc9cf90
共修改 759 个文件,包含 76962 行新增和 5299 行删除
+133
查看文件
@@ -0,0 +1,133 @@
# -*- coding: utf-8 -*-
u"""BLADE-PCB_1 主板南移 2.5 mm(2026-09-14 晚,用户要求「主板南移,进一步缩小」):布线缓存跟着平移。
/c/Python314/python .claude/r2/shift_blade1.py # 读 _blade_routes.hand.json + 缝合孔 → 写 _blade_routes.shift.json / _blade_stitch_pts.json
器件(J1 除外)与 7 个定位孔已在板上整体 dy = −2.5(`$CLAUDE_JOB_DIR` 里的 shiftcomp.js,结果见 PROGRESS)。
这里把铜按同一规则平移,**不做任何搜索**:
- **指区固定**:y ≤ FZ(11.76 = 金手指 launch 行 11.75)的点原地不动(指的引出脚 / 地母线 / launch 孔都在这里)。
- 其余点 dy = −2.5;线段一端固定一端平移(跨接缝)时:竖直的保留(只是变短),**斜的删掉**交给服务器补线。
- 平移后压进指区(任一点 y < RIP_Y)的非固定铜整网摘掉那几段,交给服务器 `MODE=repair`。
- `USB_DP` / `USB_DM` 的铜全部换成 `router.dp_blade()` 按新焊盘重新构造(纯几何)。
- 顺手修两处 DRC:B32 地指的母线段 (52,7)-(53,7) + 孔 (53,7) 跑到舌头外 ⇒ 删,改从 B32 顶层直接北上进顶层地铜;
SDA 在 (15.6, 25.832) 板上重复下发了两个孔(缓存里只有一个,重推整板时自然消失)。
"""
import io
import json
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)
import router as RT # noqa: E402
FZ = 11.76
DY = -2.5
RIP_Y = 12.4
SRC = os.path.join(HERE, '_blade_routes.hand.json')
OUT = os.path.join(HERE, '_blade_routes.shift.json')
STITCH = os.path.join(HERE, '_blade_stitch_pts.json')
def near(a, b, t=2e-3):
return abs(a - b) < t
def main():
d = json.load(io.open(SRC, encoding='utf-8'))
segs, vias = [list(s) for s in d['segs']], [list(v) for v in d['vias']]
# B32 地指(舌头外的母线段 + 孔)
segs = [s for s in segs if not (s[6] == 'GND' and s[0] == 1 and
{(s[1], s[2]), (s[3], s[4])} == {(52.0, 7.0), (53.0, 7.0)})]
vias = [v for v in vias if not (v[2] == 'GND' and near(v[0], 53.0) and near(v[1], 7.0))]
rip = set()
out_s = []
for s in segs:
f0, f1 = s[2] <= FZ, s[4] <= FZ
if f0 and f1:
out_s.append(s)
continue
y0 = s[2] if f0 else s[2] + DY
y1 = s[4] if f1 else s[4] + DY
n = [s[0], s[1], y0, s[3], y1, s[5], s[6]]
if f0 != f1 and not near(s[1], s[3]): # 跨接缝的斜线
rip.add(s[6])
continue
if f0 == f1 and min(y0, y1) < RIP_Y: # 整段平移后压进指区
rip.add(s[6])
continue
if f0 != f1 and max(y0, y1) < RIP_Y + 0.6 and s[6] not in ('+5V', '+12V'):
# 竖直但变得太短、另一头落在指区附近:多半要和别的 launch 抢地方,也交给补线
pass
out_s.append(n)
out_v = []
for v in vias:
if v[1] <= FZ:
out_v.append(v)
continue
nv = [v[0], v[1] + DY, v[2]]
if nv[1] < RIP_Y:
rip.add(v[2])
continue
out_v.append(nv)
# 被摘的网:只删「非固定、且 y < RIP_Y + 1.0」的那些铜,别的保留(repair 会从剩下的铜上接)
# —— 上面已经跳过了真正压进指区的段;这里什么都不用再删
# B32 顶层北上进地铜(顶层地铜下沿 TOP_POUR_Y 11.5)
out_s.append([1, 52.0, 7.0, 52.0, 12.3, 0.2, 'GND'])
# D± 重新构造
pads = json.load(io.open(os.path.join(HERE, '_blade_pads.json'), encoding='utf-8'))
dps, dpv = RT.dp_blade(pads)
out_s = [s for s in out_s if s[6] not in ('USB_DP', 'USB_DM', 'USB_DPJ', 'USB_DMJ')] + [list(s) for s in dps]
out_v = [v for v in out_v if v[2] not in ('USB_DP', 'USB_DM')] + [list(v) for v in dpv]
rip -= {'USB_DP', 'USB_DM', 'USB_DPJ', 'USB_DMJ'}
# 新构造的 D± 与平移过来的别网铜冲突的段 / 孔摘掉(旧 D± 斜段整体平移会压进指区,只能重构;别的网原本绕的是旧斜段)
import hand_blade1 as H
dp = [s for s in out_s if s[6] in ('USB_DP', 'USB_DM')]
dpvia = [v for v in out_v if v[2] in ('USB_DP', 'USB_DM')]
CL = 0.16
def hit_seg(s):
for q in dp:
if q[0] == s[0] and H._seg_seg(q, s) < (q[5] + s[5]) / 2.0 + CL:
return True
for v in dpvia:
if H._pt_seg(v[0], v[1], s[1], s[2], s[3], s[4]) < 0.3 + s[5] / 2.0 + CL:
return True
return False
def hit_via(v):
for q in dp:
if H._pt_seg(v[0], v[1], q[1], q[2], q[3], q[4]) < 0.3 + q[5] / 2.0 + CL:
return True
return False
keep_s, keep_v = [], []
for s in out_s:
if s[6] not in ('USB_DP', 'USB_DM') and not (s[2] <= FZ and s[4] <= FZ) and hit_seg(s):
rip.add(s[6])
continue
keep_s.append(s)
for v in out_v:
if v[2] not in ('USB_DP', 'USB_DM') and v[1] > FZ and hit_via(v):
rip.add(v[2])
continue
keep_v.append(v)
out_s, out_v = keep_s, keep_v
json.dump({'segs': out_s, 'vias': out_v, 'logs': [], 'rip': sorted(rip)},
io.open(OUT, 'w', encoding='utf-8'), ensure_ascii=False)
pts = json.load(io.open(STITCH, encoding='utf-8'))
npts = [[x, y + DY] for x, y in pts]
json.dump(npts, io.open(STITCH, 'w', encoding='utf-8'))
print(u'段 %d → %d / 孔 %d → %d;缝合孔 %d 个已平移' % (len(segs), len(out_s), len(vias), len(out_v), len(npts)))
print(u'要服务器补线的网 %d 个: %s' % (len(rip), ' '.join(sorted(rip))))
if __name__ == '__main__':
main()