2026-09-26 18:05:21 +08:00
|
|
|
|
# -*- coding: utf-8 -*-
|
2026-10-15 04:31:31 +08:00
|
|
|
|
"""把生成物落到 webapp/,并打一份单文件分发版。
|
|
|
|
|
|
|
|
|
|
|
|
单文件时代这里做 8 路字符串替换再写 3 份 16MB 副本;现在前端本身就是 webapp/ 下的
|
|
|
|
|
|
文件,本脚本只剩两件事:
|
|
|
|
|
|
write_data() 网格/工程图/行程/碰撞/螺钉 -> generated/00_data.js(普通脚本 const)
|
|
|
|
|
|
build_single() 把 webapp/ 的 css/js 原样内联成 dist/机器人手臂3D查看器.html(分发用)
|
|
|
|
|
|
|
|
|
|
|
|
S5 数据改 fetch() 后 write_data() 的 00_data.js 退役;S8 由 pack.py 的受限 ESM 内联器
|
|
|
|
|
|
接管 build_single()。
|
|
|
|
|
|
"""
|
2026-09-26 18:05:21 +08:00
|
|
|
|
import io
|
|
|
|
|
|
import json
|
|
|
|
|
|
import os
|
2026-10-15 04:31:31 +08:00
|
|
|
|
import re
|
2026-09-26 18:05:21 +08:00
|
|
|
|
import sys
|
2026-10-15 04:31:31 +08:00
|
|
|
|
import time
|
2026-09-26 18:05:21 +08:00
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
sys.stdout.reconfigure(encoding="utf-8")
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
2026-10-15 04:31:31 +08:00
|
|
|
|
from arm.paths import DATA, DIST, DOCS, GEN, WEBAPP
|
|
|
|
|
|
|
|
|
|
|
|
# 与 SHEET_METAS 的顺序一致;工程图读 draw_sheet 写出的真 SVG,
|
|
|
|
|
|
# 不再维护第二份 sheets.jsdata(单文件时代的内联载体,内容与 SVG 完全重复)。
|
|
|
|
|
|
SHEET_ORDER = ["sheet_front", "sheet_joint", "sheet_exploded", "sheet_side"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _read(path, dflt=None):
|
|
|
|
|
|
if os.path.exists(path):
|
|
|
|
|
|
return io.open(path, encoding="utf-8").read()
|
|
|
|
|
|
if dflt is None:
|
|
|
|
|
|
raise SystemExit("缺少查看器数据文件: %s(先跑 python -m arm.build_model)" % path)
|
|
|
|
|
|
return dflt
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def write_data():
|
|
|
|
|
|
t0 = time.time()
|
|
|
|
|
|
print("【数据】汇总查看器数据 -> webapp/generated/00_data.js", flush=True)
|
|
|
|
|
|
os.makedirs(GEN, exist_ok=True)
|
|
|
|
|
|
|
|
|
|
|
|
mesh = _read(DATA + "/mesh_arm.json")
|
|
|
|
|
|
svgs = [_read(DOCS + "/%s.svg" % n) for n in SHEET_ORDER]
|
|
|
|
|
|
trv = _read(DOCS + "/travel.json", "[]")
|
|
|
|
|
|
col = _read(DOCS + "/collide.json", '{"caps":[],"parts":{}}')
|
|
|
|
|
|
scr = _read(DOCS + "/screws.json", "[]")
|
|
|
|
|
|
print(" - 网格 %d KB / 工程图 %d 张 / travel %d B / collide %d KB / screws %d B"
|
|
|
|
|
|
% (len(mesh) // 1024, len(svgs), len(trv), len(col) // 1024, len(scr)), flush=True)
|
|
|
|
|
|
|
|
|
|
|
|
out = ["/* 生成物,勿手改:由 python -m arm.viewer.build_viewer 写入。",
|
|
|
|
|
|
" 普通脚本顶层 const —— 经典脚本共享全局词法作用域,后续脚本直接可见。 */"]
|
|
|
|
|
|
for name, raw in (("DATA", mesh), ("TRAVEL", trv), ("COLLIDE", col), ("SCREWS", scr)):
|
|
|
|
|
|
out.append("const %s = %s;" % (name, raw.strip()))
|
|
|
|
|
|
out.append("const SHEETS = %s;" % json.dumps(svgs, ensure_ascii=False))
|
|
|
|
|
|
text = "\n".join(out) + "\n"
|
|
|
|
|
|
tmp = GEN + "/00_data.js.tmp"
|
|
|
|
|
|
io.open(tmp, "w", encoding="utf-8").write(text)
|
|
|
|
|
|
os.replace(tmp, GEN + "/00_data.js")
|
|
|
|
|
|
print(" 完成:%d KB, %.2fs ✓" % (len(text) // 1024, time.time() - t0), flush=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def build_single():
|
|
|
|
|
|
"""分发用单文件:仍是普通脚本,所以内联是平凡的(S5 变 ESM 后交给 pack.py)。"""
|
|
|
|
|
|
t0 = time.time()
|
|
|
|
|
|
os.makedirs(DIST, exist_ok=True)
|
|
|
|
|
|
html = _read(WEBAPP + "/index.html")
|
|
|
|
|
|
total = len(html)
|
|
|
|
|
|
|
|
|
|
|
|
def sub(m, kind):
|
|
|
|
|
|
nonlocal total
|
|
|
|
|
|
body = _read(WEBAPP + "/" + m.group(1))
|
|
|
|
|
|
total += len(body)
|
|
|
|
|
|
return ("<style>\n" + body + "\n</style>") if kind == "css" \
|
|
|
|
|
|
else ("<script>\n" + body + "\n</script>")
|
|
|
|
|
|
|
|
|
|
|
|
out = re.sub(r'<link rel="stylesheet" href="([^"]+)">', lambda m: sub(m, "css"), html)
|
|
|
|
|
|
out = re.sub(r'<script src="([^"]+)"></script>', lambda m: sub(m, "js"), out)
|
|
|
|
|
|
|
|
|
|
|
|
left = re.findall(r'(?:src|href)="(?!data:)([^"]+)"', out)
|
|
|
|
|
|
assert not left, "单文件里仍有外部引用: %s" % left[:5]
|
|
|
|
|
|
assert len(out) >= total * 0.95, "内联后 %d 远小于各部之和 %d,疑似漏文件" % (len(out), total)
|
|
|
|
|
|
dst = DIST + "/机器人手臂3D查看器.html"
|
|
|
|
|
|
# Windows 上被浏览器打开的 html 是独占锁;写不进去就直接报,不要静默留旧版。
|
|
|
|
|
|
io.open(dst, "w", encoding="utf-8").write(out)
|
|
|
|
|
|
print(" - 单文件分发版 %.2f MB, %.2fs ✓ %s"
|
|
|
|
|
|
% (os.path.getsize(dst) / 1e6, time.time() - t0, dst), flush=True)
|
|
|
|
|
|
|
2026-09-27 04:48:49 +08:00
|
|
|
|
|
2026-09-27 00:28:49 +08:00
|
|
|
|
def rebuild_viewer():
|
2026-10-15 04:31:31 +08:00
|
|
|
|
"""build_model 流水线的最后一步(沿用旧函数名,调用点不改)。"""
|
|
|
|
|
|
write_data()
|
|
|
|
|
|
build_single()
|
2026-09-27 04:48:49 +08:00
|
|
|
|
|
2026-09-27 00:28:49 +08:00
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
rebuild_viewer()
|