fl-web-component 0.1.0 → 1.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 (85) hide show
  1. package/README.md +35 -24
  2. package/dist/fl-web-component.common.js +27308 -65
  3. package/dist/fl-web-component.common.js.map +1 -1
  4. package/dist/fl-web-component.css +1 -1
  5. package/dist/fl-web-component.umd.js +27308 -65
  6. package/dist/fl-web-component.umd.js.map +1 -1
  7. package/dist/fl-web-component.umd.min.js +13 -1
  8. package/dist/fl-web-component.umd.min.js.map +1 -1
  9. package/package.json +80 -47
  10. package/packages/components/button/index.vue +16 -12
  11. package/packages/components/com-card/card-page.vue +102 -0
  12. package/packages/components/com-card/index.vue +53 -0
  13. package/packages/components/com-dialogWrapper/Readme.md +53 -0
  14. package/packages/components/com-dialogWrapper/index.vue +98 -0
  15. package/packages/components/com-flcanvas/components/bspline.js +91 -0
  16. package/packages/components/com-flcanvas/components/entityFormatting.js +503 -0
  17. package/packages/components/com-flcanvas/components/round10.js +24 -0
  18. package/packages/components/com-flcanvas/index.vue +259 -0
  19. package/packages/components/com-formDialog/Readme.md +409 -0
  20. package/packages/components/com-formDialog/index.vue +471 -0
  21. package/packages/components/com-graphics/index.vue +1073 -0
  22. package/packages/components/com-graphics/per-control.vue +109 -0
  23. package/packages/components/com-graphics/pid.vue +168 -0
  24. package/packages/components/com-page/index.vue +101 -0
  25. package/packages/components/com-selectTree/Readme.md +17 -0
  26. package/packages/components/com-selectTree/index.vue +236 -0
  27. package/packages/components/com-table/column-default.vue +71 -0
  28. package/packages/components/com-table/column-dynamic.vue +36 -0
  29. package/packages/components/com-table/column-menu.vue +71 -0
  30. package/packages/components/com-table/column-slot.vue +53 -0
  31. package/packages/components/com-table/column.vue +41 -0
  32. package/packages/components/com-table/config.js +21 -0
  33. package/packages/components/com-table/index.vue +281 -0
  34. package/packages/components/com-table/table-page.vue +106 -0
  35. package/packages/components/com-tabs/index.vue +50 -0
  36. package/packages/components/com-treeDynamic/Readme.md +271 -0
  37. package/packages/components/com-treeDynamic/index.vue +207 -0
  38. package/packages/components/model/api/index.js +59 -67
  39. package/packages/components/model/api/mock/detecttree.js +38 -38
  40. package/packages/components/model/api/mock/getmodel-line.js +15830 -79332
  41. package/packages/components/model/api/mock/init.js +1 -1
  42. package/packages/components/model/api/mock/pbstree.js +486 -495
  43. package/packages/components/model/components/TextOverTooltip/index.vue +3 -3
  44. package/packages/components/model/components/annotation-toolbar.vue +4 -19
  45. package/packages/components/model/components/check-proofing-model.vue +26 -29
  46. package/packages/components/model/components/clipping-type.vue +22 -14
  47. package/packages/components/model/components/com-dialogWrapper/index.vue +22 -25
  48. package/packages/components/model/components/detect-panel.vue +38 -26
  49. package/packages/components/model/components/detect-tree.vue +9 -24
  50. package/packages/components/model/components/firstPer-panel.vue +23 -25
  51. package/packages/components/model/components/header-button.vue +31 -107
  52. package/packages/components/model/components/imageViewer/index.vue +34 -35
  53. package/packages/components/model/components/import-model.vue +127 -127
  54. package/packages/components/model/components/location-panel.vue +25 -29
  55. package/packages/components/model/components/measure-type.vue +15 -15
  56. package/packages/components/model/components/pbs-tree.vue +139 -144
  57. package/packages/components/model/components/proof-config.vue +2 -10
  58. package/packages/components/model/components/proof-for-pc.vue +35 -32
  59. package/packages/components/model/components/proof-history.vue +136 -154
  60. package/packages/components/model/components/proof-panel-detail.vue +166 -165
  61. package/packages/components/model/components/proof-panel.vue +281 -205
  62. package/packages/components/model/components/proof-project-user.vue +13 -50
  63. package/packages/components/model/components/proof-publish.vue +130 -130
  64. package/packages/components/model/components/proof-role.vue +93 -124
  65. package/packages/components/model/components/props-panel.vue +63 -54
  66. package/packages/components/model/index.vue +3225 -3213
  67. package/packages/components/model/utils/annotation-tool.js +75 -82
  68. package/packages/components/model/utils/cursor.js +15 -10
  69. package/packages/components/model/utils/detect-v1.js +23 -35
  70. package/packages/components/model/utils/index.js +25 -25
  71. package/packages/components/model/utils/threejs/measure-angle.js +180 -180
  72. package/packages/components/model/utils/threejs/measure-area.js +196 -184
  73. package/packages/components/model/utils/threejs/measure-distance.js +154 -152
  74. package/packages/components/model/utils/threejs/measure-volume.js +64 -61
  75. package/patches/camera-controls+2.9.0.patch +63 -0
  76. package/src/assets/test.png +0 -0
  77. package/src/assets/worker.glb +0 -0
  78. package/src/main.js +27 -0
  79. package/src/utils/flgltf-parser.js +141 -0
  80. package/src/utils/instance-parser.js +402 -0
  81. package/src/utils/mock.js +84746 -0
  82. package/src/utils/threejs/measure-angle.js +240 -0
  83. package/src/utils/threejs/measure-area.js +249 -0
  84. package/src/utils/threejs/measure-distance.js +195 -0
  85. package/packages/index.js +0 -24
