# -*- coding: utf-8 -*- """Assemble the single-file viewer: three.js (inline, no CDN) + mesh data + 2D sheets.""" import io import json import os import sys try: sys.stdout.reconfigure(encoding="utf-8") except Exception: pass sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from armcore import WEB, DOCS, WORKSPACE SRC = os.path.dirname(os.path.abspath(__file__)) import time def rebuild_viewer(): print("=" * 80, flush=True) print("【第五阶段 5/5】打包单文件三维交互查看器 (机器人手臂3D查看器.html)...", flush=True) print("=" * 80, flush=True) t0 = time.time() print(" - 读取 Three.js 离线 3D 渲染内核...", end="", flush=True) three = io.open(WEB + "/three_inline.js", encoding="utf-8").read() assert len(three) > 400000, len(three) print(f" 完成 ({len(three)/1024:.0f} KB) ✓", flush=True) print(" - 读取几何网格与关节运动学数据...", end="", flush=True) data = io.open(WEB + "/mesh_arm.json", encoding="utf-8").read() sheets = io.open(WEB + "/sheets.jsdata", encoding="utf-8").read() trv = io.open(DOCS + "/travel.json", encoding="utf-8").read() if os.path.exists( DOCS + "/travel.json") else "[]" col = io.open(DOCS + "/collide.json", encoding="utf-8").read() if os.path.exists( DOCS + "/collide.json") else '{"caps":[],"parts":{}}' screws = io.open(DOCS + "/screws.json", encoding="utf-8").read() if os.path.exists( DOCS + "/screws.json") else "[]" ik_solver = io.open(WEB + "/arm-ik-solver.js", encoding="utf-8").read() ik_glue = io.open(WEB + "/ik-glue.js", encoding="utf-8").read() tpl = io.open(SRC + "/viewer_tpl.html", encoding="utf-8").read() print(f" 完成 ({len(data)/1024:.0f} KB) ✓", flush=True) print(" - 打包生成全内置单文件 HTML...", end="", flush=True) out = (tpl.replace("/*__THREE__*/", three) .replace("/*__DATA__*/", data) .replace("/*__SHEETS__*/", sheets) .replace("/*__TRAVEL__*/", trv) .replace("/*__COLLIDE__*/", col) .replace("/*__SCREWS__*/", screws) .replace("/*__IKSOLVER__*/", ik_solver) .replace("/*__IKGLUE__*/", ik_glue)) assert "/*__" not in out, [l for l in out.split("\n") if "/*__" in l][:3] dst = WEB + "/机器人手臂3D查看器.html" # 【2026-10-06】三个副本逐个写、逐个报错,不要一把梭。 # 原来三行连写,任一份被浏览器独占(Windows 上打开中的 html 是独占锁)就抛异常, # 而调用方 arm_model.py 把整个 rebuild_viewer() 包在 try/except 里, # 于是只写成功第一份、另外两份静默停在旧版本 —— 表现为"代码改了但网页没变"。 # 实测就是这个坑:18:52 那次 dst 是新的,web/index.html 和根目录那份还停在 18:12。 ok, failed = True, [] for path in (dst, WEB + "/index.html", WORKSPACE + "/机器人手臂3D查看器.html"): try: io.open(path, "w", encoding="utf-8").write(out) except Exception as e: ok = False failed.append("%s (%s)" % (path, e)) sz_mb = os.path.getsize(dst) / 1e6 print(f" 完成 ({sz_mb:.2f} MB, 耗时: {time.time()-t0:.2f}s) ✓", flush=True) if failed: print("!! 以下副本写入失败(多半是浏览器正打开着该文件,Windows 独占锁):", flush=True) for f in failed: print(" - %s" % f, flush=True) print(" 关掉对应页面后重跑 python src/build_viewer.py 即可。\n", flush=True) else: print(f"-> 3D查看器已更新至: {dst}、{WEB}/index.html 与 根目录/机器人手臂3D查看器.html\n", flush=True) if __name__ == "__main__": rebuild_viewer() os._exit(0)