ThreeScene.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. <template>
  2. <div ref="threeContainer" class="three-container"></div>
  3. </template>
  4. <script setup>
  5. import { ref, onMounted, onUnmounted, watch } from "vue";
  6. import * as THREE from "three";
  7. import { VTKParserFactory } from "../utils/parsers/VTKParserFactory";
  8. import { UnstructuredGridRenderer } from "../utils/renderers/UnstructuredGridRenderer";
  9. import { PolyDataRenderer } from "../utils/renderers/PolyDataRenderer";
  10. import {
  11. initScene,
  12. initCamera,
  13. initRenderer,
  14. animateScene,
  15. cleanupScene,
  16. initControls,
  17. initAxesHelper
  18. } from "../utils/threeUtils";
  19. const props = defineProps({
  20. vtkData: {
  21. type: Object,
  22. required: true,
  23. },
  24. });
  25. const threeContainer = ref(null);
  26. let scene, camera, renderer, controls, axesHelper;
  27. // 创建坐标系指示器
  28. const createAxesHelper = () => {
  29. const axesHelper = new THREE.AxesHelper(20); // 50 是坐标轴的长度
  30. axesHelper.position.set(-100, -100, 0); // 将坐标系指示器放置在左下角
  31. console.log("Axes helper added to scene:", axesHelper); // 调试日志
  32. // 添加标签
  33. const labels = ['X', 'Y', 'Z'];
  34. const colors = [0xff0000, 0x00ff00, 0x0000ff]; // 红、绿、蓝
  35. labels.forEach((label, index) => {
  36. const sprite = new THREE.Sprite(new THREE.SpriteMaterial({ color: colors[index] }));
  37. sprite.position.set(60 * (index === 0 ? 1 : 0), 60 * (index === 1 ? 1 : 0), 60 * (index === 2 ? 1 : 0));
  38. axesHelper.add(sprite);
  39. });
  40. return axesHelper;
  41. };
  42. // 初始化场景
  43. const init = () => {
  44. scene = initScene();
  45. camera = initCamera();
  46. renderer = initRenderer(threeContainer.value);
  47. controls = initControls(camera, renderer);
  48. // 初始化 AxesHelper
  49. const { axesHelper, updateAxesHelperPosition } = initAxesHelper(scene, camera, renderer.domElement);
  50. return { updateAxesHelperPosition };
  51. };
  52. const adjustCameraToFit = (scene, camera) => {
  53. const box = new THREE.Box3().setFromObject(scene);
  54. const size = new THREE.Vector3();
  55. box.getSize(size);
  56. const maxDim = Math.max(size.x, size.y, size.z);
  57. const fov = camera.fov * (Math.PI / 180);
  58. let cameraZ = Math.abs(maxDim / Math.sin(fov / 2));
  59. const center = new THREE.Vector3();
  60. box.getCenter(center);
  61. camera.position.set(center.x, center.y, cameraZ);
  62. camera.lookAt(center);
  63. };
  64. // 渲染 VTK 数据
  65. const renderVTK = (vtkData) => {
  66. if (!vtkData) return;
  67. // 根据文件格式选择解析器
  68. const parser = VTKParserFactory.createParser(vtkData.datasetType);
  69. const parsedData = parser.parse(vtkData);
  70. // 根据文件格式选择渲染器
  71. let dataRenderer;
  72. switch (vtkData.datasetType) {
  73. case "UNSTRUCTURED_GRID":
  74. dataRenderer = new UnstructuredGridRenderer();
  75. break;
  76. case "POLYDATA":
  77. dataRenderer = new PolyDataRenderer();
  78. break;
  79. default:
  80. throw new Error(`Unsupported dataset type: ${vtkData.datasetType}`);
  81. }
  82. // 渲染数据
  83. dataRenderer.render(parsedData, scene);
  84. // 根据数据类型调整相机位置
  85. if (vtkData.datasetType === "UNSTRUCTURED_GRID") {
  86. adjustCameraForUnstructuredGrid(scene, camera);
  87. } else if (vtkData.datasetType === "POLYDATA") {
  88. adjustCameraForPolydata(scene, camera);
  89. }
  90. // adjustCameraToFit(scene, camera);
  91. };
  92. // 监听 vtkData 变化
  93. watch(
  94. () => props.vtkData,
  95. (newData) => {
  96. if (newData) {
  97. // 清空场景
  98. while (scene.children.length > 0) {
  99. scene.remove(scene.children[0]);
  100. }
  101. // 重新渲染 VTK 数据
  102. renderVTK(newData);
  103. }
  104. },
  105. { immediate: true }
  106. );
  107. // VTK 数据
  108. onMounted(() => {
  109. const { updateAxesHelperPosition } = init();
  110. animateScene(scene, camera, renderer, controls, () => {
  111. updateAxesHelperPosition(); // 更新 AxesHelper 的位置和方向
  112. });
  113. // 监听窗口大小变化
  114. window.addEventListener("resize", onWindowResize);
  115. });
  116. onUnmounted(() => {
  117. // 清理场景
  118. cleanupScene(renderer);
  119. // 移除窗口大小变化监听器
  120. window.removeEventListener("resize", onWindowResize);
  121. });
  122. const onWindowResize = () => {
  123. const width = threeContainer.value.clientWidth;
  124. const height = threeContainer.value.clientHeight;
  125. camera.aspect = width / height;
  126. camera.updateProjectionMatrix();
  127. renderer.setSize(width, height);
  128. };
  129. const adjustCameraForUnstructuredGrid = (scene, camera) => {
  130. const box = new THREE.Box3().setFromObject(scene);
  131. const size = new THREE.Vector3();
  132. box.getSize(size);
  133. const maxDim = Math.max(size.x, size.y, size.z);
  134. const fov = camera.fov * (Math.PI / 180);
  135. let cameraZ = Math.abs(maxDim / Math.sin(fov / 2));
  136. const center = new THREE.Vector3();
  137. box.getCenter(center);
  138. camera.position.set(center.x, center.y, cameraZ);
  139. camera.lookAt(center);
  140. };
  141. const adjustCameraForPolydata = (scene, camera) => {
  142. const box = new THREE.Box3().setFromObject(scene);
  143. // 手动设置边界框
  144. box.set(
  145. new THREE.Vector3(-100, -100, -100),
  146. new THREE.Vector3(100, 100, 100)
  147. );
  148. const size = new THREE.Vector3();
  149. box.getSize(size);
  150. console.log("Polydata bounding box:", box);
  151. console.log("Polydata size:", size);
  152. const maxDim = Math.max(size.x, size.y, size.z);
  153. const fov = camera.fov * (Math.PI / 180);
  154. let cameraZ = Math.abs(maxDim / Math.sin(fov / 2));
  155. const center = new THREE.Vector3();
  156. box.getCenter(center);
  157. camera.position.set(center.x, center.y, cameraZ * 0.8); // 调整相机距离
  158. camera.lookAt(center);
  159. };
  160. </script>
  161. <style>
  162. .three-container {
  163. width: 100%;
  164. height: calc(89vh - 8px); /* 给容器设置固定高度 */
  165. border: 1px solid #ccc; /* 可选:添加边框以便查看容器范围 */
  166. }
  167. </style>