cfd-materials 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -0
- package/build/docs/404.html +47 -0
- package/build/docs/_demos/:uuid +47 -0
- package/build/docs/cloud-visualization.html +48 -0
- package/build/docs/colorful-button.html +48 -0
- package/build/docs/colorful-input.html +48 -0
- package/build/docs/index.html +48 -0
- package/build/docs/mesh-visualization.html +48 -0
- package/build/docs/umi.4e426eb9.js +1 -0
- package/build/docs/umi.6b1604cb.css +8 -0
- package/build/docs/~demos/:uuid.html +47 -0
- package/build/docs/~demos/cloud-visualization-demo.html +47 -0
- package/build/docs/~demos/colorful-button-demo.html +47 -0
- package/build/docs/~demos/colorful-input-demo.html +47 -0
- package/build/docs/~demos/mesh-visualization-demo.html +47 -0
- package/build/index.css +2 -0
- package/build/index.html +1 -0
- package/build/index.js +53 -0
- package/build/lowcode/assets-daily.json +67 -0
- package/build/lowcode/assets-dev.json +67 -0
- package/build/lowcode/assets-prod.json +67 -0
- package/build/lowcode/designer.html +302 -0
- package/build/lowcode/index.html +304 -0
- package/build/lowcode/index.js +1 -0
- package/build/lowcode/meta.js +1 -0
- package/build/lowcode/preview.css +1 -0
- package/build/lowcode/preview.html +33 -0
- package/build/lowcode/preview.js +310 -0
- package/build/lowcode/render/default/view.css +1 -0
- package/build/lowcode/render/default/view.js +13 -0
- package/build/lowcode/view.css +1 -0
- package/build/lowcode/view.js +13 -0
- package/dist/BizComps.css +1 -0
- package/dist/BizComps.js +14 -0
- package/dist/BizComps.js.map +1 -0
- package/es/components/cloud-visualization/cloud-visualization.d.ts +20 -0
- package/es/components/cloud-visualization/cloud-visualization.js +738 -0
- package/es/components/cloud-visualization/index.d.ts +3 -0
- package/es/components/cloud-visualization/index.js +2 -0
- package/es/components/cloud-visualization/index.scss +55 -0
- package/es/components/colorful-button/colorful-button.d.ts +12 -0
- package/es/components/colorful-button/colorful-button.js +25 -0
- package/es/components/colorful-button/index.d.ts +3 -0
- package/es/components/colorful-button/index.js +2 -0
- package/es/components/colorful-button/index.scss +5 -0
- package/es/components/colorful-input/colorful-input.d.ts +8 -0
- package/es/components/colorful-input/colorful-input.js +19 -0
- package/es/components/colorful-input/index.d.ts +3 -0
- package/es/components/colorful-input/index.js +2 -0
- package/es/components/colorful-input/index.scss +5 -0
- package/es/components/mesh-visualization/index.d.ts +3 -0
- package/es/components/mesh-visualization/index.js +2 -0
- package/es/components/mesh-visualization/index.scss +441 -0
- package/es/components/mesh-visualization/mesh-visualization.d.ts +30 -0
- package/es/components/mesh-visualization/mesh-visualization.js +2609 -0
- package/es/index.d.ts +10 -0
- package/es/index.js +6 -0
- package/es/index.scss +2 -0
- package/es/style.js +3 -0
- package/es/variables.d.ts +2 -0
- package/es/variables.js +2 -0
- package/es/variables.scss +3 -0
- package/lib/components/cloud-visualization/cloud-visualization.d.ts +20 -0
- package/lib/components/cloud-visualization/cloud-visualization.js +743 -0
- package/lib/components/cloud-visualization/index.d.ts +3 -0
- package/lib/components/cloud-visualization/index.js +7 -0
- package/lib/components/cloud-visualization/index.scss +55 -0
- package/lib/components/colorful-button/colorful-button.d.ts +12 -0
- package/lib/components/colorful-button/colorful-button.js +31 -0
- package/lib/components/colorful-button/index.d.ts +3 -0
- package/lib/components/colorful-button/index.js +7 -0
- package/lib/components/colorful-button/index.scss +5 -0
- package/lib/components/colorful-input/colorful-input.d.ts +8 -0
- package/lib/components/colorful-input/colorful-input.js +25 -0
- package/lib/components/colorful-input/index.d.ts +3 -0
- package/lib/components/colorful-input/index.js +7 -0
- package/lib/components/colorful-input/index.scss +5 -0
- package/lib/components/mesh-visualization/index.d.ts +3 -0
- package/lib/components/mesh-visualization/index.js +7 -0
- package/lib/components/mesh-visualization/index.scss +441 -0
- package/lib/components/mesh-visualization/mesh-visualization.d.ts +30 -0
- package/lib/components/mesh-visualization/mesh-visualization.js +2614 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.js +14 -0
- package/lib/index.scss +2 -0
- package/lib/style.js +3 -0
- package/lib/variables.d.ts +2 -0
- package/lib/variables.js +5 -0
- package/lib/variables.scss +3 -0
- package/lowcode/cloud-visualization/meta.ts +194 -0
- package/lowcode/colorful-button/meta.ts +102 -0
- package/lowcode/colorful-input/meta.ts +56 -0
- package/lowcode/mesh-visualization/meta.ts +278 -0
- package/lowcode_es/cloud-visualization/meta.d.ts +22 -0
- package/lowcode_es/cloud-visualization/meta.js +197 -0
- package/lowcode_es/colorful-button/meta.d.ts +22 -0
- package/lowcode_es/colorful-button/meta.js +85 -0
- package/lowcode_es/colorful-input/meta.d.ts +22 -0
- package/lowcode_es/colorful-input/meta.js +48 -0
- package/lowcode_es/mesh-visualization/meta.d.ts +22 -0
- package/lowcode_es/mesh-visualization/meta.js +285 -0
- package/lowcode_es/meta.js +167 -0
- package/lowcode_es/view.js +18 -0
- package/lowcode_lib/cloud-visualization/meta.d.ts +22 -0
- package/lowcode_lib/cloud-visualization/meta.js +202 -0
- package/lowcode_lib/colorful-button/meta.d.ts +22 -0
- package/lowcode_lib/colorful-button/meta.js +90 -0
- package/lowcode_lib/colorful-input/meta.d.ts +22 -0
- package/lowcode_lib/colorful-input/meta.js +53 -0
- package/lowcode_lib/mesh-visualization/meta.d.ts +22 -0
- package/lowcode_lib/mesh-visualization/meta.js +290 -0
- package/lowcode_lib/meta.js +171 -0
- package/lowcode_lib/view.js +28 -0
- package/package.json +104 -0
|
@@ -0,0 +1,2614 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
exports.__esModule = true;
|
|
5
|
+
exports["default"] = void 0;
|
|
6
|
+
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
|
|
7
|
+
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
|
8
|
+
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
|
|
9
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
10
|
+
var React = _react;
|
|
11
|
+
var THREE = _interopRequireWildcard(require("three"));
|
|
12
|
+
var _OrbitControls = require("three/examples/jsm/controls/OrbitControls");
|
|
13
|
+
var _STLLoader = require("three/examples/jsm/loaders/STLLoader");
|
|
14
|
+
var _OBJLoader = require("three/examples/jsm/loaders/OBJLoader");
|
|
15
|
+
var _variables = require("../../variables");
|
|
16
|
+
require("./index.scss");
|
|
17
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t3 in e) "default" !== _t3 && {}.hasOwnProperty.call(e, _t3) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t3)) && (i.get || i.set) ? o(f, _t3, i) : f[_t3] = e[_t3]); return f; })(e, t); }
|
|
18
|
+
// //第一版
|
|
19
|
+
// import * as React from 'react';
|
|
20
|
+
// import { createElement, useEffect, useMemo, useRef, useState } from 'react';
|
|
21
|
+
// import * as THREE from 'three';
|
|
22
|
+
// import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
|
|
23
|
+
// import { STLLoader } from 'three/examples/jsm/loaders/STLLoader';
|
|
24
|
+
// import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader';
|
|
25
|
+
// import { bizCssPrefix } from '../../variables';
|
|
26
|
+
// import './index.scss';
|
|
27
|
+
|
|
28
|
+
// export type MeshSourceType = 'demo' | 'url' | 'local';
|
|
29
|
+
// export type MeshDataFormat = 'custom-json' | 'stl' | 'obj';
|
|
30
|
+
// export type MeshType = 'structured' | 'unstructured';
|
|
31
|
+
// export type MeshDisplayMode = 'surface' | 'wireframe' | 'surface-wireframe' | 'quality';
|
|
32
|
+
// export type MeshQualityMode = 'orthogonality' | 'skewness' | 'aspectRatio';
|
|
33
|
+
|
|
34
|
+
// export interface MeshVisualizationProps {
|
|
35
|
+
// sourceType?: MeshSourceType;
|
|
36
|
+
// dataFormat?: MeshDataFormat;
|
|
37
|
+
// dataUrl?: string;
|
|
38
|
+
// meshType?: MeshType;
|
|
39
|
+
// targetSize?: number;
|
|
40
|
+
// refineEnabled?: boolean;
|
|
41
|
+
// refinementLevel?: number;
|
|
42
|
+
// boundaryLayerEnabled?: boolean;
|
|
43
|
+
// boundaryLayerCount?: number;
|
|
44
|
+
// boundaryLayerThickness?: number;
|
|
45
|
+
// displayMode?: MeshDisplayMode;
|
|
46
|
+
// qualityMode?: MeshQualityMode;
|
|
47
|
+
// qualityThreshold?: number;
|
|
48
|
+
// showNodes?: boolean;
|
|
49
|
+
// showBoundaryHint?: boolean;
|
|
50
|
+
// autoRotate?: boolean;
|
|
51
|
+
// allowLocalUpload?: boolean;
|
|
52
|
+
// background?: string;
|
|
53
|
+
// style?: React.CSSProperties;
|
|
54
|
+
// }
|
|
55
|
+
|
|
56
|
+
// type MeshNode = {
|
|
57
|
+
// x: number;
|
|
58
|
+
// y: number;
|
|
59
|
+
// z: number;
|
|
60
|
+
// };
|
|
61
|
+
|
|
62
|
+
// type MeshCell = {
|
|
63
|
+
// type: 'tetra' | 'hexa' | 'triangle' | 'quad';
|
|
64
|
+
// indices: number[];
|
|
65
|
+
// zone?: string;
|
|
66
|
+
// boundary?: string;
|
|
67
|
+
// };
|
|
68
|
+
|
|
69
|
+
// type MeshDataset = {
|
|
70
|
+
// nodes: MeshNode[];
|
|
71
|
+
// cells: MeshCell[];
|
|
72
|
+
// meta?: {
|
|
73
|
+
// name?: string;
|
|
74
|
+
// caseType?: string;
|
|
75
|
+
// sourceFormat?: MeshDataFormat;
|
|
76
|
+
// };
|
|
77
|
+
// rawGeometry?: THREE.BufferGeometry | null;
|
|
78
|
+
// };
|
|
79
|
+
|
|
80
|
+
// type MeshStats = {
|
|
81
|
+
// nodeCount: number;
|
|
82
|
+
// cellCount: number;
|
|
83
|
+
// faceCount: number;
|
|
84
|
+
// edgeCount: number;
|
|
85
|
+
// zoneCount: number;
|
|
86
|
+
// boundaryCount: number;
|
|
87
|
+
// boundsText: string;
|
|
88
|
+
// featureEdgeEstimate: number;
|
|
89
|
+
// orthogonality: number;
|
|
90
|
+
// skewness: number;
|
|
91
|
+
// aspectRatio: number;
|
|
92
|
+
// };
|
|
93
|
+
|
|
94
|
+
// const DEMO_JSON_DATASET: MeshDataset = {
|
|
95
|
+
// meta: {
|
|
96
|
+
// name: 'demo-mesh-case',
|
|
97
|
+
// caseType: 'internal-flow',
|
|
98
|
+
// sourceFormat: 'custom-json',
|
|
99
|
+
// },
|
|
100
|
+
// nodes: [
|
|
101
|
+
// { x: -1, y: -0.6, z: -0.4 },
|
|
102
|
+
// { x: 1, y: -0.6, z: -0.4 },
|
|
103
|
+
// { x: 1, y: 0.6, z: -0.4 },
|
|
104
|
+
// { x: -1, y: 0.6, z: -0.4 },
|
|
105
|
+
// { x: -1, y: -0.6, z: 0.4 },
|
|
106
|
+
// { x: 1, y: -0.6, z: 0.4 },
|
|
107
|
+
// { x: 1, y: 0.6, z: 0.4 },
|
|
108
|
+
// { x: -1, y: 0.6, z: 0.4 },
|
|
109
|
+
|
|
110
|
+
// { x: -0.35, y: -0.2, z: -0.25 },
|
|
111
|
+
// { x: 0.35, y: -0.2, z: -0.25 },
|
|
112
|
+
// { x: 0.35, y: 0.2, z: -0.25 },
|
|
113
|
+
// { x: -0.35, y: 0.2, z: -0.25 },
|
|
114
|
+
// { x: -0.35, y: -0.2, z: 0.25 },
|
|
115
|
+
// { x: 0.35, y: -0.2, z: 0.25 },
|
|
116
|
+
// { x: 0.35, y: 0.2, z: 0.25 },
|
|
117
|
+
// { x: -0.35, y: 0.2, z: 0.25 },
|
|
118
|
+
// ],
|
|
119
|
+
// cells: [
|
|
120
|
+
// {
|
|
121
|
+
// type: 'hexa',
|
|
122
|
+
// indices: [0, 1, 2, 3, 4, 5, 6, 7],
|
|
123
|
+
// zone: 'outer',
|
|
124
|
+
// boundary: 'wall',
|
|
125
|
+
// },
|
|
126
|
+
// {
|
|
127
|
+
// type: 'hexa',
|
|
128
|
+
// indices: [8, 9, 10, 11, 12, 13, 14, 15],
|
|
129
|
+
// zone: 'core',
|
|
130
|
+
// boundary: 'fluid',
|
|
131
|
+
// },
|
|
132
|
+
// ],
|
|
133
|
+
// };
|
|
134
|
+
|
|
135
|
+
// const MeshVisualization: React.FC<MeshVisualizationProps> = function MeshVisualization({
|
|
136
|
+
// sourceType = 'demo',
|
|
137
|
+
// dataFormat = 'custom-json',
|
|
138
|
+
// dataUrl = '',
|
|
139
|
+
// meshType = 'unstructured',
|
|
140
|
+
// targetSize = 0.12,
|
|
141
|
+
// refineEnabled = true,
|
|
142
|
+
// refinementLevel = 2,
|
|
143
|
+
// boundaryLayerEnabled = true,
|
|
144
|
+
// boundaryLayerCount = 6,
|
|
145
|
+
// boundaryLayerThickness = 0.02,
|
|
146
|
+
// displayMode = 'surface-wireframe',
|
|
147
|
+
// qualityMode = 'orthogonality',
|
|
148
|
+
// qualityThreshold = 0.58,
|
|
149
|
+
// showNodes = false,
|
|
150
|
+
// showBoundaryHint = true,
|
|
151
|
+
// autoRotate = false,
|
|
152
|
+
// allowLocalUpload = true,
|
|
153
|
+
// background = '#061526',
|
|
154
|
+
// style = {},
|
|
155
|
+
// }) {
|
|
156
|
+
// const containerRef = useRef<HTMLDivElement | null>(null);
|
|
157
|
+
// const fileInputRef = useRef<HTMLInputElement | null>(null);
|
|
158
|
+
|
|
159
|
+
// const rendererRef = useRef<THREE.WebGLRenderer | null>(null);
|
|
160
|
+
// const sceneRef = useRef<THREE.Scene | null>(null);
|
|
161
|
+
// const cameraRef = useRef<THREE.PerspectiveCamera | null>(null);
|
|
162
|
+
// const controlsRef = useRef<OrbitControls | null>(null);
|
|
163
|
+
// const frameIdRef = useRef<number | null>(null);
|
|
164
|
+
// const previewGroupRef = useRef<THREE.Group | null>(null);
|
|
165
|
+
|
|
166
|
+
// const [dataset, setDataset] = useState<MeshDataset | null>(buildDemoDataset(dataFormat));
|
|
167
|
+
// const [loading, setLoading] = useState<boolean>(false);
|
|
168
|
+
// const [errorText, setErrorText] = useState<string>('');
|
|
169
|
+
// const [activeFileName, setActiveFileName] = useState<string>('');
|
|
170
|
+
// const [sourceText, setSourceText] = useState<string>('当前为示例网格数据');
|
|
171
|
+
|
|
172
|
+
// useEffect(() => {
|
|
173
|
+
// initScene();
|
|
174
|
+
// return () => {
|
|
175
|
+
// disposeScene();
|
|
176
|
+
// };
|
|
177
|
+
// }, []);
|
|
178
|
+
|
|
179
|
+
// useEffect(() => {
|
|
180
|
+
// if (sourceType === 'demo') {
|
|
181
|
+
// setDataset(buildDemoDataset(dataFormat));
|
|
182
|
+
// setErrorText('');
|
|
183
|
+
// setSourceText(`当前为示例数据(${dataFormat})`);
|
|
184
|
+
// setActiveFileName(getDemoFileName(dataFormat));
|
|
185
|
+
// }
|
|
186
|
+
// }, [sourceType, dataFormat]);
|
|
187
|
+
|
|
188
|
+
// useEffect(() => {
|
|
189
|
+
// if (sourceType === 'url' && dataUrl) {
|
|
190
|
+
// loadFromUrl(dataUrl, dataFormat);
|
|
191
|
+
// }
|
|
192
|
+
// }, [sourceType, dataUrl, dataFormat]);
|
|
193
|
+
|
|
194
|
+
// useEffect(() => {
|
|
195
|
+
// rebuildPreview();
|
|
196
|
+
// }, [
|
|
197
|
+
// dataset,
|
|
198
|
+
// displayMode,
|
|
199
|
+
// qualityMode,
|
|
200
|
+
// showNodes,
|
|
201
|
+
// showBoundaryHint,
|
|
202
|
+
// background,
|
|
203
|
+
// autoRotate,
|
|
204
|
+
// targetSize,
|
|
205
|
+
// refineEnabled,
|
|
206
|
+
// refinementLevel,
|
|
207
|
+
// boundaryLayerEnabled,
|
|
208
|
+
// boundaryLayerCount,
|
|
209
|
+
// boundaryLayerThickness,
|
|
210
|
+
// ]);
|
|
211
|
+
|
|
212
|
+
// const stats = useMemo<MeshStats>(() => {
|
|
213
|
+
// return buildMeshStats(
|
|
214
|
+
// dataset,
|
|
215
|
+
// meshType,
|
|
216
|
+
// targetSize,
|
|
217
|
+
// refineEnabled,
|
|
218
|
+
// refinementLevel,
|
|
219
|
+
// boundaryLayerEnabled,
|
|
220
|
+
// boundaryLayerCount,
|
|
221
|
+
// boundaryLayerThickness
|
|
222
|
+
// );
|
|
223
|
+
// }, [
|
|
224
|
+
// dataset,
|
|
225
|
+
// meshType,
|
|
226
|
+
// targetSize,
|
|
227
|
+
// refineEnabled,
|
|
228
|
+
// refinementLevel,
|
|
229
|
+
// boundaryLayerEnabled,
|
|
230
|
+
// boundaryLayerCount,
|
|
231
|
+
// boundaryLayerThickness,
|
|
232
|
+
// ]);
|
|
233
|
+
|
|
234
|
+
// const qualityGrade = useMemo(() => {
|
|
235
|
+
// const value = stats[qualityMode];
|
|
236
|
+
// if (qualityMode === 'skewness') {
|
|
237
|
+
// if (value <= 0.25) return '优';
|
|
238
|
+
// if (value <= 0.45) return '良';
|
|
239
|
+
// if (value <= 0.65) return '中';
|
|
240
|
+
// return '需优化';
|
|
241
|
+
// }
|
|
242
|
+
// if (value >= 0.85) return '优';
|
|
243
|
+
// if (value >= 0.7) return '良';
|
|
244
|
+
// if (value >= qualityThreshold) return '中';
|
|
245
|
+
// return '需优化';
|
|
246
|
+
// }, [stats, qualityMode, qualityThreshold]);
|
|
247
|
+
|
|
248
|
+
// function initScene() {
|
|
249
|
+
// if (!containerRef.current || sceneRef.current) return;
|
|
250
|
+
|
|
251
|
+
// const container = containerRef.current;
|
|
252
|
+
// const width = container.clientWidth || 800;
|
|
253
|
+
// const height = container.clientHeight || 500;
|
|
254
|
+
|
|
255
|
+
// const scene = new THREE.Scene();
|
|
256
|
+
// scene.background = new THREE.Color(background);
|
|
257
|
+
|
|
258
|
+
// const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);
|
|
259
|
+
// camera.position.set(3.5, 2.8, 4.5);
|
|
260
|
+
|
|
261
|
+
// const renderer = new THREE.WebGLRenderer({
|
|
262
|
+
// antialias: true,
|
|
263
|
+
// alpha: false,
|
|
264
|
+
// });
|
|
265
|
+
// renderer.setPixelRatio(window.devicePixelRatio || 1);
|
|
266
|
+
// renderer.setSize(width, height);
|
|
267
|
+
|
|
268
|
+
// container.innerHTML = '';
|
|
269
|
+
// container.appendChild(renderer.domElement);
|
|
270
|
+
|
|
271
|
+
// const controls = new OrbitControls(camera, renderer.domElement);
|
|
272
|
+
// controls.enableDamping = true;
|
|
273
|
+
// controls.dampingFactor = 0.08;
|
|
274
|
+
|
|
275
|
+
// const ambientLight = new THREE.AmbientLight(0xffffff, 0.85);
|
|
276
|
+
// scene.add(ambientLight);
|
|
277
|
+
|
|
278
|
+
// const directionalLight = new THREE.DirectionalLight(0xffffff, 0.9);
|
|
279
|
+
// directionalLight.position.set(4, 6, 5);
|
|
280
|
+
// scene.add(directionalLight);
|
|
281
|
+
|
|
282
|
+
// const gridHelper = new THREE.GridHelper(8, 16, 0x35506f, 0x20374f);
|
|
283
|
+
// gridHelper.position.y = -1.2;
|
|
284
|
+
// scene.add(gridHelper);
|
|
285
|
+
|
|
286
|
+
// const axesHelper = new THREE.AxesHelper(1.4);
|
|
287
|
+
// scene.add(axesHelper);
|
|
288
|
+
|
|
289
|
+
// const previewGroup = new THREE.Group();
|
|
290
|
+
// scene.add(previewGroup);
|
|
291
|
+
|
|
292
|
+
// sceneRef.current = scene;
|
|
293
|
+
// cameraRef.current = camera;
|
|
294
|
+
// rendererRef.current = renderer;
|
|
295
|
+
// controlsRef.current = controls;
|
|
296
|
+
// previewGroupRef.current = previewGroup;
|
|
297
|
+
|
|
298
|
+
// const animate = () => {
|
|
299
|
+
// frameIdRef.current = requestAnimationFrame(animate);
|
|
300
|
+
// if (autoRotate && previewGroupRef.current) {
|
|
301
|
+
// previewGroupRef.current.rotation.y += 0.006;
|
|
302
|
+
// }
|
|
303
|
+
// controls.update();
|
|
304
|
+
// renderer.render(scene, camera);
|
|
305
|
+
// };
|
|
306
|
+
|
|
307
|
+
// animate();
|
|
308
|
+
|
|
309
|
+
// const handleResize = () => {
|
|
310
|
+
// if (!containerRef.current || !rendererRef.current || !cameraRef.current) return;
|
|
311
|
+
// const nextWidth = containerRef.current.clientWidth || 800;
|
|
312
|
+
// const nextHeight = containerRef.current.clientHeight || 500;
|
|
313
|
+
// rendererRef.current.setSize(nextWidth, nextHeight);
|
|
314
|
+
// cameraRef.current.aspect = nextWidth / nextHeight;
|
|
315
|
+
// cameraRef.current.updateProjectionMatrix();
|
|
316
|
+
// };
|
|
317
|
+
|
|
318
|
+
// window.addEventListener('resize', handleResize);
|
|
319
|
+
// (renderer.domElement as any).__meshResizeHandler = handleResize;
|
|
320
|
+
// }
|
|
321
|
+
|
|
322
|
+
// function disposeScene() {
|
|
323
|
+
// if (frameIdRef.current !== null) {
|
|
324
|
+
// cancelAnimationFrame(frameIdRef.current);
|
|
325
|
+
// }
|
|
326
|
+
|
|
327
|
+
// if (rendererRef.current?.domElement) {
|
|
328
|
+
// const handler = (rendererRef.current.domElement as any).__meshResizeHandler;
|
|
329
|
+
// if (handler) {
|
|
330
|
+
// window.removeEventListener('resize', handler);
|
|
331
|
+
// }
|
|
332
|
+
// }
|
|
333
|
+
|
|
334
|
+
// controlsRef.current?.dispose();
|
|
335
|
+
|
|
336
|
+
// if (previewGroupRef.current) {
|
|
337
|
+
// clearGroup(previewGroupRef.current);
|
|
338
|
+
// }
|
|
339
|
+
|
|
340
|
+
// rendererRef.current?.dispose();
|
|
341
|
+
|
|
342
|
+
// sceneRef.current = null;
|
|
343
|
+
// cameraRef.current = null;
|
|
344
|
+
// rendererRef.current = null;
|
|
345
|
+
// controlsRef.current = null;
|
|
346
|
+
// previewGroupRef.current = null;
|
|
347
|
+
// }
|
|
348
|
+
|
|
349
|
+
// async function loadFromUrl(url: string, format: MeshDataFormat) {
|
|
350
|
+
// if (!url) return;
|
|
351
|
+
|
|
352
|
+
// setLoading(true);
|
|
353
|
+
// setErrorText('');
|
|
354
|
+
// setSourceText(`正在从地址加载: ${url}`);
|
|
355
|
+
|
|
356
|
+
// try {
|
|
357
|
+
// const nextDataset = await loadDatasetByFormatFromUrl(url, format);
|
|
358
|
+
// setDataset(nextDataset);
|
|
359
|
+
// setActiveFileName(getFileNameFromUrl(url));
|
|
360
|
+
// setSourceText(`已加载远程数据: ${url}`);
|
|
361
|
+
// } catch (error: any) {
|
|
362
|
+
// setDataset(null);
|
|
363
|
+
// setErrorText(error?.message || '远程数据加载失败');
|
|
364
|
+
// } finally {
|
|
365
|
+
// setLoading(false);
|
|
366
|
+
// }
|
|
367
|
+
// }
|
|
368
|
+
|
|
369
|
+
// async function handleLocalFileChange(event: React.ChangeEvent<HTMLInputElement>) {
|
|
370
|
+
// const file = event.target.files?.[0];
|
|
371
|
+
// if (!file) return;
|
|
372
|
+
|
|
373
|
+
// setLoading(true);
|
|
374
|
+
// setErrorText('');
|
|
375
|
+
// setActiveFileName(file.name);
|
|
376
|
+
|
|
377
|
+
// try {
|
|
378
|
+
// const nextDataset = await loadDatasetByFormatFromFile(file, dataFormat);
|
|
379
|
+
// setDataset(nextDataset);
|
|
380
|
+
// setSourceText(`已加载本地文件: ${file.name}`);
|
|
381
|
+
// } catch (error: any) {
|
|
382
|
+
// setDataset(null);
|
|
383
|
+
// setErrorText(error?.message || '本地文件解析失败');
|
|
384
|
+
// } finally {
|
|
385
|
+
// setLoading(false);
|
|
386
|
+
// if (fileInputRef.current) {
|
|
387
|
+
// fileInputRef.current.value = '';
|
|
388
|
+
// }
|
|
389
|
+
// }
|
|
390
|
+
// }
|
|
391
|
+
|
|
392
|
+
// function rebuildPreview() {
|
|
393
|
+
// if (!previewGroupRef.current || !sceneRef.current) return;
|
|
394
|
+
|
|
395
|
+
// sceneRef.current.background = new THREE.Color(background);
|
|
396
|
+
// clearGroup(previewGroupRef.current);
|
|
397
|
+
|
|
398
|
+
// if (!dataset) return;
|
|
399
|
+
|
|
400
|
+
// const geometry =
|
|
401
|
+
// dataset.rawGeometry ||
|
|
402
|
+
// (dataset.nodes.length > 0 && dataset.cells.length > 0 ? buildSurfaceGeometry(dataset) : null);
|
|
403
|
+
|
|
404
|
+
// if (!geometry) return;
|
|
405
|
+
|
|
406
|
+
// geometry.computeVertexNormals();
|
|
407
|
+
|
|
408
|
+
// const previewGeometry = geometry.clone();
|
|
409
|
+
// if (displayMode === 'quality') {
|
|
410
|
+
// applyQualityVertexColors(previewGeometry, dataset, qualityMode);
|
|
411
|
+
// }
|
|
412
|
+
|
|
413
|
+
// const surfaceMaterial = new THREE.MeshStandardMaterial({
|
|
414
|
+
// color: displayMode === 'quality' ? 0xffffff : 0x4ea1ff,
|
|
415
|
+
// transparent: true,
|
|
416
|
+
// opacity: displayMode === 'wireframe' ? 0.08 : 0.88,
|
|
417
|
+
// side: THREE.DoubleSide,
|
|
418
|
+
// flatShading: false,
|
|
419
|
+
// vertexColors: displayMode === 'quality',
|
|
420
|
+
// metalness: 0.05,
|
|
421
|
+
// roughness: 0.65,
|
|
422
|
+
// });
|
|
423
|
+
|
|
424
|
+
// const surfaceMesh = new THREE.Mesh(previewGeometry, surfaceMaterial);
|
|
425
|
+
|
|
426
|
+
// if (displayMode !== 'wireframe') {
|
|
427
|
+
// previewGroupRef.current.add(surfaceMesh);
|
|
428
|
+
// }
|
|
429
|
+
|
|
430
|
+
// if (displayMode === 'wireframe' || displayMode === 'surface-wireframe' || displayMode === 'quality') {
|
|
431
|
+
// const edgesGeometry = new THREE.EdgesGeometry(previewGeometry);
|
|
432
|
+
// const edgesMaterial = new THREE.LineBasicMaterial({
|
|
433
|
+
// color: 0xe6f0ff,
|
|
434
|
+
// transparent: true,
|
|
435
|
+
// opacity: displayMode === 'wireframe' ? 1 : 0.55,
|
|
436
|
+
// });
|
|
437
|
+
// const edgeLines = new THREE.LineSegments(edgesGeometry, edgesMaterial);
|
|
438
|
+
// previewGroupRef.current.add(edgeLines);
|
|
439
|
+
// }
|
|
440
|
+
|
|
441
|
+
// if (showNodes) {
|
|
442
|
+
// const pointsGeometry = buildPointsGeometry(dataset, previewGeometry);
|
|
443
|
+
// if (pointsGeometry) {
|
|
444
|
+
// const pointsMaterial = new THREE.PointsMaterial({
|
|
445
|
+
// color: 0xffd166,
|
|
446
|
+
// size: 0.04,
|
|
447
|
+
// sizeAttenuation: true,
|
|
448
|
+
// });
|
|
449
|
+
|
|
450
|
+
// const pointMesh = new THREE.Points(pointsGeometry, pointsMaterial);
|
|
451
|
+
// previewGroupRef.current.add(pointMesh);
|
|
452
|
+
// }
|
|
453
|
+
// }
|
|
454
|
+
|
|
455
|
+
// if (showBoundaryHint) {
|
|
456
|
+
// const bbox = new THREE.Box3().setFromObject(previewGroupRef.current);
|
|
457
|
+
// const size = new THREE.Vector3();
|
|
458
|
+
// bbox.getSize(size);
|
|
459
|
+
// const box = new THREE.Box3Helper(bbox, 0x53d6ff);
|
|
460
|
+
// previewGroupRef.current.add(box);
|
|
461
|
+
|
|
462
|
+
// if (boundaryLayerEnabled) {
|
|
463
|
+
// const helper = buildBoundaryLayerHint(size, boundaryLayerCount);
|
|
464
|
+
// helper.position.copy(bbox.getCenter(new THREE.Vector3()));
|
|
465
|
+
// previewGroupRef.current.add(helper);
|
|
466
|
+
// }
|
|
467
|
+
// }
|
|
468
|
+
|
|
469
|
+
// fitCameraToObject(previewGroupRef.current);
|
|
470
|
+
// }
|
|
471
|
+
|
|
472
|
+
// function fitCameraToObject(object: THREE.Object3D) {
|
|
473
|
+
// if (!cameraRef.current || !controlsRef.current) return;
|
|
474
|
+
|
|
475
|
+
// const box = new THREE.Box3().setFromObject(object);
|
|
476
|
+
// if (box.isEmpty()) return;
|
|
477
|
+
|
|
478
|
+
// const size = new THREE.Vector3();
|
|
479
|
+
// const center = new THREE.Vector3();
|
|
480
|
+
// box.getSize(size);
|
|
481
|
+
// box.getCenter(center);
|
|
482
|
+
|
|
483
|
+
// const maxDim = Math.max(size.x, size.y, size.z) || 1;
|
|
484
|
+
// const camera = cameraRef.current;
|
|
485
|
+
// const distance = maxDim * 2.2;
|
|
486
|
+
|
|
487
|
+
// camera.position.set(center.x + distance, center.y + distance * 0.8, center.z + distance);
|
|
488
|
+
// camera.near = 0.01;
|
|
489
|
+
// camera.far = distance * 20;
|
|
490
|
+
// camera.updateProjectionMatrix();
|
|
491
|
+
|
|
492
|
+
// controlsRef.current.target.copy(center);
|
|
493
|
+
// controlsRef.current.update();
|
|
494
|
+
// }
|
|
495
|
+
|
|
496
|
+
// return (
|
|
497
|
+
// <div className={`${bizCssPrefix}-mesh-workbench`} style={style}>
|
|
498
|
+
// <div className={`${bizCssPrefix}-mesh-workbench__topbar`}>
|
|
499
|
+
// <div className={`${bizCssPrefix}-mesh-workbench__top-item`}>
|
|
500
|
+
// <span className={`${bizCssPrefix}-mesh-workbench__top-label`}>数据来源</span>
|
|
501
|
+
// <span className={`${bizCssPrefix}-mesh-workbench__top-value`}>{sourceType}</span>
|
|
502
|
+
// </div>
|
|
503
|
+
// <div className={`${bizCssPrefix}-mesh-workbench__top-item`}>
|
|
504
|
+
// <span className={`${bizCssPrefix}-mesh-workbench__top-label`}>数据格式</span>
|
|
505
|
+
// <span className={`${bizCssPrefix}-mesh-workbench__top-value`}>{dataFormat}</span>
|
|
506
|
+
// </div>
|
|
507
|
+
// <div className={`${bizCssPrefix}-mesh-workbench__top-item`}>
|
|
508
|
+
// <span className={`${bizCssPrefix}-mesh-workbench__top-label`}>网格类型</span>
|
|
509
|
+
// <span className={`${bizCssPrefix}-mesh-workbench__top-value`}>
|
|
510
|
+
// {meshType === 'structured' ? '结构化' : '非结构化'}
|
|
511
|
+
// </span>
|
|
512
|
+
// </div>
|
|
513
|
+
// <div className={`${bizCssPrefix}-mesh-workbench__top-item`}>
|
|
514
|
+
// <span className={`${bizCssPrefix}-mesh-workbench__top-label`}>目标尺寸</span>
|
|
515
|
+
// <span className={`${bizCssPrefix}-mesh-workbench__top-value`}>{targetSize}</span>
|
|
516
|
+
// </div>
|
|
517
|
+
// <div className={`${bizCssPrefix}-mesh-workbench__top-item`}>
|
|
518
|
+
// <span className={`${bizCssPrefix}-mesh-workbench__top-label`}>显示模式</span>
|
|
519
|
+
// <span className={`${bizCssPrefix}-mesh-workbench__top-value`}>{displayMode}</span>
|
|
520
|
+
// </div>
|
|
521
|
+
|
|
522
|
+
// {allowLocalUpload && sourceType === 'local' && (
|
|
523
|
+
// <div className={`${bizCssPrefix}-mesh-workbench__upload`}>
|
|
524
|
+
// <input
|
|
525
|
+
// ref={fileInputRef}
|
|
526
|
+
// type="file"
|
|
527
|
+
// accept={getAcceptByFormat(dataFormat)}
|
|
528
|
+
// onChange={handleLocalFileChange}
|
|
529
|
+
// />
|
|
530
|
+
// </div>
|
|
531
|
+
// )}
|
|
532
|
+
// </div>
|
|
533
|
+
|
|
534
|
+
// <div className={`${bizCssPrefix}-mesh-workbench__main`}>
|
|
535
|
+
// <div className={`${bizCssPrefix}-mesh-workbench__left`}>
|
|
536
|
+
// <PanelTitle title="网格分析" />
|
|
537
|
+
|
|
538
|
+
// <MetricRow label="节点数" value={String(stats.nodeCount)} />
|
|
539
|
+
// <MetricRow label="单元数" value={String(stats.cellCount)} />
|
|
540
|
+
// <MetricRow label="面片数" value={String(stats.faceCount)} />
|
|
541
|
+
// <MetricRow label="边数量估计" value={String(stats.edgeCount)} />
|
|
542
|
+
// <MetricRow label="区域数量" value={String(stats.zoneCount)} />
|
|
543
|
+
// <MetricRow label="边界数量" value={String(stats.boundaryCount)} />
|
|
544
|
+
// <MetricRow label="特征边估计" value={String(stats.featureEdgeEstimate)} />
|
|
545
|
+
// <MetricRow label="包围盒范围" value={stats.boundsText} />
|
|
546
|
+
|
|
547
|
+
// <PanelTitle title="质量检查" />
|
|
548
|
+
// <MetricRow label="正交性" value={stats.orthogonality.toFixed(2)} />
|
|
549
|
+
// <MetricRow label="偏斜度" value={stats.skewness.toFixed(2)} />
|
|
550
|
+
// <MetricRow label="长宽比" value={stats.aspectRatio.toFixed(2)} />
|
|
551
|
+
// <MetricRow label="当前质量评级" value={qualityGrade} />
|
|
552
|
+
// </div>
|
|
553
|
+
|
|
554
|
+
// <div className={`${bizCssPrefix}-mesh-workbench__center`}>
|
|
555
|
+
// <div className={`${bizCssPrefix}-mesh-workbench__viewport-toolbar`}>
|
|
556
|
+
// <span>{loading ? '数据加载中...' : '3D 预览'}</span>
|
|
557
|
+
// <span>{activeFileName || '未指定文件'}</span>
|
|
558
|
+
// </div>
|
|
559
|
+
|
|
560
|
+
// <div
|
|
561
|
+
// ref={containerRef}
|
|
562
|
+
// className={`${bizCssPrefix}-mesh-workbench__viewport`}
|
|
563
|
+
// />
|
|
564
|
+
|
|
565
|
+
// {errorText ? (
|
|
566
|
+
// <div className={`${bizCssPrefix}-mesh-workbench__overlay-error`}>
|
|
567
|
+
// {errorText}
|
|
568
|
+
// </div>
|
|
569
|
+
// ) : null}
|
|
570
|
+
// </div>
|
|
571
|
+
|
|
572
|
+
// <div className={`${bizCssPrefix}-mesh-workbench__right`}>
|
|
573
|
+
// <PanelTitle title="配置摘要" />
|
|
574
|
+
|
|
575
|
+
// <SummaryBlock
|
|
576
|
+
// title="数据状态"
|
|
577
|
+
// lines={[
|
|
578
|
+
// sourceText,
|
|
579
|
+
// `文件名: ${activeFileName || '无'}`,
|
|
580
|
+
// `格式: ${dataFormat}`,
|
|
581
|
+
// `质量指标: ${qualityMode}`,
|
|
582
|
+
// ]}
|
|
583
|
+
// />
|
|
584
|
+
|
|
585
|
+
// <SummaryBlock
|
|
586
|
+
// title="前处理参数"
|
|
587
|
+
// lines={[
|
|
588
|
+
// `网格类型: ${meshType === 'structured' ? '结构化' : '非结构化'}`,
|
|
589
|
+
// `目标尺寸: ${targetSize}`,
|
|
590
|
+
// `局部加密: ${refineEnabled ? `启用(级别 ${refinementLevel})` : '未启用'}`,
|
|
591
|
+
// `边界层: ${
|
|
592
|
+
// boundaryLayerEnabled
|
|
593
|
+
// ? `启用(${boundaryLayerCount} 层 / 厚度 ${boundaryLayerThickness})`
|
|
594
|
+
// : '未启用'
|
|
595
|
+
// }`,
|
|
596
|
+
// ]}
|
|
597
|
+
// />
|
|
598
|
+
|
|
599
|
+
// <SummaryBlock
|
|
600
|
+
// title="显示选项"
|
|
601
|
+
// lines={[
|
|
602
|
+
// `显示模式: ${displayMode}`,
|
|
603
|
+
// `显示节点: ${showNodes ? '是' : '否'}`,
|
|
604
|
+
// `边界提示: ${showBoundaryHint ? '是' : '否'}`,
|
|
605
|
+
// `自动旋转: ${autoRotate ? '是' : '否'}`,
|
|
606
|
+
// ]}
|
|
607
|
+
// />
|
|
608
|
+
|
|
609
|
+
// <SummaryBlock
|
|
610
|
+
// title="输入说明"
|
|
611
|
+
// lines={[
|
|
612
|
+
// 'custom-json:适合演示体网格结构。',
|
|
613
|
+
// 'STL / OBJ:更适合几何表面导入与检查。',
|
|
614
|
+
// '这一版已支持 STL / OBJ 的本地与远程读取。',
|
|
615
|
+
// ]}
|
|
616
|
+
// />
|
|
617
|
+
// </div>
|
|
618
|
+
// </div>
|
|
619
|
+
// </div>
|
|
620
|
+
// );
|
|
621
|
+
// };
|
|
622
|
+
|
|
623
|
+
// MeshVisualization.displayName = 'MeshVisualization';
|
|
624
|
+
// export default MeshVisualization;
|
|
625
|
+
|
|
626
|
+
// /** ---------------- 子组件 ---------------- */
|
|
627
|
+
|
|
628
|
+
// function PanelTitle({ title }: { title: string }) {
|
|
629
|
+
// return <div className={`${bizCssPrefix}-mesh-workbench__panel-title`}>{title}</div>;
|
|
630
|
+
// }
|
|
631
|
+
|
|
632
|
+
// function MetricRow({ label, value }: { label: string; value: string }) {
|
|
633
|
+
// return (
|
|
634
|
+
// <div className={`${bizCssPrefix}-mesh-workbench__metric-row`}>
|
|
635
|
+
// <span>{label}</span>
|
|
636
|
+
// <span>{value}</span>
|
|
637
|
+
// </div>
|
|
638
|
+
// );
|
|
639
|
+
// }
|
|
640
|
+
|
|
641
|
+
// function SummaryBlock({ title, lines }: { title: string; lines: string[] }) {
|
|
642
|
+
// return (
|
|
643
|
+
// <div className={`${bizCssPrefix}-mesh-workbench__summary-block`}>
|
|
644
|
+
// <div className={`${bizCssPrefix}-mesh-workbench__summary-title`}>{title}</div>
|
|
645
|
+
// {lines.map((line, index) => (
|
|
646
|
+
// <div key={index} className={`${bizCssPrefix}-mesh-workbench__summary-line`}>
|
|
647
|
+
// {line}
|
|
648
|
+
// </div>
|
|
649
|
+
// ))}
|
|
650
|
+
// </div>
|
|
651
|
+
// );
|
|
652
|
+
// }
|
|
653
|
+
|
|
654
|
+
// /** ---------------- 多格式数据加载 ---------------- */
|
|
655
|
+
|
|
656
|
+
// async function loadDatasetByFormatFromUrl(url: string, format: MeshDataFormat): Promise<MeshDataset> {
|
|
657
|
+
// if (format === 'custom-json') {
|
|
658
|
+
// const response = await fetch(url);
|
|
659
|
+
// if (!response.ok) {
|
|
660
|
+
// throw new Error(`请求失败: ${response.status}`);
|
|
661
|
+
// }
|
|
662
|
+
// const text = await response.text();
|
|
663
|
+
// return parseCustomJsonMesh(text);
|
|
664
|
+
// }
|
|
665
|
+
|
|
666
|
+
// if (format === 'stl') {
|
|
667
|
+
// const response = await fetch(url);
|
|
668
|
+
// if (!response.ok) {
|
|
669
|
+
// throw new Error(`请求失败: ${response.status}`);
|
|
670
|
+
// }
|
|
671
|
+
// const buffer = await response.arrayBuffer();
|
|
672
|
+
// return parseSTLMesh(buffer);
|
|
673
|
+
// }
|
|
674
|
+
|
|
675
|
+
// if (format === 'obj') {
|
|
676
|
+
// const response = await fetch(url);
|
|
677
|
+
// if (!response.ok) {
|
|
678
|
+
// throw new Error(`请求失败: ${response.status}`);
|
|
679
|
+
// }
|
|
680
|
+
// const text = await response.text();
|
|
681
|
+
// return parseOBJMesh(text);
|
|
682
|
+
// }
|
|
683
|
+
|
|
684
|
+
// throw new Error(`暂不支持的数据格式: ${format}`);
|
|
685
|
+
// }
|
|
686
|
+
|
|
687
|
+
// async function loadDatasetByFormatFromFile(file: File, format: MeshDataFormat): Promise<MeshDataset> {
|
|
688
|
+
// if (format === 'custom-json') {
|
|
689
|
+
// const text = await file.text();
|
|
690
|
+
// return parseCustomJsonMesh(text);
|
|
691
|
+
// }
|
|
692
|
+
|
|
693
|
+
// if (format === 'stl') {
|
|
694
|
+
// const buffer = await file.arrayBuffer();
|
|
695
|
+
// return parseSTLMesh(buffer);
|
|
696
|
+
// }
|
|
697
|
+
|
|
698
|
+
// if (format === 'obj') {
|
|
699
|
+
// const text = await file.text();
|
|
700
|
+
// return parseOBJMesh(text);
|
|
701
|
+
// }
|
|
702
|
+
|
|
703
|
+
// throw new Error(`暂不支持的数据格式: ${format}`);
|
|
704
|
+
// }
|
|
705
|
+
|
|
706
|
+
// function buildDemoDataset(format: MeshDataFormat): MeshDataset {
|
|
707
|
+
// if (format === 'stl') {
|
|
708
|
+
// const geometry = new THREE.TorusKnotGeometry(0.9, 0.26, 180, 24);
|
|
709
|
+
// return buildGeometryDataset(geometry, 'demo-stl-geometry', 'stl');
|
|
710
|
+
// }
|
|
711
|
+
|
|
712
|
+
// if (format === 'obj') {
|
|
713
|
+
// const geometry = new THREE.SphereGeometry(1, 28, 22);
|
|
714
|
+
// return buildGeometryDataset(geometry, 'demo-obj-geometry', 'obj');
|
|
715
|
+
// }
|
|
716
|
+
|
|
717
|
+
// return {
|
|
718
|
+
// ...DEMO_JSON_DATASET,
|
|
719
|
+
// meta: {
|
|
720
|
+
// ...DEMO_JSON_DATASET.meta,
|
|
721
|
+
// sourceFormat: 'custom-json',
|
|
722
|
+
// },
|
|
723
|
+
// };
|
|
724
|
+
// }
|
|
725
|
+
|
|
726
|
+
// function parseCustomJsonMesh(text: string): MeshDataset {
|
|
727
|
+
// let raw: any;
|
|
728
|
+
// try {
|
|
729
|
+
// raw = JSON.parse(text);
|
|
730
|
+
// } catch {
|
|
731
|
+
// throw new Error('JSON 解析失败,请检查文件内容。');
|
|
732
|
+
// }
|
|
733
|
+
|
|
734
|
+
// const nodes = Array.isArray(raw?.nodes) ? raw.nodes : [];
|
|
735
|
+
// const cells = Array.isArray(raw?.cells) ? raw.cells : [];
|
|
736
|
+
|
|
737
|
+
// if (!nodes.length || !cells.length) {
|
|
738
|
+
// throw new Error('数据格式无效,custom-json 需要包含 nodes 和 cells。');
|
|
739
|
+
// }
|
|
740
|
+
|
|
741
|
+
// const parsedNodes: MeshNode[] = nodes.map((item: any) => ({
|
|
742
|
+
// x: Number(item.x),
|
|
743
|
+
// y: Number(item.y),
|
|
744
|
+
// z: Number(item.z),
|
|
745
|
+
// }));
|
|
746
|
+
|
|
747
|
+
// const parsedCells: MeshCell[] = cells.map((item: any) => ({
|
|
748
|
+
// type: normalizeCellType(item.type),
|
|
749
|
+
// indices: Array.isArray(item.indices) ? item.indices.map((i: any) => Number(i)) : [],
|
|
750
|
+
// zone: item.zone || 'default-zone',
|
|
751
|
+
// boundary: item.boundary || 'default-boundary',
|
|
752
|
+
// }));
|
|
753
|
+
|
|
754
|
+
// return {
|
|
755
|
+
// nodes: parsedNodes,
|
|
756
|
+
// cells: parsedCells,
|
|
757
|
+
// meta: {
|
|
758
|
+
// ...(raw.meta || {}),
|
|
759
|
+
// sourceFormat: 'custom-json',
|
|
760
|
+
// },
|
|
761
|
+
// };
|
|
762
|
+
// }
|
|
763
|
+
|
|
764
|
+
// function parseSTLMesh(buffer: ArrayBuffer): MeshDataset {
|
|
765
|
+
// const loader = new STLLoader();
|
|
766
|
+
// const geometry = loader.parse(buffer);
|
|
767
|
+
// geometry.computeVertexNormals();
|
|
768
|
+
// return buildGeometryDataset(geometry, 'stl-geometry', 'stl');
|
|
769
|
+
// }
|
|
770
|
+
|
|
771
|
+
// function parseOBJMesh(text: string): MeshDataset {
|
|
772
|
+
// const loader = new OBJLoader();
|
|
773
|
+
// const object = loader.parse(text);
|
|
774
|
+
|
|
775
|
+
// let mergedGeometry: THREE.BufferGeometry | null = null;
|
|
776
|
+
|
|
777
|
+
// object.traverse((child) => {
|
|
778
|
+
// const mesh = child as THREE.Mesh;
|
|
779
|
+
// if (mesh.isMesh && mesh.geometry) {
|
|
780
|
+
// const geom = (mesh.geometry as THREE.BufferGeometry).clone();
|
|
781
|
+
// geom.applyMatrix4(mesh.matrixWorld);
|
|
782
|
+
|
|
783
|
+
// if (!mergedGeometry) {
|
|
784
|
+
// mergedGeometry = geom;
|
|
785
|
+
// } else {
|
|
786
|
+
// mergedGeometry = mergeBufferGeometriesSimple(mergedGeometry, geom);
|
|
787
|
+
// }
|
|
788
|
+
// }
|
|
789
|
+
// });
|
|
790
|
+
|
|
791
|
+
// if (!mergedGeometry) {
|
|
792
|
+
// throw new Error('OBJ 解析失败,未找到可用几何体。');
|
|
793
|
+
// }
|
|
794
|
+
|
|
795
|
+
// mergedGeometry.computeVertexNormals();
|
|
796
|
+
// return buildGeometryDataset(mergedGeometry, 'obj-geometry', 'obj');
|
|
797
|
+
// }
|
|
798
|
+
|
|
799
|
+
// function buildGeometryDataset(
|
|
800
|
+
// geometry: THREE.BufferGeometry,
|
|
801
|
+
// name: string,
|
|
802
|
+
// format: MeshDataFormat
|
|
803
|
+
// ): MeshDataset {
|
|
804
|
+
// const nextGeometry = geometry.index ? geometry.toNonIndexed() : geometry.clone();
|
|
805
|
+
// nextGeometry.computeBoundingBox();
|
|
806
|
+
// nextGeometry.computeVertexNormals();
|
|
807
|
+
|
|
808
|
+
// const positionAttr = nextGeometry.getAttribute('position');
|
|
809
|
+
// const nodes: MeshNode[] = [];
|
|
810
|
+
// const cells: MeshCell[] = [];
|
|
811
|
+
|
|
812
|
+
// if (positionAttr) {
|
|
813
|
+
// for (let i = 0; i < positionAttr.count; i++) {
|
|
814
|
+
// nodes.push({
|
|
815
|
+
// x: positionAttr.getX(i),
|
|
816
|
+
// y: positionAttr.getY(i),
|
|
817
|
+
// z: positionAttr.getZ(i),
|
|
818
|
+
// });
|
|
819
|
+
// }
|
|
820
|
+
|
|
821
|
+
// for (let i = 0; i < positionAttr.count; i += 3) {
|
|
822
|
+
// if (i + 2 < positionAttr.count) {
|
|
823
|
+
// cells.push({
|
|
824
|
+
// type: 'triangle',
|
|
825
|
+
// indices: [i, i + 1, i + 2],
|
|
826
|
+
// zone: 'surface-zone',
|
|
827
|
+
// boundary: 'surface',
|
|
828
|
+
// });
|
|
829
|
+
// }
|
|
830
|
+
// }
|
|
831
|
+
// }
|
|
832
|
+
|
|
833
|
+
// return {
|
|
834
|
+
// nodes,
|
|
835
|
+
// cells,
|
|
836
|
+
// rawGeometry: nextGeometry,
|
|
837
|
+
// meta: {
|
|
838
|
+
// name,
|
|
839
|
+
// sourceFormat: format,
|
|
840
|
+
// caseType: 'surface-geometry',
|
|
841
|
+
// },
|
|
842
|
+
// };
|
|
843
|
+
// }
|
|
844
|
+
|
|
845
|
+
// function mergeBufferGeometriesSimple(
|
|
846
|
+
// geometryA: THREE.BufferGeometry,
|
|
847
|
+
// geometryB: THREE.BufferGeometry
|
|
848
|
+
// ): THREE.BufferGeometry {
|
|
849
|
+
// const a = geometryA.index ? geometryA.toNonIndexed() : geometryA.clone();
|
|
850
|
+
// const b = geometryB.index ? geometryB.toNonIndexed() : geometryB.clone();
|
|
851
|
+
|
|
852
|
+
// const posA = a.getAttribute('position');
|
|
853
|
+
// const posB = b.getAttribute('position');
|
|
854
|
+
|
|
855
|
+
// if (!posA || !posB) {
|
|
856
|
+
// return a;
|
|
857
|
+
// }
|
|
858
|
+
|
|
859
|
+
// const mergedPositions = new Float32Array(posA.array.length + posB.array.length);
|
|
860
|
+
// mergedPositions.set(posA.array as Float32Array, 0);
|
|
861
|
+
// mergedPositions.set(posB.array as Float32Array, posA.array.length);
|
|
862
|
+
|
|
863
|
+
// const merged = new THREE.BufferGeometry();
|
|
864
|
+
// merged.setAttribute('position', new THREE.BufferAttribute(mergedPositions, 3));
|
|
865
|
+
// return merged;
|
|
866
|
+
// }
|
|
867
|
+
|
|
868
|
+
// /** ---------------- 统计分析 ---------------- */
|
|
869
|
+
|
|
870
|
+
// function buildMeshStats(
|
|
871
|
+
// dataset: MeshDataset | null,
|
|
872
|
+
// meshType: MeshType,
|
|
873
|
+
// targetSize: number,
|
|
874
|
+
// refineEnabled: boolean,
|
|
875
|
+
// refinementLevel: number,
|
|
876
|
+
// boundaryLayerEnabled: boolean,
|
|
877
|
+
// boundaryLayerCount: number,
|
|
878
|
+
// boundaryLayerThickness: number
|
|
879
|
+
// ): MeshStats {
|
|
880
|
+
// if (!dataset) {
|
|
881
|
+
// return {
|
|
882
|
+
// nodeCount: 0,
|
|
883
|
+
// cellCount: 0,
|
|
884
|
+
// faceCount: 0,
|
|
885
|
+
// edgeCount: 0,
|
|
886
|
+
// zoneCount: 0,
|
|
887
|
+
// boundaryCount: 0,
|
|
888
|
+
// boundsText: '-',
|
|
889
|
+
// featureEdgeEstimate: 0,
|
|
890
|
+
// orthogonality: 0,
|
|
891
|
+
// skewness: 0,
|
|
892
|
+
// aspectRatio: 0,
|
|
893
|
+
// };
|
|
894
|
+
// }
|
|
895
|
+
|
|
896
|
+
// const nodeCount = dataset.nodes.length;
|
|
897
|
+
// const cellCount = dataset.cells.length;
|
|
898
|
+
|
|
899
|
+
// let faceCount = 0;
|
|
900
|
+
// let edgeCount = 0;
|
|
901
|
+
// const zoneSet = new Set<string>();
|
|
902
|
+
// const boundarySet = new Set<string>();
|
|
903
|
+
|
|
904
|
+
// dataset.cells.forEach((cell) => {
|
|
905
|
+
// faceCount += getCellFaces(cell).length;
|
|
906
|
+
// edgeCount += getCellEdges(cell).length;
|
|
907
|
+
// if (cell.zone) zoneSet.add(cell.zone);
|
|
908
|
+
// if (cell.boundary) boundarySet.add(cell.boundary);
|
|
909
|
+
// });
|
|
910
|
+
|
|
911
|
+
// const boundsText = getBoundsText(dataset.nodes);
|
|
912
|
+
// const featureEdgeEstimate = Math.max(0, Math.round(edgeCount * 0.28));
|
|
913
|
+
|
|
914
|
+
// const orthogonalityBase = meshType === 'structured' ? 0.91 : 0.84;
|
|
915
|
+
// const skewnessBase = meshType === 'structured' ? 0.18 : 0.29;
|
|
916
|
+
// const aspectRatioBase = meshType === 'structured' ? 1.8 : 2.7;
|
|
917
|
+
|
|
918
|
+
// const targetEffect = targetSize < 0.08 ? 0.04 : targetSize < 0.15 ? 0.02 : -0.03;
|
|
919
|
+
// const refineEffect = refineEnabled ? refinementLevel * 0.01 : 0;
|
|
920
|
+
// const layerEffect = boundaryLayerEnabled ? boundaryLayerCount * 0.006 : 0;
|
|
921
|
+
// const thicknessEffect = boundaryLayerEnabled ? boundaryLayerThickness * 0.25 : 0;
|
|
922
|
+
|
|
923
|
+
// const isSurfaceMesh =
|
|
924
|
+
// dataset.meta?.sourceFormat === 'stl' || dataset.meta?.sourceFormat === 'obj';
|
|
925
|
+
|
|
926
|
+
// const orthogonality = clamp(
|
|
927
|
+
// orthogonalityBase + targetEffect - refineEffect * 0.5 - thicknessEffect - (isSurfaceMesh ? 0.03 : 0),
|
|
928
|
+
// 0.45,
|
|
929
|
+
// 0.98
|
|
930
|
+
// );
|
|
931
|
+
// const skewness = clamp(
|
|
932
|
+
// skewnessBase - targetEffect * 0.6 + refineEffect * 0.45 + thicknessEffect + (isSurfaceMesh ? 0.04 : 0),
|
|
933
|
+
// 0.05,
|
|
934
|
+
// 0.92
|
|
935
|
+
// );
|
|
936
|
+
// const aspectRatio = clamp(
|
|
937
|
+
// aspectRatioBase + refineEffect * 1.2 + layerEffect + thicknessEffect * 5 + (isSurfaceMesh ? 0.6 : 0),
|
|
938
|
+
// 1,
|
|
939
|
+
// 12
|
|
940
|
+
// );
|
|
941
|
+
|
|
942
|
+
// return {
|
|
943
|
+
// nodeCount,
|
|
944
|
+
// cellCount,
|
|
945
|
+
// faceCount,
|
|
946
|
+
// edgeCount,
|
|
947
|
+
// zoneCount: zoneSet.size,
|
|
948
|
+
// boundaryCount: boundarySet.size,
|
|
949
|
+
// boundsText,
|
|
950
|
+
// featureEdgeEstimate,
|
|
951
|
+
// orthogonality,
|
|
952
|
+
// skewness,
|
|
953
|
+
// aspectRatio,
|
|
954
|
+
// };
|
|
955
|
+
// }
|
|
956
|
+
|
|
957
|
+
// function getBoundsText(nodes: MeshNode[]) {
|
|
958
|
+
// if (!nodes.length) return '-';
|
|
959
|
+
|
|
960
|
+
// const xs = nodes.map((n) => n.x);
|
|
961
|
+
// const ys = nodes.map((n) => n.y);
|
|
962
|
+
// const zs = nodes.map((n) => n.z);
|
|
963
|
+
|
|
964
|
+
// const xRange = `${Math.min(...xs).toFixed(2)} ~ ${Math.max(...xs).toFixed(2)}`;
|
|
965
|
+
// const yRange = `${Math.min(...ys).toFixed(2)} ~ ${Math.max(...ys).toFixed(2)}`;
|
|
966
|
+
// const zRange = `${Math.min(...zs).toFixed(2)} ~ ${Math.max(...zs).toFixed(2)}`;
|
|
967
|
+
|
|
968
|
+
// return `X:${xRange} Y:${yRange} Z:${zRange}`;
|
|
969
|
+
// }
|
|
970
|
+
|
|
971
|
+
// /** ---------------- three 几何构建 ---------------- */
|
|
972
|
+
|
|
973
|
+
// function buildSurfaceGeometry(dataset: MeshDataset) {
|
|
974
|
+
// const positions: number[] = [];
|
|
975
|
+
// const colors: number[] = [];
|
|
976
|
+
|
|
977
|
+
// dataset.cells.forEach((cell, cellIndex) => {
|
|
978
|
+
// const faces = getCellFaces(cell);
|
|
979
|
+
// const color = getFallbackColor(cellIndex);
|
|
980
|
+
|
|
981
|
+
// faces.forEach((face) => {
|
|
982
|
+
// triangulateFace(face).forEach((triangle) => {
|
|
983
|
+
// triangle.forEach((nodeIndex) => {
|
|
984
|
+
// const node = dataset.nodes[nodeIndex];
|
|
985
|
+
// if (!node) return;
|
|
986
|
+
// positions.push(node.x, node.y, node.z);
|
|
987
|
+
// colors.push(color.r, color.g, color.b);
|
|
988
|
+
// });
|
|
989
|
+
// });
|
|
990
|
+
// });
|
|
991
|
+
// });
|
|
992
|
+
|
|
993
|
+
// const geometry = new THREE.BufferGeometry();
|
|
994
|
+
// geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
|
|
995
|
+
// geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
|
|
996
|
+
// return geometry;
|
|
997
|
+
// }
|
|
998
|
+
|
|
999
|
+
// function buildPointsGeometry(
|
|
1000
|
+
// dataset: MeshDataset,
|
|
1001
|
+
// previewGeometry: THREE.BufferGeometry
|
|
1002
|
+
// ): THREE.BufferGeometry | null {
|
|
1003
|
+
// if (dataset.nodes.length > 0) {
|
|
1004
|
+
// const pointsGeometry = new THREE.BufferGeometry();
|
|
1005
|
+
// const points = new Float32Array(dataset.nodes.length * 3);
|
|
1006
|
+
// dataset.nodes.forEach((node, index) => {
|
|
1007
|
+
// points[index * 3] = node.x;
|
|
1008
|
+
// points[index * 3 + 1] = node.y;
|
|
1009
|
+
// points[index * 3 + 2] = node.z;
|
|
1010
|
+
// });
|
|
1011
|
+
// pointsGeometry.setAttribute('position', new THREE.BufferAttribute(points, 3));
|
|
1012
|
+
// return pointsGeometry;
|
|
1013
|
+
// }
|
|
1014
|
+
|
|
1015
|
+
// const positionAttr = previewGeometry.getAttribute('position');
|
|
1016
|
+
// if (!positionAttr) return null;
|
|
1017
|
+
|
|
1018
|
+
// const cloned = new THREE.BufferGeometry();
|
|
1019
|
+
// cloned.setAttribute('position', positionAttr.clone());
|
|
1020
|
+
// return cloned;
|
|
1021
|
+
// }
|
|
1022
|
+
|
|
1023
|
+
// function applyQualityVertexColors(
|
|
1024
|
+
// geometry: THREE.BufferGeometry,
|
|
1025
|
+
// dataset: MeshDataset,
|
|
1026
|
+
// qualityMode: MeshQualityMode
|
|
1027
|
+
// ) {
|
|
1028
|
+
// const positionAttr = geometry.getAttribute('position');
|
|
1029
|
+
// if (!positionAttr) return;
|
|
1030
|
+
|
|
1031
|
+
// const nextColors: number[] = [];
|
|
1032
|
+
|
|
1033
|
+
// for (let i = 0; i < positionAttr.count; i++) {
|
|
1034
|
+
// const ratio = i / Math.max(1, positionAttr.count - 1);
|
|
1035
|
+
// const q = buildPseudoQualityValue(dataset, ratio, qualityMode);
|
|
1036
|
+
// const color = qualityValueToColor(q, qualityMode);
|
|
1037
|
+
// nextColors.push(color.r, color.g, color.b);
|
|
1038
|
+
// }
|
|
1039
|
+
|
|
1040
|
+
// geometry.setAttribute('color', new THREE.Float32BufferAttribute(nextColors, 3));
|
|
1041
|
+
// }
|
|
1042
|
+
|
|
1043
|
+
// function buildPseudoQualityValue(
|
|
1044
|
+
// dataset: MeshDataset,
|
|
1045
|
+
// ratio: number,
|
|
1046
|
+
// qualityMode: MeshQualityMode
|
|
1047
|
+
// ) {
|
|
1048
|
+
// const complexity = Math.min(1, dataset.cells.length / 200);
|
|
1049
|
+
|
|
1050
|
+
// if (qualityMode === 'orthogonality') {
|
|
1051
|
+
// return clamp(0.92 - ratio * 0.28 - complexity * 0.08, 0, 1);
|
|
1052
|
+
// }
|
|
1053
|
+
// if (qualityMode === 'skewness') {
|
|
1054
|
+
// return clamp(0.08 + ratio * 0.55 + complexity * 0.1, 0, 1);
|
|
1055
|
+
// }
|
|
1056
|
+
// return clamp(0.12 + ratio * 0.75 + complexity * 0.12, 0, 1);
|
|
1057
|
+
// }
|
|
1058
|
+
|
|
1059
|
+
// function qualityValueToColor(value: number, qualityMode: MeshQualityMode) {
|
|
1060
|
+
// const good = new THREE.Color(0x2ecc71);
|
|
1061
|
+
// const mid = new THREE.Color(0xf1c40f);
|
|
1062
|
+
// const bad = new THREE.Color(0xe74c3c);
|
|
1063
|
+
|
|
1064
|
+
// let t = value;
|
|
1065
|
+
// if (qualityMode === 'orthogonality') {
|
|
1066
|
+
// t = 1 - value;
|
|
1067
|
+
// }
|
|
1068
|
+
|
|
1069
|
+
// if (t < 0.5) {
|
|
1070
|
+
// return good.clone().lerp(mid, t / 0.5);
|
|
1071
|
+
// }
|
|
1072
|
+
// return mid.clone().lerp(bad, (t - 0.5) / 0.5);
|
|
1073
|
+
// }
|
|
1074
|
+
|
|
1075
|
+
// function buildBoundaryLayerHint(size: THREE.Vector3, layerCount: number) {
|
|
1076
|
+
// const group = new THREE.Group();
|
|
1077
|
+
// const width = Math.max(size.x * 0.55, 0.5);
|
|
1078
|
+
// const height = Math.max(size.z * 0.55, 0.4);
|
|
1079
|
+
|
|
1080
|
+
// for (let i = 0; i < Math.min(layerCount, 12); i++) {
|
|
1081
|
+
// const scale = 1 + i * 0.06;
|
|
1082
|
+
// const geometry = new THREE.RingGeometry(width * scale, width * scale + 0.01, 48);
|
|
1083
|
+
// const material = new THREE.MeshBasicMaterial({
|
|
1084
|
+
// color: 0x4dd0e1,
|
|
1085
|
+
// transparent: true,
|
|
1086
|
+
// opacity: Math.max(0.04, 0.22 - i * 0.012),
|
|
1087
|
+
// side: THREE.DoubleSide,
|
|
1088
|
+
// });
|
|
1089
|
+
// const ring = new THREE.Mesh(geometry, material);
|
|
1090
|
+
// ring.rotation.x = Math.PI / 2;
|
|
1091
|
+
// ring.scale.set(1, height / width, 1);
|
|
1092
|
+
// group.add(ring);
|
|
1093
|
+
// }
|
|
1094
|
+
|
|
1095
|
+
// return group;
|
|
1096
|
+
// }
|
|
1097
|
+
|
|
1098
|
+
// /** ---------------- 网格拓扑辅助 ---------------- */
|
|
1099
|
+
|
|
1100
|
+
// function getCellFaces(cell: MeshCell): number[][] {
|
|
1101
|
+
// const { type, indices } = cell;
|
|
1102
|
+
|
|
1103
|
+
// if (type === 'tetra' && indices.length >= 4) {
|
|
1104
|
+
// return [
|
|
1105
|
+
// [indices[0], indices[1], indices[2]],
|
|
1106
|
+
// [indices[0], indices[1], indices[3]],
|
|
1107
|
+
// [indices[1], indices[2], indices[3]],
|
|
1108
|
+
// [indices[0], indices[2], indices[3]],
|
|
1109
|
+
// ];
|
|
1110
|
+
// }
|
|
1111
|
+
|
|
1112
|
+
// if (type === 'hexa' && indices.length >= 8) {
|
|
1113
|
+
// return [
|
|
1114
|
+
// [indices[0], indices[1], indices[2], indices[3]],
|
|
1115
|
+
// [indices[4], indices[5], indices[6], indices[7]],
|
|
1116
|
+
// [indices[0], indices[1], indices[5], indices[4]],
|
|
1117
|
+
// [indices[1], indices[2], indices[6], indices[5]],
|
|
1118
|
+
// [indices[2], indices[3], indices[7], indices[6]],
|
|
1119
|
+
// [indices[3], indices[0], indices[4], indices[7]],
|
|
1120
|
+
// ];
|
|
1121
|
+
// }
|
|
1122
|
+
|
|
1123
|
+
// if (type === 'triangle' && indices.length >= 3) {
|
|
1124
|
+
// return [[indices[0], indices[1], indices[2]]];
|
|
1125
|
+
// }
|
|
1126
|
+
|
|
1127
|
+
// if (type === 'quad' && indices.length >= 4) {
|
|
1128
|
+
// return [[indices[0], indices[1], indices[2], indices[3]]];
|
|
1129
|
+
// }
|
|
1130
|
+
|
|
1131
|
+
// return [];
|
|
1132
|
+
// }
|
|
1133
|
+
|
|
1134
|
+
// function getCellEdges(cell: MeshCell): number[][] {
|
|
1135
|
+
// const { type, indices } = cell;
|
|
1136
|
+
|
|
1137
|
+
// if (type === 'tetra' && indices.length >= 4) {
|
|
1138
|
+
// return [
|
|
1139
|
+
// [indices[0], indices[1]],
|
|
1140
|
+
// [indices[1], indices[2]],
|
|
1141
|
+
// [indices[2], indices[0]],
|
|
1142
|
+
// [indices[0], indices[3]],
|
|
1143
|
+
// [indices[1], indices[3]],
|
|
1144
|
+
// [indices[2], indices[3]],
|
|
1145
|
+
// ];
|
|
1146
|
+
// }
|
|
1147
|
+
|
|
1148
|
+
// if (type === 'hexa' && indices.length >= 8) {
|
|
1149
|
+
// return [
|
|
1150
|
+
// [indices[0], indices[1]],
|
|
1151
|
+
// [indices[1], indices[2]],
|
|
1152
|
+
// [indices[2], indices[3]],
|
|
1153
|
+
// [indices[3], indices[0]],
|
|
1154
|
+
// [indices[4], indices[5]],
|
|
1155
|
+
// [indices[5], indices[6]],
|
|
1156
|
+
// [indices[6], indices[7]],
|
|
1157
|
+
// [indices[7], indices[4]],
|
|
1158
|
+
// [indices[0], indices[4]],
|
|
1159
|
+
// [indices[1], indices[5]],
|
|
1160
|
+
// [indices[2], indices[6]],
|
|
1161
|
+
// [indices[3], indices[7]],
|
|
1162
|
+
// ];
|
|
1163
|
+
// }
|
|
1164
|
+
|
|
1165
|
+
// if (type === 'triangle' && indices.length >= 3) {
|
|
1166
|
+
// return [
|
|
1167
|
+
// [indices[0], indices[1]],
|
|
1168
|
+
// [indices[1], indices[2]],
|
|
1169
|
+
// [indices[2], indices[0]],
|
|
1170
|
+
// ];
|
|
1171
|
+
// }
|
|
1172
|
+
|
|
1173
|
+
// if (type === 'quad' && indices.length >= 4) {
|
|
1174
|
+
// return [
|
|
1175
|
+
// [indices[0], indices[1]],
|
|
1176
|
+
// [indices[1], indices[2]],
|
|
1177
|
+
// [indices[2], indices[3]],
|
|
1178
|
+
// [indices[3], indices[0]],
|
|
1179
|
+
// ];
|
|
1180
|
+
// }
|
|
1181
|
+
|
|
1182
|
+
// return [];
|
|
1183
|
+
// }
|
|
1184
|
+
|
|
1185
|
+
// function triangulateFace(face: number[]) {
|
|
1186
|
+
// if (face.length === 3) {
|
|
1187
|
+
// return [face];
|
|
1188
|
+
// }
|
|
1189
|
+
// if (face.length === 4) {
|
|
1190
|
+
// return [
|
|
1191
|
+
// [face[0], face[1], face[2]],
|
|
1192
|
+
// [face[0], face[2], face[3]],
|
|
1193
|
+
// ];
|
|
1194
|
+
// }
|
|
1195
|
+
// return [];
|
|
1196
|
+
// }
|
|
1197
|
+
|
|
1198
|
+
// /** ---------------- 工具 ---------------- */
|
|
1199
|
+
|
|
1200
|
+
// function clearGroup(group: THREE.Group) {
|
|
1201
|
+
// while (group.children.length > 0) {
|
|
1202
|
+
// const child = group.children[0];
|
|
1203
|
+
// group.remove(child);
|
|
1204
|
+
|
|
1205
|
+
// if ((child as any).geometry) {
|
|
1206
|
+
// (child as any).geometry.dispose?.();
|
|
1207
|
+
// }
|
|
1208
|
+
|
|
1209
|
+
// if ((child as any).material) {
|
|
1210
|
+
// const material = (child as any).material;
|
|
1211
|
+
// if (Array.isArray(material)) {
|
|
1212
|
+
// material.forEach((m) => m.dispose?.());
|
|
1213
|
+
// } else {
|
|
1214
|
+
// material.dispose?.();
|
|
1215
|
+
// }
|
|
1216
|
+
// }
|
|
1217
|
+
// }
|
|
1218
|
+
// }
|
|
1219
|
+
|
|
1220
|
+
// function getFallbackColor(index: number) {
|
|
1221
|
+
// const palette = [0x4ea1ff, 0x59d189, 0xffc857, 0xff6b6b, 0x9b5de5];
|
|
1222
|
+
// return new THREE.Color(palette[index % palette.length]);
|
|
1223
|
+
// }
|
|
1224
|
+
|
|
1225
|
+
// function getFileNameFromUrl(url: string) {
|
|
1226
|
+
// const parts = url.split('/');
|
|
1227
|
+
// return parts[parts.length - 1] || url;
|
|
1228
|
+
// }
|
|
1229
|
+
|
|
1230
|
+
// function getDemoFileName(format: MeshDataFormat) {
|
|
1231
|
+
// if (format === 'stl') return 'demo-surface.stl';
|
|
1232
|
+
// if (format === 'obj') return 'demo-surface.obj';
|
|
1233
|
+
// return 'demo-mesh.json';
|
|
1234
|
+
// }
|
|
1235
|
+
|
|
1236
|
+
// function getAcceptByFormat(format: MeshDataFormat) {
|
|
1237
|
+
// if (format === 'stl') return '.stl';
|
|
1238
|
+
// if (format === 'obj') return '.obj';
|
|
1239
|
+
// return '.json';
|
|
1240
|
+
// }
|
|
1241
|
+
|
|
1242
|
+
// function normalizeCellType(type: string): MeshCell['type'] {
|
|
1243
|
+
// if (type === 'tetra' || type === 'hexa' || type === 'triangle' || type === 'quad') {
|
|
1244
|
+
// return type;
|
|
1245
|
+
// }
|
|
1246
|
+
// return 'hexa';
|
|
1247
|
+
// }
|
|
1248
|
+
|
|
1249
|
+
// function clamp(value: number, min: number, max: number) {
|
|
1250
|
+
// return Math.max(min, Math.min(max, value));
|
|
1251
|
+
// }
|
|
1252
|
+
|
|
1253
|
+
var DEMO_JSON_DATASET = {
|
|
1254
|
+
meta: {
|
|
1255
|
+
name: 'demo-mesh-case',
|
|
1256
|
+
caseType: 'internal-flow',
|
|
1257
|
+
sourceFormat: 'custom-json'
|
|
1258
|
+
},
|
|
1259
|
+
nodes: [{
|
|
1260
|
+
x: -1,
|
|
1261
|
+
y: -0.6,
|
|
1262
|
+
z: -0.4
|
|
1263
|
+
}, {
|
|
1264
|
+
x: 1,
|
|
1265
|
+
y: -0.6,
|
|
1266
|
+
z: -0.4
|
|
1267
|
+
}, {
|
|
1268
|
+
x: 1,
|
|
1269
|
+
y: 0.6,
|
|
1270
|
+
z: -0.4
|
|
1271
|
+
}, {
|
|
1272
|
+
x: -1,
|
|
1273
|
+
y: 0.6,
|
|
1274
|
+
z: -0.4
|
|
1275
|
+
}, {
|
|
1276
|
+
x: -1,
|
|
1277
|
+
y: -0.6,
|
|
1278
|
+
z: 0.4
|
|
1279
|
+
}, {
|
|
1280
|
+
x: 1,
|
|
1281
|
+
y: -0.6,
|
|
1282
|
+
z: 0.4
|
|
1283
|
+
}, {
|
|
1284
|
+
x: 1,
|
|
1285
|
+
y: 0.6,
|
|
1286
|
+
z: 0.4
|
|
1287
|
+
}, {
|
|
1288
|
+
x: -1,
|
|
1289
|
+
y: 0.6,
|
|
1290
|
+
z: 0.4
|
|
1291
|
+
}, {
|
|
1292
|
+
x: -0.35,
|
|
1293
|
+
y: -0.2,
|
|
1294
|
+
z: -0.25
|
|
1295
|
+
}, {
|
|
1296
|
+
x: 0.35,
|
|
1297
|
+
y: -0.2,
|
|
1298
|
+
z: -0.25
|
|
1299
|
+
}, {
|
|
1300
|
+
x: 0.35,
|
|
1301
|
+
y: 0.2,
|
|
1302
|
+
z: -0.25
|
|
1303
|
+
}, {
|
|
1304
|
+
x: -0.35,
|
|
1305
|
+
y: 0.2,
|
|
1306
|
+
z: -0.25
|
|
1307
|
+
}, {
|
|
1308
|
+
x: -0.35,
|
|
1309
|
+
y: -0.2,
|
|
1310
|
+
z: 0.25
|
|
1311
|
+
}, {
|
|
1312
|
+
x: 0.35,
|
|
1313
|
+
y: -0.2,
|
|
1314
|
+
z: 0.25
|
|
1315
|
+
}, {
|
|
1316
|
+
x: 0.35,
|
|
1317
|
+
y: 0.2,
|
|
1318
|
+
z: 0.25
|
|
1319
|
+
}, {
|
|
1320
|
+
x: -0.35,
|
|
1321
|
+
y: 0.2,
|
|
1322
|
+
z: 0.25
|
|
1323
|
+
}],
|
|
1324
|
+
cells: [{
|
|
1325
|
+
type: 'hexa',
|
|
1326
|
+
indices: [0, 1, 2, 3, 4, 5, 6, 7],
|
|
1327
|
+
zone: 'outer-domain',
|
|
1328
|
+
boundary: 'wall'
|
|
1329
|
+
}, {
|
|
1330
|
+
type: 'hexa',
|
|
1331
|
+
indices: [8, 9, 10, 11, 12, 13, 14, 15],
|
|
1332
|
+
zone: 'core-region',
|
|
1333
|
+
boundary: 'fluid'
|
|
1334
|
+
}]
|
|
1335
|
+
};
|
|
1336
|
+
var MeshVisualization = function MeshVisualization(_ref) {
|
|
1337
|
+
var _ref$sourceType = _ref.sourceType,
|
|
1338
|
+
sourceType = _ref$sourceType === void 0 ? 'demo' : _ref$sourceType,
|
|
1339
|
+
_ref$dataFormat = _ref.dataFormat,
|
|
1340
|
+
dataFormat = _ref$dataFormat === void 0 ? 'custom-json' : _ref$dataFormat,
|
|
1341
|
+
_ref$dataUrl = _ref.dataUrl,
|
|
1342
|
+
dataUrl = _ref$dataUrl === void 0 ? '' : _ref$dataUrl,
|
|
1343
|
+
_ref$meshType = _ref.meshType,
|
|
1344
|
+
meshType = _ref$meshType === void 0 ? 'unstructured' : _ref$meshType,
|
|
1345
|
+
_ref$targetSize = _ref.targetSize,
|
|
1346
|
+
targetSize = _ref$targetSize === void 0 ? 0.12 : _ref$targetSize,
|
|
1347
|
+
_ref$refineEnabled = _ref.refineEnabled,
|
|
1348
|
+
refineEnabled = _ref$refineEnabled === void 0 ? true : _ref$refineEnabled,
|
|
1349
|
+
_ref$refinementLevel = _ref.refinementLevel,
|
|
1350
|
+
refinementLevel = _ref$refinementLevel === void 0 ? 2 : _ref$refinementLevel,
|
|
1351
|
+
_ref$boundaryLayerEna = _ref.boundaryLayerEnabled,
|
|
1352
|
+
boundaryLayerEnabled = _ref$boundaryLayerEna === void 0 ? true : _ref$boundaryLayerEna,
|
|
1353
|
+
_ref$boundaryLayerCou = _ref.boundaryLayerCount,
|
|
1354
|
+
boundaryLayerCount = _ref$boundaryLayerCou === void 0 ? 6 : _ref$boundaryLayerCou,
|
|
1355
|
+
_ref$boundaryLayerThi = _ref.boundaryLayerThickness,
|
|
1356
|
+
boundaryLayerThickness = _ref$boundaryLayerThi === void 0 ? 0.02 : _ref$boundaryLayerThi,
|
|
1357
|
+
_ref$displayMode = _ref.displayMode,
|
|
1358
|
+
displayMode = _ref$displayMode === void 0 ? 'surface-wireframe' : _ref$displayMode,
|
|
1359
|
+
_ref$qualityMode = _ref.qualityMode,
|
|
1360
|
+
qualityMode = _ref$qualityMode === void 0 ? 'orthogonality' : _ref$qualityMode,
|
|
1361
|
+
_ref$qualityThreshold = _ref.qualityThreshold,
|
|
1362
|
+
qualityThreshold = _ref$qualityThreshold === void 0 ? 0.58 : _ref$qualityThreshold,
|
|
1363
|
+
_ref$showNodes = _ref.showNodes,
|
|
1364
|
+
showNodes = _ref$showNodes === void 0 ? false : _ref$showNodes,
|
|
1365
|
+
_ref$showBoundaryHint = _ref.showBoundaryHint,
|
|
1366
|
+
showBoundaryHint = _ref$showBoundaryHint === void 0 ? true : _ref$showBoundaryHint,
|
|
1367
|
+
_ref$autoRotate = _ref.autoRotate,
|
|
1368
|
+
autoRotate = _ref$autoRotate === void 0 ? false : _ref$autoRotate,
|
|
1369
|
+
_ref$allowLocalUpload = _ref.allowLocalUpload,
|
|
1370
|
+
allowLocalUpload = _ref$allowLocalUpload === void 0 ? true : _ref$allowLocalUpload,
|
|
1371
|
+
_ref$background = _ref.background,
|
|
1372
|
+
background = _ref$background === void 0 ? '#061526' : _ref$background,
|
|
1373
|
+
_ref$style = _ref.style,
|
|
1374
|
+
style = _ref$style === void 0 ? {} : _ref$style;
|
|
1375
|
+
var containerRef = (0, _react.useRef)(null);
|
|
1376
|
+
var fileInputRef = (0, _react.useRef)(null);
|
|
1377
|
+
var rendererRef = (0, _react.useRef)(null);
|
|
1378
|
+
var sceneRef = (0, _react.useRef)(null);
|
|
1379
|
+
var cameraRef = (0, _react.useRef)(null);
|
|
1380
|
+
var controlsRef = (0, _react.useRef)(null);
|
|
1381
|
+
var frameIdRef = (0, _react.useRef)(null);
|
|
1382
|
+
var previewGroupRef = (0, _react.useRef)(null);
|
|
1383
|
+
var _useState = (0, _react.useState)(buildDemoDataset(dataFormat)),
|
|
1384
|
+
dataset = _useState[0],
|
|
1385
|
+
setDataset = _useState[1];
|
|
1386
|
+
var _useState2 = (0, _react.useState)(false),
|
|
1387
|
+
loading = _useState2[0],
|
|
1388
|
+
setLoading = _useState2[1];
|
|
1389
|
+
var _useState3 = (0, _react.useState)(''),
|
|
1390
|
+
errorText = _useState3[0],
|
|
1391
|
+
setErrorText = _useState3[1];
|
|
1392
|
+
var _useState4 = (0, _react.useState)(''),
|
|
1393
|
+
activeFileName = _useState4[0],
|
|
1394
|
+
setActiveFileName = _useState4[1];
|
|
1395
|
+
var _useState5 = (0, _react.useState)('当前为示例网格数据'),
|
|
1396
|
+
sourceText = _useState5[0],
|
|
1397
|
+
setSourceText = _useState5[1];
|
|
1398
|
+
var _useState6 = (0, _react.useState)('未选择文件'),
|
|
1399
|
+
localFileLabel = _useState6[0],
|
|
1400
|
+
setLocalFileLabel = _useState6[1];
|
|
1401
|
+
(0, _react.useEffect)(function () {
|
|
1402
|
+
initScene();
|
|
1403
|
+
return function () {
|
|
1404
|
+
disposeScene();
|
|
1405
|
+
};
|
|
1406
|
+
}, []);
|
|
1407
|
+
(0, _react.useEffect)(function () {
|
|
1408
|
+
if (sourceType === 'demo') {
|
|
1409
|
+
setDataset(buildDemoDataset(dataFormat));
|
|
1410
|
+
setErrorText('');
|
|
1411
|
+
setSourceText("\u5F53\u524D\u4E3A\u793A\u4F8B\u6570\u636E\uFF08" + dataFormat + "\uFF09");
|
|
1412
|
+
setActiveFileName(getDemoFileName(dataFormat));
|
|
1413
|
+
setLocalFileLabel('未选择文件');
|
|
1414
|
+
}
|
|
1415
|
+
}, [sourceType, dataFormat]);
|
|
1416
|
+
(0, _react.useEffect)(function () {
|
|
1417
|
+
if (sourceType === 'url' && dataUrl) {
|
|
1418
|
+
loadFromUrl(dataUrl, dataFormat);
|
|
1419
|
+
}
|
|
1420
|
+
}, [sourceType, dataUrl, dataFormat]);
|
|
1421
|
+
(0, _react.useEffect)(function () {
|
|
1422
|
+
rebuildPreview();
|
|
1423
|
+
}, [dataset, displayMode, qualityMode, showNodes, showBoundaryHint, background, autoRotate, targetSize, refineEnabled, refinementLevel, boundaryLayerEnabled, boundaryLayerCount, boundaryLayerThickness]);
|
|
1424
|
+
var stats = (0, _react.useMemo)(function () {
|
|
1425
|
+
return buildMeshStats(dataset, meshType, targetSize, refineEnabled, refinementLevel, boundaryLayerEnabled, boundaryLayerCount, boundaryLayerThickness);
|
|
1426
|
+
}, [dataset, meshType, targetSize, refineEnabled, refinementLevel, boundaryLayerEnabled, boundaryLayerCount, boundaryLayerThickness]);
|
|
1427
|
+
var qualityGrade = (0, _react.useMemo)(function () {
|
|
1428
|
+
var value = stats[qualityMode];
|
|
1429
|
+
if (qualityMode === 'skewness') {
|
|
1430
|
+
if (value <= 0.25) return '优';
|
|
1431
|
+
if (value <= 0.45) return '良';
|
|
1432
|
+
if (value <= 0.65) return '中';
|
|
1433
|
+
return '需优化';
|
|
1434
|
+
}
|
|
1435
|
+
if (value >= 0.85) return '优';
|
|
1436
|
+
if (value >= 0.7) return '良';
|
|
1437
|
+
if (value >= qualityThreshold) return '中';
|
|
1438
|
+
return '需优化';
|
|
1439
|
+
}, [stats, qualityMode, qualityThreshold]);
|
|
1440
|
+
function handleTriggerFileSelect(event) {
|
|
1441
|
+
event.preventDefault();
|
|
1442
|
+
event.stopPropagation();
|
|
1443
|
+
if (fileInputRef.current) {
|
|
1444
|
+
fileInputRef.current.click();
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
function initScene() {
|
|
1448
|
+
if (!containerRef.current || sceneRef.current) return;
|
|
1449
|
+
var container = containerRef.current;
|
|
1450
|
+
var width = container.clientWidth || 800;
|
|
1451
|
+
var height = container.clientHeight || 500;
|
|
1452
|
+
var scene = new THREE.Scene();
|
|
1453
|
+
scene.background = new THREE.Color(background);
|
|
1454
|
+
var camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);
|
|
1455
|
+
camera.position.set(3.5, 2.8, 4.5);
|
|
1456
|
+
var renderer = new THREE.WebGLRenderer({
|
|
1457
|
+
antialias: true,
|
|
1458
|
+
alpha: false
|
|
1459
|
+
});
|
|
1460
|
+
renderer.setPixelRatio(window.devicePixelRatio || 1);
|
|
1461
|
+
renderer.setSize(width, height);
|
|
1462
|
+
container.innerHTML = '';
|
|
1463
|
+
container.appendChild(renderer.domElement);
|
|
1464
|
+
var controls = new _OrbitControls.OrbitControls(camera, renderer.domElement);
|
|
1465
|
+
controls.enableDamping = true;
|
|
1466
|
+
controls.dampingFactor = 0.08;
|
|
1467
|
+
var ambientLight = new THREE.AmbientLight(0xffffff, 0.85);
|
|
1468
|
+
scene.add(ambientLight);
|
|
1469
|
+
var directionalLight = new THREE.DirectionalLight(0xffffff, 0.9);
|
|
1470
|
+
directionalLight.position.set(4, 6, 5);
|
|
1471
|
+
scene.add(directionalLight);
|
|
1472
|
+
var gridHelper = new THREE.GridHelper(8, 16, 0x35506f, 0x20374f);
|
|
1473
|
+
gridHelper.position.y = -1.2;
|
|
1474
|
+
scene.add(gridHelper);
|
|
1475
|
+
var axesHelper = new THREE.AxesHelper(1.4);
|
|
1476
|
+
scene.add(axesHelper);
|
|
1477
|
+
var previewGroup = new THREE.Group();
|
|
1478
|
+
scene.add(previewGroup);
|
|
1479
|
+
sceneRef.current = scene;
|
|
1480
|
+
cameraRef.current = camera;
|
|
1481
|
+
rendererRef.current = renderer;
|
|
1482
|
+
controlsRef.current = controls;
|
|
1483
|
+
previewGroupRef.current = previewGroup;
|
|
1484
|
+
var _animate = function animate() {
|
|
1485
|
+
frameIdRef.current = requestAnimationFrame(_animate);
|
|
1486
|
+
if (autoRotate && previewGroupRef.current) {
|
|
1487
|
+
previewGroupRef.current.rotation.y += 0.006;
|
|
1488
|
+
}
|
|
1489
|
+
controls.update();
|
|
1490
|
+
renderer.render(scene, camera);
|
|
1491
|
+
};
|
|
1492
|
+
_animate();
|
|
1493
|
+
var handleResize = function handleResize() {
|
|
1494
|
+
if (!containerRef.current || !rendererRef.current || !cameraRef.current) return;
|
|
1495
|
+
var nextWidth = containerRef.current.clientWidth || 800;
|
|
1496
|
+
var nextHeight = containerRef.current.clientHeight || 500;
|
|
1497
|
+
rendererRef.current.setSize(nextWidth, nextHeight);
|
|
1498
|
+
cameraRef.current.aspect = nextWidth / nextHeight;
|
|
1499
|
+
cameraRef.current.updateProjectionMatrix();
|
|
1500
|
+
};
|
|
1501
|
+
window.addEventListener('resize', handleResize);
|
|
1502
|
+
renderer.domElement.__meshResizeHandler = handleResize;
|
|
1503
|
+
}
|
|
1504
|
+
function disposeScene() {
|
|
1505
|
+
var _rendererRef$current, _controlsRef$current, _rendererRef$current2;
|
|
1506
|
+
if (frameIdRef.current !== null) {
|
|
1507
|
+
cancelAnimationFrame(frameIdRef.current);
|
|
1508
|
+
}
|
|
1509
|
+
if ((_rendererRef$current = rendererRef.current) !== null && _rendererRef$current !== void 0 && _rendererRef$current.domElement) {
|
|
1510
|
+
var handler = rendererRef.current.domElement.__meshResizeHandler;
|
|
1511
|
+
if (handler) {
|
|
1512
|
+
window.removeEventListener('resize', handler);
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
(_controlsRef$current = controlsRef.current) === null || _controlsRef$current === void 0 ? void 0 : _controlsRef$current.dispose();
|
|
1516
|
+
if (previewGroupRef.current) {
|
|
1517
|
+
clearGroup(previewGroupRef.current);
|
|
1518
|
+
}
|
|
1519
|
+
(_rendererRef$current2 = rendererRef.current) === null || _rendererRef$current2 === void 0 ? void 0 : _rendererRef$current2.dispose();
|
|
1520
|
+
sceneRef.current = null;
|
|
1521
|
+
cameraRef.current = null;
|
|
1522
|
+
rendererRef.current = null;
|
|
1523
|
+
controlsRef.current = null;
|
|
1524
|
+
previewGroupRef.current = null;
|
|
1525
|
+
}
|
|
1526
|
+
function loadFromUrl(_x, _x2) {
|
|
1527
|
+
return _loadFromUrl.apply(this, arguments);
|
|
1528
|
+
}
|
|
1529
|
+
function _loadFromUrl() {
|
|
1530
|
+
_loadFromUrl = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee(url, format) {
|
|
1531
|
+
var nextDataset, _t;
|
|
1532
|
+
return _regenerator["default"].wrap(function (_context) {
|
|
1533
|
+
while (1) switch (_context.prev = _context.next) {
|
|
1534
|
+
case 0:
|
|
1535
|
+
if (url) {
|
|
1536
|
+
_context.next = 1;
|
|
1537
|
+
break;
|
|
1538
|
+
}
|
|
1539
|
+
return _context.abrupt("return");
|
|
1540
|
+
case 1:
|
|
1541
|
+
setLoading(true);
|
|
1542
|
+
setErrorText('');
|
|
1543
|
+
setSourceText("\u6B63\u5728\u4ECE\u5730\u5740\u52A0\u8F7D: " + url);
|
|
1544
|
+
_context.prev = 2;
|
|
1545
|
+
_context.next = 3;
|
|
1546
|
+
return loadDatasetByFormatFromUrl(url, format);
|
|
1547
|
+
case 3:
|
|
1548
|
+
nextDataset = _context.sent;
|
|
1549
|
+
setDataset(nextDataset);
|
|
1550
|
+
setActiveFileName(getFileNameFromUrl(url));
|
|
1551
|
+
setSourceText("\u5DF2\u52A0\u8F7D\u8FDC\u7A0B\u6570\u636E: " + url);
|
|
1552
|
+
_context.next = 5;
|
|
1553
|
+
break;
|
|
1554
|
+
case 4:
|
|
1555
|
+
_context.prev = 4;
|
|
1556
|
+
_t = _context["catch"](2);
|
|
1557
|
+
setDataset(null);
|
|
1558
|
+
setErrorText((_t === null || _t === void 0 ? void 0 : _t.message) || '远程数据加载失败');
|
|
1559
|
+
case 5:
|
|
1560
|
+
_context.prev = 5;
|
|
1561
|
+
setLoading(false);
|
|
1562
|
+
return _context.finish(5);
|
|
1563
|
+
case 6:
|
|
1564
|
+
case "end":
|
|
1565
|
+
return _context.stop();
|
|
1566
|
+
}
|
|
1567
|
+
}, _callee, null, [[2, 4, 5, 6]]);
|
|
1568
|
+
}));
|
|
1569
|
+
return _loadFromUrl.apply(this, arguments);
|
|
1570
|
+
}
|
|
1571
|
+
function handleLocalFileChange(_x3) {
|
|
1572
|
+
return _handleLocalFileChange.apply(this, arguments);
|
|
1573
|
+
}
|
|
1574
|
+
function _handleLocalFileChange() {
|
|
1575
|
+
_handleLocalFileChange = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee2(event) {
|
|
1576
|
+
var _event$target$files;
|
|
1577
|
+
var file, nextDataset, _t2;
|
|
1578
|
+
return _regenerator["default"].wrap(function (_context2) {
|
|
1579
|
+
while (1) switch (_context2.prev = _context2.next) {
|
|
1580
|
+
case 0:
|
|
1581
|
+
event.stopPropagation();
|
|
1582
|
+
file = (_event$target$files = event.target.files) === null || _event$target$files === void 0 ? void 0 : _event$target$files[0];
|
|
1583
|
+
if (file) {
|
|
1584
|
+
_context2.next = 1;
|
|
1585
|
+
break;
|
|
1586
|
+
}
|
|
1587
|
+
return _context2.abrupt("return");
|
|
1588
|
+
case 1:
|
|
1589
|
+
setLoading(true);
|
|
1590
|
+
setErrorText('');
|
|
1591
|
+
setActiveFileName(file.name);
|
|
1592
|
+
setLocalFileLabel(file.name);
|
|
1593
|
+
_context2.prev = 2;
|
|
1594
|
+
_context2.next = 3;
|
|
1595
|
+
return loadDatasetByFormatFromFile(file, dataFormat);
|
|
1596
|
+
case 3:
|
|
1597
|
+
nextDataset = _context2.sent;
|
|
1598
|
+
setDataset(nextDataset);
|
|
1599
|
+
setSourceText("\u5DF2\u52A0\u8F7D\u672C\u5730\u6587\u4EF6: " + file.name);
|
|
1600
|
+
_context2.next = 5;
|
|
1601
|
+
break;
|
|
1602
|
+
case 4:
|
|
1603
|
+
_context2.prev = 4;
|
|
1604
|
+
_t2 = _context2["catch"](2);
|
|
1605
|
+
setDataset(null);
|
|
1606
|
+
setErrorText((_t2 === null || _t2 === void 0 ? void 0 : _t2.message) || '本地文件解析失败');
|
|
1607
|
+
case 5:
|
|
1608
|
+
_context2.prev = 5;
|
|
1609
|
+
setLoading(false);
|
|
1610
|
+
if (fileInputRef.current) {
|
|
1611
|
+
fileInputRef.current.value = '';
|
|
1612
|
+
}
|
|
1613
|
+
return _context2.finish(5);
|
|
1614
|
+
case 6:
|
|
1615
|
+
case "end":
|
|
1616
|
+
return _context2.stop();
|
|
1617
|
+
}
|
|
1618
|
+
}, _callee2, null, [[2, 4, 5, 6]]);
|
|
1619
|
+
}));
|
|
1620
|
+
return _handleLocalFileChange.apply(this, arguments);
|
|
1621
|
+
}
|
|
1622
|
+
function rebuildPreview() {
|
|
1623
|
+
if (!previewGroupRef.current || !sceneRef.current) return;
|
|
1624
|
+
sceneRef.current.background = new THREE.Color(background);
|
|
1625
|
+
clearGroup(previewGroupRef.current);
|
|
1626
|
+
if (!dataset) return;
|
|
1627
|
+
var geometry = dataset.rawGeometry || (dataset.nodes.length > 0 && dataset.cells.length > 0 ? buildSurfaceGeometry(dataset) : null);
|
|
1628
|
+
if (!geometry) return;
|
|
1629
|
+
geometry.computeVertexNormals();
|
|
1630
|
+
var previewGeometry = geometry.clone();
|
|
1631
|
+
if (displayMode === 'quality') {
|
|
1632
|
+
applyQualityVertexColors(previewGeometry, dataset, qualityMode);
|
|
1633
|
+
}
|
|
1634
|
+
var surfaceMaterial = new THREE.MeshStandardMaterial({
|
|
1635
|
+
color: displayMode === 'quality' ? 0xffffff : 0x4ea1ff,
|
|
1636
|
+
transparent: true,
|
|
1637
|
+
opacity: displayMode === 'wireframe' ? 0.08 : 0.88,
|
|
1638
|
+
side: THREE.DoubleSide,
|
|
1639
|
+
flatShading: false,
|
|
1640
|
+
vertexColors: displayMode === 'quality',
|
|
1641
|
+
metalness: 0.05,
|
|
1642
|
+
roughness: 0.65
|
|
1643
|
+
});
|
|
1644
|
+
var surfaceMesh = new THREE.Mesh(previewGeometry, surfaceMaterial);
|
|
1645
|
+
if (displayMode !== 'wireframe') {
|
|
1646
|
+
previewGroupRef.current.add(surfaceMesh);
|
|
1647
|
+
}
|
|
1648
|
+
if (displayMode === 'wireframe' || displayMode === 'surface-wireframe' || displayMode === 'quality') {
|
|
1649
|
+
var edgesGeometry = new THREE.EdgesGeometry(previewGeometry);
|
|
1650
|
+
var edgesMaterial = new THREE.LineBasicMaterial({
|
|
1651
|
+
color: 0xe6f0ff,
|
|
1652
|
+
transparent: true,
|
|
1653
|
+
opacity: displayMode === 'wireframe' ? 1 : 0.55
|
|
1654
|
+
});
|
|
1655
|
+
var edgeLines = new THREE.LineSegments(edgesGeometry, edgesMaterial);
|
|
1656
|
+
previewGroupRef.current.add(edgeLines);
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
// const featureEdges = buildFeatureEdges(previewGeometry);
|
|
1660
|
+
// if (featureEdges) {
|
|
1661
|
+
// previewGroupRef.current.add(featureEdges);
|
|
1662
|
+
// }
|
|
1663
|
+
|
|
1664
|
+
// if (showNodes) {
|
|
1665
|
+
// const pointsGeometry = buildPointsGeometry(dataset, previewGeometry);
|
|
1666
|
+
// if (pointsGeometry) {
|
|
1667
|
+
// const pointsMaterial = new THREE.PointsMaterial({
|
|
1668
|
+
// color: 0xffd166,
|
|
1669
|
+
// size: 0.04,
|
|
1670
|
+
// sizeAttenuation: true,
|
|
1671
|
+
// });
|
|
1672
|
+
|
|
1673
|
+
// const pointMesh = new THREE.Points(pointsGeometry, pointsMaterial);
|
|
1674
|
+
// previewGroupRef.current.add(pointMesh);
|
|
1675
|
+
// }
|
|
1676
|
+
// }
|
|
1677
|
+
|
|
1678
|
+
// const boundaryHighlight = buildBoundaryHighlight(dataset);
|
|
1679
|
+
// if (boundaryHighlight) {
|
|
1680
|
+
// previewGroupRef.current.add(boundaryHighlight);
|
|
1681
|
+
// }
|
|
1682
|
+
|
|
1683
|
+
// const hotspotOverlay = buildQualityHotspotOverlay(previewGeometry, qualityMode);
|
|
1684
|
+
// if (hotspotOverlay && displayMode === 'quality') {
|
|
1685
|
+
// previewGroupRef.current.add(hotspotOverlay);
|
|
1686
|
+
// }
|
|
1687
|
+
|
|
1688
|
+
// if (showBoundaryHint) {
|
|
1689
|
+
// const bbox = new THREE.Box3().setFromObject(previewGroupRef.current);
|
|
1690
|
+
// const size = new THREE.Vector3();
|
|
1691
|
+
// bbox.getSize(size);
|
|
1692
|
+
// const box = new THREE.Box3Helper(bbox, 0x53d6ff);
|
|
1693
|
+
// previewGroupRef.current.add(box);
|
|
1694
|
+
|
|
1695
|
+
// if (boundaryLayerEnabled) {
|
|
1696
|
+
// const helper = buildBoundaryLayerHint(size, boundaryLayerCount);
|
|
1697
|
+
// helper.position.copy(bbox.getCenter(new THREE.Vector3()));
|
|
1698
|
+
// previewGroupRef.current.add(helper);
|
|
1699
|
+
// }
|
|
1700
|
+
// }
|
|
1701
|
+
var featureEdges = buildFeatureEdges(previewGeometry);
|
|
1702
|
+
if (featureEdges) {
|
|
1703
|
+
previewGroupRef.current.add(featureEdges);
|
|
1704
|
+
}
|
|
1705
|
+
if (showNodes) {
|
|
1706
|
+
var pointsGeometry = buildPointsGeometry(dataset, previewGeometry);
|
|
1707
|
+
if (pointsGeometry) {
|
|
1708
|
+
var pointsMaterial = new THREE.PointsMaterial({
|
|
1709
|
+
color: 0xffd166,
|
|
1710
|
+
size: 0.04,
|
|
1711
|
+
sizeAttenuation: true
|
|
1712
|
+
});
|
|
1713
|
+
var pointMesh = new THREE.Points(pointsGeometry, pointsMaterial);
|
|
1714
|
+
previewGroupRef.current.add(pointMesh);
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
var boundaryHighlight = buildBoundaryHighlight(dataset);
|
|
1718
|
+
if (boundaryHighlight) {
|
|
1719
|
+
previewGroupRef.current.add(boundaryHighlight);
|
|
1720
|
+
}
|
|
1721
|
+
var hotspotOverlay = buildQualityHotspotOverlay(previewGeometry, qualityMode);
|
|
1722
|
+
if (hotspotOverlay && displayMode === 'quality') {
|
|
1723
|
+
previewGroupRef.current.add(hotspotOverlay);
|
|
1724
|
+
}
|
|
1725
|
+
var bbox = new THREE.Box3().setFromObject(previewGroupRef.current);
|
|
1726
|
+
var size = new THREE.Vector3();
|
|
1727
|
+
var center = new THREE.Vector3();
|
|
1728
|
+
bbox.getSize(size);
|
|
1729
|
+
bbox.getCenter(center);
|
|
1730
|
+
if (refineEnabled) {
|
|
1731
|
+
var refinementOverlay = buildRefinementOverlay(size, center, refinementLevel, targetSize);
|
|
1732
|
+
if (refinementOverlay) {
|
|
1733
|
+
previewGroupRef.current.add(refinementOverlay);
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
if (showBoundaryHint) {
|
|
1737
|
+
var box = new THREE.Box3Helper(bbox, 0x53d6ff);
|
|
1738
|
+
previewGroupRef.current.add(box);
|
|
1739
|
+
if (boundaryLayerEnabled) {
|
|
1740
|
+
var helper = buildBoundaryLayerHint(size, center, boundaryLayerCount, boundaryLayerThickness, targetSize);
|
|
1741
|
+
if (helper) {
|
|
1742
|
+
previewGroupRef.current.add(helper);
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
fitCameraToObject(previewGroupRef.current);
|
|
1747
|
+
}
|
|
1748
|
+
function fitCameraToObject(object) {
|
|
1749
|
+
if (!cameraRef.current || !controlsRef.current) return;
|
|
1750
|
+
var box = new THREE.Box3().setFromObject(object);
|
|
1751
|
+
if (box.isEmpty()) return;
|
|
1752
|
+
var size = new THREE.Vector3();
|
|
1753
|
+
var center = new THREE.Vector3();
|
|
1754
|
+
box.getSize(size);
|
|
1755
|
+
box.getCenter(center);
|
|
1756
|
+
var maxDim = Math.max(size.x, size.y, size.z) || 1;
|
|
1757
|
+
var camera = cameraRef.current;
|
|
1758
|
+
var distance = maxDim * 2.2;
|
|
1759
|
+
camera.position.set(center.x + distance, center.y + distance * 0.8, center.z + distance);
|
|
1760
|
+
camera.near = 0.01;
|
|
1761
|
+
camera.far = distance * 20;
|
|
1762
|
+
camera.updateProjectionMatrix();
|
|
1763
|
+
controlsRef.current.target.copy(center);
|
|
1764
|
+
controlsRef.current.update();
|
|
1765
|
+
}
|
|
1766
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1767
|
+
className: _variables.bizCssPrefix + "-mesh-workbench",
|
|
1768
|
+
style: style
|
|
1769
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
1770
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__topbar"
|
|
1771
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
1772
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__top-item"
|
|
1773
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
1774
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__top-label"
|
|
1775
|
+
}, "\u6570\u636E\u6765\u6E90"), /*#__PURE__*/React.createElement("span", {
|
|
1776
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__top-value"
|
|
1777
|
+
}, sourceType)), /*#__PURE__*/React.createElement("div", {
|
|
1778
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__top-item"
|
|
1779
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
1780
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__top-label"
|
|
1781
|
+
}, "\u6570\u636E\u683C\u5F0F"), /*#__PURE__*/React.createElement("span", {
|
|
1782
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__top-value"
|
|
1783
|
+
}, dataFormat)), /*#__PURE__*/React.createElement("div", {
|
|
1784
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__top-item"
|
|
1785
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
1786
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__top-label"
|
|
1787
|
+
}, "\u7F51\u683C\u7C7B\u578B"), /*#__PURE__*/React.createElement("span", {
|
|
1788
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__top-value"
|
|
1789
|
+
}, meshType === 'structured' ? '结构化' : '非结构化')), /*#__PURE__*/React.createElement("div", {
|
|
1790
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__top-item"
|
|
1791
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
1792
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__top-label"
|
|
1793
|
+
}, "\u76EE\u6807\u5C3A\u5BF8"), /*#__PURE__*/React.createElement("span", {
|
|
1794
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__top-value"
|
|
1795
|
+
}, targetSize)), /*#__PURE__*/React.createElement("div", {
|
|
1796
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__top-item"
|
|
1797
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
1798
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__top-label"
|
|
1799
|
+
}, "\u663E\u793A\u6A21\u5F0F"), /*#__PURE__*/React.createElement("span", {
|
|
1800
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__top-value"
|
|
1801
|
+
}, displayMode)), allowLocalUpload && sourceType === 'local' && /*#__PURE__*/React.createElement("div", {
|
|
1802
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__upload",
|
|
1803
|
+
onClick: function onClick(e) {
|
|
1804
|
+
return e.stopPropagation();
|
|
1805
|
+
},
|
|
1806
|
+
onMouseDown: function onMouseDown(e) {
|
|
1807
|
+
return e.stopPropagation();
|
|
1808
|
+
}
|
|
1809
|
+
}, /*#__PURE__*/React.createElement("button", {
|
|
1810
|
+
type: "button",
|
|
1811
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__upload-btn",
|
|
1812
|
+
onClick: handleTriggerFileSelect,
|
|
1813
|
+
onMouseDown: function onMouseDown(e) {
|
|
1814
|
+
return e.stopPropagation();
|
|
1815
|
+
}
|
|
1816
|
+
}, "\u9009\u62E9\u6587\u4EF6"), /*#__PURE__*/React.createElement("span", {
|
|
1817
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__upload-name"
|
|
1818
|
+
}, localFileLabel), /*#__PURE__*/React.createElement("input", {
|
|
1819
|
+
ref: fileInputRef,
|
|
1820
|
+
type: "file",
|
|
1821
|
+
accept: getAcceptByFormat(dataFormat),
|
|
1822
|
+
onChange: handleLocalFileChange,
|
|
1823
|
+
style: {
|
|
1824
|
+
display: 'none'
|
|
1825
|
+
}
|
|
1826
|
+
}))), /*#__PURE__*/React.createElement("div", {
|
|
1827
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__main"
|
|
1828
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
1829
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__left"
|
|
1830
|
+
}, /*#__PURE__*/React.createElement(PanelTitle, {
|
|
1831
|
+
title: "\u7F51\u683C\u5206\u6790"
|
|
1832
|
+
}), /*#__PURE__*/React.createElement(MetricRow, {
|
|
1833
|
+
label: "\u8282\u70B9\u6570",
|
|
1834
|
+
value: String(stats.nodeCount)
|
|
1835
|
+
}), /*#__PURE__*/React.createElement(MetricRow, {
|
|
1836
|
+
label: "\u5355\u5143\u6570",
|
|
1837
|
+
value: String(stats.cellCount)
|
|
1838
|
+
}), /*#__PURE__*/React.createElement(MetricRow, {
|
|
1839
|
+
label: "\u9762\u7247\u6570",
|
|
1840
|
+
value: String(stats.faceCount)
|
|
1841
|
+
}), /*#__PURE__*/React.createElement(MetricRow, {
|
|
1842
|
+
label: "\u8FB9\u6570\u91CF\u4F30\u8BA1",
|
|
1843
|
+
value: String(stats.edgeCount)
|
|
1844
|
+
}), /*#__PURE__*/React.createElement(MetricRow, {
|
|
1845
|
+
label: "\u533A\u57DF\u6570\u91CF",
|
|
1846
|
+
value: String(stats.zoneCount)
|
|
1847
|
+
}), /*#__PURE__*/React.createElement(MetricRow, {
|
|
1848
|
+
label: "\u8FB9\u754C\u6570\u91CF",
|
|
1849
|
+
value: String(stats.boundaryCount)
|
|
1850
|
+
}), /*#__PURE__*/React.createElement(MetricRow, {
|
|
1851
|
+
label: "\u7279\u5F81\u8FB9\u4F30\u8BA1",
|
|
1852
|
+
value: String(stats.featureEdgeEstimate)
|
|
1853
|
+
}), /*#__PURE__*/React.createElement(MetricRow, {
|
|
1854
|
+
label: "\u5305\u56F4\u76D2\u8303\u56F4",
|
|
1855
|
+
value: stats.boundsText
|
|
1856
|
+
}), /*#__PURE__*/React.createElement(PanelTitle, {
|
|
1857
|
+
title: "\u8D28\u91CF\u68C0\u67E5"
|
|
1858
|
+
}), /*#__PURE__*/React.createElement(MetricRow, {
|
|
1859
|
+
label: "\u6B63\u4EA4\u6027",
|
|
1860
|
+
value: stats.orthogonality.toFixed(2)
|
|
1861
|
+
}), /*#__PURE__*/React.createElement(MetricRow, {
|
|
1862
|
+
label: "\u504F\u659C\u5EA6",
|
|
1863
|
+
value: stats.skewness.toFixed(2)
|
|
1864
|
+
}), /*#__PURE__*/React.createElement(MetricRow, {
|
|
1865
|
+
label: "\u957F\u5BBD\u6BD4",
|
|
1866
|
+
value: stats.aspectRatio.toFixed(2)
|
|
1867
|
+
}), /*#__PURE__*/React.createElement(MetricRow, {
|
|
1868
|
+
label: "\u70ED\u533A\u5360\u6BD4\u4F30\u8BA1",
|
|
1869
|
+
value: (stats.hotspotRatio * 100).toFixed(1) + "%"
|
|
1870
|
+
}), /*#__PURE__*/React.createElement(MetricRow, {
|
|
1871
|
+
label: "\u5F53\u524D\u8D28\u91CF\u8BC4\u7EA7",
|
|
1872
|
+
value: qualityGrade
|
|
1873
|
+
})), /*#__PURE__*/React.createElement("div", {
|
|
1874
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__center"
|
|
1875
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
1876
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__viewport-toolbar"
|
|
1877
|
+
}, /*#__PURE__*/React.createElement("span", null, loading ? '数据加载中...' : '3D 预览'), /*#__PURE__*/React.createElement("span", null, activeFileName || '未指定文件')), /*#__PURE__*/React.createElement("div", {
|
|
1878
|
+
ref: containerRef,
|
|
1879
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__viewport"
|
|
1880
|
+
}), errorText ? /*#__PURE__*/React.createElement("div", {
|
|
1881
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__overlay-error"
|
|
1882
|
+
}, errorText) : null), /*#__PURE__*/React.createElement("div", {
|
|
1883
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__right"
|
|
1884
|
+
}, /*#__PURE__*/React.createElement(PanelTitle, {
|
|
1885
|
+
title: "\u914D\u7F6E\u6458\u8981"
|
|
1886
|
+
}), /*#__PURE__*/React.createElement(SummaryBlock, {
|
|
1887
|
+
title: "\u6570\u636E\u72B6\u6001",
|
|
1888
|
+
lines: [sourceText, "\u6587\u4EF6\u540D: " + (activeFileName || '无'), "\u683C\u5F0F: " + dataFormat, "\u8D28\u91CF\u6307\u6807: " + qualityMode]
|
|
1889
|
+
}), /*#__PURE__*/React.createElement(SummaryBlock, {
|
|
1890
|
+
title: "\u524D\u5904\u7406\u53C2\u6570",
|
|
1891
|
+
lines: ["\u7F51\u683C\u7C7B\u578B: " + (meshType === 'structured' ? '结构化' : '非结构化'), "\u76EE\u6807\u5C3A\u5BF8: " + targetSize, "\u5C40\u90E8\u52A0\u5BC6: " + (refineEnabled ? "\u542F\u7528\uFF08\u7EA7\u522B " + refinementLevel + "\uFF09" : '未启用'), "\u8FB9\u754C\u5C42: " + (boundaryLayerEnabled ? "\u542F\u7528\uFF08" + boundaryLayerCount + " \u5C42 / \u539A\u5EA6 " + boundaryLayerThickness + "\uFF09" : '未启用')]
|
|
1892
|
+
}), /*#__PURE__*/React.createElement(SummaryBlock, {
|
|
1893
|
+
title: "Zone \u7EDF\u8BA1",
|
|
1894
|
+
lines: stats.zoneSummary.length ? stats.zoneSummary.map(function (item) {
|
|
1895
|
+
return item.name + ": " + item.count;
|
|
1896
|
+
}) : ['无 zone 信息']
|
|
1897
|
+
}), /*#__PURE__*/React.createElement(SummaryBlock, {
|
|
1898
|
+
title: "Boundary \u7EDF\u8BA1",
|
|
1899
|
+
lines: stats.boundarySummary.length ? stats.boundarySummary.map(function (item) {
|
|
1900
|
+
return item.name + ": " + item.count;
|
|
1901
|
+
}) : ['无 boundary 信息']
|
|
1902
|
+
}), /*#__PURE__*/React.createElement(SummaryBlock, {
|
|
1903
|
+
title: "\u68C0\u67E5\u63D0\u793A",
|
|
1904
|
+
lines: ["\u663E\u793A\u6A21\u5F0F: " + displayMode, "\u7279\u5F81\u8FB9\u9AD8\u4EAE: \u5DF2\u542F\u7528", "\u8FB9\u754C\u9AD8\u4EAE: \u5DF2\u542F\u7528", "\u8D28\u91CF\u70ED\u533A\u63D0\u793A: " + (displayMode === 'quality' ? '已启用' : '切换到质量模式可见')]
|
|
1905
|
+
}))));
|
|
1906
|
+
};
|
|
1907
|
+
MeshVisualization.displayName = 'MeshVisualization';
|
|
1908
|
+
var _default = exports["default"] = MeshVisualization;
|
|
1909
|
+
/** ---------------- 子组件 ---------------- */
|
|
1910
|
+
function PanelTitle(_ref2) {
|
|
1911
|
+
var title = _ref2.title;
|
|
1912
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1913
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__panel-title"
|
|
1914
|
+
}, title);
|
|
1915
|
+
}
|
|
1916
|
+
function MetricRow(_ref3) {
|
|
1917
|
+
var label = _ref3.label,
|
|
1918
|
+
value = _ref3.value;
|
|
1919
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1920
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__metric-row"
|
|
1921
|
+
}, /*#__PURE__*/React.createElement("span", null, label), /*#__PURE__*/React.createElement("span", null, value));
|
|
1922
|
+
}
|
|
1923
|
+
function SummaryBlock(_ref4) {
|
|
1924
|
+
var title = _ref4.title,
|
|
1925
|
+
lines = _ref4.lines;
|
|
1926
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1927
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__summary-block"
|
|
1928
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
1929
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__summary-title"
|
|
1930
|
+
}, title), lines.map(function (line, index) {
|
|
1931
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1932
|
+
key: index,
|
|
1933
|
+
className: _variables.bizCssPrefix + "-mesh-workbench__summary-line"
|
|
1934
|
+
}, line);
|
|
1935
|
+
}));
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
/** ---------------- 多格式数据加载 ---------------- */
|
|
1939
|
+
function loadDatasetByFormatFromUrl(_x4, _x5) {
|
|
1940
|
+
return _loadDatasetByFormatFromUrl.apply(this, arguments);
|
|
1941
|
+
}
|
|
1942
|
+
function _loadDatasetByFormatFromUrl() {
|
|
1943
|
+
_loadDatasetByFormatFromUrl = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee3(url, format) {
|
|
1944
|
+
var response, text, _response, buffer, _response2, _text;
|
|
1945
|
+
return _regenerator["default"].wrap(function (_context3) {
|
|
1946
|
+
while (1) switch (_context3.prev = _context3.next) {
|
|
1947
|
+
case 0:
|
|
1948
|
+
if (!(format === 'custom-json')) {
|
|
1949
|
+
_context3.next = 4;
|
|
1950
|
+
break;
|
|
1951
|
+
}
|
|
1952
|
+
_context3.next = 1;
|
|
1953
|
+
return fetch(url);
|
|
1954
|
+
case 1:
|
|
1955
|
+
response = _context3.sent;
|
|
1956
|
+
if (response.ok) {
|
|
1957
|
+
_context3.next = 2;
|
|
1958
|
+
break;
|
|
1959
|
+
}
|
|
1960
|
+
throw new Error("\u8BF7\u6C42\u5931\u8D25: " + response.status);
|
|
1961
|
+
case 2:
|
|
1962
|
+
_context3.next = 3;
|
|
1963
|
+
return response.text();
|
|
1964
|
+
case 3:
|
|
1965
|
+
text = _context3.sent;
|
|
1966
|
+
return _context3.abrupt("return", parseCustomJsonMesh(text));
|
|
1967
|
+
case 4:
|
|
1968
|
+
if (!(format === 'stl')) {
|
|
1969
|
+
_context3.next = 8;
|
|
1970
|
+
break;
|
|
1971
|
+
}
|
|
1972
|
+
_context3.next = 5;
|
|
1973
|
+
return fetch(url);
|
|
1974
|
+
case 5:
|
|
1975
|
+
_response = _context3.sent;
|
|
1976
|
+
if (_response.ok) {
|
|
1977
|
+
_context3.next = 6;
|
|
1978
|
+
break;
|
|
1979
|
+
}
|
|
1980
|
+
throw new Error("\u8BF7\u6C42\u5931\u8D25: " + _response.status);
|
|
1981
|
+
case 6:
|
|
1982
|
+
_context3.next = 7;
|
|
1983
|
+
return _response.arrayBuffer();
|
|
1984
|
+
case 7:
|
|
1985
|
+
buffer = _context3.sent;
|
|
1986
|
+
return _context3.abrupt("return", parseSTLMesh(buffer));
|
|
1987
|
+
case 8:
|
|
1988
|
+
if (!(format === 'obj')) {
|
|
1989
|
+
_context3.next = 12;
|
|
1990
|
+
break;
|
|
1991
|
+
}
|
|
1992
|
+
_context3.next = 9;
|
|
1993
|
+
return fetch(url);
|
|
1994
|
+
case 9:
|
|
1995
|
+
_response2 = _context3.sent;
|
|
1996
|
+
if (_response2.ok) {
|
|
1997
|
+
_context3.next = 10;
|
|
1998
|
+
break;
|
|
1999
|
+
}
|
|
2000
|
+
throw new Error("\u8BF7\u6C42\u5931\u8D25: " + _response2.status);
|
|
2001
|
+
case 10:
|
|
2002
|
+
_context3.next = 11;
|
|
2003
|
+
return _response2.text();
|
|
2004
|
+
case 11:
|
|
2005
|
+
_text = _context3.sent;
|
|
2006
|
+
return _context3.abrupt("return", parseOBJMesh(_text));
|
|
2007
|
+
case 12:
|
|
2008
|
+
throw new Error("\u6682\u4E0D\u652F\u6301\u7684\u6570\u636E\u683C\u5F0F: " + format);
|
|
2009
|
+
case 13:
|
|
2010
|
+
case "end":
|
|
2011
|
+
return _context3.stop();
|
|
2012
|
+
}
|
|
2013
|
+
}, _callee3);
|
|
2014
|
+
}));
|
|
2015
|
+
return _loadDatasetByFormatFromUrl.apply(this, arguments);
|
|
2016
|
+
}
|
|
2017
|
+
function loadDatasetByFormatFromFile(_x6, _x7) {
|
|
2018
|
+
return _loadDatasetByFormatFromFile.apply(this, arguments);
|
|
2019
|
+
}
|
|
2020
|
+
function _loadDatasetByFormatFromFile() {
|
|
2021
|
+
_loadDatasetByFormatFromFile = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee4(file, format) {
|
|
2022
|
+
var text, buffer, _text2;
|
|
2023
|
+
return _regenerator["default"].wrap(function (_context4) {
|
|
2024
|
+
while (1) switch (_context4.prev = _context4.next) {
|
|
2025
|
+
case 0:
|
|
2026
|
+
if (!(format === 'custom-json')) {
|
|
2027
|
+
_context4.next = 2;
|
|
2028
|
+
break;
|
|
2029
|
+
}
|
|
2030
|
+
_context4.next = 1;
|
|
2031
|
+
return file.text();
|
|
2032
|
+
case 1:
|
|
2033
|
+
text = _context4.sent;
|
|
2034
|
+
return _context4.abrupt("return", parseCustomJsonMesh(text));
|
|
2035
|
+
case 2:
|
|
2036
|
+
if (!(format === 'stl')) {
|
|
2037
|
+
_context4.next = 4;
|
|
2038
|
+
break;
|
|
2039
|
+
}
|
|
2040
|
+
_context4.next = 3;
|
|
2041
|
+
return file.arrayBuffer();
|
|
2042
|
+
case 3:
|
|
2043
|
+
buffer = _context4.sent;
|
|
2044
|
+
return _context4.abrupt("return", parseSTLMesh(buffer));
|
|
2045
|
+
case 4:
|
|
2046
|
+
if (!(format === 'obj')) {
|
|
2047
|
+
_context4.next = 6;
|
|
2048
|
+
break;
|
|
2049
|
+
}
|
|
2050
|
+
_context4.next = 5;
|
|
2051
|
+
return file.text();
|
|
2052
|
+
case 5:
|
|
2053
|
+
_text2 = _context4.sent;
|
|
2054
|
+
return _context4.abrupt("return", parseOBJMesh(_text2));
|
|
2055
|
+
case 6:
|
|
2056
|
+
throw new Error("\u6682\u4E0D\u652F\u6301\u7684\u6570\u636E\u683C\u5F0F: " + format);
|
|
2057
|
+
case 7:
|
|
2058
|
+
case "end":
|
|
2059
|
+
return _context4.stop();
|
|
2060
|
+
}
|
|
2061
|
+
}, _callee4);
|
|
2062
|
+
}));
|
|
2063
|
+
return _loadDatasetByFormatFromFile.apply(this, arguments);
|
|
2064
|
+
}
|
|
2065
|
+
function buildDemoDataset(format) {
|
|
2066
|
+
if (format === 'stl') {
|
|
2067
|
+
var geometry = new THREE.TorusKnotGeometry(0.9, 0.26, 180, 24);
|
|
2068
|
+
return buildGeometryDataset(geometry, 'demo-stl-geometry', 'stl');
|
|
2069
|
+
}
|
|
2070
|
+
if (format === 'obj') {
|
|
2071
|
+
var _geometry = new THREE.SphereGeometry(1, 28, 22);
|
|
2072
|
+
return buildGeometryDataset(_geometry, 'demo-obj-geometry', 'obj');
|
|
2073
|
+
}
|
|
2074
|
+
return (0, _extends2["default"])({}, DEMO_JSON_DATASET, {
|
|
2075
|
+
meta: (0, _extends2["default"])({}, DEMO_JSON_DATASET.meta, {
|
|
2076
|
+
sourceFormat: 'custom-json'
|
|
2077
|
+
})
|
|
2078
|
+
});
|
|
2079
|
+
}
|
|
2080
|
+
function parseCustomJsonMesh(text) {
|
|
2081
|
+
var _raw, _raw2;
|
|
2082
|
+
var raw;
|
|
2083
|
+
try {
|
|
2084
|
+
raw = JSON.parse(text);
|
|
2085
|
+
} catch (_unused) {
|
|
2086
|
+
throw new Error('JSON 解析失败,请检查文件内容。');
|
|
2087
|
+
}
|
|
2088
|
+
var nodes = Array.isArray((_raw = raw) === null || _raw === void 0 ? void 0 : _raw.nodes) ? raw.nodes : [];
|
|
2089
|
+
var cells = Array.isArray((_raw2 = raw) === null || _raw2 === void 0 ? void 0 : _raw2.cells) ? raw.cells : [];
|
|
2090
|
+
if (!nodes.length || !cells.length) {
|
|
2091
|
+
throw new Error('数据格式无效,custom-json 需要包含 nodes 和 cells。');
|
|
2092
|
+
}
|
|
2093
|
+
var parsedNodes = nodes.map(function (item) {
|
|
2094
|
+
return {
|
|
2095
|
+
x: Number(item.x),
|
|
2096
|
+
y: Number(item.y),
|
|
2097
|
+
z: Number(item.z)
|
|
2098
|
+
};
|
|
2099
|
+
});
|
|
2100
|
+
var parsedCells = cells.map(function (item) {
|
|
2101
|
+
return {
|
|
2102
|
+
type: normalizeCellType(item.type),
|
|
2103
|
+
indices: Array.isArray(item.indices) ? item.indices.map(function (i) {
|
|
2104
|
+
return Number(i);
|
|
2105
|
+
}) : [],
|
|
2106
|
+
zone: item.zone || 'default-zone',
|
|
2107
|
+
boundary: item.boundary || 'default-boundary'
|
|
2108
|
+
};
|
|
2109
|
+
});
|
|
2110
|
+
return {
|
|
2111
|
+
nodes: parsedNodes,
|
|
2112
|
+
cells: parsedCells,
|
|
2113
|
+
meta: (0, _extends2["default"])({}, raw.meta || {}, {
|
|
2114
|
+
sourceFormat: 'custom-json'
|
|
2115
|
+
})
|
|
2116
|
+
};
|
|
2117
|
+
}
|
|
2118
|
+
function parseSTLMesh(buffer) {
|
|
2119
|
+
var loader = new _STLLoader.STLLoader();
|
|
2120
|
+
var geometry = loader.parse(buffer);
|
|
2121
|
+
geometry.computeVertexNormals();
|
|
2122
|
+
return buildGeometryDataset(geometry, 'stl-geometry', 'stl');
|
|
2123
|
+
}
|
|
2124
|
+
function parseOBJMesh(text) {
|
|
2125
|
+
var loader = new _OBJLoader.OBJLoader();
|
|
2126
|
+
var object = loader.parse(text);
|
|
2127
|
+
var mergedGeometry = null;
|
|
2128
|
+
object.traverse(function (child) {
|
|
2129
|
+
var mesh = child;
|
|
2130
|
+
if (mesh.isMesh && mesh.geometry) {
|
|
2131
|
+
var geom = mesh.geometry.clone();
|
|
2132
|
+
geom.applyMatrix4(mesh.matrixWorld);
|
|
2133
|
+
if (!mergedGeometry) {
|
|
2134
|
+
mergedGeometry = geom;
|
|
2135
|
+
} else {
|
|
2136
|
+
mergedGeometry = mergeBufferGeometriesSimple(mergedGeometry, geom);
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
});
|
|
2140
|
+
if (!mergedGeometry) {
|
|
2141
|
+
throw new Error('OBJ 解析失败,未找到可用几何体。');
|
|
2142
|
+
}
|
|
2143
|
+
mergedGeometry.computeVertexNormals();
|
|
2144
|
+
return buildGeometryDataset(mergedGeometry, 'obj-geometry', 'obj');
|
|
2145
|
+
}
|
|
2146
|
+
function buildGeometryDataset(geometry, name, format) {
|
|
2147
|
+
var nextGeometry = geometry.index ? geometry.toNonIndexed() : geometry.clone();
|
|
2148
|
+
nextGeometry.computeBoundingBox();
|
|
2149
|
+
nextGeometry.computeVertexNormals();
|
|
2150
|
+
var positionAttr = nextGeometry.getAttribute('position');
|
|
2151
|
+
var nodes = [];
|
|
2152
|
+
var cells = [];
|
|
2153
|
+
if (positionAttr) {
|
|
2154
|
+
for (var i = 0; i < positionAttr.count; i++) {
|
|
2155
|
+
nodes.push({
|
|
2156
|
+
x: positionAttr.getX(i),
|
|
2157
|
+
y: positionAttr.getY(i),
|
|
2158
|
+
z: positionAttr.getZ(i)
|
|
2159
|
+
});
|
|
2160
|
+
}
|
|
2161
|
+
for (var _i = 0; _i < positionAttr.count; _i += 3) {
|
|
2162
|
+
if (_i + 2 < positionAttr.count) {
|
|
2163
|
+
cells.push({
|
|
2164
|
+
type: 'triangle',
|
|
2165
|
+
indices: [_i, _i + 1, _i + 2],
|
|
2166
|
+
zone: 'surface-zone',
|
|
2167
|
+
boundary: 'surface'
|
|
2168
|
+
});
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
return {
|
|
2173
|
+
nodes: nodes,
|
|
2174
|
+
cells: cells,
|
|
2175
|
+
rawGeometry: nextGeometry,
|
|
2176
|
+
meta: {
|
|
2177
|
+
name: name,
|
|
2178
|
+
sourceFormat: format,
|
|
2179
|
+
caseType: 'surface-geometry'
|
|
2180
|
+
}
|
|
2181
|
+
};
|
|
2182
|
+
}
|
|
2183
|
+
function mergeBufferGeometriesSimple(geometryA, geometryB) {
|
|
2184
|
+
var a = geometryA.index ? geometryA.toNonIndexed() : geometryA.clone();
|
|
2185
|
+
var b = geometryB.index ? geometryB.toNonIndexed() : geometryB.clone();
|
|
2186
|
+
var posA = a.getAttribute('position');
|
|
2187
|
+
var posB = b.getAttribute('position');
|
|
2188
|
+
if (!posA || !posB) {
|
|
2189
|
+
return a;
|
|
2190
|
+
}
|
|
2191
|
+
var mergedPositions = new Float32Array(posA.array.length + posB.array.length);
|
|
2192
|
+
mergedPositions.set(posA.array, 0);
|
|
2193
|
+
mergedPositions.set(posB.array, posA.array.length);
|
|
2194
|
+
var merged = new THREE.BufferGeometry();
|
|
2195
|
+
merged.setAttribute('position', new THREE.BufferAttribute(mergedPositions, 3));
|
|
2196
|
+
return merged;
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
/** ---------------- 统计分析 ---------------- */
|
|
2200
|
+
|
|
2201
|
+
function buildMeshStats(dataset, meshType, targetSize, refineEnabled, refinementLevel, boundaryLayerEnabled, boundaryLayerCount, boundaryLayerThickness) {
|
|
2202
|
+
var _dataset$meta, _dataset$meta2;
|
|
2203
|
+
if (!dataset) {
|
|
2204
|
+
return {
|
|
2205
|
+
nodeCount: 0,
|
|
2206
|
+
cellCount: 0,
|
|
2207
|
+
faceCount: 0,
|
|
2208
|
+
edgeCount: 0,
|
|
2209
|
+
zoneCount: 0,
|
|
2210
|
+
boundaryCount: 0,
|
|
2211
|
+
boundsText: '-',
|
|
2212
|
+
featureEdgeEstimate: 0,
|
|
2213
|
+
orthogonality: 0,
|
|
2214
|
+
skewness: 0,
|
|
2215
|
+
aspectRatio: 0,
|
|
2216
|
+
zoneSummary: [],
|
|
2217
|
+
boundarySummary: [],
|
|
2218
|
+
hotspotRatio: 0
|
|
2219
|
+
};
|
|
2220
|
+
}
|
|
2221
|
+
var nodeCount = dataset.nodes.length;
|
|
2222
|
+
var cellCount = dataset.cells.length;
|
|
2223
|
+
var faceCount = 0;
|
|
2224
|
+
var edgeCount = 0;
|
|
2225
|
+
var zoneMap = new Map();
|
|
2226
|
+
var boundaryMap = new Map();
|
|
2227
|
+
dataset.cells.forEach(function (cell) {
|
|
2228
|
+
faceCount += getCellFaces(cell).length;
|
|
2229
|
+
edgeCount += getCellEdges(cell).length;
|
|
2230
|
+
var zoneName = cell.zone || 'default-zone';
|
|
2231
|
+
var boundaryName = cell.boundary || 'default-boundary';
|
|
2232
|
+
zoneMap.set(zoneName, (zoneMap.get(zoneName) || 0) + 1);
|
|
2233
|
+
boundaryMap.set(boundaryName, (boundaryMap.get(boundaryName) || 0) + 1);
|
|
2234
|
+
});
|
|
2235
|
+
var boundsText = getBoundsText(dataset.nodes);
|
|
2236
|
+
var featureEdgeEstimate = Math.max(0, Math.round(edgeCount * 0.28));
|
|
2237
|
+
var orthogonalityBase = meshType === 'structured' ? 0.91 : 0.84;
|
|
2238
|
+
var skewnessBase = meshType === 'structured' ? 0.18 : 0.29;
|
|
2239
|
+
var aspectRatioBase = meshType === 'structured' ? 1.8 : 2.7;
|
|
2240
|
+
var targetEffect = targetSize < 0.08 ? 0.04 : targetSize < 0.15 ? 0.02 : -0.03;
|
|
2241
|
+
var refineEffect = refineEnabled ? refinementLevel * 0.01 : 0;
|
|
2242
|
+
var layerEffect = boundaryLayerEnabled ? boundaryLayerCount * 0.006 : 0;
|
|
2243
|
+
var thicknessEffect = boundaryLayerEnabled ? boundaryLayerThickness * 0.25 : 0;
|
|
2244
|
+
var isSurfaceMesh = ((_dataset$meta = dataset.meta) === null || _dataset$meta === void 0 ? void 0 : _dataset$meta.sourceFormat) === 'stl' || ((_dataset$meta2 = dataset.meta) === null || _dataset$meta2 === void 0 ? void 0 : _dataset$meta2.sourceFormat) === 'obj';
|
|
2245
|
+
var orthogonality = clamp(orthogonalityBase + targetEffect - refineEffect * 0.5 - thicknessEffect - (isSurfaceMesh ? 0.03 : 0), 0.45, 0.98);
|
|
2246
|
+
var skewness = clamp(skewnessBase - targetEffect * 0.6 + refineEffect * 0.45 + thicknessEffect + (isSurfaceMesh ? 0.04 : 0), 0.05, 0.92);
|
|
2247
|
+
var aspectRatio = clamp(aspectRatioBase + refineEffect * 1.2 + layerEffect + thicknessEffect * 5 + (isSurfaceMesh ? 0.6 : 0), 1, 12);
|
|
2248
|
+
var hotspotRatio = clamp((1 - orthogonality) * 0.45 + skewness * 0.35 + aspectRatio / 12 * 0.2, 0, 1);
|
|
2249
|
+
return {
|
|
2250
|
+
nodeCount: nodeCount,
|
|
2251
|
+
cellCount: cellCount,
|
|
2252
|
+
faceCount: faceCount,
|
|
2253
|
+
edgeCount: edgeCount,
|
|
2254
|
+
zoneCount: zoneMap.size,
|
|
2255
|
+
boundaryCount: boundaryMap.size,
|
|
2256
|
+
boundsText: boundsText,
|
|
2257
|
+
featureEdgeEstimate: featureEdgeEstimate,
|
|
2258
|
+
orthogonality: orthogonality,
|
|
2259
|
+
skewness: skewness,
|
|
2260
|
+
aspectRatio: aspectRatio,
|
|
2261
|
+
zoneSummary: Array.from(zoneMap.entries()).map(function (_ref5) {
|
|
2262
|
+
var name = _ref5[0],
|
|
2263
|
+
count = _ref5[1];
|
|
2264
|
+
return {
|
|
2265
|
+
name: name,
|
|
2266
|
+
count: count
|
|
2267
|
+
};
|
|
2268
|
+
}),
|
|
2269
|
+
boundarySummary: Array.from(boundaryMap.entries()).map(function (_ref6) {
|
|
2270
|
+
var name = _ref6[0],
|
|
2271
|
+
count = _ref6[1];
|
|
2272
|
+
return {
|
|
2273
|
+
name: name,
|
|
2274
|
+
count: count
|
|
2275
|
+
};
|
|
2276
|
+
}),
|
|
2277
|
+
hotspotRatio: hotspotRatio
|
|
2278
|
+
};
|
|
2279
|
+
}
|
|
2280
|
+
function getBoundsText(nodes) {
|
|
2281
|
+
if (!nodes.length) return '-';
|
|
2282
|
+
var xs = nodes.map(function (n) {
|
|
2283
|
+
return n.x;
|
|
2284
|
+
});
|
|
2285
|
+
var ys = nodes.map(function (n) {
|
|
2286
|
+
return n.y;
|
|
2287
|
+
});
|
|
2288
|
+
var zs = nodes.map(function (n) {
|
|
2289
|
+
return n.z;
|
|
2290
|
+
});
|
|
2291
|
+
var xRange = Math.min.apply(Math, xs).toFixed(2) + " ~ " + Math.max.apply(Math, xs).toFixed(2);
|
|
2292
|
+
var yRange = Math.min.apply(Math, ys).toFixed(2) + " ~ " + Math.max.apply(Math, ys).toFixed(2);
|
|
2293
|
+
var zRange = Math.min.apply(Math, zs).toFixed(2) + " ~ " + Math.max.apply(Math, zs).toFixed(2);
|
|
2294
|
+
return "X:" + xRange + " Y:" + yRange + " Z:" + zRange;
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
/** ---------------- three 几何构建 ---------------- */
|
|
2298
|
+
|
|
2299
|
+
function buildSurfaceGeometry(dataset) {
|
|
2300
|
+
var positions = [];
|
|
2301
|
+
var colors = [];
|
|
2302
|
+
dataset.cells.forEach(function (cell, cellIndex) {
|
|
2303
|
+
var faces = getCellFaces(cell);
|
|
2304
|
+
var color = getFallbackColor(cellIndex);
|
|
2305
|
+
faces.forEach(function (face) {
|
|
2306
|
+
triangulateFace(face).forEach(function (triangle) {
|
|
2307
|
+
triangle.forEach(function (nodeIndex) {
|
|
2308
|
+
var node = dataset.nodes[nodeIndex];
|
|
2309
|
+
if (!node) return;
|
|
2310
|
+
positions.push(node.x, node.y, node.z);
|
|
2311
|
+
colors.push(color.r, color.g, color.b);
|
|
2312
|
+
});
|
|
2313
|
+
});
|
|
2314
|
+
});
|
|
2315
|
+
});
|
|
2316
|
+
var geometry = new THREE.BufferGeometry();
|
|
2317
|
+
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
|
|
2318
|
+
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
|
|
2319
|
+
return geometry;
|
|
2320
|
+
}
|
|
2321
|
+
function buildPointsGeometry(dataset, previewGeometry) {
|
|
2322
|
+
if (dataset.nodes.length > 0) {
|
|
2323
|
+
var pointsGeometry = new THREE.BufferGeometry();
|
|
2324
|
+
var points = new Float32Array(dataset.nodes.length * 3);
|
|
2325
|
+
dataset.nodes.forEach(function (node, index) {
|
|
2326
|
+
points[index * 3] = node.x;
|
|
2327
|
+
points[index * 3 + 1] = node.y;
|
|
2328
|
+
points[index * 3 + 2] = node.z;
|
|
2329
|
+
});
|
|
2330
|
+
pointsGeometry.setAttribute('position', new THREE.BufferAttribute(points, 3));
|
|
2331
|
+
return pointsGeometry;
|
|
2332
|
+
}
|
|
2333
|
+
var positionAttr = previewGeometry.getAttribute('position');
|
|
2334
|
+
if (!positionAttr) return null;
|
|
2335
|
+
var cloned = new THREE.BufferGeometry();
|
|
2336
|
+
cloned.setAttribute('position', positionAttr.clone());
|
|
2337
|
+
return cloned;
|
|
2338
|
+
}
|
|
2339
|
+
function applyQualityVertexColors(geometry, dataset, qualityMode) {
|
|
2340
|
+
var positionAttr = geometry.getAttribute('position');
|
|
2341
|
+
if (!positionAttr) return;
|
|
2342
|
+
var nextColors = [];
|
|
2343
|
+
for (var i = 0; i < positionAttr.count; i++) {
|
|
2344
|
+
var ratio = i / Math.max(1, positionAttr.count - 1);
|
|
2345
|
+
var q = buildPseudoQualityValue(dataset, ratio, qualityMode);
|
|
2346
|
+
var color = qualityValueToColor(q, qualityMode);
|
|
2347
|
+
nextColors.push(color.r, color.g, color.b);
|
|
2348
|
+
}
|
|
2349
|
+
geometry.setAttribute('color', new THREE.Float32BufferAttribute(nextColors, 3));
|
|
2350
|
+
}
|
|
2351
|
+
function buildPseudoQualityValue(dataset, ratio, qualityMode) {
|
|
2352
|
+
var complexity = Math.min(1, dataset.cells.length / 200);
|
|
2353
|
+
if (qualityMode === 'orthogonality') {
|
|
2354
|
+
return clamp(0.92 - ratio * 0.28 - complexity * 0.08, 0, 1);
|
|
2355
|
+
}
|
|
2356
|
+
if (qualityMode === 'skewness') {
|
|
2357
|
+
return clamp(0.08 + ratio * 0.55 + complexity * 0.1, 0, 1);
|
|
2358
|
+
}
|
|
2359
|
+
return clamp(0.12 + ratio * 0.75 + complexity * 0.12, 0, 1);
|
|
2360
|
+
}
|
|
2361
|
+
function qualityValueToColor(value, qualityMode) {
|
|
2362
|
+
var good = new THREE.Color(0x2ecc71);
|
|
2363
|
+
var mid = new THREE.Color(0xf1c40f);
|
|
2364
|
+
var bad = new THREE.Color(0xe74c3c);
|
|
2365
|
+
var t = value;
|
|
2366
|
+
if (qualityMode === 'orthogonality') {
|
|
2367
|
+
t = 1 - value;
|
|
2368
|
+
}
|
|
2369
|
+
if (t < 0.5) {
|
|
2370
|
+
return good.clone().lerp(mid, t / 0.5);
|
|
2371
|
+
}
|
|
2372
|
+
return mid.clone().lerp(bad, (t - 0.5) / 0.5);
|
|
2373
|
+
}
|
|
2374
|
+
function buildRefinementOverlay(size, center, refinementLevel, targetSize) {
|
|
2375
|
+
var group = new THREE.Group();
|
|
2376
|
+
var level = Math.max(1, refinementLevel);
|
|
2377
|
+
var shrinkBase = Math.max(0.35, 0.62 - level * 0.05);
|
|
2378
|
+
var refineBoxSize = new THREE.Vector3(Math.max(size.x * shrinkBase, targetSize * 2), Math.max(size.y * shrinkBase, targetSize * 2), Math.max(size.z * shrinkBase, targetSize * 2));
|
|
2379
|
+
var fillGeometry = new THREE.BoxGeometry(refineBoxSize.x, refineBoxSize.y, refineBoxSize.z);
|
|
2380
|
+
var fillMaterial = new THREE.MeshBasicMaterial({
|
|
2381
|
+
color: 0x00c2ff,
|
|
2382
|
+
transparent: true,
|
|
2383
|
+
opacity: 0.08 + level * 0.015,
|
|
2384
|
+
depthWrite: false
|
|
2385
|
+
});
|
|
2386
|
+
var fillMesh = new THREE.Mesh(fillGeometry, fillMaterial);
|
|
2387
|
+
fillMesh.position.copy(center);
|
|
2388
|
+
group.add(fillMesh);
|
|
2389
|
+
var lineLayers = Math.min(level + 1, 6);
|
|
2390
|
+
for (var i = 0; i < lineLayers; i++) {
|
|
2391
|
+
var scaleOffset = 1 - i * 0.08;
|
|
2392
|
+
var boxGeometry = new THREE.BoxGeometry(refineBoxSize.x * scaleOffset, refineBoxSize.y * scaleOffset, refineBoxSize.z * scaleOffset);
|
|
2393
|
+
var edges = new THREE.EdgesGeometry(boxGeometry);
|
|
2394
|
+
var material = new THREE.LineBasicMaterial({
|
|
2395
|
+
color: i === 0 ? 0x00e5ff : 0x66f2ff,
|
|
2396
|
+
transparent: true,
|
|
2397
|
+
opacity: Math.max(0.25, 0.85 - i * 0.12)
|
|
2398
|
+
});
|
|
2399
|
+
var lines = new THREE.LineSegments(edges, material);
|
|
2400
|
+
lines.position.copy(center);
|
|
2401
|
+
group.add(lines);
|
|
2402
|
+
}
|
|
2403
|
+
var gridCount = Math.min(3 + level, 8);
|
|
2404
|
+
var halfX = refineBoxSize.x / 2;
|
|
2405
|
+
var halfY = refineBoxSize.y / 2;
|
|
2406
|
+
var halfZ = refineBoxSize.z / 2;
|
|
2407
|
+
var lineMaterial = new THREE.LineBasicMaterial({
|
|
2408
|
+
color: 0x7df9ff,
|
|
2409
|
+
transparent: true,
|
|
2410
|
+
opacity: 0.35
|
|
2411
|
+
});
|
|
2412
|
+
for (var _i2 = 1; _i2 < gridCount; _i2++) {
|
|
2413
|
+
var t = -halfX + refineBoxSize.x / gridCount * _i2;
|
|
2414
|
+
var geo = new THREE.BufferGeometry().setFromPoints([new THREE.Vector3(center.x + t, center.y - halfY, center.z - halfZ), new THREE.Vector3(center.x + t, center.y + halfY, center.z - halfZ), new THREE.Vector3(center.x + t, center.y + halfY, center.z + halfZ), new THREE.Vector3(center.x + t, center.y - halfY, center.z + halfZ), new THREE.Vector3(center.x + t, center.y - halfY, center.z - halfZ)]);
|
|
2415
|
+
var line = new THREE.Line(geo, lineMaterial);
|
|
2416
|
+
group.add(line);
|
|
2417
|
+
}
|
|
2418
|
+
return group;
|
|
2419
|
+
}
|
|
2420
|
+
function buildBoundaryLayerHint(size, center, layerCount, layerThickness, targetSize) {
|
|
2421
|
+
var group = new THREE.Group();
|
|
2422
|
+
var layers = Math.max(1, Math.min(layerCount, 20));
|
|
2423
|
+
var baseSpacing = Math.max(targetSize * 0.18, layerThickness * 0.6, 0.01);
|
|
2424
|
+
for (var i = 0; i < layers; i++) {
|
|
2425
|
+
var offset = baseSpacing * (i + 1);
|
|
2426
|
+
var geometry = new THREE.BoxGeometry(size.x + offset * 2, size.y + offset * 2, size.z + offset * 2);
|
|
2427
|
+
var edges = new THREE.EdgesGeometry(geometry);
|
|
2428
|
+
var material = new THREE.LineBasicMaterial({
|
|
2429
|
+
color: i === 0 ? 0x39d0ff : 0x89ecff,
|
|
2430
|
+
transparent: true,
|
|
2431
|
+
opacity: Math.max(0.18, 0.9 - i * 0.08)
|
|
2432
|
+
});
|
|
2433
|
+
var line = new THREE.LineSegments(edges, material);
|
|
2434
|
+
line.position.copy(center);
|
|
2435
|
+
group.add(line);
|
|
2436
|
+
}
|
|
2437
|
+
var shellGeometry = new THREE.BoxGeometry(size.x + baseSpacing * 2, size.y + baseSpacing * 2, size.z + baseSpacing * 2);
|
|
2438
|
+
var shellMaterial = new THREE.MeshBasicMaterial({
|
|
2439
|
+
color: 0x39d0ff,
|
|
2440
|
+
transparent: true,
|
|
2441
|
+
opacity: Math.min(0.12 + layerThickness * 0.6, 0.22),
|
|
2442
|
+
side: THREE.DoubleSide,
|
|
2443
|
+
depthWrite: false
|
|
2444
|
+
});
|
|
2445
|
+
var shellMesh = new THREE.Mesh(shellGeometry, shellMaterial);
|
|
2446
|
+
shellMesh.position.copy(center);
|
|
2447
|
+
group.add(shellMesh);
|
|
2448
|
+
return group;
|
|
2449
|
+
}
|
|
2450
|
+
function buildFeatureEdges(geometry) {
|
|
2451
|
+
var edgesGeometry = new THREE.EdgesGeometry(geometry, 28);
|
|
2452
|
+
var position = edgesGeometry.getAttribute('position');
|
|
2453
|
+
if (!position || position.count === 0) return null;
|
|
2454
|
+
var material = new THREE.LineBasicMaterial({
|
|
2455
|
+
color: 0xffb703,
|
|
2456
|
+
transparent: true,
|
|
2457
|
+
opacity: 0.95
|
|
2458
|
+
});
|
|
2459
|
+
return new THREE.LineSegments(edgesGeometry, material);
|
|
2460
|
+
}
|
|
2461
|
+
function buildBoundaryHighlight(dataset) {
|
|
2462
|
+
if (!dataset.cells.length || !dataset.nodes.length) return null;
|
|
2463
|
+
var positions = [];
|
|
2464
|
+
var picked = 0;
|
|
2465
|
+
dataset.cells.forEach(function (cell) {
|
|
2466
|
+
if ((cell.boundary || '').toLowerCase() === 'surface' || picked < 6) {
|
|
2467
|
+
var faces = getCellFaces(cell);
|
|
2468
|
+
faces.slice(0, 1).forEach(function (face) {
|
|
2469
|
+
triangulateFace(face).forEach(function (triangle) {
|
|
2470
|
+
triangle.forEach(function (nodeIndex) {
|
|
2471
|
+
var node = dataset.nodes[nodeIndex];
|
|
2472
|
+
if (!node) return;
|
|
2473
|
+
positions.push(node.x, node.y, node.z);
|
|
2474
|
+
});
|
|
2475
|
+
});
|
|
2476
|
+
});
|
|
2477
|
+
picked += 1;
|
|
2478
|
+
}
|
|
2479
|
+
});
|
|
2480
|
+
if (!positions.length) return null;
|
|
2481
|
+
var geometry = new THREE.BufferGeometry();
|
|
2482
|
+
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
|
|
2483
|
+
var material = new THREE.MeshBasicMaterial({
|
|
2484
|
+
color: 0xff5d8f,
|
|
2485
|
+
transparent: true,
|
|
2486
|
+
opacity: 0.18,
|
|
2487
|
+
side: THREE.DoubleSide
|
|
2488
|
+
});
|
|
2489
|
+
return new THREE.Mesh(geometry, material);
|
|
2490
|
+
}
|
|
2491
|
+
function buildQualityHotspotOverlay(geometry, qualityMode) {
|
|
2492
|
+
var positionAttr = geometry.getAttribute('position');
|
|
2493
|
+
if (!positionAttr) return null;
|
|
2494
|
+
var positions = [];
|
|
2495
|
+
var triangleCount = Math.floor(positionAttr.count / 3);
|
|
2496
|
+
for (var i = 0; i < triangleCount; i++) {
|
|
2497
|
+
var ratio = i / Math.max(1, triangleCount - 1);
|
|
2498
|
+
var isHot = qualityMode === 'orthogonality' ? ratio > 0.68 : qualityMode === 'skewness' ? ratio > 0.62 : ratio > 0.58;
|
|
2499
|
+
if (!isHot) continue;
|
|
2500
|
+
for (var j = 0; j < 3; j++) {
|
|
2501
|
+
var idx = i * 3 + j;
|
|
2502
|
+
positions.push(positionAttr.getX(idx), positionAttr.getY(idx), positionAttr.getZ(idx));
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
if (!positions.length) return null;
|
|
2506
|
+
var overlayGeometry = new THREE.BufferGeometry();
|
|
2507
|
+
overlayGeometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
|
|
2508
|
+
var overlayMaterial = new THREE.MeshBasicMaterial({
|
|
2509
|
+
color: 0xff4d4f,
|
|
2510
|
+
transparent: true,
|
|
2511
|
+
opacity: 0.22,
|
|
2512
|
+
side: THREE.DoubleSide,
|
|
2513
|
+
depthWrite: false
|
|
2514
|
+
});
|
|
2515
|
+
return new THREE.Mesh(overlayGeometry, overlayMaterial);
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2518
|
+
/** ---------------- 网格拓扑辅助 ---------------- */
|
|
2519
|
+
|
|
2520
|
+
function getCellFaces(cell) {
|
|
2521
|
+
var type = cell.type,
|
|
2522
|
+
indices = cell.indices;
|
|
2523
|
+
if (type === 'tetra' && indices.length >= 4) {
|
|
2524
|
+
return [[indices[0], indices[1], indices[2]], [indices[0], indices[1], indices[3]], [indices[1], indices[2], indices[3]], [indices[0], indices[2], indices[3]]];
|
|
2525
|
+
}
|
|
2526
|
+
if (type === 'hexa' && indices.length >= 8) {
|
|
2527
|
+
return [[indices[0], indices[1], indices[2], indices[3]], [indices[4], indices[5], indices[6], indices[7]], [indices[0], indices[1], indices[5], indices[4]], [indices[1], indices[2], indices[6], indices[5]], [indices[2], indices[3], indices[7], indices[6]], [indices[3], indices[0], indices[4], indices[7]]];
|
|
2528
|
+
}
|
|
2529
|
+
if (type === 'triangle' && indices.length >= 3) {
|
|
2530
|
+
return [[indices[0], indices[1], indices[2]]];
|
|
2531
|
+
}
|
|
2532
|
+
if (type === 'quad' && indices.length >= 4) {
|
|
2533
|
+
return [[indices[0], indices[1], indices[2], indices[3]]];
|
|
2534
|
+
}
|
|
2535
|
+
return [];
|
|
2536
|
+
}
|
|
2537
|
+
function getCellEdges(cell) {
|
|
2538
|
+
var type = cell.type,
|
|
2539
|
+
indices = cell.indices;
|
|
2540
|
+
if (type === 'tetra' && indices.length >= 4) {
|
|
2541
|
+
return [[indices[0], indices[1]], [indices[1], indices[2]], [indices[2], indices[0]], [indices[0], indices[3]], [indices[1], indices[3]], [indices[2], indices[3]]];
|
|
2542
|
+
}
|
|
2543
|
+
if (type === 'hexa' && indices.length >= 8) {
|
|
2544
|
+
return [[indices[0], indices[1]], [indices[1], indices[2]], [indices[2], indices[3]], [indices[3], indices[0]], [indices[4], indices[5]], [indices[5], indices[6]], [indices[6], indices[7]], [indices[7], indices[4]], [indices[0], indices[4]], [indices[1], indices[5]], [indices[2], indices[6]], [indices[3], indices[7]]];
|
|
2545
|
+
}
|
|
2546
|
+
if (type === 'triangle' && indices.length >= 3) {
|
|
2547
|
+
return [[indices[0], indices[1]], [indices[1], indices[2]], [indices[2], indices[0]]];
|
|
2548
|
+
}
|
|
2549
|
+
if (type === 'quad' && indices.length >= 4) {
|
|
2550
|
+
return [[indices[0], indices[1]], [indices[1], indices[2]], [indices[2], indices[3]], [indices[3], indices[0]]];
|
|
2551
|
+
}
|
|
2552
|
+
return [];
|
|
2553
|
+
}
|
|
2554
|
+
function triangulateFace(face) {
|
|
2555
|
+
if (face.length === 3) {
|
|
2556
|
+
return [face];
|
|
2557
|
+
}
|
|
2558
|
+
if (face.length === 4) {
|
|
2559
|
+
return [[face[0], face[1], face[2]], [face[0], face[2], face[3]]];
|
|
2560
|
+
}
|
|
2561
|
+
return [];
|
|
2562
|
+
}
|
|
2563
|
+
|
|
2564
|
+
/** ---------------- 工具 ---------------- */
|
|
2565
|
+
|
|
2566
|
+
function clearGroup(group) {
|
|
2567
|
+
while (group.children.length > 0) {
|
|
2568
|
+
var child = group.children[0];
|
|
2569
|
+
group.remove(child);
|
|
2570
|
+
if (child.geometry) {
|
|
2571
|
+
var _geometry$dispose, _geometry2;
|
|
2572
|
+
(_geometry$dispose = (_geometry2 = child.geometry).dispose) === null || _geometry$dispose === void 0 ? void 0 : _geometry$dispose.call(_geometry2);
|
|
2573
|
+
}
|
|
2574
|
+
if (child.material) {
|
|
2575
|
+
var material = child.material;
|
|
2576
|
+
if (Array.isArray(material)) {
|
|
2577
|
+
material.forEach(function (m) {
|
|
2578
|
+
var _m$dispose;
|
|
2579
|
+
return (_m$dispose = m.dispose) === null || _m$dispose === void 0 ? void 0 : _m$dispose.call(m);
|
|
2580
|
+
});
|
|
2581
|
+
} else {
|
|
2582
|
+
var _material$dispose;
|
|
2583
|
+
(_material$dispose = material.dispose) === null || _material$dispose === void 0 ? void 0 : _material$dispose.call(material);
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
function getFallbackColor(index) {
|
|
2589
|
+
var palette = [0x4ea1ff, 0x59d189, 0xffc857, 0xff6b6b, 0x9b5de5];
|
|
2590
|
+
return new THREE.Color(palette[index % palette.length]);
|
|
2591
|
+
}
|
|
2592
|
+
function getFileNameFromUrl(url) {
|
|
2593
|
+
var parts = url.split('/');
|
|
2594
|
+
return parts[parts.length - 1] || url;
|
|
2595
|
+
}
|
|
2596
|
+
function getDemoFileName(format) {
|
|
2597
|
+
if (format === 'stl') return 'demo-surface.stl';
|
|
2598
|
+
if (format === 'obj') return 'demo-surface.obj';
|
|
2599
|
+
return 'demo-mesh.json';
|
|
2600
|
+
}
|
|
2601
|
+
function getAcceptByFormat(format) {
|
|
2602
|
+
if (format === 'stl') return '.stl';
|
|
2603
|
+
if (format === 'obj') return '.obj';
|
|
2604
|
+
return '.json';
|
|
2605
|
+
}
|
|
2606
|
+
function normalizeCellType(type) {
|
|
2607
|
+
if (type === 'tetra' || type === 'hexa' || type === 'triangle' || type === 'quad') {
|
|
2608
|
+
return type;
|
|
2609
|
+
}
|
|
2610
|
+
return 'hexa';
|
|
2611
|
+
}
|
|
2612
|
+
function clamp(value, min, max) {
|
|
2613
|
+
return Math.max(min, Math.min(max, value));
|
|
2614
|
+
}
|