fl-web-component 2.0.0-beta.8 → 2.0.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.
Files changed (33) hide show
  1. package/dist/fl-web-component.common.js +34645 -1611
  2. package/dist/fl-web-component.common.js.map +1 -1
  3. package/dist/fl-web-component.css +1 -1
  4. package/package.json +4 -15
  5. package/packages/components/com-card/index.vue +1 -1
  6. package/packages/components/com-flcanvas/components/bspline.js +31 -34
  7. package/packages/components/com-flcanvas/components/entityFormatting.js +810 -849
  8. package/packages/components/com-flcanvas/components/round10.js +17 -17
  9. package/packages/components/com-flcanvas/index.vue +314 -333
  10. package/packages/components/com-formDialog/index.vue +6 -4
  11. package/packages/components/com-graphics/component/ann-tool.vue +263 -208
  12. package/packages/components/com-graphics/index.vue +366 -773
  13. package/packages/components/com-graphics/pid.vue +304 -295
  14. package/packages/components/com-table/column-default.vue +2 -3
  15. package/packages/components/com-table/column-dynamic.vue +7 -4
  16. package/packages/components/com-table/column.vue +1 -2
  17. package/packages/components/com-table/index.vue +6 -5
  18. package/packages/components/com-tabs/index.vue +1 -2
  19. package/packages/components/com-tiles/index.vue +134 -136
  20. package/packages/components/com-treeDynamic/index.vue +1 -1
  21. package/packages/utils/StreamLoader.js +1548 -1489
  22. package/packages/utils/StreamLoaderParser.worker.js +9 -14
  23. package/src/main.js +2 -8
  24. package/src/utils/cloud.js +28 -28
  25. package/src/utils/cursor.js +11 -9
  26. package/src/utils/flgltf-parser.js +257 -245
  27. package/src/utils/instance-parser.js +20 -22
  28. package/src/utils/mini-devtool.js +94 -39
  29. package/src/utils/threejs/measure-angle.js +51 -13
  30. package/src/utils/threejs/measure-area.js +43 -12
  31. package/src/utils/threejs/measure-distance.js +43 -12
  32. package/src/utils/threejs/rain-shader.js +10 -10
  33. package/src/utils/threejs/snow-shader.js +9 -9
