8、多物理场仿真组件 (风力发电机) - /设计与仿真组件/multi-physics-simulation

76个工业组件库示例汇总

多物理场仿真组件 - 风力发电机模拟器

简介

这是一个基于Web的多物理场仿真组件,专注于模拟风力发电机在不同工况下的行为。它提供了一个交互式界面,允许用户设置结构、流体和电磁相关的参数,运行模拟仿真,并可视化结果。

该组件旨在为工程设计、教育和研究提供一个直观的工具,用于理解复杂的多物理场现象。

功能特点

  • 多物理场支持: 支持结构分析、流体动力学分析和电磁场分析,以及它们的耦合模拟(耦合分析待实现)。
  • 参数化设置: 通过左侧面板设置材料属性、载荷条件、流体属性、电磁属性和仿真控制参数。
  • 实时3D可视化: 使用Three.js渲染风力发电机的简化3D模型,并根据仿真结果和用户选择更新颜色映射以显示物理量分布(如应力、速度)。
  • 模型交互: 支持通过轨道控制器(OrbitControls)旋转、缩放和平移3D视图。
  • 结果展示: 在右侧面板通过选项卡展示不同物理场的关键结果指标(如最大应力、最大速度、安全系数等)和相关图表。
  • 仿真流程模拟: 模拟仿真计算过程,显示进度条、处理时间和状态信息。
  • 仿真报告: 在右侧面板实时显示仿真过程中的关键信息、警告和错误。
  • 响应式布局: 界面元素在不同屏幕尺寸下会进行调整。

使用说明

前提条件

  • 现代浏览器: 支持WebGL和最新JavaScript特性(推荐Chrome, Firefox, Edge, Safari)。
  • 网络连接: 需要加载外部库(Three.js, Chart.js, math.js - 如果需要)。

运行组件

  1. 确保index.html, styles.css, script.js 文件在同一目录下。
  2. 在浏览器中打开 index.html 文件。
  3. 如果本地运行,可能需要一个简单的本地Web服务器来避免某些浏览器关于本地文件访问的限制。

基本操作流程

  1. 选择仿真类型: 在顶部工具栏的下拉菜单中选择要进行的仿真类型(结构、流体、电磁场或耦合)。选择后,左侧参数面板会自动更新显示相关参数。
  2. 设置参数: 在左侧面板调整所选仿真类型对应的物理参数和仿真设置(如材料、风速、网格密度、仿真时间等)。
  3. 运行仿真: 点击顶部工具栏的"运行仿真"按钮 ()。
  4. 观察过程: 状态栏会显示仿真状态和进度。右侧的仿真报告区会输出日志信息。
  5. 查看结果: 仿真完成后,状态栏会显示"完成"。右侧结果面板会自动更新。
    • 点击结果面板上方的选项卡(应力分析、流场分析、电磁场)切换查看不同物理场的结果。
    • 查看关键指标数值。
    • 查看对应的结果图表。
    • 中间的3D视图会根据当前选择的结果选项卡更新颜色映射,图例会显示颜色对应的数值范围。
  6. 交互操作:
    • 使用鼠标在3D视图中拖动进行旋转,滚轮缩放,右键(或Ctrl/Cmd+拖动)平移。
    • 点击模型树中的部件名称(如"叶片1"、“塔架”)可以高亮显示对应的3D模型部分。
    • 勾选/取消勾选"线框模式"复选框可以切换模型的显示方式。
  7. 停止/重置:
    • 在仿真运行时,可以点击"停止"按钮 () 中断计算。
    • 点击"重置"按钮 () 可以将所有参数恢复到默认值,并清除仿真结果和状态。

参数说明

(根据 index.html 中定义的参数进行详细说明)

结构分析 (structural)

  • 材料类型: 定义模型部件的主要材料。
  • 密度 (kg/m³): 材料的质量密度。
  • 杨氏模量 (GPa): 材料抵抗弹性变形的能力。
  • 泊松比: 材料横向应变与纵向应变之比。
  • 风速 (m/s): 施加在结构上的等效风载荷速度。
  • 湍流强度 (%): 风载荷的不规则性程度。
  • 旋转速度 (rpm): 叶片或整个结构的旋转速度,用于计算离心力。

流体分析 (fluid)

  • 流体类型: 模拟的流体介质(如空气、水)。
  • 密度 (kg/m³): 流体的质量密度。
  • 黏度 (Pa·s): 流体的内摩擦力。
  • 入口速度 (m/s): 流体进入仿真区域的速度。
  • 湍流模型: 用于模拟湍流效应的数学模型(如k-ε, k-ω)。

电磁场分析 (electromagnetic)

  • 电导率 (S/m): 材料传导电流的能力。
  • 磁导率 (H/m): 材料对磁场的响应程度。
  • 频率 (Hz): 电磁场的频率。
  • 场强 (A/m): 施加的外部磁场强度。

仿真设置 (settings - 通用)

  • 网格密度: 控制计算网格的精细程度(粗糙、中等、精细等),影响精度和计算时间。
  • 时间步长 (s): 对于瞬态分析,每个计算步的时间长度。
  • 总时间 (s): 瞬态分析的总模拟时长。

技术实现

  • 用户界面: HTML, CSS
  • 交互逻辑: JavaScript (ES6+)
  • 3D渲染: Three.js (r128 或更高版本)
  • 3D交互: Three.js OrbitControls
  • 图表展示: Chart.js
  • 数学计算: (可选) Math.js (当前版本未使用,但已在HTML中引入)
  • 仿真计算: 当前版本使用 模拟数据 生成结果,未集成真实的物理求解器。

待办事项与未来改进

  • 集成真实的物理求解器(例如通过WebAssembly调用C++/Fortran库,或连接后端计算服务)。
  • 实现模型树与参数面板的联动(选择部件显示对应参数)。
  • 实现更精细的3D模型高亮效果。
  • 完成"显示网格"和"显示矢量"功能。
  • 实现"新建"、“载入”、"保存"仿真配置和结果的功能。
  • 实现耦合分析的参数设置和结果展示逻辑。
  • 优化3D模型和渲染性能。
  • 提供更详细的错误处理和用户反馈。
  • 增加导出仿真报告和图表数据的功能。

常见问题解答

为什么3D模型是简化的?

为了快速展示组件的核心功能和交互流程,当前版本使用了简化的几何体。在集成真实求解器后,可以替换为更详细的模型。

仿真结果准确吗?

不准确。当前版本仅使用随机生成的模拟数据来填充结果面板和图表,用于演示UI效果。结果不代表真实的物理计算。

如何集成真实的求解器?

这通常需要更复杂的技术:

  1. 后端计算: 将参数发送到服务器,由服务器上的专业仿真软件(如ANSYS, COMSOL, OpenFOAM)进行计算,然后将结果返回给前端展示。
  2. WebAssembly: 将C/C++或Fortran编写的求解器代码编译成WebAssembly,在浏览器中直接运行。这对于计算量不是特别巨大的问题是可行的。

点击模型树部件没有反应?

当前版本实现了点击模型树条目时高亮对应3D部件的功能(基于名称匹配),但尚未实现参数面板的联动更新。

更新日志

v1.0.0 (2024-04-09)

  • 初始版本,基于提供的 HTML/CSS 结构完成 JavaScript 实现。
  • 实现基本UI布局和交互逻辑。
  • 支持结构、流体、电磁仿真类型切换及对应参数面板显示。
  • 集成 Three.js 显示简化风力发电机模型及基本交互。
  • 集成 Chart.js 显示结果图表。
  • 模拟仿真运行流程、进度显示和报告生成。
  • 使用模拟数据填充结果面板和图表。
  • 实现模型树选择高亮3D部件功能。
  • 实现线框模式切换。

效果展示

在这里插入图片描述

源码

index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>多物理场仿真组件 - 风力发电机叶片应力分析</title>
  <link rel="stylesheet" href="styles.css">
  <!-- Three.js库 -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
  <!-- OrbitControls扩展 -->
  <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.min.js"></script>
  <!-- 数学计算库 -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/9.4.4/math.min.js"></script>
  <!-- 图表库 -->
  <script src="https://cdn.jsdelivr.net/npm/chart.js@3.5.1/dist/chart.min.js"></script>
