|
@@ -99,7 +99,7 @@ export class PltHandler extends BaseDataHandler {
|
|
|
specular: 0x111111,
|
|
|
shininess: 30,
|
|
|
side: THREE.DoubleSide,
|
|
|
- flatShading: false,
|
|
|
+ flatShading: options.smooth === false,
|
|
|
vertexColors: true, // 必须启用
|
|
|
transparent: true,
|
|
|
opacity: 1.0
|
|
@@ -310,6 +310,20 @@ export class PltHandler extends BaseDataHandler {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ // 添加平滑处理
|
|
|
+ if (mesh.material) {
|
|
|
+ mesh.material.flatShading = options.smooth === false;
|
|
|
+ mesh.material.needsUpdate = true;
|
|
|
+
|
|
|
+ // 添加边缘高亮增强视觉效果
|
|
|
+ if (options.smooth === false) {
|
|
|
+ mesh.material.wireframe = true;
|
|
|
+ mesh.material.wireframeLinewidth = 1;
|
|
|
+ } else {
|
|
|
+ mesh.material.wireframe = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
const colors = this.generateVertexColors(
|
|
|
zone.variables[variableName],
|
|
|
options.colorMap || 'rainbow',
|
|
@@ -340,6 +354,36 @@ export class PltHandler extends BaseDataHandler {
|
|
|
return updated;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 切换平滑渲染模式
|
|
|
+ * @param {boolean} smooth - 是否启用平滑
|
|
|
+ */
|
|
|
+ setSmoothShading(smooth) {
|
|
|
+ let changed = false;
|
|
|
+ this.objectMap.forEach(mesh => {
|
|
|
+ if (mesh.material) {
|
|
|
+ // 主要切换flatShading
|
|
|
+ mesh.material.flatShading = !smooth;
|
|
|
+
|
|
|
+ // 非平滑模式下添加wireframe增强效果
|
|
|
+ mesh.material.wireframe = !smooth;
|
|
|
+
|
|
|
+ // 非平滑模式下使用更简单的材质
|
|
|
+ if (!smooth) {
|
|
|
+ mesh.material.shininess = 0;
|
|
|
+ mesh.material.specular.set(0x000000);
|
|
|
+ } else {
|
|
|
+ mesh.material.shininess = 30;
|
|
|
+ mesh.material.specular.set(0x111111);
|
|
|
+ }
|
|
|
+
|
|
|
+ mesh.material.needsUpdate = true;
|
|
|
+ changed = true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return changed;
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
// 域控制
|
|
|
setZoneVisibility(zoneName, visible) {
|
|
@@ -426,24 +470,24 @@ export class PltHandler extends BaseDataHandler {
|
|
|
* @param {Object} options 配置
|
|
|
* @returns {boolean} 是否全部成功
|
|
|
*/
|
|
|
-generateContoursForAllZones(variableName, options = {}) {
|
|
|
- console.group("批量生成等值线调试信息");
|
|
|
- let allSuccess = true;
|
|
|
-
|
|
|
- this.objectMap.forEach((mesh, zoneName) => {
|
|
|
- if (mesh.visible) {
|
|
|
- const success = this.generateContour(zoneName, variableName, options);
|
|
|
- if (!success) {
|
|
|
- console.warn(`域 ${zoneName} 的等值线生成失败`);
|
|
|
- allSuccess = false;
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
+ generateContoursForAllZones(variableName, options = {}) {
|
|
|
+ console.group("批量生成等值线调试信息");
|
|
|
+ let allSuccess = true;
|
|
|
+
|
|
|
+ this.objectMap.forEach((mesh, zoneName) => {
|
|
|
+ if (mesh.visible) {
|
|
|
+ const success = this.generateContour(zoneName, variableName, options);
|
|
|
+ if (!success) {
|
|
|
+ console.warn(`域 ${zoneName} 的等值线生成失败`);
|
|
|
+ allSuccess = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
- console.log("批量生成等值线完成", { allSuccess });
|
|
|
- console.groupEnd();
|
|
|
- return allSuccess;
|
|
|
- }
|
|
|
+ console.log("批量生成等值线完成", { allSuccess });
|
|
|
+ console.groupEnd();
|
|
|
+ return allSuccess;
|
|
|
+ }
|
|
|
|
|
|
/**
|
|
|
* 生成等值线
|
|
@@ -452,140 +496,140 @@ generateContoursForAllZones(variableName, options = {}) {
|
|
|
* @param {Object} options 配置
|
|
|
*/
|
|
|
generateContour(zoneName, variableName, options = {}) {
|
|
|
- console.group("生成等值线调试信息");
|
|
|
+ console.group("生成等值线调试信息");
|
|
|
|
|
|
- const mesh = this.objectMap.get(zoneName);
|
|
|
- if (!mesh) {
|
|
|
- console.error("找不到指定区域的网格");
|
|
|
- console.groupEnd();
|
|
|
- return false;
|
|
|
- }
|
|
|
+ const mesh = this.objectMap.get(zoneName);
|
|
|
+ if (!mesh) {
|
|
|
+ console.error("找不到指定区域的网格");
|
|
|
+ console.groupEnd();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- const cacheKey = `${variableName}_${options.levels}_${options.minValue}_${options.maxValue}`;
|
|
|
- if (mesh.userData.contourCache?.[cacheKey]) {
|
|
|
- console.log(`使用缓存的等值线: ${zoneName}, ${variableName}`);
|
|
|
- this.scene.add(mesh.userData.contourCache[cacheKey].group);
|
|
|
- mesh.visible = false;
|
|
|
- console.groupEnd();
|
|
|
- return true;
|
|
|
- }
|
|
|
+ const cacheKey = `${variableName}_${options.levels}_${options.minValue}_${options.maxValue}`;
|
|
|
+ if (mesh.userData.contourCache?.[cacheKey]) {
|
|
|
+ console.log(`使用缓存的等值线: ${zoneName}, ${variableName}`);
|
|
|
+ this.scene.add(mesh.userData.contourCache[cacheKey].group);
|
|
|
+ mesh.visible = false;
|
|
|
+ console.groupEnd();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
|
|
|
- const zoneData = mesh.userData.originalZone;
|
|
|
- if (!zoneData?.variables?.[variableName]) {
|
|
|
- console.error(`变量不存在`, {
|
|
|
- zone: zoneName,
|
|
|
- requestedVar: variableName,
|
|
|
- availableVars: Object.keys(zoneData.variables || {})
|
|
|
- });
|
|
|
- console.groupEnd();
|
|
|
- return false;
|
|
|
- }
|
|
|
+ const zoneData = mesh.userData.originalZone;
|
|
|
+ if (!zoneData?.variables?.[variableName]) {
|
|
|
+ console.error(`变量不存在`, {
|
|
|
+ zone: zoneName,
|
|
|
+ requestedVar: variableName,
|
|
|
+ availableVars: Object.keys(zoneData.variables || {})
|
|
|
+ });
|
|
|
+ console.groupEnd();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- const positions = mesh.geometry.attributes.position.array;
|
|
|
- const scalarData = zoneData.variables[variableName];
|
|
|
+ const positions = mesh.geometry.attributes.position.array;
|
|
|
+ const scalarData = zoneData.variables[variableName];
|
|
|
|
|
|
- if (positions.length / 3 !== scalarData.length) {
|
|
|
- console.error("顶点数与标量数据长度不匹配", {
|
|
|
- positionsLength: positions.length / 3,
|
|
|
- scalarDataLength: scalarData.length
|
|
|
- });
|
|
|
- console.groupEnd();
|
|
|
- return false;
|
|
|
- }
|
|
|
+ if (positions.length / 3 !== scalarData.length) {
|
|
|
+ console.error("顶点数与标量数据长度不匹配", {
|
|
|
+ positionsLength: positions.length / 3,
|
|
|
+ scalarDataLength: scalarData.length
|
|
|
+ });
|
|
|
+ console.groupEnd();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- // 数据清洗
|
|
|
- const validIndices = [];
|
|
|
- for (let i = 0; i < scalarData.length; i++) {
|
|
|
- const x = positions[i * 3];
|
|
|
- const y = positions[i * 3 + 1];
|
|
|
- const z = positions[i * 3 + 2];
|
|
|
- if (
|
|
|
- !isNaN(scalarData[i]) && isFinite(scalarData[i]) &&
|
|
|
- !isNaN(x) && !isNaN(y) && !isNaN(z) &&
|
|
|
- isFinite(x) && isFinite(y) && isFinite(z)
|
|
|
- ) {
|
|
|
- validIndices.push(i);
|
|
|
- }
|
|
|
- }
|
|
|
+ // 数据清洗
|
|
|
+ const validIndices = [];
|
|
|
+ for (let i = 0; i < scalarData.length; i++) {
|
|
|
+ const x = positions[i * 3];
|
|
|
+ const y = positions[i * 3 + 1];
|
|
|
+ const z = positions[i * 3 + 2];
|
|
|
+ if (
|
|
|
+ !isNaN(scalarData[i]) && isFinite(scalarData[i]) &&
|
|
|
+ !isNaN(x) && !isNaN(y) && !isNaN(z) &&
|
|
|
+ isFinite(x) && isFinite(y) && isFinite(z)
|
|
|
+ ) {
|
|
|
+ validIndices.push(i);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- if (validIndices.length === 0) {
|
|
|
- console.error("无有效数据点");
|
|
|
- console.groupEnd();
|
|
|
- return false;
|
|
|
- }
|
|
|
+ if (validIndices.length === 0) {
|
|
|
+ console.error("无有效数据点");
|
|
|
+ console.groupEnd();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- // 创建清洁几何体
|
|
|
- const cleanGeometry = new THREE.BufferGeometry();
|
|
|
- const cleanPositions = new Float32Array(validIndices.length * 3);
|
|
|
- const cleanScalars = new Float32Array(validIndices.length);
|
|
|
- const cleanIndices = [];
|
|
|
-
|
|
|
- const indexMap = new Map();
|
|
|
- validIndices.forEach((origIdx, newIdx) => {
|
|
|
- indexMap.set(origIdx, newIdx);
|
|
|
- cleanPositions[newIdx * 3] = positions[origIdx * 3];
|
|
|
- cleanPositions[newIdx * 3 + 1] = positions[origIdx * 3 + 1];
|
|
|
- cleanPositions[newIdx * 3 + 2] = positions[origIdx * 3 + 2];
|
|
|
- cleanScalars[newIdx] = scalarData[origIdx];
|
|
|
- });
|
|
|
-
|
|
|
- const originalIndices = mesh.geometry.index?.array || [];
|
|
|
- for (let i = 0; i < originalIndices.length; i += 3) {
|
|
|
- const i0 = indexMap.get(originalIndices[i]);
|
|
|
- const i1 = indexMap.get(originalIndices[i + 1]);
|
|
|
- const i2 = indexMap.get(originalIndices[i + 2]);
|
|
|
- if (i0 !== undefined && i1 !== undefined && i2 !== undefined) {
|
|
|
- cleanIndices.push(i0, i1, i2);
|
|
|
- }
|
|
|
- }
|
|
|
+ // 创建清洁几何体
|
|
|
+ const cleanGeometry = new THREE.BufferGeometry();
|
|
|
+ const cleanPositions = new Float32Array(validIndices.length * 3);
|
|
|
+ const cleanScalars = new Float32Array(validIndices.length);
|
|
|
+ const cleanIndices = [];
|
|
|
|
|
|
- cleanGeometry.setAttribute('position', new THREE.BufferAttribute(cleanPositions, 3));
|
|
|
- cleanGeometry.setIndex(new THREE.BufferAttribute(new Uint32Array(cleanIndices), 1));
|
|
|
- cleanGeometry.computeVertexNormals();
|
|
|
- cleanGeometry.computeBoundingSphere();
|
|
|
+ const indexMap = new Map();
|
|
|
+ validIndices.forEach((origIdx, newIdx) => {
|
|
|
+ indexMap.set(origIdx, newIdx);
|
|
|
+ cleanPositions[newIdx * 3] = positions[origIdx * 3];
|
|
|
+ cleanPositions[newIdx * 3 + 1] = positions[origIdx * 3 + 1];
|
|
|
+ cleanPositions[newIdx * 3 + 2] = positions[origIdx * 3 + 2];
|
|
|
+ cleanScalars[newIdx] = scalarData[origIdx];
|
|
|
+ });
|
|
|
|
|
|
- if (isNaN(cleanGeometry.boundingSphere.radius)) {
|
|
|
- console.error("边界球计算失败,使用默认值");
|
|
|
- cleanGeometry.boundingSphere = new THREE.Sphere(new THREE.Vector3(0, 0, 0), 1);
|
|
|
- }
|
|
|
+ const originalIndices = mesh.geometry.index?.array || [];
|
|
|
+ for (let i = 0; i < originalIndices.length; i += 3) {
|
|
|
+ const i0 = indexMap.get(originalIndices[i]);
|
|
|
+ const i1 = indexMap.get(originalIndices[i + 1]);
|
|
|
+ const i2 = indexMap.get(originalIndices[i + 2]);
|
|
|
+ if (i0 !== undefined && i1 !== undefined && i2 !== undefined) {
|
|
|
+ cleanIndices.push(i0, i1, i2);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- // 生成等值线
|
|
|
- const contours = this.calculateContours(cleanGeometry, cleanScalars, {
|
|
|
- levels: options.levels || 5,
|
|
|
- minValue: options.minValue ?? Math.min(...cleanScalars),
|
|
|
- maxValue: options.maxValue ?? Math.max(...cleanScalars)
|
|
|
- });
|
|
|
-
|
|
|
- if (contours.length === 0) {
|
|
|
- console.error("未生成有效等值线");
|
|
|
- console.groupEnd();
|
|
|
- return false;
|
|
|
- }
|
|
|
+ cleanGeometry.setAttribute('position', new THREE.BufferAttribute(cleanPositions, 3));
|
|
|
+ cleanGeometry.setIndex(new THREE.BufferAttribute(new Uint32Array(cleanIndices), 1));
|
|
|
+ cleanGeometry.computeVertexNormals();
|
|
|
+ cleanGeometry.computeBoundingSphere();
|
|
|
+
|
|
|
+ if (isNaN(cleanGeometry.boundingSphere.radius)) {
|
|
|
+ console.error("边界球计算失败,使用默认值");
|
|
|
+ cleanGeometry.boundingSphere = new THREE.Sphere(new THREE.Vector3(0, 0, 0), 1);
|
|
|
+ }
|
|
|
|
|
|
- // 创建等值线组
|
|
|
- const contourGroup = new THREE.Group();
|
|
|
- contourGroup.name = `${zoneName}_contour`;
|
|
|
+ // 生成等值线
|
|
|
+ const contours = this.calculateContours(cleanGeometry, cleanScalars, {
|
|
|
+ levels: options.levels || 5,
|
|
|
+ minValue: options.minValue ?? Math.min(...cleanScalars),
|
|
|
+ maxValue: options.maxValue ?? Math.max(...cleanScalars)
|
|
|
+ });
|
|
|
+
|
|
|
+ if (contours.length === 0) {
|
|
|
+ console.error("未生成有效等值线");
|
|
|
+ console.groupEnd();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- contours.forEach(points => {
|
|
|
- const lineGeometry = new THREE.BufferGeometry().setFromPoints(points);
|
|
|
- contourGroup.add(new THREE.Line(lineGeometry, this.contourMaterial));
|
|
|
- });
|
|
|
+ // 创建等值线组
|
|
|
+ const contourGroup = new THREE.Group();
|
|
|
+ contourGroup.name = `${zoneName}_contour`;
|
|
|
|
|
|
- mesh.visible = false;
|
|
|
- this.removeContour(zoneName);
|
|
|
- mesh.userData.contourGroup = contourGroup;
|
|
|
- this.scene.add(contourGroup);
|
|
|
+ contours.forEach(points => {
|
|
|
+ const lineGeometry = new THREE.BufferGeometry().setFromPoints(points);
|
|
|
+ contourGroup.add(new THREE.Line(lineGeometry, this.contourMaterial));
|
|
|
+ });
|
|
|
|
|
|
- mesh.userData.contourCache = mesh.userData.contourCache || {};
|
|
|
- mesh.userData.contourCache[cacheKey] = { group: contourGroup };
|
|
|
+ mesh.visible = false;
|
|
|
+ this.removeContour(zoneName);
|
|
|
+ mesh.userData.contourGroup = contourGroup;
|
|
|
+ this.scene.add(contourGroup);
|
|
|
|
|
|
- console.log("等值线生成成功", {
|
|
|
- points: cleanPositions.length / 3,
|
|
|
- contours: contours.length
|
|
|
- });
|
|
|
- console.groupEnd();
|
|
|
- return true;
|
|
|
- }
|
|
|
+ mesh.userData.contourCache = mesh.userData.contourCache || {};
|
|
|
+ mesh.userData.contourCache[cacheKey] = { group: contourGroup };
|
|
|
+
|
|
|
+ console.log("等值线生成成功", {
|
|
|
+ points: cleanPositions.length / 3,
|
|
|
+ contours: contours.length
|
|
|
+ });
|
|
|
+ console.groupEnd();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
|
|
|
createCleanGeometry(originalGeometry, scalarData) {
|
|
|
const positions = originalGeometry.attributes.position.array;
|
|
@@ -634,109 +678,109 @@ generateContoursForAllZones(variableName, options = {}) {
|
|
|
* 计算等值线
|
|
|
*/
|
|
|
calculateContours(geometry, scalarData, options) {
|
|
|
- console.group("计算等值线调试信息");
|
|
|
- const positions = geometry.attributes.position.array;
|
|
|
- const indices = geometry.index?.array || [];
|
|
|
- const { levels, minValue, maxValue } = options;
|
|
|
- const contours = [];
|
|
|
-
|
|
|
- try {
|
|
|
- const step = (maxValue - minValue) / (levels || 1); // 防止除零
|
|
|
-
|
|
|
- // 遍历三角面
|
|
|
- for (let i = 0; i < indices.length; i += 3) {
|
|
|
- const i0 = indices[i];
|
|
|
- const i1 = indices[i + 1];
|
|
|
- const i2 = indices[i + 2];
|
|
|
-
|
|
|
- const v0 = new THREE.Vector3(positions[i0 * 3], positions[i0 * 3 + 1], positions[i0 * 3 + 2]);
|
|
|
- const v1 = new THREE.Vector3(positions[i1 * 3], positions[i1 * 3 + 1], positions[i1 * 3 + 2]);
|
|
|
- const v2 = new THREE.Vector3(positions[i2 * 3], positions[i2 * 3 + 1], positions[i2 * 3 + 2]);
|
|
|
- const s0 = scalarData[i0];
|
|
|
- const s1 = scalarData[i1];
|
|
|
- const s2 = scalarData[i2];
|
|
|
-
|
|
|
- if (isNaN(s0) || isNaN(s1) || isNaN(s2) || !isFinite(s0) || !isFinite(s1) || !isFinite(s2)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
+ console.group("计算等值线调试信息");
|
|
|
+ const positions = geometry.attributes.position.array;
|
|
|
+ const indices = geometry.index?.array || [];
|
|
|
+ const { levels, minValue, maxValue } = options;
|
|
|
+ const contours = [];
|
|
|
+
|
|
|
+ try {
|
|
|
+ const step = (maxValue - minValue) / (levels || 1); // 防止除零
|
|
|
+
|
|
|
+ // 遍历三角面
|
|
|
+ for (let i = 0; i < indices.length; i += 3) {
|
|
|
+ const i0 = indices[i];
|
|
|
+ const i1 = indices[i + 1];
|
|
|
+ const i2 = indices[i + 2];
|
|
|
+
|
|
|
+ const v0 = new THREE.Vector3(positions[i0 * 3], positions[i0 * 3 + 1], positions[i0 * 3 + 2]);
|
|
|
+ const v1 = new THREE.Vector3(positions[i1 * 3], positions[i1 * 3 + 1], positions[i1 * 3 + 2]);
|
|
|
+ const v2 = new THREE.Vector3(positions[i2 * 3], positions[i2 * 3 + 1], positions[i2 * 3 + 2]);
|
|
|
+ const s0 = scalarData[i0];
|
|
|
+ const s1 = scalarData[i1];
|
|
|
+ const s2 = scalarData[i2];
|
|
|
+
|
|
|
+ if (isNaN(s0) || isNaN(s1) || isNaN(s2) || !isFinite(s0) || !isFinite(s1) || !isFinite(s2)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (let level = 0; level <= levels; level++) {
|
|
|
+ const threshold = minValue + level * step;
|
|
|
+ const points = this.calculateTriangleContour(v0, v1, v2, s0, s1, s2, threshold);
|
|
|
+ if (points.length >= 2) { // 确保至少有 2 个点形成线段
|
|
|
+ contours.push(points);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- for (let level = 0; level <= levels; level++) {
|
|
|
- const threshold = minValue + level * step;
|
|
|
- const points = this.calculateTriangleContour(v0, v1, v2, s0, s1, s2, threshold);
|
|
|
- if (points.length >= 2) { // 确保至少有 2 个点形成线段
|
|
|
- contours.push(points);
|
|
|
- }
|
|
|
+ console.log("总等值线数量", contours.length);
|
|
|
+ } catch (error) {
|
|
|
+ console.error("等值线计算失败", error);
|
|
|
+ } finally {
|
|
|
+ console.groupEnd();
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- console.log("总等值线数量", contours.length);
|
|
|
- } catch (error) {
|
|
|
- console.error("等值线计算失败", error);
|
|
|
- } finally {
|
|
|
- console.groupEnd();
|
|
|
+ return contours;
|
|
|
}
|
|
|
|
|
|
- return contours;
|
|
|
- }
|
|
|
-
|
|
|
calculateTriangleContour(v0, v1, v2, s0, s1, s2, threshold) {
|
|
|
- const points = [];
|
|
|
+ const points = [];
|
|
|
|
|
|
- // 验证标量值范围
|
|
|
- const minScalar = Math.min(s0, s1, s2);
|
|
|
- const maxScalar = Math.max(s0, s1, s2);
|
|
|
- if (threshold < minScalar || threshold > maxScalar || !isFinite(threshold)) {
|
|
|
- return points; // 阈值超出范围或无效
|
|
|
- }
|
|
|
+ // 验证标量值范围
|
|
|
+ const minScalar = Math.min(s0, s1, s2);
|
|
|
+ const maxScalar = Math.max(s0, s1, s2);
|
|
|
+ if (threshold < minScalar || threshold > maxScalar || !isFinite(threshold)) {
|
|
|
+ return points; // 阈值超出范围或无效
|
|
|
+ }
|
|
|
|
|
|
- // 检查三角形是否退化
|
|
|
- const v01 = v1.clone().sub(v0).length();
|
|
|
- const v12 = v2.clone().sub(v1).length();
|
|
|
- const v20 = v0.clone().sub(v2).length();
|
|
|
- if (v01 < 1e-6 || v12 < 1e-6 || v20 < 1e-6) {
|
|
|
- return points; // 跳过退化三角形
|
|
|
- }
|
|
|
+ // 检查三角形是否退化
|
|
|
+ const v01 = v1.clone().sub(v0).length();
|
|
|
+ const v12 = v2.clone().sub(v1).length();
|
|
|
+ const v20 = v0.clone().sub(v2).length();
|
|
|
+ if (v01 < 1e-6 || v12 < 1e-6 || v20 < 1e-6) {
|
|
|
+ return points; // 跳过退化三角形
|
|
|
+ }
|
|
|
|
|
|
- // 计算交点
|
|
|
- const edges = [
|
|
|
- { vStart: v0, vEnd: v1, sStart: s0, sEnd: s1 }, // 边 0-1
|
|
|
- { vStart: v1, vEnd: v2, sStart: s1, sEnd: s2 }, // 边 1-2
|
|
|
- { vStart: v2, vEnd: v0, sStart: s2, sEnd: s0 } // 边 2-0
|
|
|
- ];
|
|
|
-
|
|
|
- const edgePoints = [];
|
|
|
- edges.forEach(({ vStart, vEnd, sStart, sEnd }) => {
|
|
|
- if ((sStart <= threshold && sEnd >= threshold) || (sStart >= threshold && sEnd <= threshold)) {
|
|
|
- const delta = sEnd - sStart;
|
|
|
- if (Math.abs(delta) < 1e-6) return; // 防止除零
|
|
|
- const t = (threshold - sStart) / delta;
|
|
|
- if (t >= 0 && t <= 1) { // 确保插值参数有效
|
|
|
- const point = vStart.clone().lerp(vEnd, t);
|
|
|
- edgePoints.push(point);
|
|
|
+ // 计算交点
|
|
|
+ const edges = [
|
|
|
+ { vStart: v0, vEnd: v1, sStart: s0, sEnd: s1 }, // 边 0-1
|
|
|
+ { vStart: v1, vEnd: v2, sStart: s1, sEnd: s2 }, // 边 1-2
|
|
|
+ { vStart: v2, vEnd: v0, sStart: s2, sEnd: s0 } // 边 2-0
|
|
|
+ ];
|
|
|
+
|
|
|
+ const edgePoints = [];
|
|
|
+ edges.forEach(({ vStart, vEnd, sStart, sEnd }) => {
|
|
|
+ if ((sStart <= threshold && sEnd >= threshold) || (sStart >= threshold && sEnd <= threshold)) {
|
|
|
+ const delta = sEnd - sStart;
|
|
|
+ if (Math.abs(delta) < 1e-6) return; // 防止除零
|
|
|
+ const t = (threshold - sStart) / delta;
|
|
|
+ if (t >= 0 && t <= 1) { // 确保插值参数有效
|
|
|
+ const point = vStart.clone().lerp(vEnd, t);
|
|
|
+ edgePoints.push(point);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 每条等值线应有 2 个点
|
|
|
+ if (edgePoints.length === 2) {
|
|
|
+ points.push(...edgePoints);
|
|
|
}
|
|
|
- }
|
|
|
- });
|
|
|
|
|
|
- // 每条等值线应有 2 个点
|
|
|
- if (edgePoints.length === 2) {
|
|
|
- points.push(...edgePoints);
|
|
|
+ return points;
|
|
|
}
|
|
|
|
|
|
- return points;
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* 移除等值线
|
|
|
*/
|
|
|
removeContour(zoneName) {
|
|
|
- const mesh = this.objectMap.get(zoneName);
|
|
|
- if (mesh?.userData?.contourGroup) {
|
|
|
- this.scene.remove(mesh.userData.contourGroup);
|
|
|
- mesh.userData.contourGroup.traverse(child => {
|
|
|
- if (child.isLine) child.geometry.dispose();
|
|
|
- });
|
|
|
- delete mesh.userData.contourGroup;
|
|
|
- delete mesh.userData.contourCache;
|
|
|
+ const mesh = this.objectMap.get(zoneName);
|
|
|
+ if (mesh?.userData?.contourGroup) {
|
|
|
+ this.scene.remove(mesh.userData.contourGroup);
|
|
|
+ mesh.userData.contourGroup.traverse(child => {
|
|
|
+ if (child.isLine) child.geometry.dispose();
|
|
|
+ });
|
|
|
+ delete mesh.userData.contourGroup;
|
|
|
+ delete mesh.userData.contourCache;
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
}
|