lichunyang hace 3 meses
padre
commit
11e6032868

+ 49 - 18
src/components/cloudChart/dialog/ColorCardDialog.vue

@@ -218,40 +218,67 @@
 </template>
 
 <script setup>
-import { ref, defineProps, defineEmits } from "vue"
+import { ref, defineProps, defineEmits, watch } from "vue"
 import SubDialog from "./SubDialog.vue"
 
 const props = defineProps({
   modelValue: Boolean,
   initialData: {
     type: Object,
-    default: () => ({})
+    default: () => ({
+      check1: true,
+      orientation: 'vertical',
+      X: 0.8,
+      Y: 0.05,
+      width: 0.15,
+      height: 0.4,
+      skipLevels: 2,
+      font: 'Arial',
+      fontSize: 12,
+      dataFormat: 'scientific',
+      precision: 2,
+      showTitle: true,
+      titleSource: 'variable',
+      customTitle: '',
+      titleFont: 'Arial',
+      titleFontSize: 16,
+      bold: false,
+      italic: false
+    })
   }
 })
 
 const emit = defineEmits(["update:modelValue", "confirm", "cancel"])
 
+// 将初始值转换为响应式对象
+const colorCardConfig = ref({...props.initialData})
+
+// 监听初始值变化
+watch(() => props.initialData, (newVal) => {
+  colorCardConfig.value = {...newVal}
+}, { deep: true })
+
 // 表单标签宽度
 let formLabelWidth1 = ref(140)
 let formLabelWidth2 = ref(100)
 
 // 表单数据
 let skvalue = ref({
-  check1:'1',
-  orientation:'竖直',
-  X:'0.8',
-  Y:'0.05',
-  width:'0.8',
-  height:'0.05',
-  skipc:'2',
-  font:'微软雅黑',
-  fontsize:'15',
-  dataformat:'科学计数法',
-  jingdu:'2',
-  check2:'1',
-  texttitle:'使用变量名',
-  titlefont:'Arial',
-  fontsize2:'20',
+  check1: '1',
+  orientation: '竖直',
+  X: '0.9',       // 位置更靠边
+  Y: '0.1',       // 位置更靠边
+  width: '0.5',   // 宽度增大
+  height: '0.7',  // 高度增大
+  skipc: '2',
+  font: '微软雅黑',
+  fontsize: '15',
+  dataformat: '科学计数法',
+  jingdu: '2',
+  check2: '1',
+  texttitle: '使用变量名',
+  titlefont: 'Arial',
+  fontsize2: '20',
 })
 
 // 下拉选项数据