</head>
<body>
  <div class="simulation-container">
    <!-- 顶部工具栏 -->
    <div class="toolbar">
      <div class="toolbar-section">
        <button id="new-simulation-btn" class="btn primary">新建仿真</button>
        <button id="load-simulation-btn" class="btn">载入模型</button>
        <button id="save-simulation-btn" class="btn">保存模型</button>
      </div>
      <div class="toolbar-section">
        <button id="run-simulation-btn" class="btn success">运行仿真</button>
        <button id="stop-simulation-btn" class="btn danger">停止</button>
        <button id="reset-simulation-btn" class="btn">重置</button>
      </div>
      <div class="toolbar-section simulation-type">
        <span>仿真类型:</span>
        <select id="simulation-type">
          <option value="structural" selected>结构分析</option>
          <option value="fluid">流体分析</option>
          <option value="electromagnetic">电磁场分析</option>
          <option value="coupled">耦合分析</option>
        </select>
      </div>
    </div>
    
    <!-- 主内容区域 -->
    <div class="main-content">
      <div class="left-panel">
        <div class="panel-header">
          <h3>模型树</h3>
        </div>
        <div class="panel-content">
          <div class="model-tree">
            <ul>
              <li class="expanded">
                <div class="tree-item active">风力发电机</div>
                <ul>
                  <li>
                    <div class="tree-item">叶片系统</div>
                    <ul>
                      <li><div class="tree-item">叶片1</div></li>
                      <li><div class="tree-item">叶片2</div></li>
                      <li><div class="tree-item">叶片3</div></li>
                    </ul>
                  </li>
                  <li><div class="tree-item">轮毂</div></li>
                  <li><div class="tree-item">传动系统</div></li>
                  <li><div class="tree-item">发电机</div></li>
                  <li><div class="tree-item">塔架</div></li>
                </ul>
              </li>
            </ul>
          </div>
        </div>
        
        <div class="panel-header">
          <h3>物理参数</h3>
        </div>
        <div class="panel-content">
          <div class="physics-params">
            <!-- 结构分析参数 -->
            <div class="param-group" id="structural-params">
              <h4>材料属性</h4>
              <div class="param-row">
                <label for="material-type">材料类型:</label>
                <select id="material-type">
                  <option value="composite">复合材料</option>
                  <option value="aluminum">铝合金</option>
                  <option value="steel"></option>
                  <option value="custom">自定义</option>
                </select>
              </div>
              <div class="param-row">
                <label for="density">密度 (kg/):</label>
                <input type="number" id="density" value="1850" min="0" step="10">
              </div>
              <div class="param-row">
                <label for="youngs-modulus">杨氏模量 (GPa):</label>
                <input type="number" id="youngs-modulus" value="70" min="0" step="1">
              </div>
              <div class="param-row">
                <label for="poisson-ratio">泊松比:</label>
                <input type="number" id="poisson-ratio" value="0.33" min="0" max="0.5" step="0.01">
              </div>
              
              <h4>载荷条件</h4>
              <div class="param-row">
                <label for="wind-speed">风速 (m/s):</label>
                <input type="number" id="wind-speed" value="12" min="0" max="50" step="0.5">
              </div>
              <div class="param-row">
                <label for="turbulence-intensity">湍流强度 (%):</label>
                <input type="number" id="turbulence-intensity" value="15" min="0" max="100" step="1">
              </div>
              <div class="param-row">
                <label for="rotation-speed">旋转速度 (rpm):</label>
                <input type="number" id="rotation-speed" value="15" min="0" max="30" step="0.1">
              </div>
            </div>
            
            <!-- 流体分析参数 -->
            <div class="param-group" id="fluid-params" style="display: none;">
              <h4>流体属性</h4>
              <div class="param-row">
                <label for="fluid-type">流体类型:</label>
                <select id="fluid-type">
                  <option value="air">空气</option>
                  <option value="water"></option>
                  <option value="custom">自定义</option>
                </select>
              </div>
              <div class="param-row">
                <label for="fluid-density">密度 (kg/):</label>
                <input type="number" id="fluid-density" value="1.225" step="0.001">
              </div>
              <div class="param-row">
                <label for="fluid-viscosity">黏度 (Pa·s):</label>
                <input type="number" id="fluid-viscosity" value="1.8e-5" step="1e-6">
              </div>
              
              <h4>流动条件</h4>
              <div class="param-row">
                <label for="inlet-velocity">入口速度 (m/s):</label>
                <input type="number" id="inlet-velocity" value="12" min="0" step="0.5">
              </div>
              <div class="param-row">
                <label for="turbulence-model">湍流模型:</label>
                <select id="turbulence-model">
                  <option value="ke">k-ε</option>
                  <option value="kw">k-ω</option>
                  <option value="laminar">层流</option>
                </select>
              </div>
            </div>
            
            <!-- 电磁场分析参数 -->
            <div class="param-group" id="em-params" style="display: none;">
              <h4>电磁属性</h4>
              <div class="param-row">
                <label for="conductivity">电导率 (S/m):</label>
                <input type="number" id="conductivity" value="0" min="0" step="100">
              </div>
              <div class="param-row">
                <label for="permeability">磁导率 (H/m):</label>
                <input type="number" id="permeability" value="1.257e-6" step="1e-7">
              </div>
              
              <h4>电磁场条件</h4>
              <div class="param-row">
                <label for="frequency">频率 (Hz):</label>
                <input type="number" id="frequency" value="50" min="0" step="1">
              </div>
              <div class="param-row">
                <label for="em-field-strength">场强 (A/m):</label>
                <input type="number" id="em-field-strength" value="1000" min="0" step="100">
              </div>
            </div>
            
            <!-- 仿真设置 -->
            <h4>仿真设置</h4>
            <div class="param-row">
              <label for="mesh-density">网格密度:</label>
              <select id="mesh-density">
                <option value="coarse">粗糙</option>
                <option value="medium" selected>中等</option>
                <option value="fine">精细</option>
                <option value="very-fine">非常精细</option>
              </select>
            </div>
            <div class="param-row">
              <label for="time-step">时间步长 (s):</label>
              <input type="number" id="time-step" value="0.01" min="0.001" max="1" step="0.001">
            </div>
            <div class="param-row">
              <label for="total-time">总时间 (s):</label>
              <input type="number" id="total-time" value="5" min="0.1" step="0.1">
            </div>
          </div>
        </div>
      </div>
      
      <div class="center-panel">
        <div class="visualization-container">
          <canvas id="main-canvas"></canvas>
          <div class="visualization-controls">
            <button id="zoom-in-btn" class="control-btn">+</button>
            <button id="zoom-out-btn" class="control-btn">-</button>
            <button id="rotate-btn" class="control-btn">
              <svg viewBox="0 0 24 24" width="16" height="16">
                <path d="M7.11 8.53L5.7 7.11C4.8 8.27 4.24 9.61 4.07 11h2.02c.14-.87.49-1.72 1.02-2.47zM6.09 13H4.07c.17 1.39.72 2.73 1.62 3.89l1.41-1.42c-.52-.75-.87-1.59-1.01-2.47zm1.01 5.32c1.16.9 2.51 1.44 3.9 1.61V17.9c-.87-.15-1.71-.49-2.46-1.03L7.1 18.32zM13 4.07V1L8.45 5.55 13 10V6.09c2.84.48 5 2.94 5 5.91s-2.16 5.43-5 5.91v2.02c3.95-.49 7-3.85 7-7.93s-3.05-7.44-7-7.93z"/>
              </svg>
            </button>
            <button id="pan-btn" class="control-btn">
              <svg viewBox="0 0 24 24" width="16" height="16">
                <path d="M10 9h4V6h3l-5-5-5 5h3v3zm-1 1H6V7l-5 5 5 5v-3h3v-4zm14 2l-5-5v3h-3v4h3v3l5-5zm-9 3h-4v3H7l5 5 5-5h-3v-3z"/>
              </svg>
            </button>
          </div>
          <div class="visualization-legend">
            <div class="legend-title">应力 (MPa)</div>
            <div class="legend-gradient">
              <div class="gradient"></div>
              <div class="legend-labels">
                <span>0</span>
                <span>50</span>
                <span>100</span>
                <span>150</span>
                <span>200</span>
              </div>
            </div>
          </div>
          <div class="visualization-options">
            <div class="view-option">
              <input type="checkbox" id="show-mesh" class="toggle-switch">
              <label for="show-mesh">显示网格</label>
            </div>
            <div class="view-option">
              <input type="checkbox" id="show-wireframe" class="toggle-switch">
              <label for="show-wireframe">线框模式</label>
            </div>
            <div class="view-option">
              <input type="checkbox" id="show-vectors" class="toggle-switch" checked>
              <label for="show-vectors">显示矢量</label>
            </div>
          </div>
        </div>
      </div>
      
      <div class="right-panel">
        <div class="panel-header">
          <h3>仿真结果</h3>
        </div>
        <div class="panel-content">
          <div class="results-tabs">
            <div class="tab-header">
              <div class="tab-btn active" data-tab="stress">应力分析</div>
              <div class="tab-btn" data-tab="flow">流场分析</div>
              <div class="tab-btn" data-tab="em-field">电磁场</div>
            </div>
            <div class="tab-content">
              <div class="tab-pane active" id="stress-tab">
                <div class="result-section">
                  <h4>叶片应力分析</h4>
                  <div class="result-stats">
                    <div class="stat-item">
                      <div class="stat-label">最大应力:</div>
                      <div class="stat-value" id="max-stress">0 MPa</div>
                    </div>
                    <div class="stat-item">
                      <div class="stat-label">最小应力:</div>
                      <div class="stat-value" id="min-stress">0 MPa</div>
                    </div>
                    <div class="stat-item">
                      <div class="stat-label">平均应力:</div>
                      <div class="stat-value" id="avg-stress">0 MPa</div>
                    </div>
                    <div class="stat-item">
                      <div class="stat-label">最大位移:</div>
                      <div class="stat-value" id="max-displacement">0 mm</div>
                    </div>
                  </div>
                  <div class="chart-container">
                    <canvas id="stress-chart"></canvas>
                  </div>
                  <div class="safety-factor">
                    <div class="safety-label">安全系数:</div>
                    <div class="safety-value" id="safety-factor">N/A</div>
                    <div class="safety-status" id="safety-status">未计算</div>
                  </div>
                </div>
              </div>
              <div class="tab-pane" id="flow-tab">
                <div class="result-section">
                  <h4>流体动力学分析</h4>
                  <div class="result-stats">
                    <div class="stat-item">
                      <div class="stat-label">最大速度:</div>
                      <div class="stat-value" id="max-velocity">0 m/s</div>
                    </div>
                    <div class="stat-item">
                      <div class="stat-label">最大压力:</div>
                      <div class="stat-value" id="max-pressure">0 Pa</div>
                    </div>
                    <div class="stat-item">
                      <div class="stat-label">升力系数:</div>
                      <div class="stat-value" id="lift-coefficient">0</div>
                    </div>
                    <div class="stat-item">
                      <div class="stat-label">阻力系数:</div>
                      <div class="stat-value" id="drag-coefficient">0</div>
                    </div>
                  </div>
                  <div class="chart-container">
                    <canvas id="velocity-chart"></canvas>
                  </div>
                </div>
              </div>
              <div class="tab-pane" id="em-field-tab">
                <div class="result-section">
                  <h4>电磁场分析</h4>
                  <div class="result-stats">
                    <div class="stat-item">
                      <div class="stat-label">最大磁场强度:</div>
                      <div class="stat-value" id="max-magnetic-field">0 T</div>
                    </div>
                    <div class="stat-item">
                      <div class="stat-label">最大电场强度:</div>
                      <div class="stat-value" id="max-electric-field">0 V/m</div>
                    </div>
                    <div class="stat-item">
                      <div class="stat-label">感应电流密度:</div>
                      <div class="stat-value" id="current-density">0 A/</div>
                    </div>
                  </div>
                  <div class="chart-container">
                    <canvas id="em-field-chart"></canvas>
                  </div>
                </div>
              </div>
            </div>
          </div>
          
          <div class="report-section">
            <div class="panel-header">
              <h3>仿真报告</h3>
            </div>
            <div class="report-content">
              <div class="report-item">
                <span class="report-time">08:30:45</span>
                <span class="report-message info">初始化仿真环境</span>
              </div>
              <div class="report-item">
                <span class="report-time">08:31:12</span>
                <span class="report-message">生成风力发电机模型网格</span>
              </div>
              <div class="report-item">
                <span class="report-time">08:31:45</span>
                <span class="report-message">网格生成完成,共125,342个单元</span>
              </div>
              <div class="report-item">
                <span class="report-time">08:32:10</span>
                <span class="report-message success">应力分析计算完成</span>
              </div>
              <div class="report-item">
                <span class="report-time">08:33:05</span>
                <span class="report-message warning">检测到叶片1尖端应力超过阈值</span>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
    
    <div class="status-bar">
      <div class="status-info">
        <span>仿真状态: </span>
        <span id="simulation-status" class="status-ready">就绪</span>
      </div>
      <div class="status-progress">
        <div class="progress-bar" id="simulation-progress" style="width: 0%;"></div>
      </div>
      <div class="status-time">
        <span>处理时间: </span>
        <span id="processing-time">0.00 s</span>
      </div>
      <div class="status-memory">
        <span>内存使用: </span>
        <span id="memory-usage">0 MB</span>
      </div>
    </div>
  </div>
  
  <script src="script.js"></script>