@@ -11,8 +11,8 @@ export default function installErrorConsolePanel() {
11
11
  function getCircularReplacer() {
12
12
  const seen = new WeakSet();
13
13
  return (key, value) => {
14
- if (typeof value === "object" && value !== null) {
15
- if (seen.has(value)) return "[Circular]";
14
+ if (typeof value === 'object' && value !== null) {
15
+ if (seen.has(value)) return '[Circular]';
16
16
  seen.add(value);
17
17
  }
18
18
  return value;
@@ -20,11 +20,15 @@ export default function installErrorConsolePanel() {
20
20
  }
21
21
  function safeStringify(x) {
22
22
  try {
23
- if (typeof x === "string") return x;
23
+ if (typeof x === 'string') return x;
24
24
  if (x instanceof Error) return x.stack || x.message || String(x);
25
25
  return JSON.stringify(x, getCircularReplacer(), 2);
26
26
  } catch (e) {
27
- try { return String(x); } catch (e2) { return "[unstringifiable]"; }
27
+ try {
28
+ return String(x);
29
+ } catch (e2) {
30
+ return '[unstringifiable]';
31
+ }
28
32
  }
29
33
  }
30
34
 
@@ -52,12 +56,17 @@ export default function installErrorConsolePanel() {
52
56
  display: 'flex',
53
57
  flexDirection: 'column',
54
58
  gap: '6px',
55
- padding: '8px'
59
+ padding: '8px',
56
60
  });
57
61
 
58
62
  // header
59
63
  const header = document.createElement('div');
60
- Object.assign(header.style, { display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: '8px' });
64
+ Object.assign(header.style, {
65
+ display: 'flex',
66
+ justifyContent: 'space-between',
67
+ alignItems: 'center',
68
+ gap: '8px',
69
+ });
61
70
  header.innerHTML = `<strong style="font-size:13px">JS Logs / Errors</strong><span style="opacity:.7;font-size:11px">(非阻塞)</span>`;
62
71
 
63
72
  const btns = document.createElement('div');
@@ -65,14 +74,23 @@ export default function installErrorConsolePanel() {
65
74
  const clearBtn = document.createElement('button');
66
75
  clearBtn.textContent = '清空';
67
76
  Object.assign(clearBtn.style, { marginLeft: '6px', cursor: 'pointer', fontSize: '12px' });
68
- clearBtn.onclick = () => { entriesContainer.innerHTML = ''; entryCount = 0; updateStats(); };
77
+ clearBtn.onclick = () => {
78
+ entriesContainer.innerHTML = '';
79
+ entryCount = 0;
80
+ updateStats();
81
+ };
69
82
 
70
83
  const toggleBtn = document.createElement('button');
71
84
  toggleBtn.textContent = '隐藏';
72
85
  Object.assign(toggleBtn.style, { cursor: 'pointer', fontSize: '12px' });
73
86
  toggleBtn.onclick = () => {
74
- if (entriesContainer.style.display === 'none') { entriesContainer.style.display = ''; toggleBtn.textContent = '隐藏'; }
75
- else { entriesContainer.style.display = 'none'; toggleBtn.textContent = '显示'; }
87
+ if (entriesContainer.style.display === 'none') {
88
+ entriesContainer.style.display = '';
89
+ toggleBtn.textContent = '隐藏';
90
+ } else {
91
+ entriesContainer.style.display = 'none';
92
+ toggleBtn.textContent = '显示';
93
+ }
76
94
  };
77
95
 
78
96
  const statsSpan = document.createElement('span');
@@ -93,7 +111,7 @@ export default function installErrorConsolePanel() {
93
111
  borderRadius: '6px',
94
112
  background: 'rgba(0,0,0,0.32)',
95
113
  maxHeight: 'calc(45vh - 48px)',
96
- boxSizing: 'border-box'
114
+ boxSizing: 'border-box',
97
115
  });
98
116
 
99
117
  panel.appendChild(header);
@@ -112,7 +130,12 @@ export default function installErrorConsolePanel() {
112
130
 
113
131
  // ========== 添加条目 ==========
114
132
  let entryCount = 0;
115
- function addEntry({ type = 'log', message = '', time = new Date().toLocaleString(), meta = null }) {
133
+ function addEntry({
134
+ type = 'log',
135
+ message = '',
136
+ time = new Date().toLocaleString(),
137
+ meta = null,
138
+ }) {
116
139
  const panel = createPanel();
117
140
  const entriesContainer = panel._entriesContainer;
118
141
  const updateStats = panel._statsUpdater;
@@ -144,14 +167,14 @@ export default function installErrorConsolePanel() {
144
167
  error: '#ff6b6b',
145
168
  'window.onerror': '#ff6b6b',
146
169
  resource: '#ff9966',
147
- 'unhandledrejection': '#ff8c66',
170
+ unhandledrejection: '#ff8c66',
148
171
  'console.error': '#ff6b6b',
149
172
  'console.warn': '#ffd966',
150
173
  'console.info': '#66bfff',
151
174
  'console.log': '#bfbfbf',
152
175
  'console.debug': '#a0a0a0',
153
176
  'Vue.error (2)': '#ff6b6b',
154
- 'Vue.error (3)': '#ff6b6b'
177
+ 'Vue.error (3)': '#ff6b6b',
155
178
  };
156
179
  const tagColor = colorMap[type] || '#bfbfbf';
157
180
  left.innerHTML = `<span style="font-weight:700;color:${tagColor}">${type}</span> <span style="opacity:.7;font-size:11px"> ${time}</span>`;
@@ -182,7 +205,7 @@ export default function installErrorConsolePanel() {
182
205
  overflow: 'auto',
183
206
  fontSize: '12px',
184
207
  lineHeight: '1.3',
185
- display: 'block'
208
+ display: 'block',
186
209
  });
187
210
 
188
211
  // 合成文本(message + meta)
@@ -197,14 +220,24 @@ export default function installErrorConsolePanel() {
197
220
 
198
221
  if (txt.length > COLLAPSE_LENGTH) {
199
222
  content.textContent = txt.slice(0, COLLAPSE_LENGTH) + '\n\n...(已折叠,点击展开查看全部)';
200
- expandBtn.onclick = () => { content.textContent = fullText; expandBtn.style.display = 'none'; entriesContainer.scrollTop = entriesContainer.scrollHeight; };
223
+ expandBtn.onclick = () => {
224
+ content.textContent = fullText;
225
+ expandBtn.style.display = 'none';
226
+ entriesContainer.scrollTop = entriesContainer.scrollHeight;
227
+ };
201
228
  } else {
202
229
  content.textContent = txt;
203
230
  expandBtn.style.display = 'none';
204
231
  }
205
232
 
206
233
  copyBtn.onclick = () => {
207
- try { navigator.clipboard.writeText(fullText); } catch (e) { try { prompt('复制文本:Ctrl+C, Enter', fullText); } catch (ee) {} }
234
+ try {
235
+ navigator.clipboard.writeText(fullText);
236
+ } catch (e) {
237
+ try {
238
+ prompt('复制文本:Ctrl+C, Enter', fullText);
239
+ } catch (ee) {}
240
+ }
208
241
  };
209
242
 
210
243
  entry.appendChild(header);
@@ -220,7 +253,12 @@ export default function installErrorConsolePanel() {
220
253
  // 报告包装
221
254
  function report(obj) {
222
255
  try {
223
- addEntry({ type: obj.type || 'log', message: obj.message || safeStringify(obj), time: new Date().toLocaleString(), meta: obj.meta || null });
256
+ addEntry({
257
+ type: obj.type || 'log',
258
+ message: obj.message || safeStringify(obj),
259
+ time: new Date().toLocaleString(),
260
+ meta: obj.meta || null,
261
+ });
224
262
  } catch (e) {
225
263
  console.warn('Error panel report failed', e);
226
264
  }
@@ -229,34 +267,40 @@ export default function installErrorConsolePanel() {
229
267
  // ========== 捕获逻辑 ==========
230
268
 
231
269
  // 传统同步错误
232
- window.onerror = function(message, source, lineno, colno, error) {
233
- const msg = `${message}\n at ${source}:${lineno}:${colno}\n${error && error.stack ? error.stack : ''}`;
270
+ window.onerror = function (message, source, lineno, colno, error) {
271
+ const msg = `${message}\n at ${source}:${lineno}:${colno}\n${
272
+ error && error.stack ? error.stack : ''
273
+ }`;
234
274
  report({ type: 'window.onerror', message: msg });
235
275
  return false;
236
276
  };
237
277
 
238
278
  // 资源加载错误(图片/script/css 等)
239
- window.addEventListener('error', function(event) {
240
- const target = event.target || event.srcElement;
241
- if (target && (target.src || target.href)) {
242
- const tag = target.tagName;
243
- const url = target.src || target.href;
244
- report({ type: 'resource', message: `Failed to load resource: <${tag}> ${url}` });
245
- }
246
- }, true);
279
+ window.addEventListener(
280
+ 'error',
281
+ function (event) {
282
+ const target = event.target || event.srcElement;
283
+ if (target && (target.src || target.href)) {
284
+ const tag = target.tagName;
285
+ const url = target.src || target.href;
286
+ report({ type: 'resource', message: `Failed to load resource: <${tag}> ${url}` });
287
+ }
288
+ },
289
+ true
290
+ );
247
291
 
248
292
  // 未处理的 Promise 拒绝
249
- window.addEventListener('unhandledrejection', function(event) {
293
+ window.addEventListener('unhandledrejection', function (event) {
250
294
  report({ type: 'unhandledrejection', message: safeStringify(event.reason) });
251
295
  });
252
296
 
253
297
  // 捕获 console.*(可选)
254
298
  if (CAPTURE_CONSOLE && typeof console !== 'undefined') {
255
299
  const methods = ['log', 'info', 'warn', 'error', 'debug'];
256
- methods.forEach((m) => {
300
+ methods.forEach(m => {
257
301
  if (!(m in console)) return;
258
302
  const orig = console[m].bind(console);
259
- console[m] = function(...args) {
303
+ console[m] = function (...args) {
260
304
  try {
261
305
  // 生成调用栈(方便定位是谁调用了 console)
262
306
  let stack = null;
@@ -266,19 +310,23 @@ export default function installErrorConsolePanel() {
266
310
  // 移除前两行(Error + 本函数)
267
311
  stack = err.stack.split('\n').slice(2).join('\n');
268
312
  }
269
- } catch (e) { stack = null; }
313
+ } catch (e) {
314
+ stack = null;
315
+ }
270
316
 
271
317
  const payload = {
272
318
  type: `console.${m}`,
273
319
  message: args.map(a => safeStringify(a)).join(' '),
274
- meta: stack
320
+ meta: stack,
275
321
  };
276
322
  report(payload);
277
323
  } catch (e) {
278
324
  // noop
279
325
  }
280
326
  // 保持原有行为
281
- try { orig(...args); } catch (e) {}
327
+ try {
328
+ orig(...args);
329
+ } catch (e) {}
282
330
  };
283
331
  });
284
332
  }
@@ -286,17 +334,25 @@ export default function installErrorConsolePanel() {
286
334
  // Vue 2 支持(若使用全局 Vue)
287
335
  if (typeof Vue !== 'undefined' && Vue && Vue.config) {
288
336
  try {
289
- Vue.config.errorHandler = function(err, vm, info) {
290
- report({ type: 'Vue.error (2)', message: `${info}\n${err && err.stack ? err.stack : safeStringify(err)}` });
337
+ Vue.config.errorHandler = function (err, vm, info) {
338
+ report({
339
+ type: 'Vue.error (2)',
340
+ message: `${info}\n${err && err.stack ? err.stack : safeStringify(err)}`,
341
+ });
291
342
  };
292
- } catch (e) { /* ignore */ }
343
+ } catch (e) {
344
+ /* ignore */
345
+ }
293
346
  }
294
347
 
295
348
  // Vue 3 注册函数(在 main.js 创建 app 后调用)
296
- window.registerVue3ErrorHandler = function(app) {
349
+ window.registerVue3ErrorHandler = function (app) {
297
350
  if (!app || !app.config) return;
298
351
  app.config.errorHandler = (err, instance, info) => {
299
- report({ type: 'Vue.error (3)', message: `${info}\n${err && err.stack ? err.stack : safeStringify(err)}` });
352
+ report({
353
+ type: 'Vue.error (3)',
354
+ message: `${info}\n${err && err.stack ? err.stack : safeStringify(err)}`,
355
+ });
300
356
  };
301
357
  };
302
358
 
@@ -304,6 +360,5 @@ export default function installErrorConsolePanel() {
304
360
  if (document.readyState === 'loading') {
305
361
  document.addEventListener('DOMContentLoaded', createPanel);
306
362
  } else createPanel();
307
-
308
363
  }
309
364
  // ========= End Panel =========
@@ -1,5 +1,6 @@
1
1
  import * as THREE from 'three';
2
2
  import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer';
3
+ import { Message } from 'element-ui';
3
4
  var _this = null;
4
5
  var MeasureAngle = function (renderer, scene, camera, width, height) {
5
6
  this.renderer = renderer;
@@ -56,13 +57,30 @@ MeasureAngle.prototype = {
56
57
  _this.raycaster.setFromCamera(mouse, this.camera);
57
58
  let intersects = _this.raycaster.intersectObjects(_this.scene.children, true);
58
59
  if (intersects.length > 0) {
59
- return intersects[0].point;
60
+ return { point: intersects[0].point, isModel: true };
60
61
  }
62
+
63
+ // 如果没有交点,构建一个基于最后一个确认点且面向相机的平面
64
+ if (_this.pointArray && _this.pointArray.length > 0) {
65
+ const lastPoint =
66
+ _this.pointArray.length === 1
67
+ ? _this.pointArray[0]
68
+ : _this.pointArray[_this.pointArray.length - 2];
69
+ const cameraDir = new THREE.Vector3();
70
+ _this.camera.getWorldDirection(cameraDir);
71
+ const plane = new THREE.Plane().setFromNormalAndCoplanarPoint(cameraDir, lastPoint);
72
+ const target = new THREE.Vector3();
73
+ _this.raycaster.ray.intersectPlane(plane, target);
74
+ if (target) {
75
+ return { point: target, isModel: false };
76
+ }
77
+ }
78
+
61
79
  // 如果没有交点,则创建一个在相机视图平面上的点
62
- const ray = new THREE.Ray();
63
- _this.raycaster.ray.intersectPlane(_this.plane, ray.origin);
64
- if (ray.origin) {
65
- return ray.origin;
80
+ const target = new THREE.Vector3();
81
+ _this.raycaster.ray.intersectPlane(_this.plane, target);
82
+ if (target) {
83
+ return { point: target, isModel: false };
66
84
  }
67
85
  return null;
68
86
  },
@@ -75,9 +93,16 @@ MeasureAngle.prototype = {
75
93
  return sphere;
76
94
  },
77
95
  createLine(p1, p2, config = { color: 0xff0000 }) {
78
- const lineMaterial = new THREE.LineBasicMaterial({ color: config.color, linewidth: 10 });
96
+ const lineMaterial = new THREE.LineBasicMaterial({
97
+ color: config.color,
98
+ linewidth: 10,
99
+ depthTest: false,
100
+ depthWrite: false,
101
+ transparent: true,
102
+ });
79
103
  const lineGeometry = new THREE.BufferGeometry().setFromPoints([p1, p2]);
80
104
  const line = new THREE.Line(lineGeometry, lineMaterial);
105
+ line.renderOrder = 999;
81
106
  line.frustumCulled = false;
82
107
  return line;
83
108
  },
@@ -91,8 +116,9 @@ MeasureAngle.prototype = {
91
116
  },
92
117
  mousemove(e) {
93
118
  if (_this.isCompleted || _this.pointArray.length === 0) return;
94
- const point = _this.getPosition(e);
95
- if (point) {
119
+ const positionResult = _this.getPosition(e);
120
+ if (positionResult) {
121
+ const point = positionResult.point;
96
122
  _this.pointArray.length === 1
97
123
  ? _this.pointArray.push(point)
98
124
  : _this.pointArray.splice(_this.pointArray.length - 1, 1, point);
@@ -160,8 +186,13 @@ MeasureAngle.prototype = {
160
186
  clearTimeout(_this.timer);
161
187
  _this.timer = setTimeout(() => {
162
188
  _this.isCompleted = false;
163
- const point = _this.getPosition(e);
164
- if (point) {
189
+ const positionResult = _this.getPosition(e);
190
+ if (positionResult) {
191
+ const { point, isModel } = positionResult;
192
+ if (!isModel) {
193
+ Message.warning('请点击模型进行测量');
194
+ return;
195
+ }
165
196
  if (_this.tipsLabel) {
166
197
  _this.tipsLabel.position.set(point.x + 0.1, point.y, point.z + 0.05);
167
198
  } else {
@@ -190,8 +221,8 @@ MeasureAngle.prototype = {
190
221
  _this.tipsLabel = undefined;
191
222
  }
192
223
  clearTimeout(_this.timer);
193
- const point = _this.getPosition(e);
194
- if (point) {
224
+ const positionResult = _this.getPosition(e);
225
+ if (positionResult) {
195
226
  _this.isCompleted = true;
196
227
  _this.tempPoints = undefined;
197
228
  _this.tempLine = undefined;
@@ -240,8 +271,15 @@ MeasureAngle.prototype = {
240
271
  },
241
272
  createCurve(points) {
242
273
  const geom = new THREE.BufferGeometry().setFromPoints(points);
243
- const material = new THREE.LineBasicMaterial({ color: 0xff0000 });
274
+ const material = new THREE.LineBasicMaterial({
275
+ color: 0xff0000,
276
+ linewidth: 20,
277
+ depthTest: false,
278
+ depthWrite: false,
279
+ transparent: true,
280
+ });
244
281
  _this.curveLine = new THREE.Line(geom, material);
282
+ _this.curveLine.renderOrder = 999;
245
283
  _this.curveLine.frustumCulled = false;
246
284
  _this.curves.push(_this.curveLine);
247
285
  _this.scene.add(_this.curveLine);
@@ -1,5 +1,6 @@
1
1
  import * as THREE from 'three';
2
2
  import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer';
3
+ import { Message } from 'element-ui';
3
4
  var _this = null;
4
5
  var MeasureArea = function (renderer, scene, camera, width, height) {
5
6
  this.renderer = renderer;
@@ -54,21 +55,44 @@ MeasureArea.prototype = {
54
55
  _this.raycaster.setFromCamera(mouse, _this.camera);
55
56
  let intersects = _this.raycaster.intersectObjects(_this.scene.children, true);
56
57
  if (intersects.length > 0) {
57
- return intersects[0].point;
58
+ return { point: intersects[0].point, isModel: true };
59
+ }
60
+
61
+ // 如果没有交点,构建一个基于最后一个确认点且面向相机的平面
62
+ if (_this.pointArray && _this.pointArray.length > 0) {
63
+ const lastPoint =
64
+ _this.pointArray.length === 1
65
+ ? _this.pointArray[0]
66
+ : _this.pointArray[_this.pointArray.length - 2];
67
+ const cameraDir = new THREE.Vector3();
68
+ _this.camera.getWorldDirection(cameraDir);
69
+ const plane = new THREE.Plane().setFromNormalAndCoplanarPoint(cameraDir, lastPoint);
70
+ const target = new THREE.Vector3();
71
+ _this.raycaster.ray.intersectPlane(plane, target);
72
+ if (target) {
73
+ return { point: target, isModel: false };
74
+ }
58
75
  }
59
76
 
60
77
  // 如果没有交点,则创建一个在相机视图平面上的点
61
- const ray = new THREE.Ray();
62
- _this.raycaster.ray.intersectPlane(_this.plane, ray.origin);
63
- if (ray.origin) {
64
- return ray.origin;
78
+ const target = new THREE.Vector3();
79
+ _this.raycaster.ray.intersectPlane(_this.plane, target);
80
+ if (target) {
81
+ return { point: target, isModel: false };
65
82
  }
66
83
  return null;
67
84
  },
68
85
  createLine(p1, p2, config = { color: 0xff0000 }) {
69
- const lineMaterial = new THREE.LineBasicMaterial({ color: config.color, linewidth: 20 });
86
+ const lineMaterial = new THREE.LineBasicMaterial({
87
+ color: config.color,
88
+ linewidth: 20,
89
+ depthTest: false,
90
+ depthWrite: false,
91
+ transparent: true,
92
+ });
70
93
  const lineGeometry = new THREE.BufferGeometry().setFromPoints([p1, p2]);
71
94
  const line = new THREE.Line(lineGeometry, lineMaterial);
95
+ line.renderOrder = 999;
72
96
  line.frustumCulled = false;
73
97
  return line;
74
98
  },
@@ -82,8 +106,9 @@ MeasureArea.prototype = {
82
106
  },
83
107
  mousemove(e) {
84
108
  if (_this.isCompleted || _this.pointArray.length === 0) return;
85
- const point = _this.getPosition(e);
86
- if (point) {
109
+ const positionResult = _this.getPosition(e);
110
+ if (positionResult) {
111
+ const point = positionResult.point;
87
112
  _this.pointArray.length === 1
88
113
  ? _this.pointArray.push(point)
89
114
  : _this.pointArray.splice(_this.pointArray.length - 1, 1, point);
@@ -153,8 +178,13 @@ MeasureArea.prototype = {
153
178
  clearTimeout(_this.timer);
154
179
  _this.timer = setTimeout(() => {
155
180
  _this.isCompleted = false;
156
- const point = _this.getPosition(e);
157
- if (point) {
181
+ const positionResult = _this.getPosition(e);
182
+ if (positionResult) {
183
+ const { point, isModel } = positionResult;
184
+ if (!isModel) {
185
+ Message.warning('请点击模型进行测量');
186
+ return;
187
+ }
158
188
  if (_this.tipsLabel) {
159
189
  _this.tipsLabel.position.set(point.x + 0.01, point.y, point.z + 0.05);
160
190
  } else {
@@ -182,8 +212,9 @@ MeasureArea.prototype = {
182
212
  _this.tipsLabel = undefined;
183
213
  }
184
214
  clearTimeout(_this.timer);
185
- const point = _this.getPosition(e);
186
- if (point) {
215
+ const positionResult = _this.getPosition(e);
216
+ if (positionResult) {
217
+ const point = positionResult.point;
187
218
  _this.isCompleted = true;
188
219
  if (_this.tempPoints) {
189
220
  _this.tempPoints.position.set(point.x, point.y, point.z);
@@ -1,5 +1,6 @@
1
1
  import * as THREE from 'three';
2
2
  import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer';
3
+ import { Message } from 'element-ui';
3
4
  var _this = null;
4
5
  var MeasureDistance = function (renderer, scene, camera, width, height) {
5
6
  this.renderer = renderer;
@@ -52,22 +53,45 @@ MeasureDistance.prototype = {
52
53
  _this.raycaster.setFromCamera(mouse, this.camera);
53
54
  let intersects = _this.raycaster.intersectObjects(_this.scene.children, true);
54
55
  if (intersects.length > 0) {
55
- return intersects[0].point;
56
+ return { point: intersects[0].point, isModel: true };
57
+ }
58
+
59
+ // 如果没有交点,构建一个基于最后一个确认点且面向相机的平面
60
+ if (_this.pointArray && _this.pointArray.length > 0) {
61
+ const lastPoint =
62
+ _this.pointArray.length === 1
63
+ ? _this.pointArray[0]
64
+ : _this.pointArray[_this.pointArray.length - 2];
65
+ const cameraDir = new THREE.Vector3();
66
+ _this.camera.getWorldDirection(cameraDir);
67
+ const plane = new THREE.Plane().setFromNormalAndCoplanarPoint(cameraDir, lastPoint);
68
+ const target = new THREE.Vector3();
69
+ _this.raycaster.ray.intersectPlane(plane, target);
70
+ if (target) {
71
+ return { point: target, isModel: false };
72
+ }
56
73
  }
57
74
 
58
75
  // 如果没有交点,则创建一个在相机视图平面上的点
59
- const ray = new THREE.Ray();
60
- _this.raycaster.ray.intersectPlane(_this.plane, ray.origin);
61
- if (ray.origin) {
62
- return ray.origin;
76
+ const target = new THREE.Vector3();
77
+ _this.raycaster.ray.intersectPlane(_this.plane, target);
78
+ if (target) {
79
+ return { point: target, isModel: false };
63
80
  }
64
81
 
65
82
  return null;
66
83
  },
67
84
  createLine(p1, p2, config = { color: 0xff0000 }) {
68
- const lineMaterial = new THREE.LineBasicMaterial({ color: config.color, linewidth: 15 });
85
+ const lineMaterial = new THREE.LineBasicMaterial({
86
+ color: config.color,
87
+ linewidth: 15,
88
+ depthTest: false,
89
+ depthWrite: false,
90
+ transparent: true,
91
+ });
69
92
  const lineGeometry = new THREE.BufferGeometry().setFromPoints([p1, p2]);
70
93
  const line = new THREE.Line(lineGeometry, lineMaterial);
94
+ line.renderOrder = 999;
71
95
  line.frustumCulled = false;
72
96
  return line;
73
97
  },
@@ -81,8 +105,9 @@ MeasureDistance.prototype = {
81
105
  },
82
106
  mousemove(e) {
83
107
  if (_this.isCompleted || _this.pointArray.length === 0) return;
84
- const point = _this.getPosition(e);
85
- if (point) {
108
+ const positionResult = _this.getPosition(e);
109
+ if (positionResult) {
110
+ const point = positionResult.point;
86
111
  _this.pointArray.length === 1
87
112
  ? _this.pointArray.push(point)
88
113
  : _this.pointArray.splice(_this.pointArray.length - 1, 1, point);
@@ -137,8 +162,14 @@ MeasureDistance.prototype = {
137
162
  clearTimeout(_this.timer);
138
163
  _this.timer = setTimeout(() => {
139
164
  _this.isCompleted = false;
140
- const point = _this.getPosition(e);
141
- if (point) {
165
+ const positionResult = _this.getPosition(e);
166
+ if (positionResult) {
167
+ const { point, isModel } = positionResult;
168
+ if (!isModel) {
169
+ // 提示请点击模型
170
+ Message.warning('请点击模型进行测量');
171
+ return;
172
+ }
142
173
  if (_this.tipsLabel) {
143
174
  _this.tipsLabel.position.set(point.x + 0.1, point.y, point.z + 0.05);
144
175
  } else {
@@ -167,8 +198,8 @@ MeasureDistance.prototype = {
167
198
  _this.tipsLabel = undefined;
168
199
  }
169
200
  clearTimeout(_this.timer);
170
- const point = _this.getPosition(e);
171
- if (point) {
201
+ const positionResult = _this.getPosition(e);
202
+ if (positionResult) {
172
203
  _this.isCompleted = true;
173
204
  _this.tempPoints = undefined;
174
205
  _this.tempLine = undefined;
@@ -1,12 +1,12 @@
1
- import * as THREE from 'three'
1
+ import * as THREE from 'three';
2
2
  const RainShader = {
3
3
  uniforms: {
4
- 'tDiffuse': {value: null},
5
- 'iResolution': {value: null},
6
- 'iTime': {value: 0},
7
- 'radian': {value: 0 / 360 * Math.PI},
8
- 'rainSpeed': {value: 0.2},
9
- 'rainColor': {value: new THREE.Color(1, 1, 1)}
4
+ tDiffuse: { value: null },
5
+ iResolution: { value: null },
6
+ iTime: { value: 0 },
7
+ radian: { value: (0 / 360) * Math.PI },
8
+ rainSpeed: { value: 0.2 },
9
+ rainColor: { value: new THREE.Color(1, 1, 1) },
10
10
  },
11
11
  vertexShader: `
12
12
  varying vec2 vUv;
@@ -52,7 +52,7 @@ const RainShader = {
52
52
  gl_FragColor = vec4(col,0.0);
53
53
  }
54
54
  }
55
- `
56
- }
55
+ `,
56
+ };
57
57
 
58
- export { RainShader }
58
+ export { RainShader };
@@ -1,11 +1,11 @@
1
1
  const SnowShader = {
2
2
  uniforms: {
3
- 'tDiffuse': {value: null},
4
- 'iResolution': {value: null},
5
- 'iTime': {value: 0.2},
6
- 'size': {value: 0.05},
7
- 'density': {value: 1.0},
8
- 'snowSpeed': {value: 0.6}
3
+ tDiffuse: { value: null },
4
+ iResolution: { value: null },
5
+ iTime: { value: 0.2 },
6
+ size: { value: 0.05 },
7
+ density: { value: 1.0 },
8
+ snowSpeed: { value: 0.6 },
9
9
  },
10
10
  vertexShader: `
11
11
  varying vec2 vUv;
@@ -70,6 +70,6 @@ const SnowShader = {
70
70
  gl_FragColor = vec4(colSnow,0.0);
71
71
  }
72
72
  }
73
- `
74
- }
75
- export { SnowShader }
73
+ `,
74
+ };
75
+ export { SnowShader };