78 行
3.4 KiB
Python
78 行
3.4 KiB
Python
# -*- coding: utf-8 -*-
|
||
u"""按 `pcb_place.plan()` 的新摆位,把**改摆位之前**板上抓的焊盘表刚体变换成新焊盘表(离线,不连 EDA)。
|
||
|
||
/c/Python314/python .claude/r2/synth_pads.py # 两趟收敛,写 _blade_pads.json + _blade_pad_offsets.json
|
||
|
||
为什么要有:2026-09-11 晚主板转 180°(方案 §35)要先在服务器上试布线、比完再下发;
|
||
而 `net_peers()` / 布线器吃的都是焊盘表。改摆位前的表 + 当时的摆位在
|
||
`archive_blade_prerot_0911/`(`_blade_pads.json` / `old_pos.json`)。
|
||
|
||
变换:新焊盘 = 新原点 + R(Δrot)·(旧焊盘 − 旧原点)。Δrot 只允许 0 / 180(180 的时候顺/逆时针没区别,
|
||
底层「先镜像再转」也不影响);出现 90 / 270 直接报错 —— 那种情况底层的旋转方向要实测,不许猜。
|
||
⚠ **合成的表只用于离线试布线**。摆位真下发之后必须 `pcb_route.py --dump` 重抓,并和这份逐脚对账
|
||
(对不上 = 旋转语义推错了,不许拿合成表下发铜)。
|
||
"""
|
||
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)
|
||
ARC = os.path.join(HERE, 'archive_blade_prerot_0911')
|
||
OLD_PADS = os.path.join(ARC, '_blade_pads.json')
|
||
OLD_POS = os.path.join(ARC, 'old_pos.json')
|
||
OUT = os.path.join(HERE, '_blade_pads.json')
|
||
OFFS = os.path.join(HERE, '_blade_pad_offsets.json')
|
||
|
||
|
||
def synth(new_pos):
|
||
old = json.load(io.open(OLD_PADS, encoding='utf-8'))
|
||
op = json.load(io.open(OLD_POS, encoding='utf-8'))
|
||
out, bad = [], []
|
||
for row in old:
|
||
ref = row[0]
|
||
if ref not in new_pos or ref not in op:
|
||
bad.append(ref)
|
||
continue
|
||
ox, oy, orot = op[ref]
|
||
nx, ny, nrot = new_pos[ref]
|
||
d = (nrot - orot) % 360
|
||
if d % 90:
|
||
raise SystemExit(u'%s 旋转变了 %d°,不是 90 的倍数' % (ref, d))
|
||
dx, dy = row[2] - ox, row[3] - oy
|
||
# 方案 §25.3 实测:底层先 y 镜像、再 rot=θ 按 R(+θ)(**逆时针**)转;顶层同为逆时针。
|
||
# 镜像在旋转之前 ⇒ 改 rot 只是在已有图案上再左乘 R(+Δ),两面同一个式子。
|
||
for _k in range(d // 90):
|
||
dx, dy = -dy, dx
|
||
r = list(row)
|
||
r[2], r[3] = round(nx + dx, 3), round(ny + dy, 3)
|
||
out.append(r)
|
||
return out, sorted(set(bad))
|
||
|
||
|
||
def offsets(pads, pos):
|
||
o = {}
|
||
for r in pads:
|
||
if r[0] in pos:
|
||
o.setdefault(r[0], []).append([round(r[2] - pos[r[0]][0], 4), round(r[3] - pos[r[0]][1], 4)])
|
||
return o
|
||
|
||
|
||
if __name__ == '__main__':
|
||
import importlib
|
||
import pcb_place as PL
|
||
for k in range(2):
|
||
importlib.reload(PL)
|
||
pos, _r, un, _sz = PL.plan()
|
||
pads, bad = synth(pos)
|
||
io.open(OUT, 'w', encoding='utf-8').write(json.dumps(pads, ensure_ascii=False))
|
||
io.open(OFFS, 'w', encoding='utf-8').write(json.dumps(offsets(pads, pos)))
|
||
# ⚠ 每一趟都写:pcb_place.flip_2pin 要知道焊盘表是按哪一版 rot 做的(只在最后写的话第二趟会翻回去)
|
||
json.dump({r: list(v) for r, v in pos.items()},
|
||
io.open(os.path.join(HERE, '_blade_place_synth.json'), 'w', encoding='utf-8'), indent=0)
|
||
print(u'第 %d 趟:%d 焊盘 / %d 件;缺: %s;排不下: %s' % (k + 1, len(pads), len(pos), bad or u'无', un or u'无'))
|