</body>
</html> 

styles.css

/* 全局变量 */
:root {
  --primary-color: #0070f3;
  --secondary-color: #f5f5f7;
  --text-color: #333;
  --light-text: #86868b;
  --border-color: #d2d2d7;
  --danger-color: #ff3b30;
  --success-color: #34c759;
  --warning-color: #ffcc00;
  --info-color: #5ac8fa;
  --shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  --panel-width: 280px;
  --toolbar-height: 50px;
  --status-bar-height: 30px;
}

/* 基础样式 */
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  line-height: 1.5;
  color: var(--text-color);
  background-color: white;
  overflow: hidden;
  height: 100vh;
}

h3 {
  font-size: 16px;
  font-weight: 600;
  margin: 0;
}

h4 {
  font-size: 14px;
  font-weight: 600;
  margin: 0 0 10px 0;
  color: var(--light-text);
}

/* 容器样式 */
.simulation-container {
  width: 100%;
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* 工具栏 */
.toolbar {
  height: var(--toolbar-height);
  background-color: var(--secondary-color);
  border-bottom: 1px solid var(--border-color);
  display: flex;
  align-items: center;
  padding: 0 15px;
  justify-content: space-between;
}

.toolbar-section {
  display: flex;
  gap: 8px;
  align-items: center;
}

.simulation-type span {
  font-size: 13px;
  margin-right: 5px;
}

.simulation-type select {
  width: 120px;
}

/* 按钮样式 */
.btn {
  border: none;
  border-radius: 6px;
  padding: 8px 12px;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  background-color: #e8e8ed;
  color: var(--text-color);
  transition: all 0.2s ease;
}

.btn:hover {
  background-color: #d8d8d8;
}

.btn.primary {
  background-color: var(--primary-color);
  color: white;
}

.btn.primary:hover {
  background-color: #005acf;
}

.btn.success {
  background-color: var(--success-color);
  color: white;
}

.btn.success:hover {
  background-color: #2db64f;
}

.btn.danger {
  background-color: var(--danger-color);
  color: white;
}

.btn.danger:hover {
  background-color: #e63028;
}

.btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

/* 主内容区域 */
.main-content {
  display: flex;
  flex-grow: 1;
  overflow: hidden;
}

/* 左侧面板 */
.left-panel {
  width: var(--panel-width);
  border-right: 1px solid var(--border-color);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background-color: #fff;
}

/* 中间面板 */
.center-panel {
  flex-grow: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  position: relative;
}

/* 右侧面板 */
.right-panel {
  width: var(--panel-width);
  border-left: 1px solid var(--border-color);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background-color: #fff;
}

/* 面板样式 */
.panel-header {
  padding: 15px;
  border-bottom: 1px solid var(--border-color);
  background-color: var(--secondary-color);
}

.panel-content {
  padding: 15px;
  overflow-y: auto;
  flex-grow: 1;
}

/* 模型树 */
.model-tree {
  margin-bottom: 20px;
}

.model-tree ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.model-tree li {
  margin: 0;
  padding: 0;
}

.model-tree ul ul {
  margin-left: 20px;
}

.tree-item {
  padding: 6px 10px;
  border-radius: 4px;
  cursor: pointer;
  margin: 2px 0;
  font-size: 13px;
}

.tree-item:hover {
  background-color: #f5f5f7;
}

.tree-item.active {
  background-color: var(--primary-color);
  color: white;
}

li.expanded > .tree-item::before {
  content: "▼";
  font-size: 8px;
  margin-right: 5px;
}

li:not(.expanded) > .tree-item::before {
  content: "▶";
  font-size: 8px;
  margin-right: 5px;
}

/* 参数输入样式 */
.param-group {
  margin-bottom: 20px;
}

.param-row {
  display: flex;
  align-items: center;
  margin-bottom: 10px;
}

.param-row label {
  flex: 0 0 120px;
  font-size: 13px;
}

.param-row input,
.param-row select {
  flex: 1;
  padding: 6px 8px;
  border-radius: 6px;
  border: 1px solid var(--border-color);
  font-size: 13px;
  background: white;
}

.param-row input:focus,
.param-row select:focus {
  outline: none;
  border-color: var(--primary-color);
  box-shadow: 0 0 0 2px rgba(0, 112, 243, 0.2);
}

/* 可视化容器 */
.visualization-container {
  position: relative;
  flex-grow: 1;
  background-color: #f8f8f8;
  overflow: hidden;
}

#main-canvas {
  width: 100%;
  height: 100%;
  display: block;
}

.visualization-controls {
  position: absolute;
  bottom: 15px;
  right: 15px;
  display: flex;
  gap: 5px;
}

.control-btn {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background-color: white;
  border: 1px solid var(--border-color);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: var(--shadow);
}

.control-btn:hover {
  background-color: #f5f5f5;
}

.control-btn svg {
  fill: var(--text-color);
}

/* 可视化图例 */
.visualization-legend {
  position: absolute;
  bottom: 15px;
  left: 15px;
  background-color: rgba(255, 255, 255, 0.9);
  border-radius: 6px;
  padding: 10px;
  box-shadow: var(--shadow);
}

.legend-title {
  font-size: 12px;
  font-weight: 600;
  margin-bottom: 5px;
}

.legend-gradient {
  width: 200px;
}

.gradient {
  height: 10px;
  border-radius: 5px;
  background: linear-gradient(to right, #0000ff, #00ffff, #00ff00, #ffff00, #ff0000);
}

.legend-labels {
  display: flex;
  justify-content: space-between;
  margin-top: 5px;
  font-size: 10px;
}

/* 可视化选项 */
.visualization-options {
  position: absolute;
  top: 15px;
  right: 15px;
  background-color: rgba(255, 255, 255, 0.9);
  border-radius: 6px;
  padding: 10px;
  box-shadow: var(--shadow);
}

.view-option {
  display: flex;
  align-items: center;
  margin-bottom: 5px;
  font-size: 12px;
}

.view-option:last-child {
  margin-bottom: 0;
}

.toggle-switch {
  margin-right: 5px;
}

/* 结果选项卡 */
.results-tabs {
  margin-bottom: 20px;
}

.tab-header {
  display: flex;
  border-bottom: 1px solid var(--border-color);
  margin-bottom: 15px;
}

.tab-btn {
  padding: 8px 15px;
  font-size: 13px;
  cursor: pointer;
  border-bottom: 2px solid transparent;
}

.tab-btn.active {
  border-bottom: 2px solid var(--primary-color);
  color: var(--primary-color);
  font-weight: 500;
}

.tab-content {
  position: relative;
}

.tab-pane {
  display: none;
}

.tab-pane.active {
  display: block;
}

/* 结果统计 */
.result-stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
  margin-bottom: 15px;
}