@@ -0,0 +1,1073 @@
1
+ <template>
2
+ <div id="instructions"></div>
3
+ </template>
4
+ <script>
5
+ var [
6
+ renderer,
7
+ scene,
8
+ camera,
9
+ cameraControls,
10
+ instructions,
11
+ raycaster,
12
+ mouse,
13
+ labelRenderer,
14
+ lineTexture,
15
+ curve,
16
+ downRaycaster,
17
+ velocity,
18
+ direction,
19
+ clock,
20
+ pointControls,
21
+ threeMeasure,
22
+ modelGroup,
23
+ ] = (function* (v) {
24
+ while (true) yield v;
25
+ })(null);
26
+
27
+ var [lastTime, firstTime, fpsClock, timeStamp, progress] = (function* (v) {
28
+ while (true) yield v;
29
+ })(0);
30
+
31
+ var [
32
+ roaming,
33
+ firstPerSign,
34
+ canJump,
35
+ moveForward,
36
+ moveBackward,
37
+ moveLeft,
38
+ moveRight,
39
+ measureFlag,
40
+ ] = (function* (v) {
41
+ while (true) yield v;
42
+ })(false);
43
+
44
+ var [spaceUp] = (function* (v) {
45
+ while (true) yield v;
46
+ })(true);
47
+
48
+ var clippingPlanes = [];
49
+ var removeSpeed = 200,
50
+ upSpeed = 200; //控制器移动速度 , //控制跳起时的速度
51
+ var modelGroup;
52
+
53
+ // 绘制对象映射实例表
54
+ let drawObjMapInstance = {};
55
+
56
+ import CameraControls from 'camera-controls';
57
+ import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
58
+ import { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment.js';
59
+ import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer';
60
+ import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer';
61
+ import { PointerLockControls } from 'three/examples/jsm/controls/PointerLockControls.js';
62
+ import MeasureDistance from '@/utils/threejs/measure-distance.js';
63
+ import MeasureArea from '@/utils/threejs/measure-area.js';
64
+ import MeasureAngle from '@/utils/threejs/measure-angle.js';
65
+ // import glb from '@assets/worker.glb';
66
+ import { parseData } from '@/utils/flgltf-parser';
67
+ import { handleInstancedMeshModel } from '@/utils/instance-parser';
68
+
69
+ export default {
70
+ name: 'FlModel',
71
+ props: {
72
+ data: {
73
+ type: Object,
74
+ default() {
75
+ return {};
76
+ },
77
+ },
78
+ },
79
+ data() {
80
+ return {
81
+ roamConfig: {
82
+ loop: false,
83
+ speed: 0, // 最大值为3
84
+ },
85
+ };
86
+ },
87
+ created() {
88
+ CameraControls.install({ THREE: this.THREE });
89
+ fpsClock = new this.THREE.Clock();
90
+ raycaster = new this.THREE.Raycaster();
91
+ mouse = new this.THREE.Vector2();
92
+ },
93
+ mounted() {
94
+ instructions = document.getElementById('instructions');
95
+ this.initRender();
96
+ this.initScene();
97
+ this.initCamera();
98
+ this.initControl();
99
+ this.initLight();
100
+ this.initLabelRender();
101
+ renderer.domElement.addEventListener('mouseup', this.mouseClick, false);
102
+ renderer.domElement.addEventListener('mousedown', this.mouseDown, false);
103
+ this.animate();
104
+ },
105
+ methods: {
106
+ initRender() {
107
+ renderer = new this.THREE.WebGLRenderer({
108
+ antialias: true,
109
+ alpha: true,
110
+ logarithmicDepthBuffer: true,
111
+ });
112
+ renderer.setPixelRatio(window.devicePixelRatio * 2);
113
+ renderer.setSize(window.innerWidth, window.innerHeight);
114
+ renderer.domElement.id = 'three-model';
115
+ renderer.shadowMap.enabled = true;
116
+ // 暂时注释这句 还有worker里面的 这跟渲染出的模型浅或暗有关系
117
+ renderer.outputEncoding = this.THREE.sRGBEncoding;
118
+ instructions.appendChild(renderer.domElement);
119
+ renderer.autoClear = false;
120
+ renderer.autoClearColor = false;
121
+ renderer.autoClearDepth = false;
122
+ renderer.autoClearStencil = false;
123
+ },
124
+ initScene() {
125
+ modelGroup = new this.THREE.Group();
126
+ scene = new this.THREE.Scene();
127
+ // scene.userData.recordEntity = []
128
+
129
+ // 适配客户端的坐标系,threejs坐标需绕x轴旋转90度
130
+ const mat4 = new this.THREE.Matrix4();
131
+ mat4.makeRotationX(-Math.PI / 2);
132
+ scene.applyMatrix4(mat4);
133
+ },
134
+ initCamera() {
135
+ camera = new this.THREE.PerspectiveCamera(
136
+ 45,
137
+ window.innerWidth / window.innerHeight,
138
+ 0.1,
139
+ 10000
140
+ );
141
+ camera.position.set(0, 100, 150);
142
+ },
143
+ initControl() {
144
+ // 初始化控件
145
+ cameraControls = new CameraControls(camera, renderer.domElement);
146
+ cameraControls.dollyToCursor = true;
147
+ cameraControls.smoothTime = 0.1;
148
+ cameraControls.draggingSmoothTime = 0.05;
149
+ cameraControls.truckSpeed = 2.0;
150
+ cameraControls.infinityDolly = true;
151
+ cameraControls.minDistance = 4;
152
+ },
153
+ // 初始化光源
154
+ initLight() {
155
+ const pmremGenerator = new this.THREE.PMREMGenerator(renderer);
156
+ scene.environment = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture;
157
+ },
158
+ // 初始化文字画布
159
+ initLabelRender() {
160
+ labelRenderer = new CSS2DRenderer();
161
+ labelRenderer.setSize(instructions.offsetWidth, instructions.offsetHeight);
162
+ labelRenderer.domElement.style.position = 'absolute';
163
+ labelRenderer.domElement.style.top = '0px';
164
+ labelRenderer.domElement.style.pointerEvents = 'none';
165
+ instructions.appendChild(labelRenderer.domElement);
166
+ },
167
+ // 根据模型数据绘制模型实体 业务平台可调用此方法加载模型
168
+ /*
169
+ 参数:data 模型数据
170
+ color: '' 初始化模型的颜色 在业务方 有这个需求
171
+ */
172
+ drawModel(data, color = '') {
173
+ if (Object.keys(data).length === 0) {
174
+ return;
175
+ }
176
+ const { instances, drawObjs } = parseData(data);
177
+ if (instances.length > 0) {
178
+ modelGroup = handleInstancedMeshModel(instances, drawObjs, '', scene, color);
179
+ scene.add(modelGroup);
180
+ // this.compileShader();
181
+ cameraControls.fitToSphere(modelGroup, true);
182
+ cameraControls.saveState();
183
+ }
184
+ },
185
+ compileShader() {
186
+ scene.traverse(child => {
187
+ if (child.isMesh) {
188
+ child.material.onBeforeCompile = shader => {
189
+ const vertexShader = `
190
+ precision lowp float;
191
+ attribute float visible;
192
+ varying float vVisible;
193
+ void main() {
194
+ vVisible = visible;
195
+ `;
196
+ const fragmentShader = `
197
+ precision lowp float;
198
+ varying float vVisible;
199
+ void main() {
200
+ `;
201
+ const fragColor = `
202
+ #include <dithering_fragment>
203
+ if (vVisible > 0.0) {
204
+ gl_FragColor = vec4(outgoingLight, diffuseColor.a);
205
+ } else {
206
+ discard;
207
+ }
208
+ `;
209
+ shader.vertexShader = shader.vertexShader.replace('void main() {', vertexShader);
210
+ shader.fragmentShader = shader.fragmentShader.replace(
211
+ 'void main() {',
212
+ fragmentShader
213
+ );
214
+ shader.fragmentShader = shader.fragmentShader.replace(
215
+ '#include <dithering_fragment>',
216
+ fragColor
217
+ );
218
+ child.material.transparent = true;
219
+ };
220
+ }
221
+ });
222
+ },
223
+ mouseDown() {
224
+ firstTime = new Date().getTime();
225
+ },
226
+ mouseClick(event) {
227
+ // 在测量模式下,不进行事件暴露
228
+ if (!measureFlag) {
229
+ lastTime = new Date().getTime();
230
+ if (lastTime - firstTime < 300) {
231
+ mouse.x = (event.clientX / instructions.offsetWidth) * 2 - 1;
232
+ mouse.y = -(event.clientY / instructions.offsetHeight) * 2 + 1;
233
+ raycaster.setFromCamera(mouse, camera);
234
+ const intersects = raycaster.intersectObjects(scene.children, true);
235
+ let params = {}
236
+ let cameraData = {
237
+ position: {
238
+ x: cameraControls.camera.position.x,
239
+ y: cameraControls.camera.position.y,
240
+ z: cameraControls.camera.position.z,
241
+ },
242
+ lookAt: {
243
+ heading: cameraControls._target.x,
244
+ pitch: cameraControls._target.y,
245
+ roll: cameraControls._target.z,
246
+ },
247
+ };
248
+ if (intersects.length > 0) {
249
+ params = {
250
+ objects: [intersects[0].object],
251
+ mousePosition: { x: event.clientX, y: event.clientY },
252
+ camera: cameraData,
253
+ v3Position: {
254
+ x: intersects[0].point.x,
255
+ y: intersects[0].point.y,
256
+ z: intersects[0].point.z,
257
+ }
258
+ }
259
+ } else {
260
+ params = {
261
+ objects: [],
262
+ mousePosition: { x: event.clientX, y: event.clientY },
263
+ camera: cameraData,
264
+ v3Position: {
265
+ x: -1,
266
+ y: -1,
267
+ z: -1,
268
+ }
269
+ }
270
+ }
271
+ if (event.button === 0) {
272
+ this.$emit('leftClick', params);
273
+ } else if (event.button === 2) {
274
+ this.$emit('rightClick', params);
275
+ }
276
+ }
277
+ }
278
+ },
279
+ // 窗口发生改变时, 更新渲染器与相机的大小
280
+ resize(width, height) {
281
+ if (camera) {
282
+ camera.aspect = width / height;
283
+ camera.updateProjectionMatrix();
284
+ renderer.setSize(width, height, true);
285
+ labelRenderer.setSize(width, height);
286
+ // this.timeRender()
287
+ // 这里也要更新测量
288
+ if (threeMeasure) {
289
+ threeMeasure.updateParams(width, height);
290
+ }
291
+ }
292
+ },
293
+ setModelCenter() {
294
+ // const box3 = new this.THREE.Box3();
295
+ // box3.setFromObject(modelGroup);
296
+ // const center = new this.THREE.Vector3();
297
+ // box3.getCenter(center);
298
+ // const size = box3.getSize(new this.THREE.Vector3());
299
+ // camera.position.set(center.x, center.y + size.y, center.z + size.z / 2);
300
+ // camera.lookAt(new this.THREE.Vector3(center.x, center.y, center.z));
301
+ // cameraControls.setLookAt(
302
+ // center.x,
303
+ // center.y + size.y,
304
+ // center.z + size.z / 2,
305
+ // center.x,
306
+ // center.y,
307
+ // center.z,
308
+ // true
309
+ // );
310
+ // cameraControls.update();
311
+ // camera.updateProjectionMatrix();
312
+ // cameraControls.saveState();
313
+ },
314
+ // 修改指定模型实体属性
315
+ /**
316
+ * 参数内容{
317
+ * list: [{
318
+ * name: '', // 实体的name值'
319
+ * attr: {
320
+ * 'color/visible/opacity': '颜色值/(true / false)/(0-1)'
321
+ * }, 需要修改属性的列表
322
+ * }] // 需要更改实体属性的数据
323
+ * It can be a CSS-style string. For example:
324
+ * 'rgb(250, 0,0)'
325
+ * 'rgb(100%,0%,0%)'
326
+ * 'hsl(0, 100%, 50%)'
327
+ * '#ff0000'
328
+ * '#f00'
329
+ * 'red'
330
+ * }
331
+ */
332
+ updateProperty(list) {
333
+ for (let index = 0; index < list.length; index++) {
334
+ let ele = list[index];
335
+ let targetObj = this.getObjectByName(ele.name);
336
+ for (const key in ele.attr) {
337
+ switch (key) {
338
+ case 'color':
339
+ targetObj.forEach(children => {
340
+ if (children.isMesh) {
341
+ children.setColorAt(
342
+ children.userData.instanceIndex,
343
+ new this.THREE.Color(ele.attr[key])
344
+ );
345
+ children.instanceColor.needsUpdate = true;
346
+ }
347
+ });
348
+ break;
349
+ case 'visible':
350
+ targetObj.forEach(children => {
351
+ const index = children.userData.instanceIndex;
352
+ children.geometry.attributes.visible.array[index] = ele.attr[key] ? 1.0 : 0.0;
353
+ children.geometry.attributes.visible.needsUpdate = true;
354
+ });
355
+ break;
356
+ case 'opacity':
357
+ obj.material.opacity = ele.attr[key];
358
+ obj.material.transparent = true;
359
+ break;
360
+ }
361
+ }
362
+ // obj.material.needsUpdate = true;
363
+ }
364
+ },
365
+ // 修改整体模型实体的属性
366
+ /*
367
+ {
368
+ attr: '', 需要修改属性名(color、visible(true / false)、opacity(0-1),
369
+ value: , 需要修改成的值
370
+ }
371
+ */
372
+ updateWholeProperty(params) {
373
+ if (scene) {
374
+ scene.traverse(obj => {
375
+ if (obj instanceof this.THREE.Mesh) {
376
+ switch (params.attr) {
377
+ case 'color':
378
+ obj.material.color = new this.THREE.Color(params.value);
379
+ break;
380
+ case 'visible':
381
+ obj.material.visible = params.value;
382
+ break;
383
+ case 'opacity':
384
+ obj.material.opacity = params.value;
385
+ obj.material.transparent = true;
386
+ break;
387
+ }
388
+ obj.material.needsUpdate = true;
389
+ }
390
+ });
391
+ }
392
+ },
393
+ // 清除上一次的属性修改操作 为了方便业务平台参数跟updateProperty方法的参数一样
394
+ resetProperty(list) {
395
+ for (let index = 0; index < list.length; index++) {
396
+ let ele = list[index];
397
+ let obj = this.getObjectByName(ele.name);
398
+ if (obj) {
399
+ for (const key in ele.attr) {
400
+ switch (key) {
401
+ case 'color':
402
+ obj.forEach(children => {
403
+ if (children.isMesh) {
404
+ children.setColorAt(
405
+ children.userData.instanceIndex,
406
+ children.material.userData.nColor
407
+ );
408
+ children.instanceColor.needsUpdate = true;
409
+ }
410
+ });
411
+ break;
412
+ }
413
+ }
414
+ }
415
+ }
416
+ },
417
+ // 定位到模型
418
+ locateModel(id) {
419
+ let obj = scene.getObjectByName(id);
420
+ if (obj) {
421
+ cameraControls.fitToSphere(obj.parent, true);
422
+ }
423
+ },
424
+ // 相机定位
425
+ /*
426
+ 定位参数:x: 相机的x位置, y: 相机的y位置, z: 相机的z位置
427
+ heading, pitch, roll
428
+ 视点定位可直接使用此方法
429
+ */
430
+ cameraLocation(params) {
431
+ cameraControls.setLookAt(
432
+ params.x,
433
+ params.y,
434
+ params.z,
435
+ params.heading,
436
+ params.pitch,
437
+ params.roll,
438
+ true
439
+ );
440
+ cameraControls.update(0);
441
+ },
442
+ // 添加广告牌
443
+ /*
444
+ 参数:
445
+ {
446
+ billboard: DOM节点
447
+ name: 2d广告牌的名字,用来做清除用的
448
+ x、y、z为广告牌的位置
449
+ }
450
+ */
451
+ billboard(data) {
452
+ const divLabel = new CSS2DObject(data.billboard);
453
+ divLabel.name = data.labelClass; // 这个是用来清除广告牌用的
454
+ divLabel.position.set(data.x, data.y, data.z);
455
+ scene.add(divLabel);
456
+ return divLabel;
457
+ },
458
+ // 通过名字获取实体对象, 返回数组
459
+ getObjectByName(name) {
460
+ let object = [];
461
+ scene.traverse(item => {
462
+ if (item.name === name) {
463
+ object.push(item);
464
+ }
465
+ });
466
+ return object;
467
+ },
468
+ // 通过id获取实体对象, 返回查找到的对象
469
+ getObjectById(id) {
470
+ return scene.getObjectById(id);
471
+ },
472
+ // 通过id删除对象
473
+ removeObjectById(id) {
474
+ let array = this.getObjectByName(id);
475
+ array.forEach(item => {
476
+ if (item.name === id) {
477
+ item.material && item.material.dispose();
478
+ item.geometry && item.geometry.dispose();
479
+ if (item.isMesh) {
480
+ item.clear();
481
+ }
482
+ scene.remove(item);
483
+ }
484
+ });
485
+ },
486
+ // 通过名称删除对象
487
+ removeObjectByName(name) {
488
+ let array = this.getObjectByName(name);
489
+ array.forEach(item => {
490
+ if (item.name === name) {
491
+ item.material && item.material.dispose();
492
+ item.geometry && item.geometry.dispose();
493
+ if (item.isMesh) {
494
+ item.clear();
495
+ }
496
+ scene.remove(item);
497
+ }
498
+ });
499
+ },
500
+ // 绘制曲线
501
+ /*
502
+ 参数声明:Object
503
+ {
504
+ path: [{x:0, y: 0, z: 0}], 坐标点信息
505
+ color: '', // 线条的颜色
506
+ name: '', // 线条的名字, 用来清除时使用的
507
+ }
508
+ */
509
+ drawCurve(params) {
510
+ this.removeObjectByName(params.name);
511
+ const route = [];
512
+ params.path.forEach(element => {
513
+ route.push(new this.THREE.Vector3(element.x, element.y, element.z));
514
+ });
515
+ if (route.length > 1) {
516
+ curve = new this.THREE.CatmullRomCurve3(route);
517
+ const geometryLine = new this.THREE.BufferGeometry().setFromPoints(curve.getPoints(5000));
518
+ const materialLine = new this.THREE.LineBasicMaterial({
519
+ color: params.color,
520
+ depthTest: false,
521
+ transparent: true,
522
+ });
523
+ let line = new this.THREE.Line(geometryLine, materialLine);
524
+ line.name = params.name;
525
+ scene.add(line);
526
+ }
527
+ },
528
+ // 绘制贴图曲线
529
+ drawTextureCurve(params) {
530
+ this.removeObjectByName(params.name);
531
+ const route = [];
532
+ params.path.forEach(element => {
533
+ route.push(new this.THREE.Vector3(element.x, element.y, element.z));
534
+ });
535
+ // 画曲线
536
+ if (route.length > 1) {
537
+ // 曲线 作为路径
538
+ curve = new this.THREE.CatmullRomCurve3(route, false, 'catmullrom', 0);
539
+ const geometry = new this.THREE.TubeGeometry(curve, 5000, 0.5, 4);
540
+ //加载纹理
541
+ lineTexture = new this.THREE.TextureLoader().load('/static/arrow/arrow-right.png');
542
+ lineTexture.wrapS = this.THREE.RepeatWrapping;
543
+ lineTexture.wrapT = this.THREE.RepeatWrapping;
544
+ lineTexture.repeat.set(20, 1); //水平重复20次
545
+ lineTexture.needsUpdate = true;
546
+ lineTexture.offset.y = 0.5;
547
+ const material = new this.THREE.MeshBasicMaterial({
548
+ map: lineTexture,
549
+ side: this.THREE.BackSide, //显示背面
550
+ transparent: true,
551
+ });
552
+ let line = new this.THREE.Line(geometry, material);
553
+ line.name = params.name;
554
+ scene.add(line);
555
+ }
556
+ },
557
+ // 开启漫游
558
+ /*
559
+ 参数为Object
560
+ loop: true / false是否进行循环模型 默认是false
561
+ speed: number 漫游的速度,
562
+ path: [{x: 0, y: 0, z: 0}] 漫游的坐标位置点,
563
+ name: '', 路径的名称,用来清除用的
564
+ */
565
+ startRoam(params) {
566
+ progress = 0;
567
+ this.roamConfig = Object.assign(
568
+ {},
569
+ {
570
+ loop: params.loop,
571
+ speed: params.speed,
572
+ roamName: params.name,
573
+ }
574
+ );
575
+ this.drawTextureCurve({
576
+ name: params.name,
577
+ path: params.path,
578
+ });
579
+ clock = new this.THREE.Clock();
580
+ },
581
+ // 暂停漫游
582
+ pauseRoaming() {
583
+ this.$set(this.roamConfig, 'speed', 0);
584
+ },
585
+ // 结束/退出漫游
586
+ endRoam() {
587
+ roaming = false;
588
+ progress = 0;
589
+ this.removeObjectByName(this.roamConfig.roamName);
590
+ this.roamConfig = Object.assign(
591
+ {},
592
+ {
593
+ loop: false,
594
+ speed: 0,
595
+ roamName: '',
596
+ }
597
+ );
598
+ },
599
+ // 相机跟随轨道
600
+ cameraTrack() {
601
+ if (roaming) {
602
+ // renderEnabled = true
603
+ lineTexture.offset.x -= 0.05;
604
+ // 相机和控制器的偏移
605
+ let offset = 10 / curve.getLength();
606
+ if (progress <= 1 - offset) {
607
+ // this.timeRender()
608
+ const point = curve.getPointAt(progress);
609
+ const pointBox = curve.getPointAt(progress + offset);
610
+ camera.position.set(point.x, point.y + 5, point.z);
611
+ camera.lookAt(pointBox.x, pointBox.y + 5, pointBox.z);
612
+ cameraControls.setPosition(point.x, point.y + 5, point.z, false);
613
+ cameraControls.setTarget(pointBox.x, pointBox.y + 5, pointBox.z, false);
614
+ progress += this.roamConfig.speed / 300;
615
+ } else {
616
+ // 循环漫游
617
+ if (this.roamConfig.loop) {
618
+ progress = 0;
619
+ }
620
+ }
621
+ } else {
622
+ lineTexture = null;
623
+ this.roamConfig.progress = 0;
624
+ }
625
+ },
626
+ // 全局整体炸开
627
+ /**
628
+ * 参数val: 爆炸的值
629
+ */
630
+ globalBomb(val) {
631
+ if (scene.children.length === 0) return;
632
+ for (let i = 0; i < scene.children.length; i++) {
633
+ scene.children[i].traverse(item => {
634
+ if (!item.isMesh || !item.worldDir) return;
635
+ // 爆炸公式
636
+ this.computedBomb(item, val);
637
+ });
638
+ }
639
+ this.timeRender();
640
+ },
641
+ // 单个实体模型炸开
642
+ /*
643
+ 参数:
644
+ id: '',当前选中的实体对象的id
645
+ value: 0 - 100 整数
646
+ */
647
+ localBomb(id, value) {
648
+ let target = scene.getObjectById(id);
649
+ if (target) {
650
+ this.computedBomb(target, value);
651
+ }
652
+ },
653
+ computedBomb(object, val) {
654
+ let bombPosition = new this.THREE.Vector3()
655
+ .copy(object.userData.oldPs)
656
+ .add(new this.THREE.Vector3().copy(object.worldDir).multiplyScalar(val))
657
+ .add(new this.THREE.Vector3().copy(object.userData.position));
658
+ object.position.copy(bombPosition);
659
+ },
660
+ // 设置全局整体剖切
661
+ /*
662
+ 先开启模型全局剖切模式, 会返回剖切值的最大最小值
663
+ 剖切值变换时, 使用
664
+ */
665
+ setGlobalClipping() {
666
+ const box3 = new this.THREE.Box3().setFromObject(scene.children[0]);
667
+ let max = box3.max;
668
+ let min = box3.min;
669
+ const clippingPlanes = [
670
+ new this.THREE.Plane(new this.THREE.Vector3(-1, 0, 0), max.x),
671
+ new this.THREE.Plane(new this.THREE.Vector3(0, -1, 0), max.y),
672
+ new this.THREE.Plane(new this.THREE.Vector3(0, 0, -1), max.z),
673
+ ];
674
+ renderer.clippingPlanes = clippingPlanes;
675
+ return {
676
+ min: min,
677
+ max: max,
678
+ };
679
+ },
680
+ // 更新全局剖切的值
681
+ /*
682
+ 参数:Object
683
+ {x: 1, y: 0, z: 0} 可以传一个也可以传多个
684
+ */
685
+ updateGlobalCliValue(params) {
686
+ let axis = {
687
+ x: 0,
688
+ y: 1,
689
+ z: 2,
690
+ };
691
+ for (const key in params) {
692
+ renderer.clippingPlanes[axis[key]].constant = params[key];
693
+ }
694
+ // this.timeRender()
695
+ },
696
+ // 取消全局剖切
697
+ cancelGlobalClipping() {
698
+ renderer.clippingPlanes = Object.freeze([]);
699
+ },
700
+ // 单个实体的剖切/ 局部剖切 obj 实体对象
701
+ setLocalClipping(obj) {
702
+ renderer.localClippingEnabled = true;
703
+ // mesh的boundingBox 可以获取到该对象位置的最大最小值 但是在这里我们需要把y、z轴互换
704
+ const boundingBox = new this.THREE.Box3().copy(obj.geometry.boundingBox);
705
+ boundingBox.applyMatrix4(obj.matrixWorld);
706
+ clippingPlanes = [
707
+ new this.THREE.Plane(new this.THREE.Vector3(-1, 0, 0), Math.ceil(boundingBox.max.x)),
708
+ new this.THREE.Plane(new this.THREE.Vector3(0, -1, 0), Math.ceil(boundingBox.max.y)),
709
+ new this.THREE.Plane(new this.THREE.Vector3(0, 0, -1), Math.ceil(boundingBox.max.z)),
710
+ ];
711
+ obj.material.clippingPlanes = clippingPlanes;
712
+ obj.material.needsUpdate = true;
713
+ // 将局部剖切的对象记录下来
714
+ },
715
+ // 更新局部剖切的值
716
+ /*
717
+ 参数:Object
718
+ {x: 1, y: 0, z: 0} 可以传一个也可以传多个
719
+ */
720
+ updateLocalCliValue(params) {
721
+ let axis = {
722
+ x: 0,
723
+ y: 1,
724
+ z: 2,
725
+ };
726
+ for (const key in params) {
727
+ clippingPlanes[axis[key]].constant = params[key];
728
+ }
729
+ // this.timeRender()
730
+ },
731
+ // 取消局部/单个实体的剖切
732
+ cancelLocalClipping() {
733
+ clippingPlanes = Object.freeze([]);
734
+ },
735
+ // 开启第一视角
736
+ startFirstPer() {
737
+ clock = new this.THREE.Clock();
738
+ downRaycaster = new this.THREE.Raycaster(
739
+ new this.THREE.Vector3(),
740
+ new this.THREE.Vector3(0, -1, 0),
741
+ 0,
742
+ 10
743
+ );
744
+ velocity = new this.THREE.Vector3(); //移动速度变量
745
+ direction = new this.THREE.Vector3(); //移动的方向变量
746
+ firstPerSign = true;
747
+ this.initPointerLock();
748
+ },
749
+ // 页面是否锁定
750
+ initPointerLock() {
751
+ this.home();
752
+ setTimeout(() => {
753
+ pointControls = new PointerLockControls(camera, renderer.domElement);
754
+ pointControls.lock();
755
+ // 锁定
756
+ pointControls.addEventListener('lock', () => {
757
+ cameraControls.enabled = false;
758
+ window.addEventListener('keydown', this.onKeyDown, false);
759
+ window.addEventListener('keyup', this.onKeyUp, false);
760
+ renderer.domElement.removeEventListener('mouseup', this.mouseClick, false);
761
+ renderer.domElement.removeEventListener('mousedown', this.mouseDown, false);
762
+ });
763
+ // 解锁
764
+ pointControls.addEventListener('unlock', () => {
765
+ firstPerSign = false;
766
+ cameraControls.enabled = true;
767
+ // 返回初始视角
768
+ cameraControls.reset(true);
769
+ cameraControls.update(0);
770
+ setTimeout(() => {
771
+ window.removeEventListener('keydown', this.onKeyDown);
772
+ window.removeEventListener('keyup', this.onKeyUp);
773
+ renderer.domElement.addEventListener('mouseup', this.mouseClick, false);
774
+ renderer.domElement.addEventListener('mousedown', this.mouseDown, false);
775
+ // this.timeRender()
776
+ }, 0);
777
+ });
778
+ scene.add(pointControls.object);
779
+ }, 10);
780
+ },
781
+ // 第一视角运动
782
+ firstPerspective() {
783
+ if (!cameraControls.enabled && firstPerSign) {
784
+ // 获取到控制器对象
785
+ let control = pointControls.object;
786
+ // 获取刷新时间
787
+ let delta = clock.getDelta();
788
+ // velocity每次的速度,为了保证有过渡
789
+ velocity.x -= velocity.x * 10.0 * delta;
790
+ velocity.z -= velocity.z * 10.0 * delta;
791
+ velocity.y -= 9.8 * 100.0 * delta; // 默认下降的速度
792
+ // 获取当前按键的方向并获取朝哪个方向移动
793
+ direction.z = Number(moveForward) - Number(moveBackward);
794
+ direction.x = Number(moveRight) - Number(moveLeft);
795
+ // 将法向量的值归一化
796
+ direction.normalize();
797
+ if (moveForward || moveBackward) velocity.z -= direction.z * removeSpeed * delta;
798
+ if (moveLeft || moveRight) velocity.x -= direction.x * removeSpeed * delta;
799
+ // }
800
+ // 复制相机的位置
801
+ downRaycaster.ray.origin.copy(control.position);
802
+ // 获取相机靠下5的位置
803
+ downRaycaster.ray.origin.y += 5;
804
+ // 判断是否停留在了立方体上面
805
+ let intersections = downRaycaster.intersectObjects(scene.children, true);
806
+ var onObject = intersections.length > 0;
807
+ if (onObject === true) {
808
+ velocity.y = Math.max(0, velocity.y);
809
+ canJump = true;
810
+ }
811
+ // 根据速度值移动控制器
812
+ pointControls.moveRight(-velocity.x * delta);
813
+ pointControls.moveForward(-velocity.z * delta);
814
+ control.position.y += velocity.y * delta;
815
+ // 保证控制器的y轴在平面上
816
+ if (control.position.y < 3 - 0 / 10) {
817
+ velocity.y = -0 / 10;
818
+ control.position.y = 3 - 0 / 10;
819
+ canJump = true;
820
+ }
821
+ // this.timeRender()
822
+ }
823
+ },
824
+ // 键盘监听事件
825
+ onKeyDown(event) {
826
+ if (!event.keyCode) return;
827
+ // this.timeRender()
828
+ switch (event.keyCode) {
829
+ // 前进
830
+ case 38:
831
+ case 87:
832
+ moveForward = true;
833
+ break;
834
+ // 向左
835
+ case 37:
836
+ case 65:
837
+ moveLeft = true;
838
+ break;
839
+ // 后退
840
+ case 40:
841
+ case 83:
842
+ moveBackward = true;
843
+ break;
844
+ // 向右
845
+ case 39:
846
+ case 68:
847
+ moveRight = true;
848
+ break;
849
+ // 跳跃
850
+ case 32:
851
+ if (canJump && spaceUp) velocity.y += upSpeed;
852
+ canJump = false;
853
+ spaceUp = false;
854
+ break;
855
+ }
856
+ },
857
+ onKeyUp(event) {
858
+ if (!event.keyCode) return;
859
+ // this.timeRender()
860
+ switch (event.keyCode) {
861
+ // 前进
862
+ case 38:
863
+ case 87:
864
+ moveForward = false;
865
+ break;
866
+ // 向左
867
+ case 37:
868
+ case 65:
869
+ moveLeft = false;
870
+ break;
871
+ // 后退
872
+ case 40:
873
+ case 83:
874
+ moveBackward = false;
875
+ break;
876
+ // 向右
877
+ case 39:
878
+ case 68:
879
+ moveRight = false;
880
+ break;
881
+ // 跳跃
882
+ case 32:
883
+ spaceUp = true;
884
+ break;
885
+ }
886
+ },
887
+ // 返回主视角/恢复相机初始状态
888
+ home() {
889
+ // if (roaming) {
890
+ // this.disposeRoaming()
891
+ // }
892
+ cameraControls.reset(true);
893
+ cameraControls.update(0);
894
+ // this.timeRender()
895
+ },
896
+ // 测量
897
+ /*
898
+ 参数: type: '', distance、area、angle, 暂时只提供距离、面积、角度这三种方式
899
+ */
900
+ openMeasure(type) {
901
+ if (threeMeasure) {
902
+ threeMeasure.close();
903
+ threeMeasure = null;
904
+ }
905
+ measureFlag = true;
906
+ // renderEnabled = true
907
+ switch (type) {
908
+ case 'distance':
909
+ threeMeasure = new MeasureDistance.MeasureDistance(
910
+ renderer,
911
+ scene,
912
+ camera,
913
+ instructions.offsetWidth,
914
+ instructions.offsetHeight
915
+ );
916
+ threeMeasure.start();
917
+ break;
918
+ case 'area':
919
+ threeMeasure = new MeasureArea.MeasureArea(
920
+ renderer,
921
+ scene,
922
+ camera,
923
+ instructions.offsetWidth,
924
+ instructions.offsetHeight
925
+ );
926
+ threeMeasure.start();
927
+ break;
928
+ case 'angle':
929
+ threeMeasure = new MeasureAngle.MeasureAngle(
930
+ renderer,
931
+ scene,
932
+ camera,
933
+ instructions.offsetWidth,
934
+ instructions.offsetHeight
935
+ );
936
+ threeMeasure.start();
937
+ break;
938
+ }
939
+ },
940
+ // 关闭测量
941
+ closeMeasure() {
942
+ measureFlag = false;
943
+ if (threeMeasure) {
944
+ threeMeasure.close();
945
+ threeMeasure = null;
946
+ // this.timeRender()
947
+ }
948
+ },
949
+ // 开启平移
950
+ /*
951
+ 参数: object, 当前选中的对象, distance: {x: 0, y: 0, z: 0}, 平移的距离
952
+ */
953
+ translateMesh(object, distance) {
954
+ let translateMatrix = new this.THREE.Matrix4().makeTranslation(
955
+ distance.x,
956
+ distance.y,
957
+ distance.z
958
+ );
959
+ let translateMatrixInvert = new this.THREE.Matrix4().copy(translateMatrix).invert();
960
+ if (object.userData.translateMatrixInvert) {
961
+ object.applyMatrix4(object.userData.translateMatrixInvert);
962
+ }
963
+ object.userData.translateMatrixInvert = translateMatrixInvert;
964
+ object.applyMatrix4(translateMatrix);
965
+ object.userData.position = new this.THREE.Vector3().copy(object.position);
966
+ object.userData.translate = distance;
967
+ },
968
+ // 单个模型对象旋转
969
+ /*
970
+ 参数: object, 目标实体, degrees: {x: 0, y: 0, z: 0}, 旋转的数值(0-360)
971
+ */
972
+ rotateMesh(object, degrees) {
973
+ // 在矩阵的计算里 完成撤销操作是向量去乘以一个矩阵的逆矩阵
974
+ // 变化后的向量 = 原始向量 * 变化矩阵
975
+ // 逆矩阵 = 变化矩阵.inverse()
976
+ // 原始向量 = 变化后的向量 * 逆矩阵
977
+ let center = object.userData.center;
978
+ let v = new this.THREE.Vector3(center.x, center.y, center.z).negate();
979
+ let translateMatrix = new this.THREE.Matrix4().makeTranslation(v.x, v.y, v.z);
980
+ let rotateX = new this.THREE.Matrix4().makeRotationX(degrees.x * (Math.PI / 180));
981
+ let rotateY = new this.THREE.Matrix4().makeRotationY(degrees.y * (Math.PI / 180));
982
+ let rotateZ = new this.THREE.Matrix4().makeRotationZ(degrees.z * (Math.PI / 180));
983
+ let combineMatrix = new this.THREE.Matrix4()
984
+ .multiply(rotateX)
985
+ .multiply(rotateY)
986
+ .multiply(rotateZ);
987
+ object.applyMatrix4(translateMatrix);
988
+ let combineMatrixInvert = new this.THREE.Matrix4().copy(combineMatrix).invert();
989
+ if (object.userData.combineMatrixInvert) {
990
+ object.applyMatrix4(object.userData.combineMatrixInvert);
991
+ }
992
+ object.userData.combineMatrixInvert = combineMatrixInvert;
993
+ object.applyMatrix4(combineMatrix);
994
+ let translateM = new this.THREE.Matrix4().makeTranslation(center.x, center.y, center.z);
995
+ object.applyMatrix4(translateM);
996
+ object.userData.position = new this.THREE.Vector3().copy(object.position);
997
+ object.userData.rotate = degrees;
998
+ },
999
+ // 隔离
1000
+ /*
1001
+ 参数: object, 目标实体, params: {attr: 'visible/opacity', value: '(true/false)/(0-1)' 需要修改成的值},
1002
+ */
1003
+ isolate(object, params) {
1004
+ // 隔离 将目标实体以外的实体半透明掉或者隐藏掉
1005
+ scene.traverse(item => {
1006
+ if (item.isMesh && item.name !== object.name) {
1007
+ switch (params.attr) {
1008
+ case 'visible':
1009
+ object.material.visible = params.value;
1010
+ break;
1011
+ case 'opacity':
1012
+ object.material.opacity = params.value;
1013
+ object.material.transparent = true;
1014
+ break;
1015
+ }
1016
+ item.material.needsUpdate = true;
1017
+ }
1018
+ });
1019
+ },
1020
+ // 还原操作 将修改过的实体进行恢复
1021
+ restore() {
1022
+ scene.traverse(item => {
1023
+ if (item.isMesh) {
1024
+ item.material.opacity = 1.0;
1025
+ item.material.visible = true;
1026
+ item.material.transparent = true;
1027
+ item.material.color = item.material.userData.nColor;
1028
+ item.material.needsUpdate = true;
1029
+ item.userData.translate = {
1030
+ x: 0,
1031
+ y: 0,
1032
+ z: 0,
1033
+ };
1034
+ item.userData.rotate = {
1035
+ x: 0,
1036
+ y: 0,
1037
+ z: 0,
1038
+ };
1039
+ if (item.userData.translateMatrixInvert) {
1040
+ item.applyMatrix4(item.userData.translateMatrixInvert);
1041
+ item.userData.translateMatrixInvert = null;
1042
+ }
1043
+ if (item.userData.combineMatrixInvert) {
1044
+ this.rotateMesh(item, {
1045
+ x: 0,
1046
+ y: 0,
1047
+ z: 0,
1048
+ });
1049
+ item.userData.combineMatrixInvert = null;
1050
+ }
1051
+ }
1052
+ });
1053
+ },
1054
+ animate() {
1055
+ const delta = fpsClock.getDelta();
1056
+ timeStamp += delta;
1057
+ requestAnimationFrame(this.animate);
1058
+ cameraControls.enabled && cameraControls.update(timeStamp);
1059
+ this.cameraTrack();
1060
+ this.firstPerspective();
1061
+ labelRenderer.render(scene, camera);
1062
+ renderer.render(scene, camera);
1063
+ },
1064
+ },
1065
+ };
1066
+ </script>
1067
+ <style lang="scss" scoped>
1068
+ #instructions {
1069
+ width: 100%;
1070
+ height: 100%;
1071
+ cursor: pointer;
1072
+ }
1073
+ </style>