Files
bid-doctor/docs/design/logo/scripts/vi_diagrams.py
YANG JIANKUAN 5753b51131 docs(logo): 新增 logo v5.1 正式资产、使用规范与构建脚本
- 白文印「标」(AI 生成原图经脚本抠图、统一为 #D9291C、矢量化)
- 字标:思源宋体 SemiBold + Libre Caslon;比例 H2,英文视觉内缩 C2
- svg/、png/、favicon:横版、竖版、反白、单色、印章、各尺寸图标
- scripts/:logo_lib.py 统一排版参数,build_assets.sh 一键生成
- logo-spec.md、logo-sheet.html、字体许可证(OFL)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 20:28:18 +08:00

115 lines
5.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""生成 VI 手册里的标志构成与安全区标注图(docs/design/vi/diagrams/)。
标注数值直接取自 logo_lib 的实际几何,保证和正式资产一致。
用法:.work/venv/bin/python scripts/vi_diagrams.py
"""
from logo_lib import (FINAL, FINAL_INSET, LOGO, NAVY, SB, SEAL_GAP, SEAL_SCALE, SL, SR, ST, CN_H, GAP, PY_CAP,
seal_group, wordmark)
OUT = LOGO.parent / "vi/diagrams"
OUT.mkdir(parents=True, exist_ok=True)
BLUE = "#2F5BD3"
FONT = 'font-family="PingFang SC, Noto Sans SC, sans-serif"'
body, W, H = wordmark(FINAL, NAVY, inset=FINAL_INSET)
SEAL_H = H * SEAL_SCALE
SEAL_TOP = -(SEAL_H - H) / 2
SEAL_W = (SR - SL) * SEAL_H / (SB - ST)
GAP_X = SEAL_H * SEAL_GAP
TX = SEAL_W + GAP_X
def label(x, y, text, anchor="middle", size=7.5):
return f'<text x="{x:.1f}" y="{y:.1f}" fill="{BLUE}" font-size="{size}" text-anchor="{anchor}" {FONT}>{text}</text>'
def hline(x1, x2, y, dash=True):
d = ' stroke-dasharray="2 2"' if dash else ""
return f'<line x1="{x1:.1f}" y1="{y:.1f}" x2="{x2:.1f}" y2="{y:.1f}" stroke="{BLUE}" stroke-width="0.5"{d}/>'
def vline(x, y1, y2, dash=True):
d = ' stroke-dasharray="2 2"' if dash else ""
return f'<line x1="{x:.1f}" y1="{y1:.1f}" x2="{x:.1f}" y2="{y2:.1f}" stroke="{BLUE}" stroke-width="0.5"{d}/>'
def dim_v(x, y1, y2, text, side="left"):
"""竖向尺寸线,带两端短横。"""
t = 2.5
lx = x - 4 if side == "left" else x + 4
return (vline(x, y1, y2, dash=False) + hline(x - t, x + t, y1, dash=False) + hline(x - t, x + t, y2, dash=False)
+ label(lx, (y1 + y2) / 2 + 2.6, text, "end" if side == "left" else "start"))
def dim_h(x1, x2, y, text, below=False):
t = 2.5
ly = y + 9 if below else y - 3.5
return (hline(x1, x2, y, dash=False) + vline(x1, y - t, y + t, dash=False) + vline(x2, y - t, y + t, dash=False)
+ label((x1 + x2) / 2, ly, text))
def construction_h():
seal, _ = seal_group("clean", 0, SEAL_TOP, SEAL_H)
py_top = CN_H * (1 + GAP)
x_end = TX + W
parts = [seal, f'<g transform="translate({TX:.2f} 0)">{body}</g>']
# 文字块上下边界与印章边界
parts += [hline(-30, x_end + 12, 0), hline(-30, x_end + 12, H),
hline(-30, SEAL_W + 4, SEAL_TOP), hline(-30, SEAL_W + 4, SEAL_TOP + SEAL_H)]
parts.append(dim_v(-12, 0, H, "H"))
parts.append(dim_v(-26, SEAL_TOP, SEAL_TOP + SEAL_H, "1.02H"))
# 印章与文字间距
parts.append(dim_h(SEAL_W, TX, H + 18, "0.3 × 印章高", below=True))
parts += [vline(SEAL_W, SEAL_TOP + SEAL_H, H + 22), vline(TX, H, H + 22)]
# 字标内部:中文字高、间距、拼音高
rx = x_end + 10
parts.append(dim_v(rx, 0, CN_H, "中文字高 100", side="right"))
parts.append(dim_v(rx, CN_H, py_top, "间距 14", side="right"))
parts.append(dim_v(rx, py_top, H, "拼音 20", side="right"))
# 英文视觉内缩
parts += [vline(TX, py_top - 6, H + 4), vline(x_end, py_top - 6, H + 4)]
parts.append(vline(TX + FINAL_INSET[0], py_top - 6, H + 4))
parts.append(vline(x_end - FINAL_INSET[1], py_top - 6, H + 4))
parts.append(label(TX + 2.5, H + 12, f"内缩 {FINAL_INSET[0]:g}", "start", 6.5))
parts.append(label(x_end - 2.5, H + 12, f"内缩 {FINAL_INSET[1]:g}", "end", 6.5))
vb = (-60, SEAL_TOP - 14, x_end + 150, SEAL_H + 56)
return f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="{" ".join(f"{v:.1f}" for v in vb)}">{"".join(parts)}</svg>'
def construction_v():
seal_h = W * 0.5
seal, _ = seal_group("clean", (W - seal_h) / 2, 0, seal_h)
gap = seal_h * 0.18
y_wm = seal_h + gap
parts = [seal, f'<g transform="translate(0 {y_wm:.2f})">{body}</g>']
parts += [vline(0, -8, y_wm + H + 6), vline(W, -8, y_wm + H + 6)]
parts.append(dim_h(0, W, -14, "字标宽 W"))
parts.append(dim_h((W - seal_h) / 2, (W + seal_h) / 2, seal_h + 5, "0.5W", below=True))
parts.append(dim_v(W + 12, seal_h, y_wm, "0.18 × 印章边长", side="right"))
parts += [hline((W + seal_h) / 2, W + 14, seal_h), hline(W, W + 14, y_wm)]
vb = (-20, -30, W + 130, y_wm + H + 40)
return f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="{" ".join(f"{v:.1f}" for v in vb)}">{"".join(parts)}</svg>'
def clearspace():
seal, _ = seal_group("clean", 0, SEAL_TOP, SEAL_H)
m = H / 4
x0, y0 = -m, SEAL_TOP - m
x1, y1 = TX + W + m, SEAL_TOP + SEAL_H + m
parts = [f'<rect x="{x0:.1f}" y="{y0:.1f}" width="{x1 - x0:.1f}" height="{y1 - y0:.1f}" fill="#E7EDFB" stroke="{BLUE}" stroke-width="0.6" stroke-dasharray="3 2"/>',
f'<rect x="0" y="{SEAL_TOP:.1f}" width="{TX + W:.1f}" height="{SEAL_H:.1f}" fill="#fff"/>',
seal, f'<g transform="translate({TX:.2f} 0)">{body}</g>']
# 四边标注 H/4
parts.append(dim_h(x0, 0, SEAL_TOP + SEAL_H / 2, "H/4"))
parts.append(dim_h(TX + W, x1, SEAL_TOP + SEAL_H / 2, "H/4"))
parts.append(dim_v(TX + W / 2, y0, SEAL_TOP, "H/4", side="right"))
parts.append(dim_v(TX + W / 2, SEAL_TOP + SEAL_H, y1, "H/4", side="right"))
vb = (x0 - 10, y0 - 10, x1 - x0 + 20, y1 - y0 + 20)
return f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="{" ".join(f"{v:.1f}" for v in vb)}">{"".join(parts)}</svg>'
(OUT / "construction-horizontal.svg").write_text(construction_h())
(OUT / "construction-vertical.svg").write_text(construction_v())
(OUT / "clearspace.svg").write_text(clearspace())
print("ok", sorted(p.name for p in OUT.iterdir()))