.stat-item {
  background-color: var(--secondary-color);
  border-radius: 6px;
  padding: 10px;
}

.stat-label {
  font-size: 12px;
  color: var(--light-text);
}

.stat-value {
  font-size: 16px;
  font-weight: 600;
}

/* 图表容器 */
.chart-container {
  height: 180px;
  margin-bottom: 15px;
}

/* 安全系数 */
.safety-factor {
  display: flex;
  align-items: center;
  background-color: var(--secondary-color);
  border-radius: 6px;
  padding: 10px;
}

.safety-label {
  font-size: 12px;
  color: var(--light-text);
  margin-right: 10px;
}

.safety-value {
  font-size: 18px;
  font-weight: 700;
  margin-right: 10px;
}

.safety-status {
  font-size: 12px;
  padding: 3px 8px;
  border-radius: 4px;
  background-color: #eee;
}

.safety-status.safe {
  background-color: var(--success-color);
  color: white;
}

.safety-status.warning {
  background-color: var(--warning-color);
  color: #333;
}

.safety-status.danger {
  background-color: var(--danger-color);
  color: white;
}

/* 仿真报告 */
.report-section {
  margin-top: 20px;
}

.report-content {
  max-height: 180px;
  overflow-y: auto;
  border: 1px solid var(--border-color);
  border-radius: 6px;
  font-size: 12px;
}

.report-item {
  padding: 8px 10px;
  border-bottom: 1px solid var(--border-color);
  display: flex;
}

.report-item:last-child {
  border-bottom: none;
}

.report-time {
  flex: 0 0 70px;
  color: var(--light-text);
}

.report-message {
  flex: 1;
}

.report-message.success {
  color: var(--success-color);
}

.report-message.warning {
  color: var(--warning-color);
}

.report-message.error {
  color: var(--danger-color);
}

.report-message.info {
  color: var(--info-color);
}

/* 状态栏 */
.status-bar {
  height: var(--status-bar-height);
  background-color: var(--secondary-color);
  border-top: 1px solid var(--border-color);
  display: flex;
  align-items: center;
  padding: 0 15px;
  font-size: 12px;
  color: var(--light-text);
}

.status-info {
  margin-right: 15px;
}

.status-ready {
  color: var(--light-text);
}

.status-running {
  color: var(--primary-color);
}

.status-success {
  color: var(--success-color);
}

.status-error {
  color: var(--danger-color);
}

.status-progress {
  flex: 1;
  height: 4px;
  background-color: #e0e0e0;
  border-radius: 2px;
  margin: 0 15px;
  overflow: hidden;
}

.progress-bar {
  height: 100%;
  background-color: var(--primary-color);
  border-radius: 2px;
  transition: width 0.3s ease;
}

.status-time, .status-memory {
  margin-left: 15px;
}

/* 响应式调整 */
@media (max-width: 1200px) {
  .main-content {
    flex-wrap: wrap;
  }
  
  .left-panel, .right-panel {
    width: 50%;
    border: none;
    border-bottom: 1px solid var(--border-color);
  }
  
  .center-panel {
    width: 100%;
    order: -1;
    flex-basis: 60%;
  }
}

@media (max-width: 768px) {
  .left-panel, .right-panel {
    width: 100%;
  }
  
  .center-panel {
    flex-basis: 50%;
  }
}

/* 自定义滚动条 */
::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background: #d1d1d6;
  border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
  background: #a1a1a6;
} 

script.js

// 多物理场仿真组件 - JavaScript 实现
// V1.0.0

document.addEventListener('DOMContentLoaded', init);

// --- 全局变量 ---
let scene, camera, renderer, controls, modelGroup;
let simulationInterval = null; // 存储仿真定时器ID
let chartInstances = {}; // 存储图表实例

// --- DOM元素引用 ---
const elements = {
    // 工具栏
    newSimulationBtn: document.getElementById('new-simulation-btn'),
    loadSimulationBtn: document.getElementById('load-simulation-btn'),
    saveSimulationBtn: document.getElementById('save-simulation-btn'),
    runSimulationBtn: document.getElementById('run-simulation-btn'),
    stopSimulationBtn: document.getElementById('stop-simulation-btn'),
    resetSimulationBtn: document.getElementById('reset-simulation-btn'),
    simulationTypeSelect: document.getElementById('simulation-type'),

    // 左侧面板 - 模型树 (示例,实际交互可能更复杂)
    modelTreeItems: document.querySelectorAll('.model-tree .tree-item'),

    // 左侧面板 - 参数
    structuralParams: document.getElementById('structural-params'),
    fluidParams: document.getElementById('fluid-params'),
    emParams: document.getElementById('em-params'),
    materialType: document.getElementById('material-type'),
    density: document.getElementById('density'),
    youngsModulus: document.getElementById('youngs-modulus'),
    poissonRatio: document.getElementById('poisson-ratio'),
    windSpeed: document.getElementById('wind-speed'),
    turbulenceIntensity: document.getElementById('turbulence-intensity'),
    rotationSpeed: document.getElementById('rotation-speed'),
    // ... (添加其他参数元素的引用)
    meshDensity: document.getElementById('mesh-density'),
    timeStep: document.getElementById('time-step'),
    totalTime: document.getElementById('total-time'),
    // 流体参数
    fluidType: document.getElementById('fluid-type'),
    fluidDensity: document.getElementById('fluid-density'),
    fluidViscosity: document.getElementById('fluid-viscosity'),
    inletVelocity: document.getElementById('inlet-velocity'),
    turbulenceModel: document.getElementById('turbulence-model'),
    // 电磁参数
    conductivity: document.getElementById('conductivity'),
    permeability: document.getElementById('permeability'),
    frequency: document.getElementById('frequency'),
    emFieldStrength: document.getElementById('em-field-strength'),


    // 中间面板 - 可视化
    mainCanvas: document.getElementById('main-canvas'),
    zoomInBtn: document.getElementById('zoom-in-btn'),
    zoomOutBtn: document.getElementById('zoom-out-btn'),
    rotateBtn: document.getElementById('rotate-btn'), // 注意:OrbitControls默认处理旋转
    panBtn: document.getElementById('pan-btn'),       // 注意:OrbitControls默认处理平移
    showMeshCheckbox: document.getElementById('show-mesh'),
    showWireframeCheckbox: document.getElementById('show-wireframe'),
    showVectorsCheckbox: document.getElementById('show-vectors'),
    legendTitle: document.querySelector('.visualization-legend .legend-title'),
    legendGradient: document.querySelector('.visualization-legend .gradient'),
    legendLabels: document.querySelector('.visualization-legend .legend-labels'),


    // 右侧面板 - 结果
    resultTabBtns: document.querySelectorAll('.results-tabs .tab-btn'),
    stressTabPane: document.getElementById('stress-tab'),
    flowTabPane: document.getElementById('flow-tab'),
    emFieldTabPane: document.getElementById('em-field-tab'),
    maxStress: document.getElementById('max-stress'),
    minStress: document.getElementById('min-stress'),
    avgStress: document.getElementById('avg-stress'),
    maxDisplacement: document.getElementById('max-displacement'),
    safetyFactor: document.getElementById('safety-factor'),
    safetyStatus: document.getElementById('safety-status'),
    // 流体结果
    maxVelocity: document.getElementById('max-velocity'),
    maxPressure: document.getElementById('max-pressure'),
    liftCoefficient: document.getElementById('lift-coefficient'),
    dragCoefficient: document.getElementById('drag-coefficient'),
    // 电磁结果
    maxMagneticField: document.getElementById('max-magnetic-field'),
    maxElectricField: document.getElementById('max-electric-field'),
    currentDensity: document.getElementById('current-density'),
    // 图表画布
    stressChartCanvas: document.getElementById('stress-chart'),
    velocityChartCanvas: document.getElementById('velocity-chart'),
    emFieldChartCanvas: document.getElementById('em-field-chart'),
    // 报告
    reportContent: document.querySelector('.report-content'),


    // 状态栏
    simulationStatus: document.getElementById('simulation-status'),
    simulationProgress: document.getElementById('simulation-progress'),
    processingTime: document.getElementById('processing-time'),
    memoryUsage: document.getElementById('memory-usage'),
};

// --- 应用程序状态 ---
const appState = {
    isSimulationRunning: false,
    currentSimulationType: 'structural', // 'structural', 'fluid', 'electromagnetic', 'coupled'
    simulationStartTime: 0,
    simulationProgress: 0,
    activeModelItem: null,
    activeResultTab: 'stress',
    memoryUsage: 50 + Math.random() * 10, // 初始内存使用模拟
    parameters: { // 用于存储参数值,便于重置和保存/加载
      structural: {},
      fluid: {},
      em: {},
      settings: {}
    },
    defaultParameters: { // 存储默认值
        // 在 init 或 setupEventListeners 中填充
    }
};

