分享图
动画工坊
引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>偏心铣削 IFR 原理动画</title>
    <style>
        :root {
            --bg-color: #050505;
            --grid-color: rgba(255, 255, 255, 0.04);
            --panel-bg: rgba(10, 10, 12, 0.85);
            --panel-border: rgba(255, 255, 255, 0.1);
            --accent-cyan: #00f0ff;
            --accent-green: #00ff66;
            --accent-red: #ff003c;
            --accent-amber: #ffb700;
            --text-main: #e2e8f0;
            --text-dim: #94a3b8;
            --font-mono: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
            --font-sans: system-ui, -apple-system, sans-serif;
        }

        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body {
            background-color: var(--bg-color);
            color: var(--text-main);
            font-family: var(--font-sans);
            overflow: hidden;
            width: 100vw;
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            background-image: 
                linear-gradient(var(--grid-color) 1px, transparent 1px),
                linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
            background-size: 30px 30px;
            background-position: center center;
        }

        /* 核心视图容器 */
        .viewport {
            position: relative;
            width: 100%;
            height: 100%;
            display: flex;
            flex-direction: row;
            justify-content: center;
            align-items: center;
            gap: 2vw;
            padding: 80px;
        }

        .svg-container {
            flex: 1;
            height: 100%;
            max-width: 800px;
            position: relative;
        }

        svg {
            width: 100%;
            height: 100%;
            filter: drop-shadow(0 0 20px rgba(0, 240, 255, 0.05));
        }

        /* 信息面板 (HUD) */
        .hud-panel {
            position: absolute;
            background: var(--panel-bg);
            border: 1px solid var(--panel-border);
            border-radius: 6px;
            padding: 16px;
            backdrop-filter: blur(8px);
            font-family: var(--font-mono);
            font-size: 12px;
            z-index: 10;
            box-shadow: 0 4px 24px rgba(0,0,0,0.5);
            pointer-events: none;
        }

        .hud-top-left { top: 24px; left: 24px; width: 320px; }
        .hud-bottom-left { bottom: 24px; left: 24px; width: 320px; }
        .hud-top-right { top: 24px; right: 24px; width: 280px; }
        .hud-bottom-right { bottom: 24px; right: 24px; width: 280px; }

        .hud-title {
            color: var(--accent-cyan);
            font-size: 14px;
            font-weight: bold;
            margin-bottom: 8px;
            text-transform: uppercase;
            letter-spacing: 1px;
            border-bottom: 1px solid var(--panel-border);
            padding-bottom: 4px;
        }

        .hud-row {
            display: flex;
            justify-content: space-between;
            margin-bottom: 6px;
        }

        .hud-row.highlight { color: var(--accent-amber); }
        .hud-row.success { color: var(--accent-green); }
        .hud-row.danger { color: var(--accent-red); }

        .ifr-tag {
            display: inline-block;
            background: rgba(0, 240, 255, 0.1);
            color: var(--accent-cyan);
            padding: 2px 6px;
            border-radius: 4px;
            font-size: 10px;
            margin-bottom: 8px;
            border: 1px solid rgba(0, 240, 255, 0.2);
        }

        /* 标签指示器 */
        .view-label {
            position: absolute;
            bottom: -30px;
            width: 100%;
            text-align: center;
            font-family: var(--font-mono);
            font-size: 12px;
            color: var(--text-dim);
            letter-spacing: 2px;
        }

        /* 动态发光效果 */
        @keyframes pulse-red {
            0% { filter: drop-shadow(0 0 4px var(--accent-red)); opacity: 1; }
            50% { filter: drop-shadow(0 0 12px var(--accent-red)); opacity: 0.6; }
            100% { filter: drop-shadow(0 0 4px var(--accent-red)); opacity: 1; }
        }
        .center-warning { animation: pulse-red 1s infinite; }

    </style>