@@ -284,7 +311,11 @@ let titlefontoptions = ref([
 
 // 确认操作
 const handleConfirm = () => {
-  emit("confirm", skvalue.value)
+  emit("confirm", {
+    ...colorCardConfig.value,
+    // 转换朝向值为Three.js可用的值
+    orientation: colorCardConfig.value.orientation === '竖直' ? 'vertical' : 'horizontal'
+  })
   emit("update:modelValue", false)
 }
 

+ 76 - 27
src/components/cloudChart/dialog/DomainDialog.vue

@@ -10,34 +10,41 @@
   >
     <div>
       <el-row style="margin-bottom: 10px;" :gutter="20">
-        <el-col v-for="(item,index) in domainbtnbox1" :key="index" :span="8">
-          <el-button style="width: 100%;">{{ item }}</el-button>
+        <el-col :key="showAll" :span="8">
+          <el-button @click="showAll" style="width: 100%;">显示全部</el-button>
+        </el-col>
+        <el-col :key="hideAll" :span="8">
+          <el-button @click="hideAll" style="width: 100%;">隐藏全部</el-button>
+        </el-col>
+        <el-col :key="reverseAll" :span="8">
+          <el-button @click="reverseAll" style="width: 100%;">倒转互换</el-button>
         </el-col>
       </el-row>
       <el-row style="margin-bottom: 10px;" :gutter="20">
-        <el-col v-for="(item,index) in domainbtnbox2" :key="index" :span="8">
-          <el-button style="width: 100%;">{{ item }}</el-button>
+        <el-col :span="8">
+          <el-button @click="showSelected" style="width: 100%;">显示</el-button>
+        </el-col>
+        <el-col :span="8">
+          <el-button @click="hideSelected" style="width: 100%;">隐藏</el-button>
+        </el-col>
+        <el-col :span="8">
+          <el-button style="width: 100%;">表面绘制</el-button>
         </el-col>
       </el-row>
     </div>
     <div class="classtable tabledomain">
-      <el-table 
-        :data="tableDatadomain" 
+      <el-table
+        :data="tableData" 
         style="width: 100%; height: 230px" 
         border="true" 
         :header-cell-class-name="headerCellClassName"
+        @selection-change="handleSelectionChange"
       >
-        <el-table-column prop="rowname" label="域名称" />
-        <el-table-column
-          v-for="(item, index) in tabledomainColumns"
-          :key="index"
-          :prop="item.prop"
-          :label="item.label"
-        >
-          <template #default="{ row }">
-            <el-input v-model="row[item.prop]" />
-          </template>
-        </el-table-column>
+        <el-table-column type="selection" width="55" />
+        <el-table-column prop="domain" label="域名称" />
+        <el-table-column prop="status" label="状态" /> <!-- 直接显示原始值 -->
+        <el-table-column prop="drawType" label="绘制类型" />
+        <el-table-column prop="planeRange" label="平面范围" />
       </el-table>
     </div>
   </SubDialog>
@@ -57,20 +64,62 @@ const props = defineProps({
 
 const emit = defineEmits(['update:modelValue', 'confirm', 'cancel'])
 
+const selectedRows = ref([]); // 存储选中的行
+
+// 监听选中行变化
+const handleSelectionChange = (rows) => {
+  selectedRows.value = rows;
+};
+
+// 显示全部
+const showAll = () => {
+  tableData.value.forEach(item => {
+    item.status = 'show'
+  })
+}
+
+// 隐藏全部
+const hideAll = () => {
+  tableData.value.forEach(item => {
+    item.status = 'hide'
+  })
+}
+
+// 显示选中行
+const showSelected = () => {
+  selectedRows.value.forEach(row => {
+    row.status = 'show'
+  })
+}
+
+// 隐藏选中行
+const hideSelected = () => {
+  selectedRows.value.forEach(row => {
+    row.status = 'hide'
+  })
+}
+
+// 倒转互换所有行状态
+const reverseAll = () => {
+  tableData.value.forEach(item => {
+    item.status = item.status === 'show' ? 'hide' : 'show'
+  })
+}
+
 // 按钮数据
 let domainbtnbox1 = ref(['显示全部','隐藏全部','倒转互换'])
 let domainbtnbox2 = ref(['显示','隐藏','表面绘制'])
 
 // 表格数据
-let tableDatadomain = ref([
-  {rowname:"Z1", state:'1', type:'2', area:'3'},
-  {rowname:"Z2", state:'1', type:'2', area:'3'},
-  {rowname:"Z3", state:'1', type:'2', area:'3'},
-  {rowname:"Z4", state:'1', type:'2', area:'3'},
-  {rowname:"Z1", state:'1', type:'2', area:'3'},
-  {rowname:"Z2", state:'1', type:'2', area:'3'},
-  {rowname:"Z3", state:'1', type:'2', area:'3'},
-  {rowname:"Z4", state:'1', type:'2', area:'3'},
+let tableData = ref([
+  {domain:"Z1", status:'show', drawType:'2', planeRange:'3'},
+  {domain:"Z2", status:'show', drawType:'2', planeRange:'3'},
+  {domain:"Z3", status:'show', drawType:'2', planeRange:'3'},
+  {domain:"Z4", status:'show', drawType:'2', planeRange:'3'},
+  {domain:"Z1", status:'show', drawType:'2', planeRange:'3'},
+  {domain:"Z2", status:'show', drawType:'2', planeRange:'3'},
+  {domain:"Z3", status:'show', drawType:'2', planeRange:'3'},
+  {domain:"Z4", status:'show', drawType:'2', planeRange:'3'},
 ])
 
 // 表格列配置
@@ -96,7 +145,7 @@ const headerCellClassName = ({ column }) => {
 
 // 确认操作
 const handleConfirm = () => {
-  emit('confirm', tableDatadomain.value)
+  emit('confirm', tableData.value)
   emit('update:modelValue', false)
 }
 

+ 24 - 1
src/components/cloudChart/index.vue

@@ -82,6 +82,9 @@ const props = defineProps({
 
 const emit = defineEmits(["update:modelValue", "close"])
 
+// 添加色卡配置状态
+const colorCardConfig = ref(null)
+
 let cloudbtnbox = ref([
   { url: "meshFile.png", btnname: "文件选择" },
   { url: "yu.png", btnname: "域" },
@@ -135,7 +138,8 @@ const handleSubDialogConfirm = (dialogType, data) => {
       console.log("dddddddddd云图", data)
       break
     case "色卡":
-      console.log("dddddddddd色卡", data)
+      colorCardConfig.value = data
+      applyColorCardConfig() // 应用色卡配置
       break
     case "等值线":
       console.log("dddddddddd等值线", data)
@@ -275,6 +279,25 @@ function getUrl(channelNo = "service") {
   }
   return url
 }
+
+// 添加应用色卡配置的方法
+const applyColorCardConfig = () => {
+  if (!pltData.value || !colorCardConfig.value) return
+  
+  // 确保pltData有colorCard配置对象
+  if (!pltData.value.config) {
+    pltData.value.config = {}
+  }
+  
+  pltData.value.config.colorCard = {
+    ...colorCardConfig.value,
+    visible: colorCardConfig.value.check1,
+    showTitle: colorCardConfig.value.check2
+  }
+  
+  // 强制更新云图组件
+  pltData.value = {...pltData.value}
+}
 </script>
 
 <style scoped>

+ 226 - 64
src/views/threejsView/components/ThreeScene.vue

@@ -7,19 +7,27 @@
       :text-inside="true"
       style="width: 100%; margin-bottom: 20px;"
     /> -->
-  <div ref="threeContainer" class="three-container" :style="{height: height}"></div>
+  <div ref="threeContainer" class="three-container" :style="{height: height}">
+    <!-- 添加色卡Canvas -->
+    <canvas 
+      v-if="showColorCard" 
+      ref="colorCardCanvas" 
+      class="color-card-canvas"
+      :style="colorCardStyle"
+    ></canvas>
+  </div>
 </template>
 
 <script setup>
 import { ref, onMounted, onUnmounted, watch } from "vue"
 import * as THREE from "three"
-import { VTKParserFactory } from "../utils/parsers/VTKParserFactory"
-import { UnstructuredGridRenderer } from "../utils/renderers/UnstructuredGridRenderer"
-import { PolyDataRenderer } from "../utils/renderers/PolyDataRenderer"
-import { xyzDataRenderer } from "../utils/renderers/xyzDataRenderer"
-import { bdfDataRenderer } from "../utils/renderers/bdfDataRenderer"
-import { CgnsJSONRenderer } from "../utils/renderers/CgnsJSONRenderer";
-import { PltDataRenderer } from "../utils/renderers/pltDataRenderer";
+import { VTKParserFactory } from "@views/threejsView/utils/parsers/VTKParserFactory"
+import { UnstructuredGridRenderer } from "@views/threejsView/utils/renderers/UnstructuredGridRenderer"
+import { PolyDataRenderer } from "@views/threejsView/utils/renderers/PolyDataRenderer"
+import { xyzDataRenderer } from "@views/threejsView/utils/renderers/xyzDataRenderer"
+import { bdfDataRenderer } from "@views/threejsView/utils/renderers/bdfDataRenderer"
+import { CgnsJSONRenderer } from "@views/threejsView/utils/renderers/CgnsJSONRenderer";
+import { PltDataRenderer } from "@views/threejsView/utils/renderers/pltDataRenderer";
 import {
   initScene,
   initCamera,
@@ -42,6 +50,8 @@ const props = defineProps({
 })
 
 const threeContainer = ref(null);
+const colorCardCanvas = ref(null)
+const colorCardContext = ref(null)
 const progress = ref(0); // 进度条的值
 
 let scene, camera, renderer, controls
@@ -146,10 +156,136 @@ const renderVTK = (data) => {
 
 }
 
+// 计算色卡显示状态和样式
+const showColorCard = computed(() => {
+  return props.data?.config?.colorCard?.visible
+})
+
+const colorCardStyle = computed(() => {
+  if (!showColorCard.value) return {}
+  const config = props.data.config.colorCard
+  return {
+    position: 'absolute',
+    [config.orientation === 'vertical' ? 'right' : 'bottom']: '10px',
+    [config.orientation === 'vertical' ? 'top' : 'left']: '50%',
+    transform: config.orientation === 'vertical' 
+      ? 'translateY(-50%)' 
+      : 'translateX(-50%)',
+    width: config.orientation === 'vertical' 
+      ? `${config.width * 100}px` 
+      : `${config.width * 100}%`,
+    height: config.orientation === 'vertical' 
+      ? `${config.height * 100}%` 
+      : `${config.height * 100}px`,
+    zIndex: 1000
+  }
+})
+// 初始化色卡
+const initColorCard = () => {
+  if (!colorCardCanvas.value) return
+  colorCardContext.value = colorCardCanvas.value.getContext('2d')
+  renderColorCard()
+}
+
+// 渲染色卡
+const renderColorCard = () => {
+  if (!showColorCard.value || !colorCardContext.value) return
+  
+  const config = props.data.config.colorCard
+  const ctx = colorCardContext.value
+  const width = colorCardCanvas.value.width
+  const height = colorCardCanvas.value.height
+  
+  // 清除画布
+  ctx.clearRect(0, 0, width, height)
+  
+  // 创建渐变
+  let gradient
+  if (config.orientation === 'vertical') {
+    gradient = ctx.createLinearGradient(0, height, 0, 0)
+  } else {
+    gradient = ctx.createLinearGradient(0, 0, width, 0)
+  }
+  
+  // 添加色卡颜色
+  gradient.addColorStop(0, '#0000ff') // 蓝色
+  gradient.addColorStop(0.5, '#00ff00') // 绿色
+  gradient.addColorStop(1, '#ff0000') // 红色
+  
+  // 填充渐变
+  ctx.fillStyle = gradient
+  ctx.fillRect(0, 0, width, height)
+  
+  // 添加刻度
+  ctx.strokeStyle = '#000'
+  ctx.lineWidth = 1
+  ctx.font = `${config.fontSize}px ${config.font}`
+  ctx.fillStyle = '#000'
+  
+  // 根据朝向绘制刻度
+  if (config.orientation === 'vertical') {
+    // 垂直色卡刻度
+    const step = height / 10
+    for (let i = 0; i <= 10; i++) {
+      const y = i * step
+      ctx.beginPath()
+      ctx.moveTo(width * 0.7, y)
+      ctx.lineTo(width, y)
+      ctx.stroke()
+      
+      // 跳过指定层级的标签
+      if (i % (config.skipLevels + 1) === 0) {
+        const value = (1 - i / height * 1).toFixed(config.precision)
+        ctx.fillText(value, 5, y + 5)
+      }
+    }
+  } else {
+    // 水平色卡刻度
+    const step = width / 10
+    for (let i = 0; i <= 10; i++) {
+      const x = i * step
+      ctx.beginPath()
+      ctx.moveTo(x, 0)
+      ctx.lineTo(x, height * 0.3)
+      ctx.stroke()
+      
+      // 跳过指定层级的标签
+      if (i % (config.skipLevels + 1) === 0) {
+        const value = (i / width * 1).toFixed(config.precision)
+        ctx.fillText(value, x - 10, height - 5)
+      }
+    }
+  }
+  
+  // 添加标题
+  if (config.showTitle) {
+    ctx.font = `bold ${config.titleFontSize}px ${config.titleFont}`
+    ctx.textAlign = 'center'
+    const title = config.titleSource === 'custom' 
+      ? config.customTitle 
+      : 'Color Scale'
+    
+    if (config.orientation === 'vertical') {
+      ctx.save()
+      ctx.translate(20, height / 2)
+      ctx.rotate(-Math.PI / 2)
+      ctx.fillText(title, 0, 0)
+      ctx.restore()
+    } else {
+      ctx.fillText(title, width / 2, height - 15)
+    }
+  }
+}
+
 // 监听 data 变化
 watch(
   () => props.data,
   (newData) => {
+    if (showColorCard.value) {
+      nextTick(() => {
+        initColorCard()
+      })
+    }
     if (newData) {
       // 清空场景
       while (scene.children.length > 0) {
@@ -170,6 +306,9 @@ watch(
 onMounted(() => {
   nextTick(() => {
     init()
+    if (showColorCard.value) {
+      initColorCard()
+    }
     animateScene(scene, camera, renderer, controls)
     // 监听窗口大小变化
     window.addEventListener("resize", onWindowResize)
@@ -344,64 +483,85 @@ const adjustCameraForCgns = (scene, camera) => {
   console.log("Adjusted camera fov:", camera.fov);
 };
 
-function adjustCameraForPlt(scene, camera, paddingFactor = 1.2) {
-  // 1. 计算所有PLT对象的联合包围盒
-  const bbox = new THREE.Box3();
-  let hasPLTObjects = false;
-
-  scene.traverse(obj => {
-    if (obj.isMesh && obj.userData?.isPLT) {
-      if (!obj.geometry.boundingBox) {
-        obj.geometry.computeBoundingBox();
-      }
-      bbox.union(obj.geometry.boundingBox);
-      hasPLTObjects = true;
+/**
+ * 调整相机位置和参数,使整个PLT模型在视图中可见
+ * @param {THREE.Scene} scene - Three.js场景对象
+ * @param {THREE.PerspectiveCamera} camera - 透视相机
+ * @param {number} [paddingFactor] - 视图边距系数(1.0表示紧贴边界)
+ */
+const adjustCameraForPlt = (scene, camera, params = {}) => {
+    const {
+        padding = 2.0,
+        forceTopView = false
+    } = params;
+
+    // 1. 确保场景和相机已初始化
+    if (!scene || !camera) {
+        console.error('场景或相机未初始化');
+        return;
     }
-  });
 
-  if (!hasPLTObjects) {
-    console.warn('未找到PLT网格对象');
-    return;
-  }
+    // 2. 计算包围盒(包含可见网格)
+    const box = new THREE.Box3();
+    const visibleMeshes = [];
+    
+    scene.traverse(child => {
+        if (child.isMesh && child.visible) {
+            if (!child.geometry.boundingBox) {
+                child.geometry.computeBoundingBox();
+            }
+            box.union(child.geometry.boundingBox);
+            visibleMeshes.push(child);
+        }
+    });
+
+    // 3. 处理空场景情况
+    if (visibleMeshes.length === 0) {
+        console.warn('没有可见网格,使用默认视角');
+        camera.position.set(0, 10, 0);
+        camera.lookAt(0, 0, 0);
+        return;
+    }
 
-  // 2. 计算场景中心点和尺寸
-  const center = new THREE.Vector3();
-  bbox.getCenter(center);
-  const size = bbox.getSize(new THREE.Vector3());
-  const maxDim = Math.max(size.x, size.y, size.z);
-  const minDim = Math.min(size.x, size.y, size.z);
-  
-  // 3. 智能相机位置计算(考虑不同维度比例)
-  const cameraPosition = center.clone();
-  const aspect = camera.aspect;
-  
-  // 根据场景形状调整相机距离
-  let distance;
-  if (maxDim / minDim > 5) { // 长条形场景
-    distance = maxDim * paddingFactor * 1.5;
-  } else { // 常规场景
-    distance = maxDim * paddingFactor * (aspect > 1 ? 1.5 : 2.0);
-  }
+    // 4. 计算模型参数
+    const size = box.getSize(new THREE.Vector3());
+    const center = box.getCenter(new THREE.Vector3());
+    const maxDim = Math.max(size.x, size.y, size.z);
+    const distance = padding * maxDim;
+
+    // 5. 设置相机位置(根据模型特征优化)
+    if (forceTopView || size.y > size.x * 1.5) {
+        // 高模型或强制顶视图
+        camera.position.set(
+            center.x,
+            center.y + distance * 1.5,
+            center.z
+        );
+        camera.up.set(0, 0, 1); // Z轴朝上
+    } else {
+        // 常规模型
+        camera.position.set(
+            center.x + distance * 0.7,
+            center.y + distance * 0.5,
+            center.z + distance * 0.7
+        );
+        camera.up.set(0, 1, 0); // Y轴朝上
+    }
 
-  // 4. 设置相机位置和参数
-  cameraPosition.z += distance;
-  camera.position.copy(cameraPosition);
-  camera.lookAt(center);
-  
-  // 5. 自适应相机远平面
-  camera.far = distance * 10;
-  camera.near = Math.max(0.1, distance / 100);
-  camera.updateProjectionMatrix();
+    // 6. 确保相机看向中心点
+    camera.lookAt(center);
+    camera.near = 0.1 * maxDim; // 动态调整近平面
+    camera.far = 100 * maxDim;   // 动态调整远平面
+    camera.updateProjectionMatrix();
+
+    // 7. 打印调试信息
+    console.log(`相机调整完成:
+    模型中心:${center.toArray().map(v => v.toFixed(2))}
+    模型尺寸:${size.toArray().map(v => v.toFixed(2))}
+    相机位置:${camera.position.toArray().map(v => v.toFixed(2))}
+    视锥体:near=${camera.near.toFixed(2)}, far=${camera.far.toFixed(2)}`);
+};
 
-  // 6. 返回相机参数用于后续控制
-  return {
-    center,
-    distance,
-    boundingBox: bbox,
-    size,
-    aspectRatio: aspect
-  };
-}
 </script>
 
 <style>
@@ -413,7 +573,9 @@ function adjustCameraForPlt(scene, camera, paddingFactor = 1.2) {
 .el-checkbox__label{
   font-size: 12px;
 }
-/* .el-progress-bar__outer{
-  height: 15px !important;
-} */
+.color-card-canvas {
+  pointer-events: none; /* 允许点击穿透 */
+  border: 1px solid #ccc;
+  background: white;
+}
 </style>

+ 115 - 227
src/views/threejsView/utils/renderers/PltDataRenderer.js

@@ -1,245 +1,133 @@
 import * as THREE from 'three';
 
 export class PltDataRenderer {
-  constructor(updateProgress, onComplete) {
-    this.updateProgress = updateProgress || (() => {});
-    this.onComplete = onComplete || (() => {});
-    this.chunkSize = 5000; // 每个区域的最大三角面片数(用于分块加载)
-    this.defaultMaterial = new THREE.MeshPhongMaterial({
-      color: 0x4477ff,
-      specular: 0x111111,
-      shininess: 30,
-      side: THREE.DoubleSide, // 关键修改:双面渲染
-      flatShading: false,     // 平滑着色
-      transparent: true,
-      opacity: 0.95
-    });
-    this.wireframeMaterial = new THREE.MeshBasicMaterial({
-      color: 0x00ff00,
-      wireframe: true
-    });
-  }
-  
-
-  async render(pltData, scene, specificZoneIndex = null) {
-    console.log("原始PLT数据结构诊断:", {
-      zones: pltData.zones.map(zone => ({
-        name: zone.name,
-        verticesType: zone.vertices?.constructor?.name,
-        verticesLength: zone.vertices?.length,
-        verticesSample: zone.vertices?.slice(0, 3),
-        indicesType: zone.indices?.constructor?.name,
-        indicesLength: zone.indices?.length,
-        indicesSample: zone.indices?.slice(0, 3)
-      }))
-    });
-    if (!pltData?.zones?.length) {
-      console.error('Invalid PLT data');
-      return;
-    }
-
-    // 清空现有PLT相关对象
-    this._cleanupPreviousRender(scene);
-
-    // 如果指定了具体区域索引,则只渲染该区域
-    if (specificZoneIndex !== null) {
-      if (specificZoneIndex < 0 || specificZoneIndex >= pltData.zones.length) {
-        console.error(`Invalid zone index: ${specificZoneIndex} (total zones: ${pltData.zones.length})`);
-        return;
-      }
-      
-      const zone = pltData.zones[specificZoneIndex];
-      await this._renderZone(zone, scene, specificZoneIndex, pltData.zones.length);
-      console.log(`[DEBUG] 仅渲染了区域 ${specificZoneIndex}: ${zone.name}`);
-      return;
+    constructor(updateProgress, onComplete) {
+        this.updateProgress = updateProgress;
+        this.onComplete = onComplete;
+        this.meshGroup = new THREE.Group();
+        
+        this.defaultMaterial = new THREE.MeshPhongMaterial({
+            color: 0xffffff, // 改为白色基础色
+            specular: 0x111111, // 适当的高光
+            shininess: 30,
+            side: THREE.DoubleSide,
+            flatShading: false, // 改为false获得更平滑的 shading
+            vertexColors: true,
+            transparent: true,
+            opacity: 1.0 // 不透明度设为1
+        });
+        this.defaultLights = [
+            new THREE.AmbientLight(0xffffff, 1.0), // 环境光(强度增强)
+            new THREE.DirectionalLight(0xffffff, 1.5), // 平行光(强度增强)
+            new THREE.HemisphereLight(0xffffbb, 0x080820, 0.8) // 天光
+        ];
     }
 
-    // 否则渲染所有区域(原有逻辑)
-    const totalZones = pltData.zones.length;
-    for (const [index, zone] of pltData.zones.entries()) {
-      try {
-        await this._renderZone(zone, scene, index, totalZones);
-        this.updateProgress((index + 1) / totalZones);
-      } catch (error) {
-        console.error(`Failed to render zone ${zone.name}:`, error);
-      }
-      await new Promise(resolve => setTimeout(resolve, 0));
+    async render(pltData, scene) {
+      // 清除旧光照
+        scene.traverse(obj => {
+            if (obj.isLight) scene.remove(obj);
+        });
+        
+        // 添加新光照
+        this.defaultLights.forEach(light => {
+            if (light.isDirectionalLight) {
+                light.position.set(1, 1, 1).normalize();
+            }
+            scene.add(light);
+        });
+        this.clearScene(scene);
+        scene.add(this.meshGroup);
+
+        try {
+            await this.updateProgressAsync('开始渲染PLT模型...');
+            
+            // 处理每个zone
+            for (const zone of pltData.zones) {
+                await this.updateProgressAsync(`正在处理区域: ${zone.name}...`);
+                
+                // 创建几何体
+                const geometry = new THREE.BufferGeometry();
+                
+                // 设置顶点属性
+                geometry.setAttribute(
+                    'position',
+                    new THREE.BufferAttribute(new Float32Array(zone.vertices), 3)
+                );
+
+                // 设置索引
+                if (zone.indices && zone.indices.length > 0) {
+                    geometry.setIndex(
+                        new THREE.BufferAttribute(new Uint32Array(zone.indices), 1)
+                    );
+                }
+
+                // 设置顶点颜色(使用CoefPressure变量)
+                if (zone.variables && zone.variables.CoefPressure) {
+                    const colors = this.generateVertexColors(zone.variables.CoefPressure);
+                    geometry.setAttribute(
+                        'color',
+                        new THREE.BufferAttribute(new Float32Array(colors), 3)
+                    );
+                }
+
+                await this.updateProgressAsync('优化几何体...');
+                geometry.computeVertexNormals();
+                geometry.computeBoundingSphere();
+
+                await this.updateProgressAsync('创建网格...');
+                const mesh = new THREE.Mesh(geometry, this.defaultMaterial);
+                mesh.name = zone.name;
+                this.meshGroup.add(mesh);
+            }
+
+            if (this.onComplete) this.onComplete();
+        } catch (error) {
+            console.error('PLT渲染错误:', error);
+            if (this.onComplete) this.onComplete(error);
+        }
     }
 
-    this.onComplete();
-  }
-
-  async _renderZone(zone, scene, zoneIndex, totalZones) {
-    return new Promise((resolve) => {
-      // 使用requestAnimationFrame避免阻塞UI
-      requestAnimationFrame(() => {
-        if (!zone.vertices || !zone.indices) {
-          console.warn(`Zone ${zone.name} has no geometry data`);
-          return resolve();
+    generateVertexColors(data) {
+        // 添加数据验证
+        if (!data || !Array.isArray(data) || data.length === 0) {
+            console.warn('无效的顶点颜色数据,使用默认颜色');
+            return new Array(data.length * 3).fill(0.5); // 返回灰色
         }
 
-        // 创建几何体
-        const geometry = this._createZoneGeometry(zone);
+        const min = Math.min(...data);
+        const max = Math.max(...data);
+        const range = max - min || 1; // 避免除以0
         
-        // 分配材质(交替使用实体和线框材质)
-        const material = zoneIndex % 2 === 0 
-          ? this.defaultMaterial.clone()
-          : this.wireframeMaterial.clone();
-        
-        // 设置区域特定颜色
-        if (zoneIndex % 2 === 0) {
-          material.color.setHSL(zoneIndex / totalZones, 0.7, 0.5);
-        } else {
-          material.color.setHSL(zoneIndex / totalZones, 0.7, 0.7);
-        }
-
-        // 创建网格
-        const mesh = new THREE.Mesh(geometry, material);
-        mesh.name = `plt_zone_${zoneIndex}`;
-        mesh.userData = {
-          zoneName: zone.name,
-          zoneType: zone.type,
-          isPLT: true
-        };
-
-        // 添加到场景
-        scene.add(mesh);
+        const colors = new Array(data.length * 3);
         
-        // 添加区域标签(可选)
-        this._addZoneLabel(zone, mesh, zoneIndex);
-
-        console.log(`Rendered zone ${zone.name} with ${geometry.index.count / 3} triangles`);
-        resolve();
-      });
-    });
-  }
-
-  _createZoneGeometry(zone) {
-    const geometry = new THREE.BufferGeometry();
-    
-    // 顶点数据
-    geometry.setAttribute(
-      'position',
-      new THREE.BufferAttribute(zone.vertices, 3)
-    );
-  
-    // 安全极值计算函数
-    const getArrayExtremes = (arr) => {
-      let min = Infinity, max = -Infinity;
-      for (let i = 0; i < arr.length; i++) {
-        if (arr[i] < min) min = arr[i];
-        if (arr[i] > max) max = arr[i];
-      }
-      return { min, max };
-    };
-  
-    // 索引处理
-    let indicesArray = zone.indices || new Uint32Array(0);
-    if (indicesArray.length > 0) {
-      // 计算极值
-      const { min: minIndex, max: maxIndex } = getArrayExtremes(indicesArray);
-      const vertexCount = zone.vertices.length / 3;
-  
-      // 索引验证
-      if (maxIndex >= vertexCount) {
-        console.warn(`修正${maxIndex - vertexCount + 1}个超出范围的索引`);
-        const validIndices = new Uint32Array(indicesArray.length);
-        for (let i = 0; i < indicesArray.length; i++) {
-          validIndices[i] = indicesArray[i] >= vertexCount ? 0 : indicesArray[i];
+        for (let i = 0; i < data.length; i++) {
+            const normalized = (data[i] - min) / range;
+            
+            // 热力图颜色映射
+            colors[i * 3] = normalized;         // R
+            colors[i * 3 + 1] = 0;              // G
+            colors[i * 3 + 2] = 1 - normalized; // B
         }
-        indicesArray = validIndices;
-      }
-  
-      // 四边形转换
-      if (indicesArray.length % 4 === 0) {
-        console.log('四边形->三角形转换');
-        const triCount = indicesArray.length / 4 * 6;
-        const triangles = new Uint32Array(triCount);
         
-        for (let i = 0; i < indicesArray.length / 4; i++) {
-          const base = i * 4;
-          triangles[i*6]   = indicesArray[base];
-          triangles[i*6+1] = indicesArray[base+1];
-          triangles[i*6+2] = indicesArray[base+2];
-          triangles[i*6+3] = indicesArray[base];
-          triangles[i*6+4] = indicesArray[base+2];
-          triangles[i*6+5] = indicesArray[base+3];
-        }
-        indicesArray = triangles;
-      }
-  
-      geometry.setIndex(new THREE.BufferAttribute(indicesArray, 1));
+        return colors;
     }
-  
-    // 计算边界
-    geometry.computeBoundingSphere();
-    geometry.computeBoundingBox();
-    
-    console.log('几何体验证:', {
-      vertices: zone.vertices.length / 3,
-      triangles: indicesArray.length / 3,
-      radius: geometry.boundingSphere.radius.toFixed(2)
-    });
 
-    geometry.computeVertexNormals(); 
-    console.log('法线检查:', {
-      firstNormal: geometry.attributes.normal?.array.slice(0, 3)
-    });
-  
-    return geometry;
-  }
-
-  _addZoneLabel(zone, mesh, zoneIndex) {
-    // 使用CSS2DRenderer需要先初始化
-    if (!this.labelRenderer) return;
-    
-    const labelDiv = document.createElement('div');
-    labelDiv.className = 'plt-zone-label';
-    labelDiv.textContent = `${zone.name}`;
-    labelDiv.style.backgroundColor = `hsla(${zoneIndex * 360 / 8}, 70%, 50%, 0.7)`;
-    
-    const label = new THREE.CSS2DObject(labelDiv);
-    label.position.set(0, 0, 0); // 位置会在动画中更新
-    
-    mesh.userData.label = label;
-    mesh.add(label);
-  }
-
-  _cleanupPreviousRender(scene) {
-    scene.children.forEach(obj => {
-      if (obj.userData?.isPLT) {
-        // 清理几何体和材质
-        if (obj.geometry) obj.geometry.dispose();
-        if (obj.material) {
-          if (Array.isArray(obj.material)) {
-            obj.material.forEach(m => m.dispose());
-          } else {
-            obj.material.dispose();
-          }
-        }
-        // 清理标签
-        if (obj.userData.label) {
-          obj.remove(obj.userData.label);
+    async updateProgressAsync(message) {
+        if (this.updateProgress) {
+            this.updateProgress(message);
+            await new Promise(resolve => requestAnimationFrame(resolve));
         }
-        scene.remove(obj);
-      }
-    });
-  }
+    }
 
-  // 更新标签位置(需要在动画循环中调用)
-  updateLabels(camera) {
-    if (!this.labelRenderer) return;
-    
-    this.labelRenderer.render(scene, camera);
-    
-    scene.children.forEach(obj => {
-      if (obj.userData?.label) {
-        // 将标签定位到几何中心
-        obj.geometry.computeBoundingSphere();
-        const center = obj.geometry.boundingSphere.center.clone();
-        obj.localToWorld(center);
-        obj.userData.label.position.copy(center);
-      }
-    });
-  }
+    clearScene(scene) {
+        this.meshGroup.traverse(child => {
+            if (child.isMesh) {
+                child.geometry.dispose();
+                child.material.dispose();
+            }
+        });
+        scene.remove(this.meshGroup);
+        this.meshGroup = new THREE.Group();
+    }
 }

+ 2 - 1
vite.config.ts

@@ -40,7 +40,8 @@ export default defineConfig(({ mode }) => {
         resolve: {
             // ↓路径别名
             alias: {
-                '@': resolve('src')
+                '@': resolve('src'),
+                 '@views': resolve('src/views')
             }
         },