feat(arm): 线束走线可视化落地,并修复承压面盒切误伤底壁

- 查看器新增线束/走线指示系统(4芯主干、电机出线分支、过关节缓冲环、T接小板),
  配套《线束走线与总线拓扑设计方案》文档与 src/test_harness_collision.py 干涉校验
- 取消 P04/P06/P07/P08 的敞口侧定位环盒切(俯仰关节局部坐标使切刀上侵底壁 3mm,
  平白造出 3x6.1mm 缺角),底壁补齐;盒切仅保留原本专用的 P05(M3),开关改名 M3_FENCE_OPEN_CUT
- 3MF 基准 profile(537 键)从手作原件抽出固化到 docs/bambu_profile_reference.json,
  导出链路不再依赖 out/ 二进制;P01_A 也改由当前模型生成,原件备份进 .bak/
- strength.json、BOM、screws、截图与三份查看器 HTML 同步重生成
This commit is contained in:
cbb
2026-10-07 19:08:47 +08:00
parent d8f6c69406
commit c754521bde
175 changed files with 5719 additions and 332 deletions
+32 -11
View File
@@ -88,13 +88,36 @@ VIEWS = {
# 四个视图分别对应 P04/P05/P06/P07 的后端。ax/po 沿轴向,rd 拉近到 R35 座圈。
# 沿电机轴线从尾端面看过去:承压面(R35圆面) + 4xS02 沉孔,凸台消失后应是干净圆面
"boss_p04": "?az=0.55&po=1.30&rd=150&tx=19.5&ty=490&tz=-158&xray=0&hide=P01,P02,P03,P05,P06,P07,P08,P09,M3,M4,M5,M6,ENV",
"user_fork_fixed": "?az=0.55&po=1.30&rd=150&tx=19.5&ty=490&tz=-158&xray=0&hide=P01,P02,P03,P06,P07,P08,P09,M1,M4,M5,M6,ENV,S0,S1",
"bay_p04_inside": "?az=0.55&po=2.10&rd=150&tx=19.5&ty=490&tz=-158&xray=0&hide=P01,P02,P03,P05,P06,P07,P08,P09,M1,M2,M3,M4,M5,M6,ENV,S0,S1",
"bay_p06_inside": "?az=0.55&po=2.10&rd=150&tx=19.5&ty=332&tz=-158&xray=0&hide=P01,P02,P03,P04,P05,P07,P08,P09,M1,M2,M3,M4,M5,M6,ENV,S0,S1",
"bay_p07_inside": "?az=0.55&po=2.10&rd=150&tx=19.5&ty=227&tz=-158&xray=0&hide=P01,P02,P03,P04,P05,P06,P08,P09,M1,M2,M3,M4,M5,M6,ENV,S0,S1",
"p04_both_ends": "?az=1.5708&po=1.5708&rd=200&tx=19.5&ty=490&tz=-128&xray=0&hide=P01,P02,P03,P05,P06,P07,P08,P09,M1,M3,M4,M5,M6,ENV,S0,S1",
"p04_m2_side": "?az=1.2&po=1.9&rd=240&tx=19.5&ty=490&tz=-128&xray=0&hide=P01,P02,P03,P05,P06,P07,P08,P09,M1,M3,M4,M5,M6,ENV,S0,S1",
"p06_m4_side": "?az=1.2&po=1.9&rd=240&tx=19.5&ty=332&tz=-128&xray=0&hide=P01,P02,P03,P04,P05,P07,P08,P09,M1,M2,M3,M5,M6,ENV,S0,S1",
"p07_m5_side": "?az=1.2&po=1.9&rd=240&tx=19.5&ty=227&tz=-128&xray=0&hide=P01,P02,P03,P04,P05,P06,P08,P09,M1,M2,M3,M4,M6,ENV,S0,S1",
"p05_m3_side": "?az=1.2&po=1.9&rd=240&tx=19.5&ty=400&tz=-128&xray=0&hide=P01,P02,P03,P04,P06,P07,P08,P09,M1,M2,M4,M5,M6,ENV,S0,S1",
"boss_p05": "?az=1.5708&po=1.5708&rd=190&tx=18.5&ty=392&tz=-128&xray=0&hide=P01,P02,P03,P04,P06,P07,P08,P09,M2,M4,M5,M6,ENV",
"user_compare_view": "?cam=side&az=0&po=1.5708&rd=460&tx=19.5&ty=380&tz=-128&xray=0&hide=P01,P02,P03,P09,M1,M6,ENV",
"user_p07_check": "?az=1.5708&po=1.5708&rd=260&tx=19.5&ty=220&tz=-128&xray=0&hide=P01,P02,P03,P04,P05,P06,P09,M1,M2,M3,M4,M5,M6,ENV",
"boss_p06": "?az=1.5708&po=1.5708&rd=190&tx=18.5&ty=332&tz=-128&xray=0&hide=P01,P02,P03,P04,P05,P07,P08,P09,M1,M2,M3,M5,M6,ENV",
"boss_p07": "?az=0&po=0.0&rd=210&tx=19.5&ty=215&tz=-128&xray=0&hide=P01,P02,P03,P04,P05,P06,P08,P09,M1,M2,M3,M4,M6,ENV",
"svg_side": f"file:///{DOCS}/sheet_side.svg",
"svg_joint": f"file:///{DOCS}/sheet_joint.svg",
"svg_exploded": f"file:///{DOCS}/sheet_exploded.svg",
"svg_front": f"file:///{DOCS}/sheet_front.svg",
"user_circle_check": "?az=0.8&po=1.95&rd=240&tx=19.5&ty=430&tz=-128&xray=0&hide=M1,M2,M3,M4,M5,M6,ENV",
"user_exact_view": "?az=1.5708&po=1.92&rd=150&tx=19.5&ty=440&tz=-128&xray=0&hide=P01,P02,P03,M1,M2,M3,M4,M5,M6,ENV",
"shot_p08_user": "?az=1.5708&po=1.92&rd=150&tx=19.5&ty=176&tz=-128&xray=0&hide=P01,P02,P03,P04,P05,P06,P07,P09,M1,M2,M3,M4,M5,M6,ENV",
"p05_flank_view": "?az=0&po=1.5708&rd=200&tx=19.5&ty=440&tz=-135&xray=0&hide=P01,P02,P03,P07,P08,P09,M1,M4,M5,M6,ENV",
"harness_full_iso": "?cam=iso&xray=0",
"harness_overview": "?az=-0.7&po=1.2&rd=520&tx=0&ty=400&tz=-120&xray=0",
"harness_xray_internal": "?az=-0.7&po=1.2&rd=520&tx=0&ty=400&tz=-120&alpha=0.35&xray=1",
"harness_p07_window": "?az=-0.65&po=1.25&rd=260&tx=25&ty=275&tz=-130&xray=0",
"harness_p07_xray": "?az=-0.65&po=1.25&rd=260&tx=25&ty=275&tz=-130&alpha=0.35&xray=1",
"harness_p04_p05": "?az=0.8&po=1.35&rd=280&tx=25&ty=470&tz=-128&xray=0",
"harness_p04_p05_xray": "?az=0.8&po=1.35&rd=280&tx=25&ty=470&tz=-128&alpha=0.35&xray=1",
"harness_p06_window": "?az=-0.65&po=1.25&rd=240&tx=25&ty=355&tz=-128&xray=0",
"harness_xray_full": "?cam=iso&xray=1",
}
os.makedirs(SHOTS, exist_ok=True)
want = sys.argv[1:] or list(VIEWS)
@@ -102,13 +125,10 @@ want = sys.argv[1:] or list(VIEWS)
def take_shot(nm):
q = VIEWS[nm]
out = "%s/%s.png" % (SHOTS, nm)
# 不预先删除旧图:Edge 的 --screenshot 本来就会覆盖,而 os.remove 在带
# safe-delete 垫片的环境里会把"删到回收站"的失败抛出来,直接打断整轮渲染。
import time, tempfile
t_bust = int(time.time() * 1000)
target = q if q.startswith("file://") else "file:///" + HTML.replace("\\", "/") + q + (
f"&still=1&_t={t_bust}" if "?" in q else f"?still=1&_t={t_bust}")
# 每次用独立 profile:共用默认 profile 时并发实例会互相抢锁,表现为随机 FAIL。
udd = tempfile.mkdtemp(prefix="eshot")
cmd = [EDGE, "--headless=new", "--use-gl=angle", "--use-angle=d3d11",
"--disable-cache", "--disable-application-cache", "--disk-cache-size=0",
@@ -122,13 +142,14 @@ def take_shot(nm):
ok = os.path.exists(out) and os.path.getsize(out) > 20000
err = (r.stderr or b"").decode("utf-8", errors="ignore").strip()[:90].replace("\n", " ")
sz = os.path.getsize(out) if os.path.exists(out) else 0
print("%-12s %s %8s %s" % (nm, "OK " if ok else "FAIL", sz, err), flush=True)
print("%-18s %s %8s %s" % (nm, "OK " if ok else "FAIL", sz, err), flush=True)
return ok
from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=min(4, os.cpu_count() or 2)) as executor:
results = list(executor.map(take_shot, want))
if __name__ == "__main__":
from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=min(4, os.cpu_count() or 2)) as executor:
results = list(executor.map(take_shot, want))
print("edge:", EDGE)
print("all shots ok:", all(results))
os._exit(0)
print("edge:", EDGE)
print("all shots ok:", all(results))
os._exit(0)