分享图
动画工坊
引擎就绪
<!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>
        @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Noto+Sans+SC:wght@400;500;700;900&display=swap');

        :root {
            --bg-base: #020617;
            --bg-panel: rgba(15, 23, 42, 0.75);
            --accent-cyan: #06b6d4;
            --accent-cyan-glow: rgba(6, 182, 212, 0.6);
            --accent-orange: #f59e0b;
            --accent-orange-glow: rgba(245, 158, 11, 0.6);
            --text-main: #f8fafc;
            --text-muted: #94a3b8;
            --metal-light: #cbd5e1;
            --metal-dark: #475569;
            --border-color: rgba(255, 255, 255, 0.15);
            --panel-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.5);
        }

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

        body {
            background-color: var(--bg-base);
            color: var(--text-main);
            font-family: 'Noto Sans SC', sans-serif;
            overflow: hidden;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            width: 100vw;
            background-image: 
                linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px),
                linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px);
            background-size: 40px 40px;
            background-position: center center;
        }

        /* 顶部标题区 */
        .header {
            position: absolute;
            top: 40px;
            text-align: center;
            z-index: 10;
        }

        .header h1 {
            font-size: 2.2rem;
            font-weight: 900;
            letter-spacing: 2px;
            background: linear-gradient(90deg, #fff, var(--accent-cyan));
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            margin-bottom: 8px;
            filter: drop-shadow(0 2px 4px rgba(0,0,0,0.8));
        }

        .header p {
            font-size: 1rem;
            color: var(--text-muted);
            letter-spacing: 1px;
        }
        
        .ifr-badge {
            display: inline-block;
            margin-top: 10px;
            padding: 4px 12px;
            background: rgba(6, 182, 212, 0.15);
            border: 1px solid var(--accent-cyan);
            border-radius: 20px;
            font-size: 0.85rem;
            color: var(--accent-cyan);
            font-weight: bold;
            box-shadow: 0 0 10px var(--accent-cyan-glow);
        }

        /* 核心动画容器 */
        .main-view {
            width: 100%;
            max-width: 1200px;
            height: 50vh;
            min-height: 400px;
            display: flex;
            align-items: center;
            justify-content: center;
            position: relative;
            margin-top: -50px;
        }

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

        /* 底部仪表盘 */
        .dashboard {
            position: absolute;
            bottom: 40px;
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 24px;
            z-index: 10;
            width: 100%;
            max-width: 900px;
        }

        .metrics-container {
            display: flex;
            gap: 20px;
            width: 100%;
            justify-content: center;
        }

        .metric-card {
            background: var(--bg-panel);
            backdrop-filter: blur(12px);
            -webkit-backdrop-filter: blur(12px);
            border: 1px solid var(--border-color);
            border-radius: 16px;
            padding: 20px 24px;
            width: 260px;
            box-shadow: var(--panel-shadow);
            display: flex;
            flex-direction: column;
            position: relative;
            overflow: hidden;
            transition: all 0.3s ease;
        }
        
        .metric-card::before {
            content: '';
            position: absolute;
            top: 0; left: 0; width: 100%; height: 3px;
            background: var(--accent-cyan);
        }
        
        .metric-card.orange::before { background: var(--accent-orange); }

        .metric-title {
            font-size: 0.9rem;
            color: var(--text-muted);
            margin-bottom: 8px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .metric-value {
            font-family: 'JetBrains Mono', monospace;
            font-size: 2.5rem;
            font-weight: 700;
            color: var(--text-main);
            display: flex;
            align-items: baseline;
            gap: 8px;
        }

        .metric-unit {
            font-size: 1.1rem;
            color: var(--text-muted);
            font-family: 'Noto Sans SC', sans-serif;
        }

        /* 交互控制器 */
        .control-panel {
            background: var(--bg-panel);
            backdrop-filter: blur(12px);
            border: 1px solid var(--border-color);
            border-radius: 40px;
            padding: 16px 40px;
            display: flex;
            align-items: center;
            gap: 20px;
            box-shadow: var(--panel-shadow);
            width: 100%;
            max-width: 600px;
        }

        .status-indicator {
            display: flex;
            align-items: center;
            gap: 8px;
            font-size: 0.85rem;
            font-weight: 700;
            letter-spacing: 1px;
            width: 80px;
        }

        .status-dot {
            width: 10px;
            height: 10px;
            border-radius: 50%;
            background: #22c55e;
            box-shadow: 0 0 10px #22c55e;
            animation: pulse 1.5s infinite;
        }

        .status-indicator.manual .status-dot {
            background: var(--accent-orange);
            box-shadow: 0 0 10px var(--accent-orange-glow);
            animation: none;
        }

        @keyframes pulse {
            0% { transform: scale(0.95); opacity: 1; }
            50% { transform: scale(1.2); opacity: 0.5; }
            100% { transform: scale(0.95); opacity: 1; }
        }

        .slider-container {
            flex-grow: 1;
            display: flex;
            align-items: center;
            position: relative;
        }

        input[type=range] {
            -webkit-appearance: none;
            width: 100%;
            background: transparent;
        }

        input[type=range]::-webkit-slider-runnable-track {
            width: 100%;
            height: 6px;
            background: rgba(255, 255, 255, 0.1);
            border-radius: 3px;
            border: 1px solid rgba(0,0,0,0.5);
        }

        input[type=range]::-webkit-slider-thumb {
            -webkit-appearance: none;
            height: 24px;
            width: 24px;
            border-radius: 50%;
            background: var(--text-main);
            border: 3px solid var(--bg-base);
            box-shadow: 0 0 15px var(--text-main);
            cursor: pointer;
            margin-top: -10px;
            transition: transform 0.1s;
        }

        input[type=range]::-webkit-slider-thumb:hover {
            transform: scale(1.2);
        }
        
        input[type=range]:active::-webkit-slider-thumb {
            transform: scale(1.1);
            background: var(--accent-orange);
            box-shadow: 0 0 20px var(--accent-orange);
        }

        .slider-label {
            font-size: 0.85rem;
            color: var(--text-muted);
            white-space: nowrap;
        }

        /* SVG 内嵌样式 */
        .svg-text {
            font-family: 'Noto Sans SC', sans-serif;
            fill: var(--text-main);
            font-size: 14px;
            filter: drop-shadow(0 2px 4px rgba(0,0,0,0.8));
        }
        .svg-text.bold { font-weight: 700; }
        .svg-text.cyan { fill: var(--accent-cyan); }
        .svg-text.orange { fill: var(--accent-orange); }
    </style>
</head>
<body>

    <div class="header">
        <h1>自适应弹性喉口限流系统</h1>
        <p>TRIZ 最终理想解 (IFR):利用系统内部流体压力,实现无源被动限流</p>
        <div class="ifr-badge">核心机理:血管自调节效应</div>
    </div>

    <div class="main-view">
        <svg viewBox="0 0 1000 500" preserveAspectRatio="xMidYMid meet">
            <defs>
                <!-- 钢壳渐变 -->
                <linearGradient id="metalGrad" x1="0" y1="0" x2="0" y2="1">
                    <stop offset="0%" stop-color="#94a3b8" />
                    <stop offset="50%" stop-color="#cbd5e1" />
                    <stop offset="100%" stop-color="#475569" />
                </linearGradient>
                
                <!-- 橡胶网格纹理 (钢丝骨架) -->
                <pattern id="wireMesh" width="12" height="12" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
                    <line x1="0" y1="0" x2="0" y2="12" stroke="rgba(255,255,255,0.15)" stroke-width="1.5" />
                    <line x1="0" y1="0" x2="12" y2="0" stroke="rgba(255,255,255,0.15)" stroke-width="1.5" />
                </pattern>

                <!-- 流体发光滤镜 -->
                <filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
                    <feGaussianBlur stdDeviation="8" result="blur" />
                    <feComposite in="SourceGraphic" in2="blur" operator="over" />
                </filter>
                
                <filter id="glow-orange" x="-20%" y="-20%" width="140%" height="140%">
                    <feGaussianBlur stdDeviation="6" result="blur" />
                    <feComposite in="SourceGraphic" in2="blur" operator="over" />
                </filter>

                <!-- 箭头标记 -->
                <marker id="arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
                    <path d="M 0 1 L 10 5 L 0 9 z" fill="var(--text-muted)" />
                </marker>
                
                <marker id="arrow-cyan" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
                    <path d="M 0 1 L 10 5 L 0 9 z" fill="var(--accent-cyan)" />
                </marker>
            </defs>

            <!-- 背景辅助线 (蓝图风格) -->
            <g stroke="rgba(255,255,255,0.05)" stroke-width="1">
                <line x1="0" y1="250" x2="1000" y2="250" stroke-dasharray="10 10" />
                <line x1="500" y1="0" x2="500" y2="500" stroke-dasharray="10 10" />
            </g>

            <!-- 流体主区域 (动态路径填充) -->
            <path id="fluidArea" fill="rgba(6, 182, 212, 0.15)" stroke="rgba(6, 182, 212, 0.5)" stroke-width="2" filter="url(#glow)"/>

            <!-- 动态流线 (由 JS 生成并更新) -->
            <g id="streamlinesGroup"></g>

            <!-- ================= 上半部分结构 ================= -->
            <!-- 外部不锈钢壳 (上) -->
            <path d="M 50 10 L 950 10 L 950 50 L 50 50 Z" fill="url(#metalGrad)" />
            <!-- 限位环 (上) -->
            <rect x="290" y="50" width="10" height="80" fill="url(#metalGrad)" />
            <rect x="700" y="50" width="10" height="80" fill="url(#metalGrad)" />
            
            <!-- 弹性橡胶喉口 (上) - 动态路径 -->
            <path id="rubberTop" fill="#b45309" />
            <path id="rubberTopWire" fill="url(#wireMesh)" />
            <path id="rubberTopEdge" fill="none" stroke="#f59e0b" stroke-width="3" filter="url(#glow-orange)" />

            <!-- ================= 下半部分结构 ================= -->
            <!-- 外部不锈钢壳 (下) -->
            <path d="M 50 490 L 950 490 L 950 450 L 50 450 Z" fill="url(#metalGrad)" />
            <!-- 限位环 (下) -->
            <rect x="290" y="370" width="10" height="80" fill="url(#metalGrad)" />
            <rect x="700" y="370" width="10" height="80" fill="url(#metalGrad)" />

            <!-- 弹性橡胶喉口 (下) - 动态路径 -->
            <path id="rubberBottom" fill="#b45309" />
            <path id="rubberBottomWire" fill="url(#wireMesh)" />
            <path id="rubberBottomEdge" fill="none" stroke="#f59e0b" stroke-width="3" filter="url(#glow-orange)" />

            <!-- ================= 标注与受力指示 ================= -->
            <!-- IFR 受力箭头组 (随压力显现) -->
            <g id="forceArrowsGroup" stroke="#ef4444" stroke-width="2" opacity="0">
                <!-- 上方受力 -->
                <path d="M 470 200 L 470 170" marker-end="url(#arrow)" />
                <path d="M 500 210 L 500 180" marker-end="url(#arrow)" />
                <path d="M 530 200 L 530 170" marker-end="url(#arrow)" />
                <!-- 下方受力 -->
                <path d="M 470 300 L 470 330" marker-end="url(#arrow)" />
                <path d="M 500 290 L 500 320" marker-end="url(#arrow)" />
                <path d="M 530 300 L 530 330" marker-end="url(#arrow)" />
            </g>

            <!-- 静态标注线和文字 -->
            <g class="annotations">
                <!-- 进料端 -->
                <text x="80" y="220" class="svg-text bold cyan">进料端 (高压)</text>
                <text x="80" y="240" class="svg-text">流体流速 <tspan id="lblVelocityIn">v1</tspan></text>
                <path d="M 70 250 L 150 250" stroke="var(--accent-cyan)" stroke-width="2" marker-end="url(#arrow-cyan)" />

                <!-- 出料端 -->
                <text x="820" y="220" class="svg-text bold cyan">出料端 (稳流)</text>
                <text x="820" y="240" class="svg-text">输出流量保持稳定</text>
                <path d="M 810 250 L 890 250" stroke="var(--accent-cyan)" stroke-width="2" marker-end="url(#arrow-cyan)" />

                <!-- 喉口标注 -->
                <path d="M 500 110 L 500 40 L 550 40" stroke="var(--text-muted)" stroke-width="1.5" fill="none" />
                <text x="560" y="45" class="svg-text orange bold">高强度聚氨酯弹性体 (内衬钢丝)</text>
                
                <path d="M 290 90 L 250 90" stroke="var(--text-muted)" stroke-width="1.5" fill="none" />
                <text x="190" y="95" class="svg-text">不锈钢限位环</text>

                <path d="M 750 30 L 800 30" stroke="var(--text-muted)" stroke-width="1.5" fill="none" />
                <text x="810" y="35" class="svg-text">坚固外壳</text>
                
                <!-- 动态受力文本 -->
                <text id="txtForce" x="410" y="255" class="svg-text bold" fill="#ef4444" opacity="0">系统压力直接挤压形变</text>
            </g>

            <!-- 动态孔径尺寸指示线 -->
            <g id="dimensionGroup">
                <line x1="610" y1="130" x2="630" y2="130" stroke="var(--text-main)" stroke-width="1" id="dimTopLine" />
                <line x1="610" y1="370" x2="630" y2="370" stroke="var(--text-main)" stroke-width="1" id="dimBottomLine" />
                <line x1="620" y1="130" x2="620" y2="370" stroke="var(--text-main)" stroke-width="1.5" id="dimVerLine" marker-start="url(#arrow)" marker-end="url(#arrow)" />
                <rect x="635" y="240" width="60" height="20" fill="var(--bg-base)" rx="4" />
                <text x="665" y="255" text-anchor="middle" class="svg-text bold" id="lblDiameter">200 mm</text>
            </g>
        </svg>
    </div>

    <div class="dashboard">
        <div class="metrics-container">
            <!-- 压力表 -->
            <div class="metric-card">
                <div class="metric-title">系统压力 (P) <span>输入端</span></div>
                <div class="metric-value">
                    <span id="valPressure">0.00</span>
                    <span class="metric-unit">MPa</span>
                </div>
            </div>
            <!-- 孔径表 -->
            <div class="metric-card orange">
                <div class="metric-title">自适应喉口孔径 (D) <span>节流元件</span></div>
                <div class="metric-value" style="color: var(--accent-orange);">
                    <span id="valDiameter">200</span>
                    <span class="metric-unit">mm</span>
                </div>
            </div>
            <!-- 流量表 -->
            <div class="metric-card">
                <div class="metric-title">输出流量 (Q) <span>被动稳流</span></div>
                <div class="metric-value">
                    <span id="valFlow">0.0</span>
                    <span class="metric-unit">m³/h</span>
                </div>
            </div>
        </div>

        <div class="control-panel">
            <div class="status-indicator" id="statusIndicator">
                <div class="status-dot"></div>
                <span id="statusText">AUTO</span>
            </div>
            <div class="slider-label">低压</div>
            <div class="slider-container">
                <input type="range" id="pressureSlider" min="0" max="1" step="0.001" value="0">
            </div>
            <div class="slider-label">高压</div>
        </div>
    </div>

    <script>
        // DOM 元素引用
        const paths = {
            rubberTop: document.getElementById('rubberTop'),
            rubberTopWire: document.getElementById('rubberTopWire'),
            rubberTopEdge: document.getElementById('rubberTopEdge'),
            rubberBottom: document.getElementById('rubberBottom'),
            rubberBottomWire: document.getElementById('rubberBottomWire'),
            rubberBottomEdge: document.getElementById('rubberBottomEdge'),
            fluidArea: document.getElementById('fluidArea'),
            forceArrowsGroup: document.getElementById('forceArrowsGroup'),
            txtForce: document.getElementById('txtForce'),
            dimTopLine: document.getElementById('dimTopLine'),
            dimBottomLine: document.getElementById('dimBottomLine'),
            dimVerLine: document.getElementById('dimVerLine'),
            lblDiameter: document.getElementById('lblDiameter'),
            streamlinesGroup: document.getElementById('streamlinesGroup')
        };

        const metrics = {
            pressure: document.getElementById('valPressure'),
            diameter: document.getElementById('valDiameter'),
            flow: document.getElementById('valFlow'),
            velocity: document.getElementById('lblVelocityIn')
        };

        const slider = document.getElementById('pressureSlider');
        const statusIndicator = document.getElementById('statusIndicator');
        const statusText = document.getElementById('statusText');

        // 系统状态与参数
        let state = {
            autoPlay: true,
            time: 0,
            targetPressure: 0,
            currentPressure: 0, // 0 到 1 之间
            streamOffsets: [0, 0, 0, 0, 0] // 5条流线的偏移量
        };

        // 初始化流线 SVG 元素
        const streamLines = [];
        for (let i = 0; i < 5; i++) {
            const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
            path.setAttribute("fill", "none");
            path.setAttribute("stroke", "rgba(255,255,255,0.7)");
            path.setAttribute("stroke-width", i === 2 ? "2" : "1"); // 中间线粗一点
            path.setAttribute("stroke-dasharray", "15 30");
            paths.streamlinesGroup.appendChild(path);
            streamLines.push(path);
        }

        // 核心渲染函数
        function render(dt) {
            // 1. 状态更新逻辑 (平滑插值)
            if (state.autoPlay) {
                // 自动播放模式:压力随时间呈正弦波动,模拟系统压力波动
                state.time += dt * 0.001;
                // 映射到 0.05 ~ 0.95 避免贴边
                state.targetPressure = 0.5 + 0.45 * Math.sin(state.time); 
                slider.value = state.targetPressure;
            } else {
                state.targetPressure = parseFloat(slider.value);
            }
            
            // 阻尼跟随,使形变平滑
            state.currentPressure += (state.targetPressure - state.currentPressure) * 0.08;
            
            const P = state.currentPressure; // 0.0 ~ 1.0

            // 2. 物理与几何参数计算
            // 孔径最小120mm,最大200mm。对应的像素形变量
            // 原始管道半高: 120px (对应直径200mm)
            // 单侧最大压缩量: 48px (对应直径减少80mm) -> 方便视觉表现放大至 60px
            const maxDisplacement = 65; 
            const disp = P * maxDisplacement; 
            
            // 动态直径数据计算
            const realDiameter = 200 - P * 80;
            
            // 流量计算模型:展示无源限流的“理想终极解”特性
            // 基础流量模型: Q = k * P * A. A 与 D^2 成正比。
            // 我们构造一个平滑的渐近曲线,表现出压力增大但流量稳定(甚至在极高压时略微下降,保护后级)
            // 公式:Q = 450 * (1 - exp(-4 * P)) * (1 - 0.1 * P)
            let flowRate = 480 * (1 - Math.exp(-4.5 * P)) * (1 - 0.05 * P);
            if (P < 0.01) flowRate = 0;

            // 3. 构建 SVG 动态路径
            const topY = 130;
            const bottomY = 370;
            const ctrlY_top = topY + disp;
            const ctrlY_bottom = bottomY - disp;
            
            // 上橡胶体路径
            const topRubberPath = `M 150 50 L 850 50 L 850 ${topY} L 700 ${topY} C 620 ${topY}, 600 ${ctrlY_top}, 500 ${ctrlY_top} C 400 ${ctrlY_top}, 380 ${topY}, 300 ${topY} L 150 ${topY} Z`;
            // 上橡胶边缘路径
            const topEdgePath = `M 150 ${topY} L 300 ${topY} C 380 ${topY}, 400 ${ctrlY_top}, 500 ${ctrlY_top} C 600 ${ctrlY_top}, 620 ${topY}, 700 ${topY} L 850 ${topY}`;
            
            // 下橡胶体路径
            const bottomRubberPath = `M 150 450 L 850 450 L 850 ${bottomY} L 700 ${bottomY} C 620 ${bottomY}, 600 ${ctrlY_bottom}, 500 ${ctrlY_bottom} C 400 ${ctrlY_bottom}, 380 ${bottomY}, 300 ${bottomY} L 150 ${bottomY} Z`;
            // 下橡胶边缘路径
            const bottomEdgePath = `M 150 ${bottomY} L 300 ${bottomY} C 380 ${bottomY}, 400 ${ctrlY_bottom}, 500 ${ctrlY_bottom} C 600 ${ctrlY_bottom}, 620 ${bottomY}, 700 ${bottomY} L 850 ${bottomY}`;

            // 流体区域路径
            const fluidPath = `M 50 ${topY} L 300 ${topY} C 380 ${topY}, 400 ${ctrlY_top}, 500 ${ctrlY_top} C 600 ${ctrlY_top}, 620 ${topY}, 700 ${topY} L 950 ${topY} L 950 ${bottomY} L 700 ${bottomY} C 620 ${bottomY}, 600 ${ctrlY_bottom}, 500 ${ctrlY_bottom} C 400 ${ctrlY_bottom}, 380 ${bottomY}, 300 ${bottomY} L 50 ${bottomY} Z`;

            // 应用路径
            paths.rubberTop.setAttribute('d', topRubberPath);
            paths.rubberTopWire.setAttribute('d', topRubberPath);
            paths.rubberTopEdge.setAttribute('d', topEdgePath);
            
            paths.rubberBottom.setAttribute('d', bottomRubberPath);
            paths.rubberBottomWire.setAttribute('d', bottomRubberPath);
            paths.rubberBottomEdge.setAttribute('d', bottomEdgePath);
            
            paths.fluidArea.setAttribute('d', fluidPath);

            // 4. 更新流线和动画
            // 流速随着压力略微增加(文丘里效应),但整体流量受到截面积减小的限制
            const baseSpeed = 2 + P * 4; 
            
            for (let i = 0; i < 5; i++) {
                const k = i - 2; // -2, -1, 0, 1, 2
                const startY = 250 + k * 45;
                // 中心处的 Y 坐标随形变挤压
                const midY = 250 + k * (45 - disp/2.5);
                
                const streamPath = `M 50 ${startY} L 300 ${startY} C 380 ${startY}, 400 ${midY}, 500 ${midY} C 600 ${midY}, 620 ${startY}, 700 ${startY} L 950 ${startY}`;
                
                streamLines[i].setAttribute('d', streamPath);
                
                // 更新虚线偏移量产生流动动画
                state.streamOffsets[i] -= baseSpeed * (1 + 0.1 * Math.abs(k)); // 中间稍微慢一点,边缘快一点造成错落感
                streamLines[i].setAttribute('stroke-dashoffset', state.streamOffsets[i]);
                
                // 透明度随压力增强
                streamLines[i].setAttribute('opacity', 0.4 + 0.6 * P);
            }

            // 5. 动态标注更新
            // 孔径标注线位置更新
            paths.dimTopLine.setAttribute('y1', ctrlY_top);
            paths.dimTopLine.setAttribute('y2', ctrlY_top);
            paths.dimBottomLine.setAttribute('y1', ctrlY_bottom);
            paths.dimBottomLine.setAttribute('y2', ctrlY_bottom);
            paths.dimVerLine.setAttribute('y1', ctrlY_top + 5);
            paths.dimVerLine.setAttribute('y2', ctrlY_bottom - 5);
            
            // 受力指示器 (IFR 特征):压力越大,向内的挤压箭头越明显
            paths.forceArrowsGroup.setAttribute('opacity', P * 1.5); // 加强显示
            paths.txtForce.setAttribute('opacity', P > 0.3 ? (P - 0.3) * 2 : 0);

            // 6. 仪表盘数据更新
            metrics.pressure.innerText = P.toFixed(2);
            metrics.diameter.innerText = realDiameter.toFixed(0);
            metrics.flow.innerText = flowRate.toFixed(1);
            
            // 进料速度指示器
            metrics.velocity.innerText = (1 + P*3).toFixed(1) + " m/s";

            // 颜色动态变化:压力较高时仪表盘数值变红/橙警告
            if(P > 0.75) {
                metrics.pressure.style.color = '#ef4444';
            } else if (P > 0.5) {
                metrics.pressure.style.color = 'var(--accent-orange)';
            } else {
                metrics.pressure.style.color = 'var(--text-main)';
            }
        }

        // 主循环
        let lastTime = 0;
        function animate(timestamp) {
            const dt = timestamp - lastTime;
            lastTime = timestamp;
            if(dt < 100) { // 防止切后台回来瞬间的时间跳变
                render(dt);
            }
            requestAnimationFrame(animate);
        }

        // 交互事件监听
        slider.addEventListener('mousedown', () => {
            state.autoPlay = false;
            statusIndicator.classList.add('manual');
            statusText.innerText = 'MANUAL';
        });
        
        slider.addEventListener('touchstart', () => {
            state.autoPlay = false;
            statusIndicator.classList.add('manual');
            statusText.innerText = 'MANUAL';
        }, {passive: true});

        // 页面加载完成后自动启动
        window.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>柔性自适应节流阀 - TRIZ 最终理想解</title>
    <style>
        :root {
            --bg-color: #07090f;
            --panel-bg: #111625;
            --border-color: #2a3556;
            --text-main: #e2e8f0;
            --text-muted: #64748b;
            --accent-cyan: #00f0ff;
            --accent-blue: #3b82f6;
            --accent-orange: #ff5500;
            --accent-amber: #f59e0b;
            --rubber-color: #1e293b;
            --pipe-color: #334155;
            --font-mono: 'SF Mono', 'Cascadia Code', 'Fira Code', 'Courier New', monospace;
            --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", 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);
            height: 100vh;
            display: flex;
            flex-direction: column;
            overflow: hidden;
        }

        .header {
            padding: 1.5rem 2rem;
            display: flex;
            justify-content: space-between;
            align-items: flex-end;
            border-bottom: 1px solid var(--border-color);
            background: linear-gradient(to bottom, #111625, #07090f);
            z-index: 10;
        }

        .title-group h1 {
            font-size: 1.5rem;
            font-weight: 600;
            letter-spacing: 1px;
            margin-bottom: 0.5rem;
            display: flex;
            align-items: center;
            gap: 0.75rem;
        }

        .title-group h1::before {
            content: '';
            display: inline-block;
            width: 8px;
            height: 24px;
            background-color: var(--accent-cyan);
            box-shadow: 0 0 10px var(--accent-cyan);
        }

        .subtitle {
            font-size: 0.85rem;
            color: var(--text-muted);
            max-width: 600px;
            line-height: 1.5;
        }

        .ifr-badge {
            background-color: rgba(0, 240, 255, 0.1);
            border: 1px solid var(--accent-cyan);
            color: var(--accent-cyan);
            padding: 0.5rem 1rem;
            border-radius: 4px;
            font-size: 0.8rem;
            font-family: var(--font-mono);
            box-shadow: 0 0 15px rgba(0, 240, 255, 0.15) inset;
        }

        .main-container {
            flex: 1;
            display: flex;
            position: relative;
            justify-content: center;
            align-items: center;
            background-image: 
                linear-gradient(rgba(42, 53, 86, 0.3) 1px, transparent 1px),
                linear-gradient(90deg, rgba(42, 53, 86, 0.3) 1px, transparent 1px);
            background-size: 40px 40px;
            overflow: hidden;
        }

        .svg-wrapper {
            width: 100%;
            height: 100%;
            max-width: 1200px;
            max-height: 700px;
            padding: 2rem;
            position: relative;
            z-index: 2;
        }

        svg {
            width: 100%;
            height: 100%;
            filter: drop-shadow(0 10px 30px rgba(0,0,0,0.5));
        }

        /* Overlay UI */
        .hud-panel {
            position: absolute;
            background: rgba(17, 22, 37, 0.85);
            backdrop-filter: blur(10px);
            border: 1px solid var(--border-color);
            border-radius: 8px;
            padding: 1rem;
            z-index: 5;
            display: flex;
            flex-direction: column;
            gap: 1rem;
        }

        .hud-left {
            bottom: 2rem;
            left: 2rem;
            min-width: 280px;
        }

        .hud-right {
            top: 2rem;
            right: 2rem;
            min-width: 220px;
        }

        .data-row {
            display: flex;
            justify-content: space-between;
            align-items: center;
            font-family: var(--font-mono);
            font-size: 0.9rem;
        }

        .data-label {
            color: var(--text-muted);
            font-family: var(--font-sans);
            font-size: 0.8rem;
        }

        .data-value {
            color: var(--accent-cyan);
            font-weight: bold;
            font-size: 1.1rem;
            text-shadow: 0 0 8px rgba(0, 240, 255, 0.5);
        }

        .data-value.warning {
            color: var(--accent-orange);
            text-shadow: 0 0 8px rgba(255, 85, 0, 0.5);
        }

        /* Custom Slider */
        .control-group {
            display: flex;
            flex-direction: column;
            gap: 0.5rem;
            margin-top: 0.5rem;
            padding-top: 1rem;
            border-top: 1px dashed var(--border-color);
        }

        .slider-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .mode-indicator {
            font-size: 0.7rem;
            padding: 2px 6px;
            border-radius: 3px;
            background: var(--accent-blue);
            color: white;
            transition: all 0.3s ease;
        }

        .mode-indicator.manual {
            background: var(--accent-orange);
        }

        input[type=range] {
            -webkit-appearance: none;
            width: 100%;
            background: transparent;
        }
        
        input[type=range]:focus {
            outline: none;
        }
        
        input[type=range]::-webkit-slider-runnable-track {
            width: 100%;
            height: 6px;
            cursor: pointer;
            background: var(--pipe-color);
            border-radius: 3px;
        }
        
        input[type=range]::-webkit-slider-thumb {
            height: 16px;
            width: 16px;
            border-radius: 50%;
            background: var(--accent-cyan);
            cursor: pointer;
            -webkit-appearance: none;
            margin-top: -5px;
            box-shadow: 0 0 10px var(--accent-cyan);
            transition: transform 0.1s, background 0.3s;
        }

        input[type=range]:active::-webkit-slider-thumb {
            transform: scale(1.2);
            background: var(--accent-orange);
            box-shadow: 0 0 10px var(--accent-orange);
        }

        /* SVG Inner Styling via CSS */
        .flow-line {
            fill: none;
            stroke-width: 2.5;
            stroke-linecap: round;
        }
        
        .skeleton-wire {
            fill: none;
            stroke: rgba(255, 255, 255, 0.15);
            stroke-width: 1.5;
            stroke-dasharray: 4 4;
        }

        .dim-text {
            font-family: var(--font-mono);
            font-size: 14px;
            fill: var(--accent-cyan);
            text-anchor: middle;
        }

        .annotation-text {
            font-family: var(--font-sans);
            font-size: 12px;
            fill: var(--text-muted);
        }
    </style>
</head>
<body>

    <header class="header">
        <div class="title-group">
            <h1>柔性自适应节流阀原理演示</h1>
            <p class="subtitle">
                最终理想解 (IFR):无需外部传感器与能源,利用管道内流体压力上升自身的物理力,压缩高强度弹性喉口,实现自动减缩孔径进行无源限流。压力降低后依靠骨架回弹复位。
            </p>
        </div>
        <div class="ifr-badge">TRIZ / 资源利用 / 最终理想解</div>
    </header>

    <main class="main-container">
        
        <!-- Left HUD: Interaction & System Status -->
        <div class="hud-panel hud-left">
            <div class="data-row">
                <span class="data-label">系统运行状态</span>
                <span class="data-value" id="status-text">自适应调节中</span>
            </div>
            <div class="data-row">
                <span class="data-label">喉口孔径 (D)</span>
                <span class="data-value" id="aperture-readout">200 mm</span>
            </div>
            <div class="data-row">
                <span class="data-label">上游瞬时压力 (P)</span>
                <span class="data-value" id="pressure-readout">0.00 MPa</span>
            </div>
            
            <div class="control-group">
                <div class="slider-header">
                    <span class="data-label">压力模拟控制</span>
                    <span class="mode-indicator" id="mode-badge">自动播放</span>
                </div>
                <input type="range" id="pressure-slider" min="0" max="1" step="0.001" value="0">
                <span class="data-label" style="text-align: right; font-size: 0.7rem;">*滑动以切换手动模式</span>
            </div>
        </div>

        <!-- Right HUD: Structural Info -->
        <div class="hud-panel hud-right">
            <div class="data-row" style="border-bottom: 1px solid var(--border-color); padding-bottom: 0.5rem; margin-bottom: 0.5rem;">
                <span class="data-label" style="color:var(--text-main); font-weight:bold;">关键构件说明</span>
            </div>
            <div class="data-row">
                <span class="data-label">外壳材质</span>
                <span class="data-value" style="font-size:0.9rem; color:var(--text-muted)">高强度不锈钢</span>
            </div>
            <div class="data-row">
                <span class="data-label">节流元件</span>
                <span class="data-value" style="font-size:0.9rem; color:var(--accent-amber)">聚氨酯/橡胶</span>
            </div>
            <div class="data-row">
                <span class="data-label">内部支撑</span>
                <span class="data-value" style="font-size:0.9rem; color:var(--text-muted)">预应力钢丝骨架</span>
            </div>
            <div class="data-row">
                <span class="data-label">压力适用范围</span>
                <span class="data-value" style="font-size:0.9rem; color:var(--text-muted)">0 - 1.0 MPa</span>
            </div>
        </div>

        <div class="svg-wrapper">
            <svg id="core-svg" viewBox="0 0 1000 500" preserveAspectRatio="xMidYMid meet">
                <defs>
                    <!-- Flow Gradients -->
                    <linearGradient id="flow-grad" x1="0%" y1="0%" x2="100%" y2="0%">
                        <stop id="grad-stop-1" offset="0%" stop-color="#00f0ff" />
                        <stop id="grad-stop-2" offset="50%" stop-color="#3b82f6" />
                        <stop id="grad-stop-3" offset="100%" stop-color="#00f0ff" />
                    </linearGradient>
                    
                    <!-- Rubber Gradients -->
                    <linearGradient id="rubber-grad-top" x1="0%" y1="0%" x2="0%" y2="100%">
                        <stop offset="0%" stop-color="#111827" />
                        <stop offset="80%" stop-color="#1e293b" />
                        <stop id="rubber-inner-top" offset="100%" stop-color="#334155" />
                    </linearGradient>
                    <linearGradient id="rubber-grad-bottom" x1="0%" y1="100%" x2="0%" y2="0%">
                        <stop offset="0%" stop-color="#111827" />
                        <stop offset="80%" stop-color="#1e293b" />
                        <stop id="rubber-inner-bottom" offset="100%" stop-color="#334155" />
                    </linearGradient>

                    <!-- Metallic Pipe Gradient -->
                    <linearGradient id="pipe-grad" x1="0%" y1="0%" x2="0%" y2="100%">
                        <stop offset="0%" stop-color="#475569" />
                        <stop offset="50%" stop-color="#94a3b8" />
                        <stop offset="100%" stop-color="#334155" />
                    </linearGradient>

                    <filter id="glow-cyan" x="-20%" y="-20%" width="140%" height="140%">
                        <feGaussianBlur stdDeviation="5" result="blur" />
                        <feComposite in="SourceGraphic" in2="blur" operator="over" />
                    </filter>
                    
                    <filter id="glow-orange" x="-20%" y="-20%" width="140%" height="140%">
                        <feGaussianBlur stdDeviation="8" result="blur" />
                        <feComposite in="SourceGraphic" in2="blur" operator="over" />
                    </filter>

                    <!-- Arrow Marker -->
                    <marker id="arrowhead" markerWidth="6" markerHeight="6" refX="3" refY="3" orient="auto">
                        <polygon points="0 0, 6 3, 0 6" fill="var(--accent-cyan)" />
                    </marker>
                    <marker id="arrowhead-reverse" markerWidth="6" markerHeight="6" refX="3" refY="3" orient="auto">
                        <polygon points="6 0, 0 3, 6 6" fill="var(--accent-cyan)" />
                    </marker>
                </defs>

                <!-- Background Elements -->
                <!-- Flow Direction Indicator -->
                <path d="M 50,450 L 950,450" stroke="rgba(255,255,255,0.05)" stroke-width="1" />
                <path d="M 850,450 L 950,450" stroke="rgba(255,255,255,0.1)" stroke-width="2" marker-end="url(#arrowhead)" />
                <text x="900" y="440" class="annotation-text" text-anchor="middle">流体方向</text>

                <!-- Outer Steel Pipe Casing -->
                <g id="pipe-casing">
                    <!-- Top Wall -->
                    <rect x="100" y="50" width="800" height="20" fill="url(#pipe-grad)" stroke="#1e293b" />
                    <rect x="80" y="30" width="40" height="60" fill="#64748b" rx="2" /> <!-- Flange Left -->
                    <rect x="880" y="30" width="40" height="60" fill="#64748b" rx="2" /> <!-- Flange Right -->
                    
                    <!-- Bottom Wall -->
                    <rect x="100" y="430" width="800" height="20" fill="url(#pipe-grad)" stroke="#1e293b" />
                    <rect x="80" y="410" width="40" height="60" fill="#64748b" rx="2" /> <!-- Flange Left -->
                    <rect x="880" y="410" width="40" height="60" fill="#64748b" rx="2" /> <!-- Flange Right -->
                </g>

                <!-- Dynamic Fluid Flow Lines -->
                <g id="fluid-lines" filter="url(#glow-cyan)"></g>

                <!-- Rubber Elastic Throat (Dynamic Paths) -->
                <g id="throat-system">
                    <!-- Top Rubber -->
                    <path id="rubber-top" d="" fill="url(#rubber-grad-top)" stroke="#0f172a" stroke-width="2" />
                    <g id="skeleton-top"></g> <!-- Populated by JS -->
                    
                    <!-- Bottom Rubber -->
                    <path id="rubber-bottom" d="" fill="url(#rubber-grad-bottom)" stroke="#0f172a" stroke-width="2" />
                    <g id="skeleton-bottom"></g> <!-- Populated by JS -->
                </g>

                <!-- Dimension Indicators -->
                <g id="dimensions" opacity="0.8">
                    <!-- Center vertical line indicating narrowest point -->
                    <line x1="500" y1="70" x2="500" y2="430" stroke="rgba(0, 240, 255, 0.2)" stroke-width="1" stroke-dasharray="5 5" />
                    
                    <!-- Dynamic Dimension Arrows -->
                    <line id="dim-line-top" x1="500" y1="250" x2="500" y2="150" stroke="var(--accent-cyan)" stroke-width="1.5" marker-end="url(#arrowhead)" />
                    <line id="dim-line-bottom" x1="500" y1="250" x2="500" y2="350" stroke="var(--accent-cyan)" stroke-width="1.5" marker-end="url(#arrowhead)" />
                    
                    <!-- Fixed bounding lines for measurement -->
                    <line x1="520" y1="150" x2="550" y2="150" id="dim-bound-top" stroke="rgba(255,255,255,0.3)" stroke-width="1" />
                    <line x1="520" y1="350" x2="550" y2="350" id="dim-bound-bottom" stroke="rgba(255,255,255,0.3)" stroke-width="1" />
                    
                    <!-- Dynamic Text -->
                    <text id="dim-text-val" x="535" y="255" class="dim-text">200mm</text>
                </g>
                
                <!-- Labels -->
                <text x="350" y="100" class="annotation-text" fill="rgba(255,255,255,0.5)">钢丝骨架内衬</text>
                <line x1="350" y1="110" x2="350" y2="130" stroke="rgba(255,255,255,0.3)" stroke-width="1" />
                
                <text x="650" y="400" class="annotation-text" fill="rgba(255,255,255,0.5)">高弹性聚氨酯喉体</text>
                <line x1="650" y1="385" x2="650" y2="365" stroke="rgba(255,255,255,0.3)" stroke-width="1" />

                <!-- Upstream Pressure Visualization Area (Overlay) -->
                <rect x="120" y="70" width="100" height="360" fill="url(#glow-orange)" id="pressure-zone-glow" opacity="0" style="mix-blend-mode: screen;" />
                <text x="170" y="255" class="annotation-text" id="pressure-zone-text" text-anchor="middle" fill="#ff5500" opacity="0" font-weight="bold">高压积聚区</text>
            </svg>
        </div>
    </main>

    <script>
        document.addEventListener('DOMContentLoaded', () => {
            // Configuration & Constants
            const CONFIG = {
                svgWidth: 1000,
                svgHeight: 500,
                centerY: 250,
                pipeStartX: 100,
                pipeEndX: 900,
                throatCenterX: 500,
                rubberBaseTop: 70,    // Inner edge of top pipe casing
                rubberBaseBottom: 430, // Inner edge of bottom pipe casing
                uncompressedGap: 280,  // Visual pixels mapped to 200mm
                compressedGap: 168,    // Visual pixels mapped to 120mm (280 * 120/200)
                maxPressure: 1.0,      // MPa
                numFlowLines: 11,
                numSkeletonLines: 5
            };

            // Max displacement per rubber side
            const maxDisplacement = (CONFIG.uncompressedGap - CONFIG.compressedGap) / 2;

            // DOM Elements
            const rubberTop = document.getElementById('rubber-top');
            const rubberBottom = document.getElementById('rubber-bottom');
            const skeletonTop = document.getElementById('skeleton-top');
            const skeletonBottom = document.getElementById('skeleton-bottom');
            const fluidLinesGroup = document.getElementById('fluid-lines');
            const slider = document.getElementById('pressure-slider');
            const modeBadge = document.getElementById('mode-badge');
            const apertureReadout = document.getElementById('aperture-readout');
            const pressureReadout = document.getElementById('pressure-readout');
            const gradStop1 = document.getElementById('grad-stop-1');
            const gradStop2 = document.getElementById('grad-stop-2');
            const rubberInnerTop = document.getElementById('rubber-inner-top');
            const rubberInnerBottom = document.getElementById('rubber-inner-bottom');
            const dimLineTop = document.getElementById('dim-line-top');
            const dimLineBottom = document.getElementById('dim-line-bottom');
            const dimBoundTop = document.getElementById('dim-bound-top');
            const dimBoundBottom = document.getElementById('dim-bound-bottom');
            const dimTextVal = document.getElementById('dim-text-val');
            const pressureZoneGlow = document.getElementById('pressure-zone-glow');
            const pressureZoneText = document.getElementById('pressure-zone-text');
            const fluidLinesGroupFilter = fluidLinesGroup; 

            // State
            let currentPressure = 0; // 0.0 to 1.0
            let isAutoMode = true;
            let autoPlayTime = 0;
            let lastFrameTime = performance.now();
            let flowOffsets = Array(CONFIG.numFlowLines).fill(0);
            let animationFrameId;
            let interactionTimeout;

            // Utility: Color Interpolation
            function lerpColor(c1, c2, t) {
                const hex2rgb = hex => [parseInt(hex.slice(1,3),16), parseInt(hex.slice(3,5),16), parseInt(hex.slice(5,7),16)];
                const [r1, g1, b1] = hex2rgb(c1);
                const [r2, g2, b2] = hex2rgb(c2);
                const r = Math.round(r1 + (r2 - r1) * t);
                const g = Math.round(g1 + (g2 - g1) * t);
                const b = Math.round(b1 + (b2 - b1) * t);
                return `rgb(${r},${g},${b})`;
            }

            // Initialization: Create SVG Sub-elements
            function initSVG() {
                // Initialize Fluid Lines
                for (let i = 0; i < CONFIG.numFlowLines; i++) {
                    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
                    path.setAttribute('class', 'flow-line');
                    path.setAttribute('stroke', 'url(#flow-grad)');
                    // Dash array creates the dotted flow effect
                    path.setAttribute('stroke-dasharray', `${10 + Math.random()*20} ${15 + Math.random()*20}`);
                    path.setAttribute('id', `flow-line-${i}`);
                    fluidLinesGroup.appendChild(path);
                }

                // Initialize Skeleton Wires
                for (let i = 0; i < CONFIG.numSkeletonLines; i++) {
                    const pTop = document.createElementNS('http://www.w3.org/2000/svg', 'path');
                    pTop.setAttribute('class', 'skeleton-wire');
                    pTop.setAttribute('id', `skeleton-top-${i}`);
                    skeletonTop.appendChild(pTop);

                    const pBottom = document.createElementNS('http://www.w3.org/2000/svg', 'path');
                    pBottom.setAttribute('class', 'skeleton-wire');
                    pBottom.setAttribute('id', `skeleton-bottom-${i}`);
                    skeletonBottom.appendChild(pBottom);
                }
            }

            // Core Render Function
            function updateVisualization(pressure, deltaTime) {
                // Constrain pressure
                pressure = Math.max(0, Math.min(1, pressure));
                
                // 1. Calculate Physical Displacement
                const displacement = maxDisplacement * pressure;
                
                // Actual mm value mapping
                const currentApertureMM = 200 - (80 * pressure);
                
                // Top inner boundary Y
                const uncompressedTopInnerY = CONFIG.centerY - (CONFIG.uncompressedGap / 2);
                const currentTopInnerY = uncompressedTopInnerY + displacement;
                
                // Bottom inner boundary Y
                const uncompressedBottomInnerY = CONFIG.centerY + (CONFIG.uncompressedGap / 2);
                const currentBottomInnerY = uncompressedBottomInnerY - displacement;

                // Bezier Curve Control Point Offsets
                const cX1 = 300, cX2 = 400; // Left side of throat curve
                const cX3 = 600, cX4 = 700; // Right side of throat curve

                // 2. Update Rubber Paths
                // Top Rubber Profile
                const topD = `M ${CONFIG.pipeStartX},${CONFIG.rubberBaseTop} 
                              L ${CONFIG.pipeEndX},${CONFIG.rubberBaseTop} 
                              L ${CONFIG.pipeEndX},${uncompressedTopInnerY} 
                              C ${cX4},${uncompressedTopInnerY} ${cX3},${currentTopInnerY} ${CONFIG.throatCenterX},${currentTopInnerY} 
                              C ${cX2},${currentTopInnerY} ${cX1},${uncompressedTopInnerY} ${CONFIG.pipeStartX},${uncompressedTopInnerY} Z`;
                rubberTop.setAttribute('d', topD);

                // Bottom Rubber Profile
                const bottomD = `M ${CONFIG.pipeStartX},${CONFIG.rubberBaseBottom} 
                                 L ${CONFIG.pipeEndX},${CONFIG.rubberBaseBottom} 
                                 L ${CONFIG.pipeEndX},${uncompressedBottomInnerY} 
                                 C ${cX4},${uncompressedBottomInnerY} ${cX3},${currentBottomInnerY} ${CONFIG.throatCenterX},${currentBottomInnerY} 
                                 C ${cX2},${currentBottomInnerY} ${cX1},${uncompressedBottomInnerY} ${CONFIG.pipeStartX},${uncompressedBottomInnerY} Z`;
                rubberBottom.setAttribute('d', bottomD);

                // 3. Update Skeleton Wires (Interpolated between base and inner surface)
                for(let i=0; i<CONFIG.numSkeletonLines; i++) {
                    const ratio = (i + 1) / (CONFIG.numSkeletonLines + 1); // e.g., 0.2, 0.4, 0.6...
                    
                    // Top skeleton line
                    const wireYTopEnd = CONFIG.rubberBaseTop + (uncompressedTopInnerY - CONFIG.rubberBaseTop) * ratio;
                    const wireYTopMid = CONFIG.rubberBaseTop + (currentTopInnerY - CONFIG.rubberBaseTop) * ratio;
                    const topWireD = `M ${CONFIG.pipeStartX},${wireYTopEnd} 
                                      C ${cX1},${wireYTopEnd} ${cX2},${wireYTopMid} ${CONFIG.throatCenterX},${wireYTopMid} 
                                      C ${cX3},${wireYTopMid} ${cX4},${wireYTopEnd} ${CONFIG.pipeEndX},${wireYTopEnd}`;
                    document.getElementById(`skeleton-top-${i}`).setAttribute('d', topWireD);

                    // Bottom skeleton line
                    const wireYBottomEnd = CONFIG.rubberBaseBottom - (CONFIG.rubberBaseBottom - uncompressedBottomInnerY) * ratio;
                    const wireYBottomMid = CONFIG.rubberBaseBottom - (CONFIG.rubberBaseBottom - currentBottomInnerY) * ratio;
                    const bottomWireD = `M ${CONFIG.pipeStartX},${wireYBottomEnd} 
                                         C ${cX1},${wireYBottomEnd} ${cX2},${wireYBottomMid} ${CONFIG.throatCenterX},${wireYBottomMid} 
                                         C ${cX3},${wireYBottomMid} ${cX4},${wireYBottomEnd} ${CONFIG.pipeEndX},${wireYBottomEnd}`;
                    document.getElementById(`skeleton-bottom-${i}`).setAttribute('d', bottomWireD);
                }

                // 4. Update Fluid Lines
                // Dynamic flow speed calculation based on venturi effect and pressure
                // Flow is generally faster under high pressure, especially in the narrow throat.
                // We simulate this by moving the dashoffset.
                const baseSpeed = 1.0;
                const pressureMultiplier = 4.0;
                const currentSpeed = baseSpeed + (pressure * pressureMultiplier);

                const currentApertureVisual = currentBottomInnerY - currentTopInnerY;

                for(let i=0; i<CONFIG.numFlowLines; i++) {
                    // Calculate relative vertical position (-1 to 1)
                    const relY = (i / (CONFIG.numFlowLines - 1)) * 2 - 1; 
                    
                    // Apply a power curve to bunch lines closer to the center slightly
                    const distProfile = Math.sign(relY) * Math.pow(Math.abs(relY), 0.8);
                    
                    const startY = CONFIG.centerY + distProfile * (CONFIG.uncompressedGap / 2) * 0.85; // 0.85 margin to not hit rubber exactly
                    const midY = CONFIG.centerY + distProfile * (currentApertureVisual / 2) * 0.85;

                    const flowD = `M ${CONFIG.pipeStartX},${startY} 
                                   C ${cX1},${startY} ${cX2},${midY} ${CONFIG.throatCenterX},${midY} 
                                   C ${cX3},${midY} ${cX4},${startY} ${CONFIG.pipeEndX},${startY}`;
                    
                    const pathEl = document.getElementById(`flow-line-${i}`);
                    pathEl.setAttribute('d', flowD);
                    
                    // Animate Offset
                    flowOffsets[i] -= currentSpeed * (deltaTime / 16); // Normalize to 60fps roughly
                    pathEl.setAttribute('stroke-dashoffset', flowOffsets[i]);
                }

                // 5. Update Colors & Materials based on Pressure
                const colorCyan = '#00f0ff';
                const colorBlue = '#3b82f6';
                const colorOrange = '#ff5500';
                const colorRed = '#ef4444';
                const colorRubberBase = '#334155';
                const colorRubberStress = '#9a3412'; // Dark amber/orange for stress

                // Fluid Gradient changes (Upstream gets orange/red under high pressure)
                gradStop1.setAttribute('stop-color', lerpColor(colorCyan, colorRed, Math.pow(pressure, 1.5)));
                gradStop2.setAttribute('stop-color', lerpColor(colorBlue, colorOrange, pressure));
                // Stop 3 remains cyan (regulated flow downstream)

                // Rubber inner edge glow indicates stress
                const stressColor = lerpColor(colorRubberBase, colorRubberStress, pressure);
                rubberInnerTop.setAttribute('stop-color', stressColor);
                rubberInnerBottom.setAttribute('stop-color', stressColor);

                // Upstream visual warning overlay
                pressureZoneGlow.style.opacity = (pressure * 0.6).toFixed(2);
                pressureZoneText.style.opacity = pressure > 0.5 ? ((pressure - 0.5) * 2).toFixed(2) : 0;

                // 6. Update UI Annotations
                // Dimensions
                dimLineTop.setAttribute('y2', currentTopInnerY);
                dimLineBottom.setAttribute('y2', currentBottomInnerY);
                dimBoundTop.setAttribute('y1', currentTopInnerY);
                dimBoundTop.setAttribute('y2', currentTopInnerY);
                dimBoundBottom.setAttribute('y1', currentBottomInnerY);
                dimBoundBottom.setAttribute('y2', currentBottomInnerY);
                dimTextVal.textContent = `${currentApertureMM.toFixed(1)} mm`;

                // Readouts
                apertureReadout.textContent = `${currentApertureMM.toFixed(1)} mm`;
                const actualPressureValue = (pressure * CONFIG.maxPressure).toFixed(2);
                pressureReadout.textContent = `${actualPressureValue} MPa`;
                
                if(pressure > 0.8) {
                    pressureReadout.classList.add('warning');
                    apertureReadout.classList.add('warning');
                    document.getElementById('status-text').textContent = "高压强力限流中";
                    document.getElementById('status-text').style.color = "var(--accent-orange)";
                } else if (pressure > 0.3) {
                    pressureReadout.classList.remove('warning');
                    apertureReadout.classList.remove('warning');
                    document.getElementById('status-text').textContent = "自适应调节中";
                    document.getElementById('status-text').style.color = "var(--accent-cyan)";
                } else {
                    pressureReadout.classList.remove('warning');
                    apertureReadout.classList.remove('warning');
                    document.getElementById('status-text').
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TRIZ 最终理想解 - 柔性自适应无源限流器</title>
    <style>
        :root {
            --bg-base: #06090e;
            --panel-bg: rgba(13, 17, 28, 0.85);
            --border-glow: #1e3a5f;
            --text-main: #e2e8f0;
            --text-muted: #64748b;
            --neon-cyan: #00f0ff;
            --neon-red: #ff3c00;
            --rubber-color: #d97706;
            --font-mono: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
        }

        body, html {
            margin: 0;
            padding: 0;
            height: 100%;
            background-color: var(--bg-base);
            background-image: 
                radial-gradient(circle at 50% 50%, rgba(0, 240, 255, 0.03) 0%, transparent 60%),
                linear-gradient(rgba(255, 255, 255, 0.02) 1px, transparent 1px),
                linear-gradient(90deg, rgba(255, 255, 255, 0.02) 1px, transparent 1px);
            background-size: 100% 100%, 40px 40px, 40px 40px;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
            color: var(--text-main);
            display: flex;
            align-items: center;
            justify-content: center;
            overflow: hidden;
        }

        .dashboard {
            width: 96%;
            max-width: 1400px;
            display: flex;
            flex-direction: column;
            gap: 1.5rem;
            position: relative;
            z-index: 10;
        }

        .header {
            display: flex;
            justify-content: space-between;
            align-items: flex-end;
            padding-bottom: 1rem;
            border-bottom: 1px solid rgba(0, 240, 255, 0.2);
        }

        .header-title {
            margin: 0;
            font-size: 1.75rem;
            font-weight: 300;
            letter-spacing: 2px;
            color: var(--neon-cyan);
            text-transform: uppercase;
        }

        .header-subtitle {
            font-family: var(--font-mono);
            font-size: 0.85rem;
            color: var(--text-muted);
            letter-spacing: 1px;
        }

        .visualizer-container {
            position: relative;
            width: 100%;
            aspect-ratio: 21 / 9;
            background: rgba(4, 7, 12, 0.6);
            border: 1px solid var(--border-glow);
            border-radius: 8px;
            box-shadow: 0 0 30px rgba(0, 0, 0, 0.8), inset 0 0 20px rgba(0, 240, 255, 0.05);
            overflow: hidden;
            backdrop-filter: blur(4px);
        }

        .svg-canvas {
            width: 100%;
            height: 100%;
            display: block;
        }

        .hud-overlay {
            position: absolute;
            top: 20px;
            left: 20px;
            pointer-events: none;
        }

        .hud-badge {
            background: rgba(0, 240, 255, 0.1);
            border: 1px solid var(--neon-cyan);
            color: var(--neon-cyan);
            padding: 4px 10px;
            font-size: 0.75rem;
            font-family: var(--font-mono);
            border-radius: 4px;
            box-shadow: 0 0 10px rgba(0, 240, 255, 0.2);
        }

        .control-panel {
            display: grid;
            grid-template-columns: 1fr 1fr 1fr;
            gap: 1.5rem;
            background: var(--panel-bg);
            padding: 1.5rem;
            border-radius: 8px;
            border: 1px solid rgba(255, 255, 255, 0.05);
        }

        .data-card {
            display: flex;
            flex-direction: column;
            gap: 0.5rem;
            padding: 1rem;
            background: rgba(0, 0, 0, 0.4);
            border-left: 3px solid var(--neon-cyan);
            border-radius: 0 6px 6px 0;
        }

        .data-card.pressure-card { border-left-color: var(--neon-red); }
        .data-card.aperture-card { border-left-color: var(--rubber-color); }
        
        .data-label {
            font-size: 0.8rem;
            color: var(--text-muted);
            text-transform: uppercase;
            letter-spacing: 1px;
        }

        .data-value {
            font-family: var(--font-mono);
            font-size: 1.8rem;
            font-weight: bold;
            display: flex;
            align-items: baseline;
            gap: 0.5rem;
        }

        .data-unit {
            font-size: 1rem;
            color: var(--text-muted);
            font-weight: normal;
        }

        .slider-container {
            display: flex;
            flex-direction: column;
            gap: 0.75rem;
            margin-top: 0.5rem;
        }

        input[type=range] {
            -webkit-appearance: none;
            width: 100%;
            background: transparent;
        }

        input[type=range]::-webkit-slider-thumb {
            -webkit-appearance: none;
            height: 20px;
            width: 20px;
            border-radius: 50%;
            background: var(--neon-red);
            cursor: pointer;
            margin-top: -8px;
            box-shadow: 0 0 15px var(--neon-red);
        }

        input[type=range]::-webkit-slider-runnable-track {
            width: 100%;
            height: 4px;
            cursor: pointer;
            background: rgba(255, 255, 255, 0.1);
            border-radius: 2px;
        }

        .auto-tag {
            font-size: 0.75rem;
            color: var(--neon-cyan);
            background: rgba(0, 240, 255, 0.1);
            padding: 2px 6px;
            border-radius: 4px;
            margin-left: 10px;
            display: inline-block;
            transition: opacity 0.3s;
        }

        .hidden { opacity: 0.2; }

    </style>
</head>
<body>

    <div class="dashboard">
        <div class="header">
            <div>
                <h1 class="header-title">柔性自适应无源限流器</h1>
                <div class="header-subtitle">TRIZ 最终理想解 (IFR) 原理动态推演系统 // VASCULAR AUTO-REGULATION MODEL</div>
            </div>
            <div class="header-subtitle" style="text-align: right;">
                STATUS: <span style="color: var(--neon-cyan)">ACTIVE</span><br>
                POWER: PASSIVE / ZERO-ENERGY
            </div>
        </div>

        <div class="visualizer-container">
            <div class="hud-overlay">
                <span class="hud-badge">SYS. IFR_VISUALIZATION</span>
            </div>
            <svg class="svg-canvas" viewBox="0 0 1200 500" preserveAspectRatio="xMidYMid meet">
                <defs>
                    <!-- Flow Gradient (Dynamic based on pressure) -->
                    <linearGradient id="fluidGradient" x1="0%" y1="0%" x2="100%" y2="0%">
                        <stop id="grad-start" offset="0%" stop-color="#00f0ff" />
                        <stop id="grad-mid" offset="40%" stop-color="#00f0ff" />
                        <stop offset="65%" stop-color="#00f0ff" />
                        <stop offset="100%" stop-color="#00f0ff" />
                    </linearGradient>

                    <!-- Metallic Casing Gradient -->
                    <linearGradient id="metalGrad" x1="0%" y1="0%" x2="0%" y2="100%">
                        <stop offset="0%" stop-color="#475569" />
                        <stop offset="50%" stop-color="#1e293b" />
                        <stop offset="100%" stop-color="#0f172a" />
                    </linearGradient>

                    <!-- Rubber Texture Pattern -->
                    <pattern id="rubberHatch" width="8" height="8" patternTransform="rotate(45)" patternUnits="userSpaceOnUse">
                        <line x1="0" y1="0" x2="0" y2="8" stroke="#78350f" stroke-width="2" opacity="0.5"/>
                    </pattern>

                    <!-- Glow Filter -->
                    <filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
                        <feGaussianBlur stdDeviation="4" result="blur" />
                        <feComposite in="SourceGraphic" in2="blur" operator="over" />
                    </filter>
                    
                    <filter id="intense-glow" x="-50%" y="-50%" width="200%" height="200%">
                        <feGaussianBlur stdDeviation="8" result="blur" />
                        <feComposite in="SourceGraphic" in2="blur" operator="over" />
                    </filter>
                </defs>

                <!-- Grid Background inside SVG -->
                <g stroke="rgba(255,255,255,0.03)" stroke-width="1">
                    <line x1="300" y1="0" x2="300" y2="500" stroke-dasharray="4 4" />
                    <line x1="600" y1="0" x2="600" y2="500" stroke-dasharray="4 4" />
                    <line x1="900" y1="0" x2="900" y2="500" stroke-dasharray="4 4" />
                    <line x1="0" y1="250" x2="1200" y2="250" stroke="rgba(0,240,255,0.1)" />
                </g>

                <!-- Fluid Dynamics Group (Rendered below pipes) -->
                <g id="fluid-group"></g>

                <!-- Steel Casing Structure -->
                <!-- Top Casing -->
                <path d="M -50 90 L 1250 90 L 1250 150 L -50 150 Z" fill="url(#metalGrad)" stroke="#64748b" stroke-width="1.5" />
                <!-- Bottom Casing -->
                <path d="M -50 350 L 1250 350 L 1250 410 L -50 410 Z" fill="url(#metalGrad)" stroke="#64748b" stroke-width="1.5" />
                
                <!-- Inner Pipe Guidelines -->
                <line x1="0" y1="150" x2="400" y2="150" stroke="#cbd5e1" stroke-width="3" />
                <line x1="0" y1="350" x2="400" y2="350" stroke="#cbd5e1" stroke-width="3" />
                <line x1="800" y1="150" x2="1200" y2="150" stroke="#cbd5e1" stroke-width="3" />
                <line x1="800" y1="350" x2="1200" y2="350" stroke="#cbd5e1" stroke-width="3" />

                <!-- Elastic Rubber Throat Core -->
                <g id="throat-group">
                    <!-- Top Rubber Base -->
                    <path id="rubber-top" d="M 400 90 L 800 90 L 800 150 C 650 150, 550 150, 400 150 Z" fill="#b45309" stroke="#fbbf24" stroke-width="2" />
                    <path id="rubber-top-tex" d="M 400 90 L 800 90 L 800 150 C 650 150, 550 150, 400 150 Z" fill="url(#rubberHatch)" />
                    
                    <!-- Bottom Rubber Base -->
                    <path id="rubber-bottom" d="M 400 410 L 800 410 L 800 350 C 650 350, 550 350, 400 350 Z" fill="#b45309" stroke="#fbbf24" stroke-width="2" />
                    <path id="rubber-bottom-tex" d="M 400 410 L 800 410 L 800 350 C 650 350, 550 350, 400 350 Z" fill="url(#rubberHatch)" />

                    <!-- Internal Steel Wire Skeleton (Generated dynamically) -->
                    <g id="skeleton-group"></g>
                </g>

                <!-- Annotation Lines and Labels -->
                <g class="annotations" font-family="'JetBrains Mono', Consolas, monospace" font-size="13" fill="#cbd5e1" font-weight="bold">
                    <!-- Left: Problem / Resource -->
                    <polyline points="200,280 200,440 220,440" fill="none" stroke="#ff3c00" stroke-width="1.5" />
                    <circle cx="200" cy="280" r="3" fill="#ff3c00" />
                    <text x="230" y="444" fill="#ff3c00">1. 动能资源化</text>
                    <text x="230" y="464" font-size="11" fill="#94a3b8" font-weight="normal">压力升高,自动驱动形变(无需外部电源)</text>

                    <!-- Center: Core Innovation -->
                    <polyline points="600,150 600,60 620,60" fill="none" stroke="#fbbf24" stroke-width="1.5" />
                    <circle cx="600" cy="150" r="3" fill="#fbbf24" />
                    <text x="630" y="64" fill="#fbbf24">2. 仿生弹性喉口 (可变节流)</text>
                    <text x="630" y="84" font-size="11" fill="#94a3b8" font-weight="normal">利用橡胶骨架随压力收缩孔径,抵消流量增量</text>

                    <!-- Right: IFR State -->
                    <polyline points="1000,280 1000,440 1020,440" fill="none" stroke="#00f0ff" stroke-width="1.5" />
                    <circle cx="1000" cy="280" r="3" fill="#00f0ff" filter="url(#glow)"/>
                    <text x="1030" y="444" fill="#00f0ff">3. 最终理想解 (IFR)</text>
                    <text x="1030" y="464" font-size="11" fill="#94a3b8" font-weight="normal">消除矛盾:零传感器实现无源自适应恒定稳流</text>
                </g>
            </svg>
        </div>

        <div class="control-panel">
            <!-- Manual Pressure Override -->
            <div class="data-card pressure-card">
                <div class="data-label">
                    系统入口流体压力 (P_in) 
                    <span class="auto-tag" id="auto-status">自动演示中</span>
                </div>
                <div class="slider-container">
                    <input type="range" id="pressure-slider" min="0" max="1" step="0.01" value="0">
                </div>
                <div class="data-value" style="color: var(--neon-red); margin-top: 5px;">
                    <span id="val-pressure">0.00</span> <span class="data-unit">MPa</span>
                </div>
            </div>

            <!-- Throat Status -->
            <div class="data-card aperture-card">
                <div class="data-label">喉口实时动态孔径 (D_throat)</div>
                <div class="data-value" style="color: var(--rubber-color); margin-top: auto;">
                    <span id="val-aperture">200</span> <span class="data-unit">mm</span>
                </div>
                <div style="font-size: 0.75rem; color: #64748b; font-family: var(--font-mono);">
                    额定: 200mm | 极限压缩: 120mm
                </div>
            </div>

            <!-- Output Status -->
            <div class="data-card">
                <div class="data-label">出口稳态流量判定 (Q_out)</div>
                <div class="data-value" style="color: var(--neon-cyan); margin-top: auto;">
                    <span id="val-flow">STABLE</span> <span class="data-unit">100% REGULATED</span>
                </div>
                <div style="font-size: 0.75rem; color: #64748b; font-family: var(--font-mono);">
                    状态: <span id="flow-status-text" style="color: #00f0ff;">畅通 (低压)</span>
                </div>
            </div>
        </div>
    </div>

    <script>
        document.addEventListener("DOMContentLoaded", () => {
            // Configuration
            const numFluidLines = 9;
            const fluidGroup = document.getElementById('fluid-group');
            const skeletonGroup = document.getElementById('skeleton-group');
            const fluidPaths = [];
            
            // Create Fluid Lines
            for (let i = 0; i < numFluidLines; i++) {
                const p = document.createElementNS("http://www.w3.org/2000/svg", "path");
                p.setAttribute("fill", "none");
                p.setAttribute("stroke", "url(#fluidGradient)");
                // Central lines are thicker
                const width = i === 4 ? 4 : (i === 3 || i === 5) ? 3 : 2;
                p.setAttribute("stroke-width", width.toString());
                p.setAttribute("stroke-dasharray", "15 45");
                p.setAttribute("filter", "url(#glow)");
                fluidGroup.appendChild(p);
                fluidPaths.push(p);
                
                // Add a faint background track for each line
                const track = document.createElementNS("http://www.w3.org/2000/svg", "path");
                track.setAttribute("fill", "none");
                track.setAttribute("stroke", "rgba(0, 240, 255, 0.15)");
                track.setAttribute("stroke-width", "1");
                fluidGroup.appendChild(track);
                fluidPaths.push({ isTrack: true, el: track, index: i });
            }

            // Create Rubber Skeleton Lines (Steel Wires)
            const numSkeletons = 4;
            const skeletonsTop = [];
            const skeletonsBottom = [];
            for (let i = 0; i < numSkeletons; i++) {
                const st = document.createElementNS("http://www.w3.org/2000/svg", "path");
                st.setAttribute("fill", "none");
                st.setAttribute("stroke", "rgba(255, 255, 255, 0.4)");
                st.setAttribute("stroke-width", "1.5");
                st.setAttribute("stroke-dasharray", "3 4");
                skeletonGroup.appendChild(st);
                skeletonsTop.push(st);

                const sb = document.createElementNS("http://www.w3.org/2000/svg", "path");
                sb.setAttribute("fill", "none");
                sb.setAttribute("stroke", "rgba(255, 255, 255, 0.4)");
                sb.setAttribute("stroke-width", "1.5");
                sb.setAttribute("stroke-dasharray", "3 4");
                skeletonGroup.appendChild(sb);
                skeletonsBottom.push(sb);
            }

            // State & Interaction
            let time = 0;
            let dashOffset = 0;
            let isManual = false;
            let targetPressure = 0;
            let currentPressure = 0; // Smoothed
            
            const slider = document.getElementById('pressure-slider');
            const autoStatus = document.getElementById('auto-status');
            
            slider.addEventListener('input', (e) => {
                isManual = true;
                targetPressure = parseFloat(e.target.value);
                autoStatus.innerText = "手动控制中";
                autoStatus.classList.add('hidden');
                autoStatus.style.color = "#ff3c00";
                autoStatus.style.background = "rgba(255, 60, 0, 0.1)";
            });

            // Geometry Constraints
            const center = 250;
            const maxGap = 200;      // 350 - 150 = 200 (at 0 MPa)
            const minGap = 120;      // Target min gap (at 1 MPa)
            const maxDeformation = (maxGap - minGap) / 2; // 40px per side

            function updatePathInterpolation() {
                // Determine target pressure (Sine wave if auto)
                if (!isManual) {
                    targetPressure = (Math.sin(time * 0.8) + 1) / 2; // Cycles between 0 and 1
                    slider.value = targetPressure;
                }

                // Easing for smooth visual transition
                currentPressure += (targetPressure - currentPressure) * 0.1;
                
                // Deformation Calculation
                const defY = maxDeformation * currentPressure; // 0 to 40
                
                // --- 1. Update Rubber Throat Shapes ---
                const topY = 150 + defY;
                const botY = 350 - defY;
                
                const topPath = `M 400 90 L 800 90 L 800 150 C 650 ${topY}, 550 ${topY}, 400 150 Z`;
                document.getElementById('rubber-top').setAttribute('d', topPath);
                document.getElementById('rubber-top-tex').setAttribute('d', topPath);
                
                const botPath = `M 400 410 L 800 410 L 800 350 C 650 ${botY}, 550 ${botY}, 400 350 Z`;
                document.getElementById('rubber-bottom').setAttribute('d', botPath);
                document.getElementById('rubber-bottom-tex').setAttribute('d', botPath);

                // --- 2. Update Wire Skeletons ---
                for (let i = 0; i < numSkeletons; i++) {
                    const depthTop = 105 + (i * 12);
                    const defFactorT = (depthTop - 90) / 60; 
                    const yCtrlTop = depthTop + (defY * defFactorT);
                    skeletonsTop[i].setAttribute('d', `M 400 ${depthTop} C 550 ${yCtrlTop}, 650 ${yCtrlTop}, 800 ${depthTop}`);

                    const depthBot = 395 - (i * 12);
                    const defFactorB = (410 - depthBot) / 60;
                    const yCtrlBot = depthBot - (defY * defFactorB);
                    skeletonsBottom[i].setAttribute('d', `M 400 ${depthBot} C 550 ${yCtrlBot}, 650 ${yCtrlBot}, 800 ${depthBot}`);
                }

                // --- 3. Update Fluid Dynamics ---
                const currentGap = maxGap - (defY * 2);
                const gapRatio = currentGap / maxGap;

                // Base flow speed + increase in the throat due to restriction
                // However, visual dash speed is uniform, so we manipulate offset globally, 
                // but we simulate higher kinetic energy via color and frequency.
                const baseSpeed = 2;
                const kineticMultiplier = 1 + (currentPressure * 2);
                dashOffset -= baseSpeed * kineticMultiplier;

                fluidPaths.forEach((pathObj, i) => {
                    const isTrack = pathObj.isTrack;
                    const el = isTrack ? pathObj.el : pathObj;
                    const idx = isTrack ? pathObj.index : i;
                    
                    // Offset from center for this specific streamline
                    const originalOffset = (idx - Math.floor(numFluidLines/2)) * 22;
                    const currentOffset = originalOffset * gapRatio;
                    
                    const yEnter = center + originalOffset;
                    const yMid = center + currentOffset;

                    // Smooth B-spline like curve through the throat
                    const d = `M -50 ${yEnter} L 400 ${yEnter} C 500 ${yEnter}, 500 ${yMid}, 600 ${yMid} C 700 ${yMid}, 700 ${yEnter}, 800 ${yEnter} L 1250 ${yEnter}`;
                    
                    el.setAttribute('d', d);
                    if (!isTrack) {
                        el.setAttribute('stroke-dashoffset', dashOffset.toString());
                    }
                });

                // --- 4. Update Colors (Pressure visualization) ---
                // Red shifts as pressure increases on the left side
                const r = Math.floor(0 + currentPressure * 255);
                const g = Math.floor(240 - currentPressure * 180); // Cyan to Orange/Red
                const b = Math.floor(255 - currentPressure * 255);
                
                const highPressureColor = `rgb(${r}, ${g}, ${b})`;
                document.getElementById('grad-start').setAttribute('stop-color', highPressureColor);
                document.getElementById('grad-mid').setAttribute('stop-color', highPressureColor);
                
                // Color intensity logic for glow
                if(currentPressure > 0.8) {
                    document.getElementById('rubber-top').setAttribute('filter', 'url(#glow)');
                    document.getElementById('rubber-bottom').setAttribute('filter', 'url(#glow)');
                } else {
                    document.getElementById('rubber-top').removeAttribute('filter');
                    document.getElementById('rubber-bottom').removeAttribute('filter');
                }

                // --- 5. Update UI Data ---
                document.getElementById('val-pressure').innerText = currentPressure.toFixed(2);
                document.getElementById('val-aperture').innerText = Math.round(maxGap - (defY * 2));
                
                const statusText = document.getElementById('flow-status-text');
                if (currentPressure < 0.3) {
                    statusText.innerText = "畅通 (低压区)";
                    statusText.style.color = "#00f0ff";
                } else if (currentPressure < 0.7) {
                    statusText.innerText = "自适应调节中 (中压区)";
                    statusText.style.color = "#fbbf24";
                } else {
                    statusText.innerText = "高压强力限流 (稳压输出)";
                    statusText.style.color = "#ff3c00";
                }
            }

            // Animation Loop
            let lastTime = 0;
            function animate(timestamp) {
                const deltaTime = timestamp - lastTime;
                lastTime = timestamp;
                
                // time scale for auto-play
                time += 0.015; 
                
                updatePathInterpolation();
                requestAnimationFrame(animate);
            }
            
            // Start Loop
            requestAnimationFrame(animate);
        });
    </script>
</body>
</html>
积分规则:每轮对话扣10分