// --- 日志记录 ---
const logger = {
    info: (message) => {
        console.log(`[INFO] ${message}`);
        addReportItem('info', message);
    },
    warn: (message) => {
        console.warn(`[WARN] ${message}`);
        addReportItem('warning', message);
    },
    error: (message) => {
        console.error(`[ERROR] ${message}`);
        addReportItem('error', message);
    },
    success: (message) => {
        console.log(`[SUCCESS] ${message}`);
        addReportItem('success', message);
    }
};

// --- 初始化 ---
function init() {
    logger.info("初始化多物理场仿真组件...");
    if (!elements.mainCanvas) {
        logger.error("初始化失败: 未找到主画布元素 'main-canvas'。");
        return;
    }
    if (typeof THREE === 'undefined') {
      logger.error("初始化失败: Three.js 库未加载。");
      return;
    }
     if (typeof Chart === 'undefined') {
        logger.warn("Chart.js 库未加载,图表功能将不可用。");
    }

    setupEventListeners();
    setupThreeJS();
    setDefaultParameters(); // 必须在setupEventListeners之后,因为它会读取初始值
    handleSimulationTypeChange(); // 根据默认选择显示正确的参数面板
    handleResultTabChange(appState.activeResultTab); // 显示默认结果选项卡
    updateStatus('就绪', 'ready');
    updateMemoryUsage();
    animate(); // 启动Three.js渲染循环
    initializeCharts(); // 初始化图表
    logger.info("组件初始化完成。");
}

// --- 事件监听器 ---
function setupEventListeners() {
    // 工具栏按钮
    elements.runSimulationBtn.addEventListener('click', startSimulation);
    elements.stopSimulationBtn.addEventListener('click', stopSimulation);
    elements.resetSimulationBtn.addEventListener('click', resetSimulation);
    elements.newSimulationBtn.addEventListener('click', () => logger.info('点击了“新建仿真”按钮(功能待实现)'));
    elements.loadSimulationBtn.addEventListener('click', () => logger.info('点击了“载入模型”按钮(功能待实现)'));
    elements.saveSimulationBtn.addEventListener('click', () => logger.info('点击了“保存模型”按钮(功能待实现)'));


    // 仿真类型切换
    elements.simulationTypeSelect.addEventListener('change', handleSimulationTypeChange);

    // 模型树点击
    elements.modelTreeItems.forEach(item => {
        item.addEventListener('click', () => handleModelTreeClick(item));
    });

    // 结果选项卡切换
    elements.resultTabBtns.forEach(btn => {
        btn.addEventListener('click', () => handleResultTabChange(btn.dataset.tab));
    });

    // 可视化控制
    elements.zoomInBtn.addEventListener('click', () => zoomCamera(0.8));
    elements.zoomOutBtn.addEventListener('click', () => zoomCamera(1.2));
    // pan/rotate 由 OrbitControls 处理

    // 可视化选项
    elements.showMeshCheckbox.addEventListener('change', toggleMeshVisibility);
    elements.showWireframeCheckbox.addEventListener('change', toggleWireframe);
    // elements.showVectorsCheckbox.addEventListener('change', toggleVectors); // 待实现


    // 参数输入 (示例 - 需要为所有参数添加监听)
    elements.windSpeed.addEventListener('change', () => updateParameter('structural', 'windSpeed', elements.windSpeed.value));
    elements.density.addEventListener('change', () => updateParameter('structural', 'density', elements.density.value));
    // ... 为所有 structural, fluid, em, settings 参数添加监听 ...
     elements.materialType.addEventListener('change', () => updateParameter('structural', 'materialType', elements.materialType.value));
    elements.youngsModulus.addEventListener('change', () => updateParameter('structural', 'youngsModulus', elements.youngsModulus.value));
    elements.poissonRatio.addEventListener('change', () => updateParameter('structural', 'poissonRatio', elements.poissonRatio.value));
    elements.turbulenceIntensity.addEventListener('change', () => updateParameter('structural', 'turbulenceIntensity', elements.turbulenceIntensity.value));
    elements.rotationSpeed.addEventListener('change', () => updateParameter('structural', 'rotationSpeed', elements.rotationSpeed.value));

    elements.fluidType.addEventListener('change', () => updateParameter('fluid', 'fluidType', elements.fluidType.value));
    elements.fluidDensity.addEventListener('change', () => updateParameter('fluid', 'fluidDensity', elements.fluidDensity.value));
    elements.fluidViscosity.addEventListener('change', () => updateParameter('fluid', 'fluidViscosity', elements.fluidViscosity.value));
    elements.inletVelocity.addEventListener('change', () => updateParameter('fluid', 'inletVelocity', elements.inletVelocity.value));
    elements.turbulenceModel.addEventListener('change', () => updateParameter('fluid', 'turbulenceModel', elements.turbulenceModel.value));

    elements.conductivity.addEventListener('change', () => updateParameter('em', 'conductivity', elements.conductivity.value));
    elements.permeability.addEventListener('change', () => updateParameter('em', 'permeability', elements.permeability.value));
    elements.frequency.addEventListener('change', () => updateParameter('em', 'frequency', elements.frequency.value));
    elements.emFieldStrength.addEventListener('change', () => updateParameter('em', 'emFieldStrength', elements.emFieldStrength.value));

    elements.meshDensity.addEventListener('change', () => updateParameter('settings', 'meshDensity', elements.meshDensity.value));
    elements.timeStep.addEventListener('change', () => updateParameter('settings', 'timeStep', elements.timeStep.value));
    elements.totalTime.addEventListener('change', () => updateParameter('settings', 'totalTime', elements.totalTime.value));


    // 窗口大小调整
    window.addEventListener('resize', onWindowResize);
}

// --- Three.js 相关 ---
function setupThreeJS() {
    const container = elements.mainCanvas.parentElement;
    const width = container.clientWidth;
    const height = container.clientHeight;

    // 场景
    scene = new THREE.Scene();
    scene.background = new THREE.Color(0xf8f8f8);

    // 相机
    camera = new THREE.PerspectiveCamera(50, width / height, 0.1, 2000);
    camera.position.set(15, 10, 25); // 调整相机初始位置

    // 渲染器
    renderer = new THREE.WebGLRenderer({ canvas: elements.mainCanvas, antialias: true });
    renderer.setSize(width, height);
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.shadowMap.enabled = true; // 启用阴影

    // 光照
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
    scene.add(ambientLight);

    const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
    directionalLight.position.set(10, 20, 15);
    directionalLight.castShadow = true;
    // 配置阴影贴图质量
    directionalLight.shadow.mapSize.width = 1024;
    directionalLight.shadow.mapSize.height = 1024;
    directionalLight.shadow.camera.near = 0.5;
    directionalLight.shadow.camera.far = 50;
    directionalLight.shadow.camera.left = -15;
    directionalLight.shadow.camera.right = 15;
    directionalLight.shadow.camera.top = 15;
    directionalLight.shadow.camera.bottom = -15;
    scene.add(directionalLight);

    // 控制器
    controls = new THREE.OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true;
    controls.dampingFactor = 0.1;
    controls.screenSpacePanning = false; // 限制平移在一个平面上可能更直观
    controls.target.set(0, 2, 0); // 设置控制器的目标点
    controls.update();

    // 网格地面
    const gridHelper = new THREE.GridHelper(50, 50, 0xcccccc, 0xe0e0e0);
    scene.add(gridHelper);

    // 创建风力发电机模型组
    modelGroup = new THREE.Group();
    scene.add(modelGroup);

    // 创建风力发电机模型 (简化版)
    createWindTurbineModel();
}

function createWindTurbineModel() {
    // 清除旧模型
    while (modelGroup.children.length > 0) {
        modelGroup.remove(modelGroup.children[0]);
    }

    const towerHeight = 12;
    const towerRadius = 0.5;
    const bladeLength = 6;
    const hubRadius = 0.8;

    // 材料
    const metalMaterial = new THREE.MeshStandardMaterial({ color: 0xb0b0b0, metalness: 0.8, roughness: 0.5 });
    const bladeMaterial = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 0.8 });

    // 塔架
    const towerGeometry = new THREE.CylinderGeometry(towerRadius * 0.8, towerRadius, towerHeight, 16);
    const tower = new THREE.Mesh(towerGeometry, metalMaterial);
    tower.position.y = towerHeight / 2;
    tower.castShadow = true;
    tower.receiveShadow = true;
    modelGroup.add(tower);

    // 机舱 (简化为盒子)
    const nacelleGeometry = new THREE.BoxGeometry(2, 1.5, 3);
    const nacelle = new THREE.Mesh(nacelleGeometry, metalMaterial);
    nacelle.position.y = towerHeight;
    nacelle.position.z = -0.5; // 向前偏移一点
    nacelle.castShadow = true;
    nacelle.receiveShadow = true;
    modelGroup.add(nacelle);

    // 轮毂
    const hubGeometry = new THREE.CylinderGeometry(hubRadius * 0.8, hubRadius, 0.8, 16);
    const hub = new THREE.Mesh(hubGeometry, metalMaterial);
    hub.position.y = towerHeight;
    hub.position.z = 1; // 机舱前方
    hub.rotation.x = Math.PI / 2; // 旋转使其朝前
    hub.castShadow = true;
    hub.receiveShadow = true;
    modelGroup.add(hub);

    // 叶片 (简化为长方体)
    for (let i = 0; i < 3; i++) {
        const bladeGeometry = new THREE.BoxGeometry(0.3, bladeLength, 0.1); // 宽度, 长度, 厚度
        const blade = new THREE.Mesh(bladeGeometry, bladeMaterial);

        // 设置叶片的中心点在根部
        blade.geometry.translate(0, bladeLength / 2, 0);

        // 定位和旋转叶片
        blade.position.y = towerHeight;
        blade.position.z = 1;
        const angle = (i * 2 * Math.PI) / 3;
        blade.rotation.z = angle;

        // 让叶片稍微倾斜一点(模拟攻角)
        blade.rotation.x = -Math.PI / 18; // 10度

        blade.castShadow = true;
        blade.receiveShadow = true;
        modelGroup.add(blade);
    }

    logger.info("创建了简化的风力发电机模型。");
}