</head>
<body>

    <!-- HUD 面板:左上 - TRIZ IFR 理论分析 -->
    <div class="hud-panel hud-top-left">
        <div class="hud-title">System Architecture</div>
        <div class="ifr-tag">TRIZ: 最终理想解 (IFR)</div>
        <div class="hud-row" style="color: var(--text-dim); white-space: normal; line-height: 1.5; margin-bottom: 12px;">
            利用"嵌套双轴"与"偏心距"资源,在极少增加复杂度的前提下,使高速铣刀绝对覆盖物理中心点,从根本上消灭线速度为零的切割死角。
        </div>
        <div class="hud-row"><span>Outer Shaft:</span><span id="ui-outer-status">IDLE</span></div>
        <div class="hud-row"><span>Inner Z-Axis:</span><span id="ui-z-status">RETRACTED</span></div>
        <div class="hud-row"><span>Cut Mode:</span><span id="ui-mode-status" class="highlight">STANDBY</span></div>
    </div>

    <!-- HUD 面板:左下 - 关键参数 -->
    <div class="hud-panel hud-bottom-left">
        <div class="hud-title">Kinematic Parameters</div>
        <div class="hud-row"><span>Eccentric Offset (E):</span><span class="highlight">2.50 mm</span></div>
        <div class="hud-row"><span>Cutter Radius (R):</span><span>3.50 mm</span></div>
        <div class="hud-row"><span>Center Overlap:</span><span class="success">1.00 mm (Coverage)</span></div>
        <div class="hud-row"><span>Motor Spin (V1):</span><span id="ui-rpm-spin">0 RPM</span></div>
        <div class="hud-row"><span>Orbit Speed (V2):</span><span id="ui-rpm-orbit">0 RPM</span></div>
    </div>

    <!-- HUD 面板:右上 - 实时遥测数据 -->
    <div class="hud-panel hud-top-right">
        <div class="hud-title">Real-time Telemetry</div>
        <div class="hud-row"><span>Cycle Time:</span><span id="ui-time">0.00s</span></div>
        <div class="hud-row"><span>Phase:</span><span id="ui-phase">WAIT</span></div>
        <div class="hud-row"><span>Orbit Angle:</span><span id="ui-angle-orbit">0.0°</span></div>
        <div class="hud-row"><span>Spin Angle:</span><span id="ui-angle-spin">0.0°</span></div>
        <div class="hud-row"><span>Z Depth:</span><span id="ui-z-depth">0.00 mm</span></div>
        <div class="hud-row"><span>Center Speed:</span><span id="ui-center-speed" class="danger">0.0 m/s</span></div>
    </div>

    <!-- HUD 面板:右下 - 图例 -->
    <div class="hud-panel hud-bottom-right">
        <div class="hud-title">Visual Legend</div>
        <div class="hud-row"><span style="color: var(--accent-red)">●</span><span> Zero-speed Deadzone</span></div>
        <div class="hud-row"><span style="color: var(--accent-cyan)">—</span><span> High-speed Milling Path</span></div>
        <div class="hud-row"><span style="color: var(--accent-green)">—</span><span> Orbit Trajectory</span></div>
        <div class="hud-row"><span style="color: var(--text-dim)">—</span><span> Mechanical Structure</span></div>
    </div>

    <!-- 核心视图区 -->
    <div class="viewport">
        
        <!-- 左侧:立面机械结构剖视图 -->
        <div class="svg-container">
            <svg id="svg-side" viewBox="0 0 500 600">
                <defs>
                    <linearGradient id="metal-grad" x1="0%" y1="0%" x2="100%" y2="0%">
                        <stop offset="0%" stop-color="#1e293b" />
                        <stop offset="20%" stop-color="#334155" />
                        <stop offset="50%" stop-color="#475569" />
                        <stop offset="80%" stop-color="#334155" />
                        <stop offset="100%" stop-color="#1e293b" />
                    </linearGradient>
                    <linearGradient id="brass-grad" x1="0%" y1="0%" x2="100%" y2="0%">
                        <stop offset="0%" stop-color="#78350f" />
                        <stop offset="50%" stop-color="#b45309" />
                        <stop offset="100%" stop-color="#78350f" />
                    </linearGradient>
                    <filter id="glow-cyan" x="-20%" y="-20%" width="140%" height="140%">
                        <feGaussianBlur stdDeviation="4" result="blur" />
                        <feComposite in="SourceGraphic" in2="blur" operator="over" />
                    </filter>
                    <filter id="glow-red" x="-50%" y="-50%" width="200%" height="200%">
                        <feGaussianBlur stdDeviation="6" result="blur" />
                        <feComposite in="SourceGraphic" in2="blur" operator="over" />
                    </filter>
                </defs>

                <!-- 背景参考线 -->
                <line x1="250" y1="50" x2="250" y2="550" stroke="rgba(255,255,255,0.1)" stroke-width="1" stroke-dasharray="10,5"/>
                <text x="260" y="70" fill="rgba(255,255,255,0.3)" font-family="monospace" font-size="10">Absolute Center Axis</text>

                <!-- 皮头与球杆底座 -->
                <g id="side-cue">
                    <rect x="210" y="450" width="80" height="150" fill="url(#metal-grad)" stroke="#000" stroke-width="2"/>
                    <path d="M 210 450 Q 250 430 290 450 Z" fill="url(#brass-grad)" stroke="#000" stroke-width="1"/>
                    <!-- 被切割前的轮廓虚线 -->
                    <path d="M 210 450 Q 250 400 290 450" fill="none" stroke="rgba(255,255,255,0.2)" stroke-width="1" stroke-dasharray="4,4"/>
                    <!-- 动态切割成型轮廓 (由JS更新控制) -->
                    <path id="side-cue-tip" d="M 210 450 Q 250 450 290 450" fill="url(#brass-grad)" stroke="#000"/>
                </g>

                <!-- 外部主轴与侧刀 -->
                <g id="side-outer-assembly">
                    <rect x="180" y="100" width="140" height="180" fill="url(#metal-grad)" stroke="#0f172a" stroke-width="2" opacity="0.6"/>
                    <!-- 侧刀 -->
                    <path d="M 160 280 L 180 280 L 180 350 L 170 380 L 160 380 Z" fill="#64748b" stroke="#000"/>
                    <path d="M 340 280 L 320 280 L 320 350 L 330 380 L 340 380 Z" fill="#64748b" stroke="#000"/>
                </g>

                <!-- 内部推缸轴与偏心铣刀 (受Z轴推拉与公转引起的投影X轴平移) -->
                <g id="side-inner-assembly">
                    <!-- 偏心距参考线 -->
                    <line x1="250" y1="120" x2="250" y2="400" stroke="rgba(255,255,255,0.2)" stroke-dasharray="2,2"/>
                    
                    <!-- 内部核心组件 (偏移一定距离E) -->
                    <g id="side-eccentric-core" transform="translate(40, 0)">
                        <line x1="250" y1="120" x2="250" y2="400" stroke="var(--accent-cyan)" stroke-width="1" stroke-dasharray="4,4" opacity="0.5"/>
                        <text x="255" y="240" fill="var(--accent-cyan)" font-family="monospace" font-size="10" opacity="0.8">Eccentric Axis</text>
                        <!-- 微型电机 -->
                        <rect x="220" y="150" width="60" height="120" rx="4" fill="#1e1b4b" stroke="var(--accent-cyan)" stroke-width="1.5"/>
                        <rect x="235" y="270" width="30" height="40" fill="#312e81" stroke="#000"/>
                        <!-- 高速成型铣刀 -->
                        <g id="side-milling-cutter">
                            <!-- 铣刀头: 半径比偏心距大,确保越过绝对中心线 -->
                            <path d="M 200 310 L 300 310 Q 250 380 200 310" fill="#0891b2" stroke="var(--accent-cyan)" stroke-width="2" filter="url(#glow-cyan)"/>
                            <!-- 铣刀轴心点 -->
                            <circle cx="250" cy="310" r="3" fill="#fff"/>
                        </g>
                    </g>
                    <!-- 动态标示偏心距 E -->
                    <path id="side-e-line" d="M 250 200 L 290 200" stroke="var(--accent-amber)" stroke-width="2" marker-start="url(#arrow)" marker-end="url(#arrow)"/>
                    <text x="260" y="195" fill="var(--accent-amber)" font-family="monospace" font-size="10">E</text>
                </g>

                <!-- 中心死角高亮 -->
                <circle id="side-center-warn" cx="250" cy="425" r="4" fill="var(--accent-red)" class="center-warning" filter="url(#glow-red)"/>

            </svg>
            <div class="view-label">[ X-Z CROSS SECTION / KINEMATICS ]</div>
        </div>

        <!-- 右侧:俯视原理轨迹图 (IFR 核心展示) -->
        <div class="svg-container">
            <svg id="svg-top" viewBox="0 0 500 500">
                <!-- 目标皮头轮廓 -->
                <circle cx="250" cy="250" r="120" fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="2" stroke-dasharray="10,5"/>
                <text x="250" y="120" fill="rgba(255,255,255,0.3)" font-family="monospace" font-size="10" text-anchor="middle">Cue Tip Outer Boundary</text>
                
                <!-- 绝对中心死角标记 -->
                <circle id="top-center-warn" cx="250" cy="250" r="6" fill="var(--accent-red)" class="center-warning" filter="url(#glow-red)"/>
                <circle cx="250" cy="250" r="2" fill="#fff"/>

                <!-- 公转轨迹 (Orbit) -->
                <circle cx="250" cy="250" r="40" fill="none" stroke="var(--accent-green)" stroke-width="1" stroke-dasharray="6,4" opacity="0.4"/>
                
                <!-- 动态切削轨迹绘制层 -->
                <g id="top-trails-layer" filter="url(#glow-cyan)"></g>

                <!-- 偏心铣刀组 -->
                <g id="top-orbit-group">
                    <!-- 偏心臂 E -->
                    <line x1="250" y1="250" x2="290" y2="250" stroke="var(--accent-amber)" stroke-width="2"/>
                    <text x="265" y="240" fill="var(--accent-amber)" font-family="monospace" font-size="10">E</text>
                    
                    <!-- 高速铣刀 -->
                    <g id="top-spin-group" transform="translate(290, 250)">
                        <circle cx="0" cy="0" r="56" fill="rgba(6, 182, 212, 0.1)" stroke="var(--accent-cyan)" stroke-width="1.5" stroke-dasharray="4,2"/>
                        <circle cx="0" cy="0" r="2" fill="var(--accent-cyan)"/>
                        <!-- 刀齿 -->
                        <polygon points="-56,0 -46,-5 -46,5" fill="#fff"/>
                        <polygon points="28,-48 23,-39 32,-35" fill="#fff"/>
                        <polygon points="28,48 23,39 32,35" fill="#fff"/>
                    </g>
                </g>

                <!-- 覆盖率高亮区域 (中心点被彻底扫过的证明) -->
                <circle cx="250" cy="250" r="16" fill="none" stroke="var(--accent-cyan)" stroke-width="1" opacity="0.2"/>
            </svg>
            <div class="view-label">[ X-Y TOP-DOWN TRAJECTORY / IFR ]</div>
        </div>

    </div>

    <script>
        // 核心参数设定
        const E = 40; // 偏心距 (视觉缩放比例)
        const R = 56; // 铣刀半径 (R > E 保证覆盖中心)
        const MAX_Z = 120; // Z轴最大下压行程
        
        // 动画状态变量
        let time = 0;
        let phase = 'APPROACH'; // APPROACH, MILL, RETRACT, IDLE
        let orbitAngle = 0;
        let spinAngle = 0;
        let zOffset = 0;
        
        // 轨迹记录
        const trail1 = [];
        const trail2 = [];
        const trail3 = [];
        const MAX_TRAIL_LENGTH = 800; // 长轨迹展示致密成型过程

        // DOM 元素引用
        const domSideInner = document.getElementById('side-inner-assembly');
        const domSideMilling = document.getElementById('side-milling-cutter');
        const domSideCueTip = document.getElementById('side-cue-tip');
        const domSideWarn = document.getElementById('side-center-warn');
        
        const domTopOrbit = document.getElementById('top-orbit-group');
        const domTopSpin = document.getElementById('top-spin-group');
        const domTopWarn = document.getElementById('top-center-warn');
        const domTrailsLayer = document.getElementById('top-trails-layer');
        
        // 生成三个轨迹路径元素
        const path1 = document.createElementNS("http://www.w3.org/2000/svg", "path");
        const path2 = document.createElementNS("http://www.w3.org/2000/svg", "path");
        const path3 = document.createElementNS("http://www.w3.org/2000/svg", "path");
        [path1, path2, path3].forEach(p => {
            p.setAttribute("fill", "none");
            p.setAttribute("stroke", "var(--accent-cyan)");
            p.setAttribute("stroke-width", "0.75");
            p.setAttribute("opacity", "0.6");
            domTrailsLayer.appendChild(p);
        });

        // UI 元素引用
        const uiTime = document.getElementById('ui-time');
        const uiPhase = document.getElementById('ui-phase');
        const uiAngleOrbit = document.getElementById('ui-angle-orbit');
        const uiAngleSpin = document.getElementById('ui-angle-spin');
        const uiZDepth = document.getElementById('ui-z-depth');
        const uiRPMOrbit = document.getElementById('ui-rpm-orbit');
        const uiRPMSpin = document.getElementById('ui-rpm-spin');
        const uiOuterStatus = document.getElementById('ui-outer-status');
        const uiZStatus = document.getElementById('ui-z-status');
        const uiModeStatus = document.getElementById('ui-mode-status');
        const uiCenterSpeed = document.getElementById('ui-center-speed');

        // 平滑插值函数
        function easeInOutQuad(t) {
            return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
        }

        // 轨迹更新函数
        function updateTrails(tx1, ty1, tx2, ty2, tx3, ty3) {
            trail1.push(`${tx1.toFixed(1)},${ty1.toFixed(1)}`);
            trail2.push(`${tx2.toFixed(1)},${ty2.toFixed(1)}`);
            trail3.push(`${tx3.toFixed(1)},${ty3.toFixed(1)}`);

            if (trail1.length > MAX_TRAIL_LENGTH) {
                trail1.shift(); trail2.shift(); trail3.shift();
            }

            if (trail1.length > 1) {
                path1.setAttribute("d", "M " + trail1.join(" L "));
                path2.setAttribute("d", "M " + trail2.join(" L "));
                path3.setAttribute("d", "M " + trail3.join(" L "));
            }
        }

        function clearTrails() {
            trail1.length = 0; trail2.length = 0; trail3.length = 0;
            path1.setAttribute("d", "");
            path2.setAttribute("d", "");
            path3.setAttribute("d", "");
        }

        // 主动画循环
        function animate() {
            time += 1; // 帧计时器
            const cycle = 1200; // 完整周期帧数
            const tInCycle = time % cycle;

            let orbitSpeed = 0;
            let spinSpeed = 0;

            // 状态机逻辑
            if (tInCycle < 200) {
                // 1. APPROACH: 主轴空转,内轴下压
                phase = 'APPROACH';
                const progress = tInCycle / 200;
                zOffset = easeInOutQuad(progress) * MAX_Z;
                orbitSpeed = 0.02 * progress; // 慢慢启动公转
                spinSpeed = 0;
            } else if (tInCycle < 900) {
                // 2. MILL: 到达底部,开启高速成型铣削
                phase = 'MILL';
                zOffset = MAX_Z;
                orbitSpeed = 0.02;
                // 模拟 60 RPM 公转与 8000 RPM 自转的高比例差异
                spinSpeed = 0.8; 
            } else if (tInCycle < 1100) {
                // 3. RETRACT: 抬升内轴
                phase = 'RETRACT';
                const progress = (tInCycle - 900) / 200;
                zOffset = MAX_Z - easeInOutQuad(progress) * MAX_Z;
                orbitSpeed = 0.02 * (1 - progress);
                spinSpeed = 0.8 * (1 - progress);
            } else {
                // 4. IDLE: 重置
                phase = 'IDLE';
                zOffset = 0;
                orbitSpeed = 0;
                spinSpeed = 0;
                if (tInCycle === 1101) clearTrails();
            }

            // 更新角度
            orbitAngle += orbitSpeed;
            spinAngle += spinSpeed;

            // --- 侧视图更新 (Side View) ---
            // 为了体现2D下的公转,我们将内轴的X坐标根据公转角度做正弦投影平移
            const sideXOffset = E * Math.cos(orbitAngle);
            domSideInner.setAttribute('transform', `translate(${sideXOffset}, ${zOffset})`);
            
            // 铣刀在侧视图中的自转视觉模拟 (3D感)
            const cutterScaleX = Math.cos(spinAngle);
            domSideMilling.setAttribute('transform', `scale(${cutterScaleX}, 1)`);

            // 动态形成皮头顶弧的动画逻辑
            if (phase === 'MILL') {
                // 模拟慢慢切出完美弧度
                const millProgress = (tInCycle - 200) / 700;
                const controlY = 450 - (20 * millProgress);
                domSideCueTip.setAttribute('d', `M 210 450 Q 250 ${controlY} 290 450 Z`);
            } else if (phase === 'IDLE' || phase === 'APPROACH') {
                domSideCueTip.setAttribute('d', `M 210 450 Q 250 450 290 450 Z`);
            }

            // 中心警告红点的消灭逻辑
            const warnOpacity = phase === 'MILL' ? 0 : 1;
            domSideWarn.style.opacity = warnOpacity;
            domTopWarn.style.opacity = warnOpacity;

            // --- 俯视图更新 (Top-Down View) ---
            // 整个轨道组自转 (围绕250,250)
            const orbitDeg = orbitAngle * (180 / Math.PI);
            domTopOrbit.setAttribute('transform', `rotate(${orbitDeg}, 250, 250)`);
            
            // 铣刀自身自转
            const spinDeg = spinAngle * (180 / Math.PI);
            domTopSpin.setAttribute('transform', `translate(290, 250) rotate(${spinDeg}, 0, 0)`);

            // 计算轨迹点 (绝对坐标)
            if (phase === 'MILL') {
                const cx = 250, cy = 250;
                const motorX = cx + E * Math.cos(orbitAngle);
                const motorY = cy + E * Math.sin(orbitAngle);
                
                // 3个刀齿的绝对位置
                const a1 = orbitAngle + spinAngle;
                const a2 = a1 + (2 * Math.PI / 3);
                const a3 = a1 + (4 * Math.PI / 3);

                const tx1 = motorX + R * Math.cos(a1);
                const ty1 = motorY + R * Math.sin(a1);
                const tx2 = motorX + R * Math.cos(a2);
                const ty2 = motorY + R * Math.sin(a2);
                const tx3 = motorX + R * Math.cos(a3);
                const ty3 = motorY + R * Math.sin(a3);

                updateTrails(tx1, ty1, tx2, ty2, tx3, ty3);
            }

            // --- UI 遥测数据更新 ---
            uiTime.innerText = (time / 60).toFixed(2) + 's';
            uiPhase.innerText = phase;
            uiPhase.style.color = phase === 'MILL' ? 'var(--accent-green)' : 'var(--accent-amber)';
            
            uiAngleOrbit.innerText = (orbitDeg % 360).toFixed(1) + '°';
            uiAngleSpin.innerText = (spinDeg % 360).toFixed(1) + '°';
            uiZDepth.innerText = (-zOffset / 10).toFixed(2) + ' mm';
            
            uiRPMOrbit.innerText = phase === 'MILL' ? '60 RPM' : (orbitSpeed > 0 ? 'ACCEL' : '0 RPM');
            uiRPMSpin.innerText = phase === 'MILL' ? '8400 RPM' : (spinSpeed > 0 ? 'ACCEL' : '0 RPM');
            
            uiOuterStatus.innerText = (orbitSpeed > 0) ? 'ROTATING (60 RPM)' : 'IDLE';
            uiOuterStatus.className = (orbitSpeed > 0) ? 'success' : '';
            
            uiZStatus.innerText = phase === 'APPROACH' ? 'DESCENDING' : (phase === 'RETRACT' ? 'ASCENDING' : (phase === 'MILL' ? 'LOCKED (-12.0mm)' : 'RETRACTED'));
            
            uiModeStatus.innerText = phase === 'MILL' ? 'ECCENTRIC MILLING' : 'STANDBY';
            uiModeStatus.className = phase === 'MILL' ? 'success' : 'highlight';

            uiCenterSpeed.innerText = phase === 'MILL' ? '8.4 m/s (OVERLAP)' : '0.0 m/s (DEAD)';
            uiCenterSpeed.className = phase === 'MILL' ? 'success' : 'danger';

            requestAnimationFrame(animate);
        }

        // 初始化启动
        document.addEventListener('DOMContentLoaded', () => {
            // 确保页面加载后自动无干预播放
            requestAnimationFrame(animate);
        });

    </script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>双套轴偏心成型铣削 - 机械结构与IFR原理演示</title>
    <style>
        :root {
            /* 工业级暗色主题 */
            --bg-base: #06090e;
            --grid-line: rgba(45, 60, 80, 0.4);
            --grid-major: rgba(60, 120, 200, 0.15);
            --panel-bg: rgba(10, 15, 25, 0.75);
            --panel-border: rgba(0, 255, 255, 0.15);
            
            /* 状态色彩体系 */
            --c-cyan: #00f0ff;
            --c-cyan-dim: rgba(0, 240, 255, 0.3);
            --c-amber: #ffb700;
            --c-green: #00ff66;
            --c-red: #ff3366;
            --c-metal-dark: #1e293b;
            --c-metal-mid: #475569;
            --c-metal-light: #94a3b8;
            
            --text-main: #e2e8f0;
            --text-dim: #64748b;
            --font-mono: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
            --font-sans: system-ui, -apple-system, sans-serif;
        }

        * { margin: 0; padding: 0; box-sizing: border-box; }

        body {
            background-color: var(--bg-base);
            color: var(--text-main);
            font-family: var(--font-sans);
            overflow: hidden;
            width: 100vw;
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            /* 工程图纸网格背景 */
            background-image: 
                linear-gradient(var(--grid-line) 1px, transparent 1px),
                linear-gradient(90deg, var(--grid-line) 1px, transparent 1px),
                linear-gradient(var(--grid-major) 2px, transparent 2px),
                linear-gradient(90deg, var(--grid-major) 2px, transparent 2px);
            background-size: 20px 20px, 20px 20px, 100px 100px, 100px 100px;
            background-position: center center;
        }

        .viewport {
            position: relative;
            width: 100%;
            height: 100%;
            display: flex;
            flex-direction: row;
            justify-content: center;
            align-items: center;
            gap: 4vw;
            padding: 60px;
        }

        .svg-container {
            flex: 1;
            height: 100%;
            max-width: 600px;
            position: relative;
            background: radial-gradient(circle at center, rgba(0,240,255,0.03) 0%, transparent 60%);
            border: 1px solid rgba(255,255,255,0.02);
            border-radius: 12px;
            box-shadow: inset 0 0 40px rgba(0,0,0,0.5);
        }

        svg {
            width: 100%;
            height: 100%;
            display: block;
        }

        /* 极简工程 HUD 面板 */
        .hud {
            position: absolute;
            background: var(--panel-bg);
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
            border: 1px solid var(--panel-border);
            border-radius: 4px;
            padding: 12px 16px;
            font-family: var(--font-mono);
            font-size: 11px;
            z-index: 10;
            pointer-events: none;
            box-shadow: 0 8px 32px rgba(0,0,0,0.6);
        }

        .hud-top-left { top: 20px; left: 20px; width: 340px; }
        .hud-top-right { top: 20px; right: 20px; width: 260px; }
        .hud-bottom-center { 
            bottom: 20px; 
            left: 50%; 
            transform: translateX(-50%);
            display: flex;
            gap: 24px;
            padding: 10px 30px;
        }

        .hud-title {
            color: var(--c-cyan);
            font-size: 12px;
            font-weight: 600;
            margin-bottom: 8px;
            padding-bottom: 4px;
            border-bottom: 1px solid var(--panel-border);
            letter-spacing: 1px;
            display: flex;
            justify-content: space-between;
        }

        .hud-row {
            display: flex;
            justify-content: space-between;
            margin-bottom: 4px;
            line-height: 1.6;
        }

        .val-cyan { color: var(--c-cyan); }
        .val-amber { color: var(--c-amber); }
        .val-green { color: var(--c-green); text-shadow: 0 0 5px rgba(0,255,102,0.4); }
        .val-red { color: var(--c-red); text-shadow: 0 0 5px rgba(255,51,102,0.4); }

        .desc-text {
            color: var(--c-metal-light);
            white-space: normal;
            line-height: 1.5;
            margin-bottom: 8px;
            font-size: 10.5px;
        }

        .view-label {
            position: absolute;
            bottom: 16px;
            width: 100%;
            text-align: center;
            font-family: var(--font-mono);
            font-size: 11px;
            color: var(--c-metal-light);
            letter-spacing: 2px;
            text-transform: uppercase;
        }

        /* 动态发光与警示动画 */
        @keyframes pulse-warn {
            0% { filter: drop-shadow(0 0 2px var(--c-red)); opacity: 1; stroke-width: 1; }
            50% { filter: drop-shadow(0 0 8px var(--c-red)); opacity: 0.5; stroke-width: 2; }
            100% { filter: drop-shadow(0 0 2px var(--c-red)); opacity: 1; stroke-width: 1; }
        }
        .warn-pulse { animation: pulse-warn 1.5s infinite; }
        
        .sys-status-indicator {
            display: inline-block;
            width: 8px;
            height: 8px;
            border-radius: 50%;
            background: var(--c-amber);
            margin-right: 6px;
        }
        .sys-status-indicator.active {
            background: var(--c-green);
            box-shadow: 0 0 8px var(--c-green);
        }
    </style>
