# -*- coding: utf-8 -*- u"""焊接用**器件值打印稿**:底板 + 旧版刀片,1:1,同一张 A4 竖版(PDF + 600 dpi PNG)。 /c/Python314/python .claude/r2/print_value.py # 出 .claude/r2/print/value_1to1.pdf / .png powershell -ExecutionPolicy Bypass -File .claude/r2/print/print_1to1.ps1 -Image .claude/r2/print/value_1to1.png -Printer "EPSON3F4E59 (L4260 Series)" - 两块板都画**背面(器件面)、左右镜像**:和把板子翻过来拿在手里看到的一致,字照常可读。 - 器件值文字的位置 = `silk_value_plan.py` 排好并下发到 EDA 的位置(`_silk_value_plan.json`)。 - 焊盘 / 器件 = `_silk_value_geom.json`(板上读回);红框 = 1 脚(二极管 / 有极性电容 / IC 的方向)。 - 正面只有大件(插槽、插座、电解、侧键焊盘),列在页脚,不单独画。 """ 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, HERE) import matplotlib # noqa: E402 matplotlib.use('Agg') import matplotlib.pyplot as plt # noqa: E402 from matplotlib.patches import Rectangle, Polygon # noqa: E402 from silk_value_plan import pad_box, nums, M # noqa: E402 plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'DejaVu Sans'] plt.rcParams['axes.unicode_minus'] = False A4 = (210.0, 297.0) MM = 1 / 25.4 OUT = os.path.join(HERE, 'print') geom = json.load(io.open(os.path.join(HERE, '_silk_value_geom.json'), encoding='utf-8')) extra = json.load(io.open(os.path.join(HERE, '_silk_value_extra.json'), encoding='utf-8')) plan = json.load(io.open(os.path.join(HERE, '_silk_value_plan.json'), encoding='utf-8')) restore = json.load(io.open(os.path.join(HERE, '_silk_value_restore.json'), encoding='utf-8')) dump = json.load(io.open(os.path.join(HERE, '_silk_value_dump.json'), encoding='utf-8')) NARROW = matplotlib.font_manager.FontProperties(fname=r'C:\Windows\Fonts\ARIALN.TTF') CAP = 0.716 # Arial 大写字高 / em def polar(c): r = c['ref'] if r[0] in 'UQDY' or r.startswith('DZ') or r.startswith('J'): return True fp = c.get('fp') or '' return r.startswith('C') and ('CAP-' in fp or 'CASE' in fp) def draw(fig, name, uuid, x0, y0): g = geom[uuid] outline = [(x * M, y * M) for x, y in nums(g['polys'][0])] BW = max(p[0] for p in outline) BH = max(p[1] for p in outline) ax = fig.add_axes([x0 / A4[0], y0 / A4[1], (BW + 2) / A4[0], (BH + 2) / A4[1]]) ax.set_xlim(BW + 1, -1) # 背面看:左右镜像 ax.set_ylim(-1, BH + 1) ax.set_aspect('equal') ax.axis('off') ax.add_patch(Polygon(outline, closed=True, fill=False, ec='k', lw=0.6)) for f in [f for f in extra[uuid]['F'] if f['layer'] == 12]: pl = json.loads(f['poly'])['polygon'] q = [(x * M, y * M) for x, y in nums(pl)] if len(q) > 2: ax.add_patch(Polygon(q, closed=True, fc='0.8', ec='k', lw=0.3, hatch='///')) for p in g['pads']: # 安装孔等自由焊盘 b = pad_box([p['x'], p['y'], p['rot'], p['layer'], p['pad']]) ax.add_patch(Rectangle(b[:2], b[2] - b[0], b[3] - b[1], fc='0.35', ec='none')) for c in g['comps']: bot = c['layer'] == 2 bs = [] for p in c['pads']: if p['layer'] not in (2, 12): continue b = pad_box([p['x'], p['y'], p['rot'], p['layer'], p['pad']]) bs.append(b) col = '#d9b64a' if bot else '#e6e6e6' ax.add_patch(Rectangle(b[:2], b[2] - b[0], b[3] - b[1], fc=col, ec='none')) if bot and p['n'] == '1' and polar(c) and len(c['pads']) >= 2: ax.add_patch(Rectangle(b[:2], b[2] - b[0], b[3] - b[1], fill=False, ec='#d00000', lw=0.45)) if bot and bs: x1, y1 = min(b[0] for b in bs) - 0.15, min(b[1] for b in bs) - 0.15 x2, y2 = max(b[2] for b in bs) + 0.15, max(b[3] for b in bs) + 0.15 ax.add_patch(Rectangle((x1, y1), x2 - x1, y2 - y1, fill=False, ec='#1a3fa0', lw=0.25)) texts = {s['id']: s['text'] for s in dump[name]['strings']} layer = {s['id']: s['layer'] for s in dump[name]['strings']} for sid, x, y, rot, fs in plan[name]['rows']: if layer[sid] != 4: continue # 正面的写在页脚 fsm = fs * M ax.text(x * M, y * M, texts[sid], fontproperties=NARROW, fontsize=fsm / CAP * 72 / 25.4 * 0.98, ha='center', va='center', rotation=rot, color='k') return BW, BH def ruler(fig, x0, y0, length): ax = fig.add_axes([0, 0, 1, 1], facecolor='none') ax.set_xlim(0, A4[0]) ax.set_ylim(0, A4[1]) ax.axis('off') ax.plot([x0, x0 + length], [y0, y0], 'k', lw=0.6) for k in range(0, int(length) + 1): t = 2.0 if k % 10 == 0 else (1.2 if k % 5 == 0 else 0.6) ax.plot([x0 + k, x0 + k], [y0, y0 + t], 'k', lw=0.25) ax.text(x0 + length + 2, y0, u'%d mm(打出来量,应为 %.1f)' % (length, length), fontsize=6, va='center') def main(): os.makedirs(OUT, exist_ok=True) fig = plt.figure(figsize=(A4[0] * MM, A4[1] * MM)) txt = fig.add_axes([0, 0, 1, 1], facecolor='none') txt.set_xlim(0, A4[0]) txt.set_ylim(0, A4[1]) txt.axis('off') base, blade = restore['BASE']['pcb'], restore['BLADE']['pcb'] txt.text(A4[0] / 2, 289, u'PhoneRail R2 焊接图 · 器件值 · 1:1(底板 BASE-PCB + 刀片 BLADE-PCB 旧版)', fontsize=9, ha='center', weight='bold') txt.text(A4[0] / 2, 284.5, u'两块都是【背面 / 器件面,左右镜像】= 把板子翻过来看到的样子。红框 = 封装 1 脚(IC 定位;二极管多为负极、电解/钽电容为正极)。打印「实际大小 100%」。', fontsize=6, ha='center') ya = 180.0 txt.text(56, ya + 100.5, u'底板 98 × 98(背面)', fontsize=7, weight='bold') draw(fig, 'BASE', base, 55.0, ya - 1) yb = 97.0 txt.text(60, yb + 69.5, u'刀片 89.5 × 67(背面,金手指朝下)', fontsize=7, weight='bold') draw(fig, 'BLADE', blade, 59.25, yb - 1) ruler(fig, 20, 86, 80) lines = [ u'正面器件(不在图上):', u' 底板:C2 / C3 = 470uF 25V 电解(注意极性) J26 / J27 = 2510-4A 风扇座 J33 = 1×5P 2.54 排针 J34~J38 = PCIE-64P11L 插槽', u' 刀片:J1 = 金手指(不焊) K1~K4 = 侧键焊盘(不焊)', u'字号:大多 1.0 mm,挤的地方 0.85 / 0.7 mm。只写了器件值;看位号请开 EDA。', u'文件:.claude/r2/print/value_1to1.pdf(print_value.py 生成)', ] for i, s in enumerate(lines): txt.text(20, 74 - i * 5, s, fontsize=6.2) pdf = os.path.join(OUT, 'value_1to1.pdf') png = os.path.join(OUT, 'value_1to1.png') fig.savefig(pdf) fig.savefig(png, dpi=600) print(u'已出 %s / %s' % (pdf, png)) if __name__ == '__main__': main()