function animate() {
    requestAnimationFrame(animate);
    controls.update(); // 更新控制器 (阻尼效果)
    renderer.render(scene, camera);

    // 更新状态栏信息 (可以降低频率)
    if (Math.random() < 0.05) { // 每秒大约更新一次
        updateMemoryUsage();
    }
}

function onWindowResize() {
    const container = elements.mainCanvas.parentElement;
    const width = container.clientWidth;
    const height = container.clientHeight;

    camera.aspect = width / height;
    camera.updateProjectionMatrix();
    renderer.setSize(width, height);
}

function zoomCamera(factor) {
    camera.position.multiplyScalar(factor);
    // 保持相机朝向目标点
    controls.update();
}

function toggleMeshVisibility() {
    const showMesh = elements.showMeshCheckbox.checked;
    logger.info(`切换网格显示: ${showMesh ? '开启' : '关闭'} (功能待实现)`);
    // 实际实现需要遍历模型,找到带有顶点信息的几何体,并可能使用如 EdgesGeometry 来显示边框
}

function toggleWireframe() {
    const showWireframe = elements.showWireframeCheckbox.checked;
     modelGroup.traverse((child) => {
        if (child.isMesh && child.material) {
            if (Array.isArray(child.material)) {
                 child.material.forEach(mat => mat.wireframe = showWireframe);
            } else {
                child.material.wireframe = showWireframe;
            }
        }
    });
    logger.info(`切换线框模式: ${showWireframe ? '开启' : '关闭'}`);
}


// --- 仿真控制 ---
function startSimulation() {
    if (appState.isSimulationRunning) {
        logger.warn("仿真已经在运行中。");
        return;
    }

    logger.info(`开始 ${appState.currentSimulationType} 仿真...`);
    appState.isSimulationRunning = true;
    appState.simulationStartTime = Date.now();
    appState.simulationProgress = 0;

    updateStatus('运行中', 'running');
    setControlsDisabled(true);
    elements.simulationProgress.style.width = '0%';

    // 清除旧的报告项 (可选)
    // elements.reportContent.innerHTML = '';
    addReportItem('info', `启动 ${getSimulationTypeName(appState.currentSimulationType)} 仿真`);
    addReportItem('info', '生成网格...');

    let progress = 0;
    let currentTime = 0;
    const totalDuration = parseFloat(elements.totalTime.value) || 5; // 总仿真时间 (s)
    const timeStep = parseFloat(elements.timeStep.value) || 0.01;
    const totalSteps = Math.ceil(totalDuration / timeStep);
    let currentStep = 0;

    // 模拟仿真过程
    simulationInterval = setInterval(() => {
        currentStep++;
        progress = Math.min(100, (currentStep / totalSteps) * 100);
        currentTime = currentStep * timeStep;

        appState.simulationProgress = progress;
        elements.simulationProgress.style.width = `${progress}%`;
        elements.processingTime.textContent = `${((Date.now() - appState.simulationStartTime) / 1000).toFixed(2)} s`;

        // 模拟报告更新
        if (currentStep === Math.floor(totalSteps * 0.1)) {
             addReportItem('info', `网格生成完成,共 ${Math.floor(80000 + Math.random() * 100000)} 个单元`);
        }
        if (currentStep === Math.floor(totalSteps * 0.5)) {
             addReportItem('info', '求解器开始迭代...');
             if (appState.currentSimulationType === 'structural' && Math.random() < 0.3) {
                 addReportItem('warning', '检测到单元质量较低,可能影响精度');
             }
        }
         if (currentStep === Math.floor(totalSteps * 0.8) && appState.currentSimulationType === 'fluid') {
             addReportItem('info', '流场逐渐稳定...');
         }


        if (progress >= 100) {
            simulationComplete();
        }
    }, 50); // 每50毫秒更新一次进度

}

function stopSimulation() {
    if (!appState.isSimulationRunning) {
        logger.warn("仿真未在运行。");
        return;
    }
    clearInterval(simulationInterval);
    simulationInterval = null;
    appState.isSimulationRunning = false;
    updateStatus('已停止', 'error'); // 使用错误状态表示非正常结束
    setControlsDisabled(false);
    logger.info("仿真已手动停止。");
    addReportItem('warning', '仿真被用户手动停止');
}

function simulationComplete() {
     clearInterval(simulationInterval);
     simulationInterval = null;
     appState.isSimulationRunning = false;
     appState.simulationProgress = 100;
     elements.simulationProgress.style.width = '100%';
     updateStatus('完成', 'success');
     setControlsDisabled(false);
     logger.success(`仿真完成,总用时: ${((Date.now() - appState.simulationStartTime) / 1000).toFixed(2)} s`);
     addReportItem('success', `仿真计算完成`);

     // 生成模拟结果
     generateMockResults();
     updateResultDisplay();
     updateCharts();
}

function resetSimulation() {
    logger.info("重置仿真设置和结果...");
    if (appState.isSimulationRunning) {
        stopSimulation();
    }
    // 重置参数到默认值
    resetParametersToDefault();

    // 重置状态栏
    appState.simulationProgress = 0;
    elements.simulationProgress.style.width = '0%';
    elements.processingTime.textContent = '0.00 s';
    updateStatus('就绪', 'ready');

    // 清除结果显示
    clearResultsDisplay();
    clearCharts(); // 清除图表数据


    // 重置模型视觉效果 (例如线框模式)
    if (elements.showWireframeCheckbox.checked) {
        elements.showWireframeCheckbox.checked = false;
        toggleWireframe();
    }
    // ... 其他视觉重置

     // 清空报告区 (可选)
    // elements.reportContent.innerHTML = '';
    addReportItem('info', '仿真环境已重置');

    logger.info("仿真已重置。");
}

function setControlsDisabled(isDisabled) {
    elements.runSimulationBtn.disabled = isDisabled;
    elements.stopSimulationBtn.disabled = !isDisabled; // 停止按钮只在运行时启用
    elements.resetSimulationBtn.disabled = isDisabled;
    elements.newSimulationBtn.disabled = isDisabled;
    elements.loadSimulationBtn.disabled = isDisabled;
    elements.saveSimulationBtn.disabled = isDisabled;
    elements.simulationTypeSelect.disabled = isDisabled;

    // 禁用所有参数输入
    const paramInputs = document.querySelectorAll('.physics-params input, .physics-params select');
    paramInputs.forEach(input => input.disabled = isDisabled);
}

// --- 参数与面板处理 ---
function handleSimulationTypeChange() {
    const selectedType = elements.simulationTypeSelect.value;
    appState.currentSimulationType = selectedType;
    logger.info(`切换仿真类型为: ${getSimulationTypeName(selectedType)}`);

    // 隐藏所有参数组
    elements.structuralParams.style.display = 'none';
    elements.fluidParams.style.display = 'none';
    elements.emParams.style.display = 'none';

    // 显示对应的参数组
    switch (selectedType) {
        case 'structural':
            elements.structuralParams.style.display = 'block';
            elements.legendTitle.textContent = '应力 (MPa)'; // 更新图例标题
            break;
        case 'fluid':
            elements.fluidParams.style.display = 'block';
            elements.legendTitle.textContent = '速度 (m/s)'; // 更新图例标题
            break;
        case 'electromagnetic':
            elements.emParams.style.display = 'block';
            elements.legendTitle.textContent = '磁场强度 (T)'; // 更新图例标题
            break;
        case 'coupled':
            // 耦合分析可能需要显示多个参数组,或者有特殊的参数组
            elements.structuralParams.style.display = 'block'; // 示例:显示结构
            elements.fluidParams.style.display = 'block';     // 示例:显示流体
            elements.legendTitle.textContent = '综合物理量'; // 更新图例标题
            logger.warn("耦合分析参数面板显示待实现。");
            break;
    }
     // 更新结果选项卡的可视状态(例如,如果不是流体分析,禁用流场选项卡)
     updateResultTabsAvailability();
}

function getSimulationTypeName(typeKey) {
    switch(typeKey) {
        case 'structural': return '结构分析';
        case 'fluid': return '流体分析';
        case 'electromagnetic': return '电磁场分析';
        case 'coupled': return '耦合分析';
        default: return '未知类型';
    }
}