</head>
<body>

    <!-- HUD 面板区域 -->
    <div class="hud hud-top-left">
        <div class="hud-title">
            <span>[SYS.01] 机械关联架构 (双套轴)</span>
            <span>TRIZ / IFR</span>
        </div>
        <div class="desc-text">
            原理:外部主轴承载侧刀(提供基准旋转),内部推缸轴硬性连接偏心法兰。<br>
            成型铣刀通过法兰安装在绝对中心线偏置距 (E) 处,利用微型电机产生 8000 RPM 独立自转,并跟随内轴进行 60 RPM 公转,以此彻底消除物理中心的“零线速度死角”。
        </div>
        <div class="hud-row"><span>外部主轴状态 (公转基准):</span><span id="ui-outer-state" class="val-amber">待机</span></div>
        <div class="hud-row"><span>内部推缸行程 (Z轴向下):</span><span id="ui-z-depth" class="val-cyan">0.00 mm</span></div>
        <div class="hud-row"><span>偏心法兰偏移距 (E):</span><span class="val-cyan">18.00 mm (固定配置)</span></div>
        <div class="hud-row"><span>成型铣刀半径 (R):</span><span class="val-cyan">24.00 mm (R > E 确保过心)</span></div>
    </div>

    <div class="hud hud-top-right">
        <div class="hud-title"><span>[SYS.02] 实时遥测数据</span></div>
        <div class="hud-row"><span>运行阶段:</span><span id="ui-phase" class="val-amber">初始化</span></div>
        <div class="hud-row"><span>公转转速 (主电机):</span><span id="ui-orbit-rpm">0 RPM</span></div>
        <div class="hud-row"><span>自转转速 (微电机):</span><span id="ui-spin-rpm">0 RPM</span></div>
        <div class="hud-row"><span>物理中心点切削线速度:</span><span id="ui-center-speed" class="val-red">0.0 m/s (死角)</span></div>
        <div class="hud-row" style="margin-top: 8px; border-top: 1px dashed rgba(255,255,255,0.1); padding-top: 8px;">
            <span>中心光洁度预估:</span><span id="ui-quality" class="val-red">起毛/撕裂风险极高</span>
        </div>
    </div>

    <div class="hud hud-bottom-center">
        <div class="hud-row" style="margin:0; align-items:center;">
            <span class="sys-status-indicator" id="ind-power"></span> 系统电源
        </div>
        <div class="hud-row" style="margin:0; align-items:center;">
            <span class="sys-status-indicator" id="ind-mill"></span> 高速铣削
        </div>
        <div class="hud-row" style="margin:0; align-items:center;">
            <span class="sys-status-indicator" id="ind-ifr"></span> IFR 理想解达成
        </div>
    </div>

    <!-- 核心可视化区 -->
    <div class="viewport">
        
        <!-- 左侧:机械结构立面剖视图 -->
        <div class="svg-container">
            <svg id="svg-side" viewBox="0 0 500 600">
                <defs>
                    <!-- 机械材质渐变 -->
                    <linearGradient id="grad-outer" x1="0%" y1="0%" x2="100%" y2="0%">
                        <stop offset="0%" stop-color="#1e293b"/>
                        <stop offset="20%" stop-color="#334155"/>
                        <stop offset="50%" stop-color="#64748b"/>
                        <stop offset="80%" stop-color="#334155"/>
                        <stop offset="100%" stop-color="#1e293b"/>
                    </linearGradient>
                    <linearGradient id="grad-inner" x1="0%" y1="0%" x2="100%" y2="0%">
                        <stop offset="0%" stop-color="#0f172a"/>
                        <stop offset="30%" stop-color="#1e293b"/>
                        <stop offset="70%" stop-color="#1e293b"/>
                        <stop offset="100%" stop-color="#0f172a"/>
                    </linearGradient>
                    <linearGradient id="grad-motor" x1="0%" y1="0%" x2="100%" y2="0%">
                        <stop offset="0%" stop-color="#78350f"/>
                        <stop offset="50%" stop-color="#b45309"/>
                        <stop offset="100%" stop-color="#78350f"/>
                    </linearGradient>
                    <linearGradient id="grad-cuetip" x1="0%" y1="0%" x2="100%" y2="0%">
                        <stop offset="0%" stop-color="#8b4513"/>
                        <stop offset="50%" stop-color="#d2691e"/>
                        <stop offset="100%" stop-color="#8b4513"/>
                    </linearGradient>
                    <!-- 发光滤镜 -->
                    <filter id="glow-cyan" x="-20%" y="-20%" width="140%" height="140%">
                        <feGaussianBlur stdDeviation="3" result="blur" />
                        <feComposite in="SourceGraphic" in2="blur" operator="over" />
                    </filter>
                    <!-- 切削遮罩 -->
                    <clipPath id="clip-cuetip">
                        <!-- 动态变化的遮罩路径,用于显示皮头被削掉的过程 -->
                        <path id="side-cuetip-mask" d="M 190 600 L 190 400 L 310 400 L 310 600 Z" />
                    </clipPath>
                </defs>

                <!-- 绝对中心线基准 -->
                <line x1="250" y1="20" x2="250" y2="580" stroke="var(--c-cyan)" stroke-width="1" stroke-dasharray="8,4" opacity="0.4"/>
                <text x="255" y="40" fill="var(--c-cyan)" font-family="var(--font-mono)" font-size="10" opacity="0.6">ABSOLUTE CENTER AXIS</text>

                <!-- 底部:球杆与皮头 -->
                <g id="side-base">
                    <!-- 夹具夹持的球杆先锋套 -->
                    <rect x="210" y="440" width="80" height="160" fill="#cbd5e1" stroke="#0f172a" stroke-width="2"/>
                    <!-- 被切割的皮头 (应用了动态 Clip Path) -->
                    <path d="M 210 400 L 290 400 L 290 440 L 210 440 Z" fill="url(#grad-cuetip)" stroke="#000" stroke-width="1" clip-path="url(#clip-cuetip)"/>
                    <!-- 理想弧度虚线参考 -->
                    <path d="M 210 440 L 210 420 Q 250 395 290 420 L 290 440" fill="none" stroke="rgba(255,255,255,0.3)" stroke-width="1" stroke-dasharray="2,2"/>
                    <!-- 中心死角警示点 -->
                    <circle id="side-center-deadzone" cx="250" cy="400" r="5" fill="none" stroke="var(--c-red)" stroke-width="2" class="warn-pulse"/>
                </g>

                <!-- 外部组件 (外部主轴,固定Z高度,原位自转代表公转基准) -->
                <g id="side-outer-assembly">
                    <!-- 外主轴剖切视窗壳体 -->
                    <path d="M 170 50 L 330 50 L 330 180 L 170 180 Z" fill="url(#grad-outer)" stroke="#000" stroke-width="2"/>
                    <!-- 内部空腔 -->
                    <rect x="190" y="50" width="120" height="130" fill="#020617"/>
                    <text x="175" y="65" fill="#94a3b8" font-family="var(--font-mono)" font-size="9">OUTER SPINDLE</text>
                    <!-- 侧刀支架 (固定在外轴上,负责侧边切削) -->
                    <rect x="150" y="180" width="20" height="260" fill="url(#grad-outer)" stroke="#000"/>
                    <rect x="330" y="180" width="20" height="260" fill="url(#grad-outer)" stroke="#000"/>
                    <polygon points="170,440 185,440 185,400 170,410" fill="#94a3b8" stroke="#000"/>
                    <polygon points="330,440 315,440 315,400 330,410" fill="#94a3b8" stroke="#000"/>
                </g>

                <!-- 内部组件 (推缸轴,随Z轴下降,同时被外轴带动产生公转视觉位移) -->
                <!-- 在2D侧视图中,内轴本体居中只做Z轴移动;偏心法兰及电机做X轴投影摆动 -->
                <g id="side-inner-assembly">
                    <!-- 内主轴 (Z轴推杆) -->
                    <rect id="side-inner-shaft" x="200" y="20" width="100" height="160" fill="url(#grad-inner)" stroke="#000" stroke-width="2"/>
                    <line x1="200" y1="180" x2="300" y2="180" stroke="var(--c-cyan)" stroke-width="2" opacity="0.5"/>
                    <text x="205" y="100" fill="var(--c-cyan)" font-family="var(--font-mono)" font-size="10" opacity="0.8">INNER Z-AXIS</text>

                    <!-- 动态连接法兰 (连接居中内轴和偏心电机) -->
                    <path id="side-flange" d="" fill="#475569" stroke="#000" stroke-width="2"/>
                    
                    <!-- 偏心微型电机与刀具组 -->
                    <g id="side-eccentric-group">
                        <!-- 偏心轴线指示 -->
                        <line x1="0" y1="-30" x2="0" y2="100" stroke="var(--c-amber)" stroke-width="1" stroke-dasharray="4,4"/>
                        <text x="5" y="-10" fill="var(--c-amber)" font-family="var(--font-mono)" font-size="9">E-AXIS</text>
                        <!-- 微型电机壳体 -->
                        <rect x="-25" y="0" width="50" height="60" rx="4" fill="url(#grad-motor)" stroke="#000" stroke-width="2"/>
                        <rect x="-10" y="60" width="20" height="15" fill="#334155" stroke="#000"/>
                        <text x="-18" y="20" fill="#fff" font-family="var(--font-mono)" font-size="8" transform="rotate(-90, -18, 20)">8000RPM</text>
                        
                        <!-- 高速成型半球铣刀 -->
                        <g id="side-cutter" filter="url(#glow-cyan)">
                            <!-- 半球形刀刃侧视 -->
                            <path d="M -30 75 Q 0 105 30 75 Z" fill="#00f0ff" fill-opacity="0.2" stroke="var(--c-cyan)" stroke-width="2"/>
                            <!-- 切削刃剖面 -->
                            <path d="M -30 75 L 30 75" stroke="var(--c-cyan)" stroke-width="1"/>
                            <circle cx="0" cy="75" r="2" fill="#fff"/>
                        </g>
                    </g>
                </g>
                
                <!-- 偏心距标注 E (固定在法兰区域) -->
                <line id="side-e-line" x1="250" y1="200" x2="280" y2="200" stroke="var(--c-amber)" stroke-width="1.5" marker-start="url(#arrow)" marker-end="url(#arrow)"/>
                <circle id="side-e-point1" cx="250" cy="200" r="2" fill="var(--c-amber)"/>
                <circle id="side-e-point2" cx="280" cy="200" r="2" fill="var(--c-amber)"/>

            </svg>
            <div class="view-label">图 1: 机械联动立面剖切图</div>
        </div>

        <!-- 右侧:俯视原理轨迹图 (展示绝对中心如何被完全覆盖) -->
        <div class="svg-container">
            <svg id="svg-top" viewBox="0 0 500 500">
                <defs>
                    <radialGradient id="grad-base-top" cx="50%" cy="50%" r="50%">
                        <stop offset="80%" stop-color="#8b4513" stop-opacity="0.1"/>
                        <stop offset="100%" stop-color="#d2691e" stop-opacity="0.3"/>
                    </radialGradient>
                </defs>

                <!-- 皮头外轮廓与中心线 -->
                <circle cx="250" cy="250" r="100" fill="url(#grad-base-top)" stroke="var(--c-metal-light)" stroke-width="2" stroke-dasharray="10,5"/>
                <text x="250" y="130" fill="var(--c-metal-light)" font-family="var(--font-mono)" font-size="10" text-anchor="middle">CUE TIP BOUNDARY (Ø14.0mm)</text>
                
                <line x1="150" y1="250" x2="350" y2="250" stroke="rgba(255,255,255,0.1)" stroke-width="1"/>
                <line x1="250" y1="150" x2="250" y2="350" stroke="rgba(255,255,255,0.1)" stroke-width="1"/>

                <!-- 绝对物理中心 (问题点:常规车削时线速度为0) -->
                <circle id="top-center-deadzone" cx="250" cy="250" r="6" fill="var(--c-red)" class="warn-pulse"/>
                
                <!-- 切削轨迹保留层 -->
                <g id="top-trails" opacity="0.7"></g>

                <!-- 动态旋转系统 -->
                <g id="top-rotating-system">
                    <!-- 公转基准:内轴中心 (即绝对中心) -->
                    <circle cx="250" cy="250" r="40" fill="none" stroke="var(--c-metal-mid)" stroke-width="2"/>
                    
                    <!-- 刚性连接法兰 (确保内外轴的机械约束) -->
                    <path id="top-flange" d="M 250 230 L 320 230 A 20 20 0 0 1 320 270 L 250 270 Z" fill="var(--c-metal-mid)" stroke="#0f172a" stroke-width="2"/>
                    <!-- 法兰固定螺栓视觉 -->
                    <circle cx="265" cy="240" r="3" fill="#1e293b"/>
                    <circle cx="265" cy="260" r="3" fill="#1e293b"/>
                    <circle cx="295" cy="250" r="4" fill="#0f172a"/>

                    <!-- 偏心距标注 -->
                    <line x1="250" y1="250" x2="320" y2="250" stroke="var(--c-amber)" stroke-width="2"/>
                    <text x="280" y="245" fill="var(--c-amber)" font-family="var(--font-mono)" font-size="12" font-weight="bold">E</text>

                    <!-- 高速铣刀盘组件 (挂载在偏心端 E 处) -->
                    <g id="top-cutter-group" transform="translate(320, 250)">
                        <!-- 电机外壳边界 -->
                        <circle cx="0" cy="0" r="25" fill="#b45309" stroke="#78350f" stroke-width="2"/>
                        <!-- 高速刀盘 R > E -->
                        <circle id="top-blade" cx="0" cy="0" r="75" fill="rgba(0, 240, 255, 0.1)" stroke="var(--c-cyan)" stroke-width="1.5" stroke-dasharray="4,4" filter="url(#glow-cyan)"/>
                        
                        <!-- 铣刀切削刃 (3刃设计) -->
                        <g id="top-cutter-teeth">
                            <polygon points="0,-10 0,10 -75,0" fill="#fff" opacity="0.8"/>
                            <polygon points="-8.6,-5 8.6,5 37.5,64.9" fill="#fff" opacity="0.8"/>
                            <polygon points="8.6,-5 -8.6,5 37.5,-64.9" fill="#fff" opacity="0.8"/>
                        </g>
                        <!-- 偏心自转轴心 -->
                        <circle cx="0" cy="0" r="3" fill="#fff"/>
                    </g>
                </g>

                <!-- 提示:铣刀半径完美覆盖中心点 -->
                <circle cx="250" cy="250" r="75" fill="none" stroke="var(--c-cyan-dim)" stroke-width="1" stroke-dasharray="2,4"/>

            </svg>
            <div class="view-label">图 2: IFR 破除死角原理俯视图</div>
        </div>

    </div>

    <script>
        /**
         * 机械参数定义
         */
        const PARAMS = {
            E_SIDE: 35,     // 侧视图偏心距投影缩放系数
            E_TOP: 70,      // 俯视图偏心法兰实际绘制长度
            R_CUTTER: 75,   // 俯视图铣刀半径 (必须大于 E 才能过中心)
            Z_MAX: 135,     // 内推缸最大下压行程
            CYCLE: 1500,    // 单次动画完整周期帧数
            RPM_ORBIT: 60,  // 模拟公转慢速
            RPM_SPIN: 8000  // 模拟自转高速
        };

        // 动画状态变量
        let frame = 0;
        let orbitAngle = 0; // 公转角度 (弧度)
        let spinAngle = 0;  // 自转角度 (弧度)
        let zDepth = 0;     // 当前推缸下降深度
        let phase = 'INIT'; // INIT, DESCEND, MILL, RETRACT

        // DOM 获取
        const elSideInnerShaft = document.getElementById('side-inner-shaft');
        const elSideFlange = document.getElementById('side-flange');
        const elSideEccGroup = document.getElementById('side-eccentric-group');
        const elSideEMarker1 = document.getElementById('side-e-point1');
        const elSideEMarker2 = document.getElementById('side-e-point2');
        const elSideELine = document.getElementById('side-e-line');
        const elSideCutter = document.getElementById('side-cutter');
        const elSideMask = document.getElementById('side-cuetip-mask');
        const elSideDeadzone = document.getElementById('side-center-deadzone');
        
        const elTopSystem = document.getElementById('top-rotating-system');
        const elTopTeeth = document.getElementById('top-cutter-teeth');
        const elTopTrails = document.getElementById('top-trails');
        const elTopDeadzone = document.getElementById('top-center-deadzone');

        // UI 获取
        const uiPhase = document.getElementById('ui-phase');
        const uiOrbitRpm = document.getElementById('ui-orbit-rpm');
        const uiSpinRpm = document.getElementById('ui-spin-rpm');
        const uiCenterSpeed = document.getElementById('ui-center-speed');
        const uiQuality = document.getElementById('ui-quality');
        const uiZDepth = document.getElementById('ui-z-depth');
        const uiOuterState = document.getElementById('ui-outer-state');
        
        const indPower = document.getElementById('ind-power');
        const indMill = document.getElementById('ind-mill');
        const indIfr = document.getElementById('ind-ifr');

        // 轨迹路径元素预分配
        const numTrails = 3;
        const trailPaths = [];
        const trailData = Array(numTrails).fill().map(() => []);
        const MAX_TRAIL_LEN = 120; // 防止轨迹过长卡顿,控制在体现一个过心切削即可
        
        for(let i=0; i<numTrails; i++) {
            let path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
            path.setAttribute('fill', 'none');
            path.setAttribute('stroke', 'var(--c-cyan)');
            path.setAttribute('stroke-width', '0.8');
            path.setAttribute('opacity', '0.6');
            elTopTrails.appendChild(path);
            trailPaths.push(path);
        }

        // 缓动函数
        const easeInOutCubic = t => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;

        /**
         * 核心动画循环
         */
        function animate() {
            frame++;
            const tCycle = frame % PARAMS.CYCLE;
            
            // 速度系数
            let orbitSpeed = 0;
            let spinSpeed = 0;

            // --- 阶段状态机逻辑 ---
            if (tCycle < 200) {
                // 1. DESCEND: 启动公转,内轴下降
                phase = 'DESCEND';
                let progress = tCycle / 200;
                zDepth = easeInOutCubic(progress) * PARAMS.Z_MAX;
                orbitSpeed = 0.015 * progress;
                spinSpeed = 0;
                clearTrails();
            } else if (tCycle < 1100) {
                // 2. MILL: 抵达底部,开启高速自转,进行偏心铣削
                phase = 'MILL';
                zDepth = PARAMS.Z_MAX;
                orbitSpeed = 0.015;
                spinSpeed = 0.6; // 视觉上的高速自转
            } else if (tCycle < 1300) {
                // 3. RETRACT: 停止高速自转,内轴回缩
                phase = 'RETRACT';
                let progress = (tCycle - 1100) / 200;
                zDepth = PARAMS.Z_MAX - easeInOutCubic(progress) * PARAMS.Z_MAX;
                orbitSpeed = 0.015 * (1 - progress);
                spinSpeed = 0.6 * (1 - progress);
            } else {
                // 4. INIT: 复位等待
                phase = 'INIT';
                zDepth = 0;
                orbitSpeed = 0;
                spinSpeed = 0;
            }

            orbitAngle += orbitSpeed;
            spinAngle += spinSpeed;

            // --- 侧视图更新 (Side View) ---
            
            // 内轴本体仅作Z轴下压
            elSideInnerShaft.setAttribute('y', 20 + zDepth);
            elSideInnerShaft.setAttribute('height', 160); // 长度保持一致或拉伸均可,这里通过改变y模拟下推
            
            // 计算偏心电机在2D侧视下的X轴投影位置 (绕250做正弦振荡)
            const motorX = 250 + PARAMS.E_SIDE * Math.cos(orbitAngle);
            const motorY = 200 + zDepth; // 电机挂载在内轴底部
            
            // 核心机械连接:动态绘制连接法兰,消除悬空感
            // 从中心内轴底部 (Y: 180+zDepth, X: 200~300) 连接到电机顶部 (Y: motorY, X: motorX-25~motorX+25)
            const innerBaseY = 180 + zDepth;
            const pathD = `M 200 ${innerBaseY} L 300 ${innerBaseY} L ${motorX + 25} ${motorY} L ${motorX - 25} ${motorY} Z`;
            elSideFlange.setAttribute('d', pathD);

            // 更新电机及刀具组的位置
            elSideEccGroup.setAttribute('transform', `translate(${motorX}, ${motorY})`);
            
            // 标注线跟随
            elSideEMarker1.setAttribute('cy', motorY + 30);
            elSideEMarker2.setAttribute('cx', motorX);
            elSideEMarker2.setAttribute('cy', motorY + 30);
            elSideELine.setAttribute('y1', motorY + 30);
            elSideELine.setAttribute('x2', motorX);
            elSideELine.setAttribute('y2', motorY + 30);

            // 刀具自转3D投影效果
            const scaleX = Math.cos(spinAngle);
            elSideCutter.setAttribute('transform', `translate(0,0) scale(${scaleX}, 1)`);

            // 皮头切削遮罩形态演化 (基于相位)
            if (phase === 'MILL') {
                // 铣削过程中,弧顶渐渐形成
                let millProgress = Math.min((tCycle - 200) / 600, 1); // 前600帧切完
                let cpY = 400 - (18 * millProgress); // 控制点下降,两边保持400
                elSideMask.setAttribute('d', `M 190 600 L 190 400 Q 250 ${cpY} 310 400 L 310 600 Z`);
            } else if (phase === 'INIT' || phase === 'DESCEND') {
                elSideMask.setAttribute('d', `M 190 600 L 190 400 L 310 400 L 310 600 Z`); // 平头
            }

            // 警示红点控制
            const isMill = phase === 'MILL';
            elSideDeadzone.style.opacity = isMill ? '0' : '1';
            elTopDeadzone.style.opacity = isMill ? '0' : '1';


            // --- 俯视图更新 (Top View) ---
            
            const orbitDeg = orbitAngle * (180 / Math.PI);
            const spinDeg = spinAngle * (180 / Math.PI);

            // 整体系统公转
            elTopSystem.setAttribute('transform', `rotate(${orbitDeg}, 250, 250)`);
            // 铣刀刃自转
            elTopTeeth.setAttribute('transform', `rotate(${spinDeg}, 0, 0)`);

            // 绘制过心切削轨迹
            if (isMill) {
                const cx = 250, cy = 250;
                // 电机实际中心坐标
                const mx = cx + PARAMS.E_TOP * Math.cos(orbitAngle);
                const my = cy + PARAMS.E_TOP * Math.sin(orbitAngle);
                
                // 3个刀尖的绝对坐标
                const a1 = orbitAngle + spinAngle;
                const a2 = a1 + (2 * Math.PI / 3);
                const a3 = a1 + (4 * Math.PI / 3);

                const pts = [
                    {x: mx + PARAMS.R_CUTTER * Math.cos(a1), y: my + PARAMS.R_CUTTER * Math.sin(a1)},
                    {x: mx + PARAMS.R_CUTTER * Math.cos(a2), y: my + PARAMS.R_CUTTER * Math.sin(a2)},
                    {x: mx + PARAMS.R_CUTTER * Math.cos(a3), y: my + PARAMS.R_CUTTER * Math.sin(a3)}
                ];

                for(let i=0; i<3; i++) {
                    trailData[i].push(`${pts[i].x.toFixed(1)},${pts[i].y.toFixed(1)}`);
                    if(trailData[i].length > MAX_TRAIL_LEN) trailData[i].shift();
                    if(trailData[i].length > 1) {
                        trailPaths[i].setAttribute('d', 'M ' + trailData[i].join(' L '));
                    }
                }
            }


            // --- UI 状态数据同步 ---
            
            uiPhase.innerText = phase === 'DESCEND' ? '内轴进给下压' : 
                               (phase === 'MILL' ? '高速偏心铣削中' : 
                               (phase === 'RETRACT' ? '刀具抬起回退' : '系统待命'));
            uiPhase.className = isMill ? 'val-green' : 'val-amber';

            uiZDepth.innerText = (zDepth / 10).toFixed(2) + ' mm';
            uiOuterState.innerText = orbitSpeed > 0 ? `匀速公转 (${PARAMS.RPM_ORBIT} RPM)` : '停止锁定';
            uiOuterState.className = orbitSpeed > 0 ? 'val-cyan' : 'val-amber';

            uiOrbitRpm.innerText = orbitSpeed > 0 ? `${PARAMS.RPM_ORBIT} RPM` : '0 RPM';
            uiOrbitRpm.className = orbitSpeed > 0 ? 'val-cyan' : '';

            uiSpinRpm.innerText = isMill ? `${PARAMS.RPM_SPIN} RPM` : '0 RPM';
            uiSpinRpm.className = isMill ? 'val-cyan' : '';

            // 核心IFR展示:速度对比
            if (isMill) {
                uiCenterSpeed.innerText = '10.05 m/s (绝对覆盖)';
                uiCenterSpeed.className = 'val-green';
                uiQuality.innerText = '完美弧面光洁度 (撕裂消除)';
                uiQuality.className = 'val-green';
                indMill.classList.add('active');
                indIfr.classList.add('active');
            } else {
                uiCenterSpeed.innerText = '0.00 m/s (切割死角)';
                uiCenterSpeed.className = 'val-red';
                uiQuality.innerText = '起毛/撕裂风险极高';
                uiQuality.className = 'val-red';
                indMill.classList.remove('active');
                indIfr.classList.remove('active');
            }

            indPower.classList.add('active');

            requestAnimationFrame(animate);
        }

        function clearTrails() {
            for(let i=0; i<numTrails; i++) {
                trailData[i] = [];
                trailPaths[i].setAttribute('d', '');
            }
        }

        // 页面加载完成后自动执行,无需用户干预
        window.addEventListener('DOMContentLoaded', () => {
            requestAnimationFrame(animate);
        });

    </script>
</body>
</html>
积分规则:第一轮对话扣减8分,后续每轮扣6分