diff --git a/src/utils/html_generator.py b/src/utils/html_generator.py
index 8002825..5d3d576 100644
--- a/src/utils/html_generator.py
+++ b/src/utils/html_generator.py
@@ -179,6 +179,7 @@ class HTMLGenerator:
+
@@ -495,6 +496,65 @@ class HTMLGenerator:
pointcloudMesh.visible = pointcloudVisible;
}}
}}
+
+ // ─── 分模拆分动画 ───
+ let isSplit = false;
+ let splitAnimId = null;
+
+ function splitMold() {{
+ if (!cavityMesh && !coreMesh) return;
+ isSplit = !isSplit;
+ const btn = document.getElementById('splitBtn');
+ btn.textContent = isSplit ? '合模' : '分模拆分';
+
+ // 计算拆分距离:取边界框高度的 40%
+ const bboxDims = geometryData.bounding_box?.dimensions || [100, 100, 100];
+ const splitDist = bboxDims[1] * 0.4;
+
+ // 目标位置
+ const cavityTargetY = isSplit ? splitDist : 0;
+ const coreTargetY = isSplit ? -splitDist : 0;
+
+ // 分型面动画目标透明度
+ const partingTargetOpacity = isSplit ? 0 : 0.3;
+
+ // 记录起始位置
+ const cavityStartY = cavityMesh ? cavityMesh.position.y : 0;
+ const coreStartY = coreMesh ? coreMesh.position.y : 0;
+ const partingStartOpacity = partingMesh ? partingMesh.material.opacity : 0.3;
+
+ const duration = 800; // ms
+ const startTime = performance.now();
+
+ if (splitAnimId) cancelAnimationFrame(splitAnimId);
+
+ function animateSplit(now) {{
+ const elapsed = now - startTime;
+ const t = Math.min(elapsed / duration, 1);
+ // 缓动函数 (easeInOutCubic)
+ const ease = t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
+
+ if (cavityMesh) {{
+ cavityMesh.position.y = cavityStartY + (cavityTargetY - cavityStartY) * ease;
+ }}
+ if (coreMesh) {{
+ coreMesh.position.y = coreStartY + (coreTargetY - coreStartY) * ease;
+ }}
+ if (partingMesh) {{
+ partingMesh.material.opacity = partingStartOpacity + (partingTargetOpacity - partingStartOpacity) * ease;
+ if (isSplit && t >= 1) partingMesh.visible = false;
+ else partingMesh.visible = true;
+ }}
+
+ if (t < 1) {{
+ splitAnimId = requestAnimationFrame(animateSplit);
+ }} else {{
+ splitAnimId = null;
+ }}
+ }}
+
+ splitAnimId = requestAnimationFrame(animateSplit);
+ }}