function setDefaultParameters() {
    appState.defaultParameters = {
        structural: {
            materialType: elements.materialType.value,
            density: elements.density.value,
            youngsModulus: elements.youngsModulus.value,
            poissonRatio: elements.poissonRatio.value,
            windSpeed: elements.windSpeed.value,
            turbulenceIntensity: elements.turbulenceIntensity.value,
            rotationSpeed: elements.rotationSpeed.value,
        },
        fluid: {
            fluidType: elements.fluidType.value,
            fluidDensity: elements.fluidDensity.value,
            fluidViscosity: elements.fluidViscosity.value,
            inletVelocity: elements.inletVelocity.value,
            turbulenceModel: elements.turbulenceModel.value,
        },
        em: {
             conductivity: elements.conductivity.value,
            permeability: elements.permeability.value,
            frequency: elements.frequency.value,
            emFieldStrength: elements.emFieldStrength.value,
        },
        settings: {
            meshDensity: elements.meshDensity.value,
            timeStep: elements.timeStep.value,
            totalTime: elements.totalTime.value,
        }
    };
    // 初始化当前参数状态
    appState.parameters = JSON.parse(JSON.stringify(appState.defaultParameters)); // 深拷贝
}

function resetParametersToDefault() {
    for (const category in appState.defaultParameters) {
        for (const param in appState.defaultParameters[category]) {
            const element = elements[param]; // 假设 element ID 和参数名一致
            if (element) {
                element.value = appState.defaultParameters[category][param];
                // 更新 appState.parameters
                updateParameter(category, param, element.value, false); // 不记录日志
            }
        }
    }
     // 特别处理 simulationTypeSelect 的重置(如果需要)
    elements.simulationTypeSelect.value = 'structural'; // 或者存储默认值
    handleSimulationTypeChange(); // 触发面板更新
}

function updateParameter(category, paramName, value, log = true) {
    if (appState.parameters[category]) {
        appState.parameters[category][paramName] = value;
         if (log) {
            logger.info(`参数更新: [${category}] ${paramName} = ${value}`);
         }
    } else {
        logger.warn(`尝试更新未知参数类别: ${category}`);
    }
}


// --- 结果处理 ---
function handleResultTabChange(tabId) {
    appState.activeResultTab = tabId;

    // 移除所有按钮和面板的 active 类
    elements.resultTabBtns.forEach(btn => btn.classList.remove('active'));
    document.querySelectorAll('.results-tabs .tab-pane').forEach(pane => pane.classList.remove('active'));

    // 添加 active 类到当前按钮和面板
    const currentBtn = document.querySelector(`.results-tabs .tab-btn[data-tab="${tabId}"]`);
    const currentPane = document.getElementById(`${tabId}-tab`); // HTML中ID是 xxx-tab

    if (currentBtn) currentBtn.classList.add('active');
    if (currentPane) currentPane.classList.add('active');

    logger.info(`切换到结果选项卡: ${tabId}`);
    // 可能需要根据选项卡更新可视化 (例如改变颜色映射或显示的物理量)
    updateVisualizationForTab(tabId);
}

function updateResultTabsAvailability() {
     // 根据当前仿真类型启用/禁用结果选项卡
    const isStructural = appState.currentSimulationType === 'structural' || appState.currentSimulationType === 'coupled';
    const isFluid = appState.currentSimulationType === 'fluid' || appState.currentSimulationType === 'coupled';
    const isEM = appState.currentSimulationType === 'electromagnetic' || appState.currentSimulationType === 'coupled';

    const stressBtn = document.querySelector(`.results-tabs .tab-btn[data-tab="stress"]`);
    const flowBtn = document.querySelector(`.results-tabs .tab-btn[data-tab="flow"]`);
    const emBtn = document.querySelector(`.results-tabs .tab-btn[data-tab="em-field"]`);

    if(stressBtn) stressBtn.style.display = isStructural ? 'inline-block' : 'none';
    if(flowBtn) flowBtn.style.display = isFluid ? 'inline-block' : 'none';
    if(emBtn) emBtn.style.display = isEM ? 'inline-block' : 'none';

    // 如果当前激活的tab被禁用了,切换到一个可用的tab
     if (!isStructural && appState.activeResultTab === 'stress') handleResultTabChange('flow'); // 切换到流体(如果可用)
     if (!isFluid && appState.activeResultTab === 'flow') handleResultTabChange('stress'); // 切换到结构(如果可用)
     if (!isEM && appState.activeResultTab === 'em-field') handleResultTabChange('stress'); // 切换到结构(如果可用)

     // 如果所有特定tab都不可用(例如刚切换到耦合或特定类型),确保至少有一个tab是active的
     const activeBtn = document.querySelector('.results-tabs .tab-btn.active');
     if (!activeBtn || activeBtn.style.display === 'none') {
         if(isStructural) handleResultTabChange('stress');
         else if(isFluid) handleResultTabChange('flow');
         else if(isEM) handleResultTabChange('em-field');
     }
}


function generateMockResults() {
    // 根据仿真类型生成不同的模拟结果
    const results = {};
    const stressMax = 50 + Math.random() * 150; // 50 - 200 MPa
    const displacementMax = (0.5 + Math.random() * 2) * (parseFloat(appState.parameters.structural?.windSpeed || 10) / 10); // 0.5 - 2.5 mm (与风速相关)
    const factor = 1.5 + Math.random() * 3; // 1.5 - 4.5

    results.structural = {
        maxStress: stressMax.toFixed(1),
        minStress: (stressMax * (0.05 + Math.random() * 0.1)).toFixed(1), // 最小应力是最大应力的5%-15%
        avgStress: (stressMax * (0.3 + Math.random() * 0.2)).toFixed(1), // 平均应力是最大应力的30%-50%
        maxDisplacement: displacementMax.toFixed(2),
        safetyFactor: factor.toFixed(2),
        safetyStatus: factor < 2 ? 'danger' : (factor < 3 ? 'warning' : 'safe'),
        stressData: Array.from({ length: 10 }, () => Math.random() * stressMax) // 模拟图表数据
    };

     const velocityMax = parseFloat(appState.parameters.fluid?.inletVelocity || 12) * (1 + Math.random() * 0.5); // 最大速度是入口速度的1-1.5倍
    const pressureMax = 100 + Math.random() * 500; // 100 - 600 Pa
    results.fluid = {
        maxVelocity: velocityMax.toFixed(1),
        maxPressure: pressureMax.toFixed(0),
        liftCoefficient: (0.5 + Math.random() * 0.8).toFixed(3),
        dragCoefficient: (0.05 + Math.random() * 0.1).toFixed(3),
        velocityData: Array.from({ length: 10 }, () => Math.random() * velocityMax) // 模拟图表数据
    };

    const magFieldMax = (0.1 + Math.random() * 1.5).toFixed(2); // 0.1 - 1.6 T
    const elecFieldMax = (1000 + Math.random() * 9000).toFixed(0); // 1k - 10k V/m
    results.em = {
        maxMagneticField: magFieldMax,
        maxElectricField: elecFieldMax,
        currentDensity: (1e4 + Math.random() * 9e4).toExponential(2), // 1e4 - 1e5 A/m^2
        emFieldData: Array.from({ length: 10 }, () => Math.random() * magFieldMax) // 模拟图表数据
    };

    appState.results = results; // 将结果存入状态
}

function updateResultDisplay() {
    if (!appState.results) {
        logger.warn("没有仿真结果可供显示。");
        return;
    }

    // 更新结构分析结果
    if (appState.results.structural) {
        const struct = appState.results.structural;
        elements.maxStress.textContent = `${struct.maxStress} MPa`;
        elements.minStress.textContent = `${struct.minStress} MPa`;
        elements.avgStress.textContent = `${struct.avgStress} MPa`;
        elements.maxDisplacement.textContent = `${struct.maxDisplacement} mm`;
        elements.safetyFactor.textContent = struct.safetyFactor;
        elements.safetyStatus.textContent = struct.safetyStatus === 'safe' ? '安全' : (struct.safetyStatus === 'warning' ? '警告' : '危险');
        elements.safetyStatus.className = `safety-status ${struct.safetyStatus}`; // 更新类以改变颜色
    }

     // 更新流体分析结果
    if (appState.results.fluid) {
        const fluid = appState.results.fluid;
        elements.maxVelocity.textContent = `${fluid.maxVelocity} m/s`;
        elements.maxPressure.textContent = `${fluid.maxPressure} Pa`;
        elements.liftCoefficient.textContent = fluid.liftCoefficient;
        elements.dragCoefficient.textContent = fluid.dragCoefficient;
    }

     // 更新电磁分析结果
    if (appState.results.em) {
        const em = appState.results.em;
        elements.maxMagneticField.textContent = `${em.maxMagneticField} T`;
        elements.maxElectricField.textContent = `${em.maxElectricField} V/m`;
        elements.currentDensity.textContent = `${em.currentDensity} A/m²`;
    }
}

function clearResultsDisplay() {
     elements.maxStress.textContent = `0 MPa`;
     elements.minStress.textContent = `0 MPa`;
     elements.avgStress.textContent = `0 MPa`;
     elements.maxDisplacement.textContent = `0 mm`;
     elements.safetyFactor.textContent = 'N/A';
     elements.safetyStatus.textContent = '未计算';
     elements.safetyStatus.className = 'safety-status';

     elements.maxVelocity.textContent = '0 m/s';
     elements.maxPressure.textContent = '0 Pa';
     elements.liftCoefficient.textContent = '0';
     elements.dragCoefficient.textContent = '0';

     elements.maxMagneticField.textContent = '0 T';
     elements.maxElectricField.textContent = '0 V/m';
     elements.currentDensity.textContent = '0 A/m²';

     appState.results = null; // 清除存储的结果
}

function updateVisualizationForTab(tabId) {
    // 根据当前结果选项卡更新3D视图(例如颜色映射)
    logger.info(`更新3D可视化以反映 ${tabId} 结果 (功能待实现)`);

    // 示例:改变图例和可能的颜色映射
    let legendMin = 0, legendMax = 100;
    let colorMapFunction = (value) => new THREE.Color(0xffffff); // 默认白色

    if (tabId === 'stress' && appState.results?.structural) {
        legendMin = parseFloat(appState.results.structural.minStress);
        legendMax = parseFloat(appState.results.structural.maxStress);
        elements.legendTitle.textContent = '应力 (MPa)';
        // 定义应力颜色映射函数 (例如,从蓝到红)
        colorMapFunction = (value) => {
             const t = Math.max(0, Math.min(1, (value - legendMin) / (legendMax - legendMin || 1)));
             return new THREE.Color().setHSL(0.7 * (1 - t), 1, 0.5); // HSL: 0.7=蓝, 0=红
        };
    } else if (tabId === 'flow' && appState.results?.fluid) {
        legendMin = 0;
        legendMax = parseFloat(appState.results.fluid.maxVelocity);
        elements.legendTitle.textContent = '速度 (m/s)';
        // 定义速度颜色映射函数 (例如,从冷色到暖色)
         colorMapFunction = (value) => {
             const t = Math.max(0, Math.min(1, (value - legendMin) / (legendMax - legendMin || 1)));
             return new THREE.Color().setHSL(0.6 - t * 0.6, 1, 0.5); // HSL: 0.6=青, 0=红
        };
    } else if (tabId === 'em-field' && appState.results?.em) {
        legendMin = 0;
        legendMax = parseFloat(appState.results.em.maxMagneticField);
        elements.legendTitle.textContent = '磁场强度 (T)';
         // 定义磁场颜色映射函数
         colorMapFunction = (value) => {
             const t = Math.max(0, Math.min(1, (value - legendMin) / (legendMax - legendMin || 1)));
             return new THREE.Color().setHSL(0.15 + t * 0.4, 1, 0.5); // HSL: 0.15=黄绿, 0.55=蓝紫
        };
    }

    // 更新图例标签
    if (elements.legendLabels) {
        elements.legendLabels.innerHTML = `
            <span>${legendMin.toFixed(1)}</span>
            <span>${((legendMin + legendMax) / 2).toFixed(1)}</span>
            <span>${legendMax.toFixed(1)}</span>
        `;
    }

    // 应用颜色映射到模型 (这里需要更复杂的逻辑来将数据映射到顶点颜色)
    applyColorMapToModel(colorMapFunction);
}

function applyColorMapToModel(colorMapFunction) {
    // 这是一个非常简化的示例,实际应用需要顶点数据和结果数据
    modelGroup.traverse((child) => {
        if (child.isMesh && child.material && !child.material.wireframe) {
             // 模拟一个随机值来应用颜色映射
             const randomValueForColor = Math.random() * 150; // 假设一个范围
             const color = colorMapFunction(randomValueForColor);
             if (Array.isArray(child.material)) {
                child.material.forEach(mat => {
                    if (mat.isMeshStandardMaterial) mat.color.copy(color);
                });
             } else if (child.material.isMeshStandardMaterial) {
                 child.material.color.copy(color);
             }
        }
    });
}

// --- 图表处理 ---
function initializeCharts() {
     if (typeof Chart === 'undefined') return; // 如果Chart.js未加载,则不初始化

     const chartOptions = {
        responsive: true,
        maintainAspectRatio: false,
        scales: {
            y: { beginAtZero: true }
        },
        plugins: { legend: { display: false } }
     };

     if (elements.stressChartCanvas) {
        chartInstances.stress = new Chart(elements.stressChartCanvas.getContext('2d'), {
            type: 'bar',
            data: { labels: [], datasets: [{ label: '应力', data: [], backgroundColor: 'rgba(255, 99, 132, 0.6)' }] },
            options: chartOptions
        });
     }
    if (elements.velocityChartCanvas) {
        chartInstances.velocity = new Chart(elements.velocityChartCanvas.getContext('2d'), {
            type: 'line',
            data: { labels: [], datasets: [{ label: '速度', data: [], borderColor: 'rgba(54, 162, 235, 1)', tension: 0.1 }] },
            options: chartOptions
        });
     }
      if (elements.emFieldChartCanvas) {
        chartInstances.emField = new Chart(elements.emFieldChartCanvas.getContext('2d'), {
            type: 'radar', // 雷达图示例
            data: {
                labels: ['点1', '点2', '点3', '点4', '点5'],
                datasets: [{ label: '磁场强度', data: [], backgroundColor: 'rgba(255, 206, 86, 0.2)', borderColor: 'rgba(255, 206, 86, 1)', borderWidth: 1 }]
            },
             options: { maintainAspectRatio: false, scales: { r: { beginAtZero: true } } }
        });
     }
}

function updateCharts() {
     if (typeof Chart === 'undefined' || !appState.results) return;

     if (chartInstances.stress && appState.results.structural) {
         const stressData = appState.results.structural.stressData || [];
         chartInstances.stress.data.labels = stressData.map((_, i) => `区域 ${i + 1}`);
         chartInstances.stress.data.datasets[0].data = stressData;
         chartInstances.stress.update();
     }
     if (chartInstances.velocity && appState.results.fluid) {
        const velocityData = appState.results.fluid.velocityData || [];
         chartInstances.velocity.data.labels = velocityData.map((_, i) => `时间点 ${i + 1}`);
         chartInstances.velocity.data.datasets[0].data = velocityData;
         chartInstances.velocity.update();
     }
     if (chartInstances.emField && appState.results.em) {
        const emData = appState.results.em.emFieldData || [0,0,0,0,0]; // 确保有5个数据点
         chartInstances.emField.data.datasets[0].data = emData.slice(0, 5); // 取前5个
         chartInstances.emField.update();
     }
}

function clearCharts() {
     if (typeof Chart === 'undefined') return;
      for (const key in chartInstances) {
         chartInstances[key].data.labels = [];
         chartInstances[key].data.datasets[0].data = [];
         chartInstances[key].update();
      }
}


// --- 状态栏与报告 ---
function updateStatus(text, statusClass) { // statusClass: 'ready', 'running', 'success', 'error'
    elements.simulationStatus.textContent = text;
    elements.simulationStatus.className = `status-${statusClass}`;
}

function updateMemoryUsage() {
    // 模拟内存变化
    if (appState.isSimulationRunning) {
        appState.memoryUsage += (Math.random() * 5 - 2); // 运行时内存波动
    } else {
        appState.memoryUsage -= Math.random() * 1; // 非运行时缓慢减少
    }
    appState.memoryUsage = Math.max(30, Math.min(500, appState.memoryUsage)); // 限制在30-500MB
    elements.memoryUsage.textContent = `${appState.memoryUsage.toFixed(1)} MB`;
}

function addReportItem(type, message) { // type: 'info', 'warning', 'error', 'success'
    const item = document.createElement('div');
    item.className = 'report-item';

    const timeSpan = document.createElement('span');
    timeSpan.className = 'report-time';
    timeSpan.textContent = new Date().toLocaleTimeString();

    const messageSpan = document.createElement('span');
    messageSpan.className = `report-message ${type}`;
    messageSpan.textContent = message;

    item.appendChild(timeSpan);
    item.appendChild(messageSpan);

    elements.reportContent.appendChild(item);
    // 滚动到底部
    elements.reportContent.scrollTop = elements.reportContent.scrollHeight;
}


// --- 模型树交互 (示例) ---
function handleModelTreeClick(itemElement) {
    // 移除旧的 active 类
    if (appState.activeModelItem) {
        appState.activeModelItem.classList.remove('active');
    }
    // 添加 active 类到新项
    itemElement.classList.add('active');
    appState.activeModelItem = itemElement;

    const itemName = itemElement.textContent.trim();
    logger.info(`在模型树中选择了: ${itemName}`);

    // 可以在这里添加逻辑,例如在3D视图中高亮对应的部件
    highlightModelPart(itemName);
}

function highlightModelPart(partName) {
    logger.info(`高亮显示模型部件: ${partName} (功能待实现)`);
    // 实际实现需要根据 partName 查找 3D 对象并应用高亮效果 (例如改变材质或添加轮廓线)
     modelGroup.traverse((child) => {
         if (child.isMesh) {
             // 简单的基于名称的匹配(可能不准确)
             const nameMatch = partName.toLowerCase().includes('叶片') && child.geometry.type === 'BoxGeometry' ||
                              partName.toLowerCase().includes('塔架') && child.geometry.type === 'CylinderGeometry' && child.position.y < 10 ||
                              partName.toLowerCase().includes('轮毂') && child.geometry.type === 'CylinderGeometry' && child.position.y >= 10;

             if (nameMatch) {
                  // 临时高亮:改变颜色
                 if (child.userData.originalColor === undefined) {
                    child.userData.originalColor = child.material.color.clone();
                 }
                 child.material.color.set(0xffaa00); // 设置为橙色高亮
                 child.material.needsUpdate = true;
             } else if(child.userData.originalColor) {
                 // 恢复非选中对象的颜色
                  child.material.color.copy(child.userData.originalColor);
                  child.material.needsUpdate = true;
                  delete child.userData.originalColor; // 清除标记
             }
         }
     });

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

地上一の鹅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值