fl-web-component 2.0.8 → 2.0.9

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 (29) hide show
  1. package/README.md +2 -0
  2. package/dist/fl-web-component.common.js +7198 -1250
  3. package/dist/fl-web-component.common.js.map +1 -1
  4. package/dist/fl-web-component.css +1 -1
  5. package/package.json +1 -1
  6. package/packages/components/com-graphics/component/context.js +123 -0
  7. package/packages/components/com-graphics/index.vue +1148 -69
  8. package/packages/utils/StreamLoader.js +73 -16
  9. package/src/utils/threejs/editor/command.js +36 -0
  10. package/src/utils/threejs/editor/commands/add-element-command.js +41 -0
  11. package/src/utils/threejs/editor/commands/add-group-command.js +10 -0
  12. package/src/utils/threejs/editor/commands/clone-element-command.js +100 -0
  13. package/src/utils/threejs/editor/commands/move-element-command.js +57 -0
  14. package/src/utils/threejs/editor/commands/multi-command.js +29 -0
  15. package/src/utils/threejs/editor/commands/remove-element-command.js +46 -0
  16. package/src/utils/threejs/editor/commands/reset-original-model-style-command.js +30 -0
  17. package/src/utils/threejs/editor/commands/set-geometry-params-command.js +41 -0
  18. package/src/utils/threejs/editor/commands/set-original-model-style-command.js +56 -0
  19. package/src/utils/threejs/editor/commands/set-position-command.js +54 -0
  20. package/src/utils/threejs/editor/commands/set-rotation-command.js +53 -0
  21. package/src/utils/threejs/editor/commands/set-scale-command.js +54 -0
  22. package/src/utils/threejs/editor/commands/set-value-command.js +107 -0
  23. package/src/utils/threejs/editor/constants.js +107 -0
  24. package/src/utils/threejs/editor/element-factory.js +163 -0
  25. package/src/utils/threejs/editor/event-bus.js +34 -0
  26. package/src/utils/threejs/editor/history.js +80 -0
  27. package/src/utils/threejs/editor/scene-command-service.js +1529 -0
  28. package/src/utils/threejs/editor/scene-event-bridge.js +32 -0
  29. package/src/utils/threejs/editor/scene-helpers.js +415 -0
@@ -19,6 +19,7 @@ export class StreamLoader {
19
19
 
20
20
  // 钩子函数
21
21
  this.renderModelData = config.renderModelData || (async () => {});
22
+ this.onRangeStreamComplete = config.onRangeStreamComplete;
22
23
  // 外部提供的交互状态检查函数(可选)
23
24
  this.externalEnsureNotInteracting = config.ensureNotInteracting;
24
25
  this.prefixIdKey = config.prefixIdKey || 'documentId';
@@ -161,6 +162,19 @@ export class StreamLoader {
161
162
  const batchPrimitives = [];
162
163
  const batchSize = this.batchSize;
163
164
  let batchStart = 0;
165
+ const streamStats = {
166
+ totalBatches: 0,
167
+ totalMeshes: 0,
168
+ totalPrimitives: 0,
169
+ };
170
+
171
+ const processStreamBatch = async (meshesToProcess, primitivesToProcess) => {
172
+ if (!meshesToProcess || meshesToProcess.length === 0) return;
173
+ await this.processBatchData(meshesToProcess, primitivesToProcess, list, range, abortSignal);
174
+ streamStats.totalBatches += 1;
175
+ streamStats.totalMeshes += meshesToProcess.length;
176
+ streamStats.totalPrimitives += primitivesToProcess ? primitivesToProcess.length : 0;
177
+ };
164
178
 
165
179
  const ensureNotAborted = () => {
166
180
  if (abortSignal && abortSignal.aborted) {
@@ -189,7 +203,6 @@ export class StreamLoader {
189
203
  if (abortPromise) await abortPromise;
190
204
  const interactionPromise = ensureNotInteracting();
191
205
  if (interactionPromise) await interactionPromise;
192
-
193
206
  let content;
194
207
  try {
195
208
  content = await reader.read();
@@ -223,7 +236,7 @@ export class StreamLoader {
223
236
  const meshesToProcess = batchMeshes.slice(batchStart, batchStart + batchSize);
224
237
  const primitivesToProcess = batchPrimitives.slice(batchStart, batchStart + batchSize);
225
238
  batchStart += batchSize;
226
- this.processBatchData(meshesToProcess, primitivesToProcess, list, range, abortSignal);
239
+ await processStreamBatch(meshesToProcess, primitivesToProcess);
227
240
  }
228
241
 
229
242
  if (batchMeshes.length - batchStart > 0) {
@@ -234,7 +247,7 @@ export class StreamLoader {
234
247
  const meshesToProcess = batchMeshes.slice(batchStart);
235
248
  const primitivesToProcess = batchPrimitives.slice(batchStart);
236
249
  batchStart = batchMeshes.length;
237
- this.processBatchData(meshesToProcess, primitivesToProcess, list, range, abortSignal);
250
+ await processStreamBatch(meshesToProcess, primitivesToProcess);
238
251
  }
239
252
  break;
240
253
  }
@@ -264,7 +277,7 @@ export class StreamLoader {
264
277
  const meshesToProcess = batchMeshes.slice(batchStart, batchStart + batchSize);
265
278
  const primitivesToProcess = batchPrimitives.slice(batchStart, batchStart + batchSize);
266
279
  batchStart += batchSize;
267
- this.processBatchData(meshesToProcess, primitivesToProcess, list, range, abortSignal);
280
+ await processStreamBatch(meshesToProcess, primitivesToProcess);
268
281
  }
269
282
 
270
283
  if (batchStart >= batchSize * 4) {
@@ -278,7 +291,7 @@ export class StreamLoader {
278
291
  await this.workerRequest('streamDispose', { streamId: localStreamId });
279
292
  } catch (e) {}
280
293
  }
281
- return;
294
+ return streamStats;
282
295
  }
283
296
 
284
297
  const decoder = new TextDecoder('utf-8');
@@ -310,7 +323,7 @@ export class StreamLoader {
310
323
  if (batchMeshes.length > 0) {
311
324
  await ensureNotAborted();
312
325
  await this.ensureNotInteracting(abortSignal);
313
- await this.processBatchData(batchMeshes, batchPrimitives, list, range, abortSignal);
326
+ await processStreamBatch(batchMeshes, batchPrimitives);
314
327
  }
315
328
  break;
316
329
  }
@@ -410,7 +423,7 @@ export class StreamLoader {
410
423
  if (batchMeshes.length >= this.batchSize) {
411
424
  await ensureNotAborted();
412
425
  await this.ensureNotInteracting(abortSignal);
413
- await this.processBatchData(batchMeshes, batchPrimitives, list, range, abortSignal);
426
+ await processStreamBatch(batchMeshes, batchPrimitives);
414
427
 
415
428
  batchMeshes.length = 0;
416
429
  batchPrimitives.length = 0;
@@ -423,20 +436,32 @@ export class StreamLoader {
423
436
  }
424
437
  }
425
438
  }
439
+ return streamStats;
426
440
  }
427
441
 
428
442
  // ----------------------------------------------------------------
429
443
  // 数据处理与渲染
430
444
  // ----------------------------------------------------------------
431
445
 
432
- processBatchData(meshes, primitives, list, range, abortSignal = null) {
446
+ async processBatchData(meshes, primitives, list, range, abortSignal = null) {
433
447
  try {
434
448
  if (abortSignal && abortSignal.aborted) {
435
449
  throw new DOMException('Request was aborted', 'AbortError');
436
450
  }
437
- this.renderModelData(meshes, primitives, list, range); // TODO
451
+ const renderResult = await this.renderModelData(meshes, primitives, list, range, null, undefined, {
452
+ suppressLoadComplete: true,
453
+ source: 'inRangeDis2',
454
+ });
455
+ if (renderResult && renderResult.canceled) {
456
+ throw new DOMException('Batch loading was canceled', 'AbortError');
457
+ }
458
+ if (abortSignal && abortSignal.aborted) {
459
+ throw new DOMException('Request was aborted', 'AbortError');
460
+ }
461
+ return renderResult;
438
462
  } catch (error) {
439
463
  console.error('Failed to render batch data:', error);
464
+ throw error;
440
465
  }
441
466
  }
442
467
 
@@ -481,10 +506,24 @@ export class StreamLoader {
481
506
  this.currentStreamReader = reader;
482
507
  this.activeStreamReaders.add(reader);
483
508
 
484
- await this.parseStreamImmediate(reader, list, range, internalController.signal, requestId);
509
+ const streamStats = await this.parseStreamImmediate(
510
+ reader,
511
+ list,
512
+ range,
513
+ internalController.signal,
514
+ requestId
515
+ );
516
+
517
+ if (internalController.signal.aborted) {
518
+ throw new DOMException('Request was aborted', 'AbortError');
519
+ }
485
520
 
486
521
  if (res) {
487
- return res;
522
+ return {
523
+ stream: res,
524
+ requestId,
525
+ ...(streamStats || {}),
526
+ };
488
527
  }
489
528
  return null;
490
529
  } catch (error) {
@@ -634,7 +673,7 @@ export class StreamLoader {
634
673
  // Adaptation: utils used getPrimitivesByRangeStreamWithAutoAbort which calls getPrimitivesByRangeStream
635
674
  // Here I simplify by calling getPrimitivesByRangeStream directly but managing request key
636
675
 
637
- const buffer = await this.getPrimitivesByRangeStream(
676
+ const streamResult = await this.getPrimitivesByRangeStream(
638
677
  range,
639
678
  list,
640
679
  range,
@@ -642,8 +681,8 @@ export class StreamLoader {
642
681
  );
643
682
 
644
683
  this.cleanupRequest(request.requestId);
645
- if (!buffer) return null;
646
- return buffer;
684
+ if (!streamResult) return null;
685
+ return streamResult;
647
686
  } catch (error) {
648
687
  if (error.name === 'AbortError') {
649
688
  // throw error;
@@ -807,7 +846,11 @@ export class StreamLoader {
807
846
  async fetchJsonStream(list, range, abortSignal = null, requestId = null) {
808
847
  try {
809
848
  const loadStartTime = Date.now();
810
- await this.fetchPrimitiveBufferByStream(range, list, abortSignal, requestId);
849
+ const streamResult = await this.fetchPrimitiveBufferByStream(range, list, abortSignal, requestId);
850
+ return {
851
+ ...(streamResult || {}),
852
+ duration: Date.now() - loadStartTime,
853
+ };
811
854
  } catch (err) {
812
855
  if (err.name === 'AbortError') {
813
856
  throw err;
@@ -1390,8 +1433,22 @@ export class StreamLoader {
1390
1433
  // }
1391
1434
 
1392
1435
  try {
1393
- await this.fetchJsonStream(primaryItem, range, this.currentAbortController.signal, requestId);
1436
+ const streamResult = await this.fetchJsonStream(
1437
+ primaryItem,
1438
+ range,
1439
+ this.currentAbortController.signal,
1440
+ requestId
1441
+ );
1394
1442
  if (this.currentRequestId === requestId) {
1443
+ if (typeof this.onRangeStreamComplete === 'function') {
1444
+ await this.onRangeStreamComplete({
1445
+ source: 'inRangeDis2',
1446
+ requestId,
1447
+ item: primaryItem,
1448
+ range,
1449
+ ...(streamResult || {}),
1450
+ });
1451
+ }
1395
1452
  this.currentAbortController = null;
1396
1453
  this.currentRequestId = null;
1397
1454
  } else {
@@ -0,0 +1,36 @@
1
+ export default class Command {
2
+ constructor(service) {
3
+ this.service = service;
4
+ this.type = 'Command';
5
+ this.name = '命令';
6
+ this.updatable = false;
7
+ this.inMemory = true;
8
+ }
9
+
10
+ execute() {}
11
+
12
+ undo() {}
13
+
14
+ canUpdate() {
15
+ return false;
16
+ }
17
+
18
+ update() {}
19
+
20
+ getObject() {
21
+ return this.object || null;
22
+ }
23
+
24
+ getChangedKeys() {
25
+ return [];
26
+ }
27
+
28
+ toJSON() {
29
+ return {
30
+ type: this.type,
31
+ name: this.name,
32
+ };
33
+ }
34
+
35
+ fromJSON() {}
36
+ }
@@ -0,0 +1,41 @@
1
+ import Command from '../command';
2
+ import { COMMAND_TYPE } from '../constants';
3
+ import { insertObjectAt, removeObjectFromParent } from '../scene-helpers';
4
+
5
+ export default class AddElementCommand extends Command {
6
+ constructor(service, object, options = {}) {
7
+ super(service);
8
+ this.type = COMMAND_TYPE.ADD_ELEMENT;
9
+ this.name = '新增元素';
10
+ this.object = object;
11
+ this.parentUuid = options.parentUuid || '';
12
+ this.index = options.index;
13
+ }
14
+
15
+ execute() {
16
+ const parent = this.service.getObjectByUuid(this.parentUuid) || this.service.getCustomRoot();
17
+ this.parentUuid = parent.uuid;
18
+ insertObjectAt(parent, this.object, this.index);
19
+ this.index = parent.children.indexOf(this.object);
20
+ this.object.updateMatrixWorld(true);
21
+ }
22
+
23
+ undo() {
24
+ removeObjectFromParent(this.object);
25
+ }
26
+
27
+ toJSON() {
28
+ return {
29
+ ...super.toJSON(),
30
+ object: this.object.toJSON(),
31
+ parentUuid: this.parentUuid,
32
+ index: this.index,
33
+ };
34
+ }
35
+
36
+ fromJSON(json) {
37
+ this.parentUuid = json.parentUuid;
38
+ this.index = json.index;
39
+ this.object = this.service.parseObjectJSON(json.object);
40
+ }
41
+ }
@@ -0,0 +1,10 @@
1
+ import AddElementCommand from './add-element-command';
2
+ import { COMMAND_TYPE } from '../constants';
3
+
4
+ export default class AddGroupCommand extends AddElementCommand {
5
+ constructor(service, object, options = {}) {
6
+ super(service, object, options);
7
+ this.type = COMMAND_TYPE.ADD_GROUP;
8
+ this.name = '新增组';
9
+ }
10
+ }
@@ -0,0 +1,100 @@
1
+ import Command from '../command';
2
+ import { COMMAND_TYPE } from '../constants';
3
+ import { cloneCustomObject } from '../element-factory';
4
+ import { insertObjectAt, removeObjectFromParent } from '../scene-helpers';
5
+
6
+ const DEFAULT_CLONE_OFFSET_MIN = 1;
7
+ const DEFAULT_CLONE_OFFSET_RATIO = 0.2;
8
+
9
+ function normalizeOffsetArray(offset = []) {
10
+ if (!Array.isArray(offset) || offset.length !== 3) return null;
11
+ return offset.map(value => Number(value) || 0);
12
+ }
13
+
14
+ function createWorldOffsetVector(THREE, source) {
15
+ const bounds = new THREE.Box3();
16
+ const size = new THREE.Vector3();
17
+ source.updateMatrixWorld(true);
18
+ bounds.setFromObject(source);
19
+ bounds.getSize(size);
20
+ const baseOffset = Math.max(
21
+ Math.max(size.x || 0, size.y || 0, size.z || 0) * DEFAULT_CLONE_OFFSET_RATIO,
22
+ DEFAULT_CLONE_OFFSET_MIN
23
+ );
24
+ return new THREE.Vector3(baseOffset, 0, baseOffset);
25
+ }
26
+
27
+ function convertWorldOffsetToLocal(THREE, parent, worldOffset) {
28
+ if (!parent) return worldOffset.clone();
29
+ parent.updateMatrixWorld(true);
30
+ const worldOrigin = parent.getWorldPosition(new THREE.Vector3());
31
+ const localOrigin = parent.worldToLocal(worldOrigin.clone());
32
+ const localTarget = parent.worldToLocal(worldOrigin.clone().add(worldOffset));
33
+ return localTarget.sub(localOrigin);
34
+ }
35
+
36
+ function resolveCloneOffset(THREE, source, targetParent, options = {}) {
37
+ const normalizedOffset = normalizeOffsetArray(options.positionOffset);
38
+ if (normalizedOffset) {
39
+ return new THREE.Vector3().fromArray(normalizedOffset);
40
+ }
41
+ if (options.disableAutoOffset === true || !THREE || !source) {
42
+ return new THREE.Vector3(0, 0, 0);
43
+ }
44
+ return convertWorldOffsetToLocal(THREE, targetParent, createWorldOffsetVector(THREE, source));
45
+ }
46
+
47
+ export default class CloneElementCommand extends Command {
48
+ constructor(service, uuid, options = {}) {
49
+ super(service);
50
+ this.type = COMMAND_TYPE.CLONE_ELEMENT;
51
+ this.name = '复制元素';
52
+ this.source = service.getObjectByUuid(uuid);
53
+ const targetParent =
54
+ service.getObjectByUuid(options.parentUuid) ||
55
+ (this.source && this.source.parent) ||
56
+ service.getCustomRoot();
57
+ this.parentUuid = targetParent.uuid;
58
+ this.index = options.index;
59
+ this.object = this.source ? cloneCustomObject(service.THREE, this.source, targetParent) : null;
60
+ this.positionOffset = resolveCloneOffset(service.THREE, this.source, targetParent, options);
61
+ if (this.object) {
62
+ // 复制后默认沿平面做一次可见偏移,避免与原对象完全重合。
63
+ this.object.position.add(this.positionOffset);
64
+ this.object.updateMatrixWorld(true);
65
+ }
66
+ }
67
+
68
+ execute() {
69
+ if (!this.object) return;
70
+ const parent = this.service.getObjectByUuid(this.parentUuid) || this.service.getCustomRoot();
71
+ insertObjectAt(parent, this.object, this.index);
72
+ this.index = parent.children.indexOf(this.object);
73
+ this.object.updateMatrixWorld(true);
74
+ }
75
+
76
+ undo() {
77
+ if (!this.object) return;
78
+ removeObjectFromParent(this.object);
79
+ }
80
+
81
+ toJSON() {
82
+ return {
83
+ ...super.toJSON(),
84
+ sourceUuid: this.source ? this.source.uuid : '',
85
+ parentUuid: this.parentUuid,
86
+ index: this.index,
87
+ positionOffset: this.positionOffset ? this.positionOffset.toArray() : [0, 0, 0],
88
+ object: this.object ? this.object.toJSON() : null,
89
+ };
90
+ }
91
+
92
+ fromJSON(json) {
93
+ this.parentUuid = json.parentUuid;
94
+ this.index = json.index;
95
+ this.positionOffset = Array.isArray(json.positionOffset)
96
+ ? new this.service.THREE.Vector3().fromArray(json.positionOffset)
97
+ : new this.service.THREE.Vector3(0, 0, 0);
98
+ this.object = json.object ? this.service.parseObjectJSON(json.object) : null;
99
+ }
100
+ }
@@ -0,0 +1,57 @@
1
+ import Command from '../command';
2
+ import { COMMAND_TYPE } from '../constants';
3
+ import { getObjectIndex, insertObjectAt } from '../scene-helpers';
4
+
5
+ export default class MoveElementCommand extends Command {
6
+ constructor(service, uuid, options = {}) {
7
+ super(service);
8
+ this.type = COMMAND_TYPE.MOVE_ELEMENT;
9
+ this.name = '移动元素';
10
+ this.object = service.getObjectByUuid(uuid);
11
+ this.oldParentUuid = this.object && this.object.parent ? this.object.parent.uuid : '';
12
+ this.oldIndex = getObjectIndex(this.object);
13
+ this.newParentUuid = options.targetParentUuid || '';
14
+ this.newIndex = options.index;
15
+ }
16
+
17
+ execute() {
18
+ if (!this.object) return;
19
+ const targetParent =
20
+ this.service.getObjectByUuid(this.newParentUuid) || this.service.getCustomRoot();
21
+ insertObjectAt(targetParent, this.object, this.newIndex);
22
+ this.newParentUuid = targetParent.uuid;
23
+ this.newIndex = targetParent.children.indexOf(this.object);
24
+ this.object.updateMatrixWorld(true);
25
+ }
26
+
27
+ undo() {
28
+ if (!this.object) return;
29
+ const oldParent =
30
+ this.service.getObjectByUuid(this.oldParentUuid) || this.service.getCustomRoot();
31
+ insertObjectAt(oldParent, this.object, this.oldIndex);
32
+ this.object.updateMatrixWorld(true);
33
+ }
34
+
35
+ getChangedKeys() {
36
+ return ['parentUuid'];
37
+ }
38
+
39
+ toJSON() {
40
+ return {
41
+ ...super.toJSON(),
42
+ uuid: this.object ? this.object.uuid : '',
43
+ oldParentUuid: this.oldParentUuid,
44
+ oldIndex: this.oldIndex,
45
+ newParentUuid: this.newParentUuid,
46
+ newIndex: this.newIndex,
47
+ };
48
+ }
49
+
50
+ fromJSON(json) {
51
+ this.object = this.service.getObjectByUuid(json.uuid);
52
+ this.oldParentUuid = json.oldParentUuid;
53
+ this.oldIndex = json.oldIndex;
54
+ this.newParentUuid = json.newParentUuid;
55
+ this.newIndex = json.newIndex;
56
+ }
57
+ }
@@ -0,0 +1,29 @@
1
+ import Command from '../command';
2
+ import { COMMAND_TYPE } from '../constants';
3
+
4
+ export default class MultiCommand extends Command {
5
+ constructor(service, commands = [], options = {}) {
6
+ super(service);
7
+ this.type = COMMAND_TYPE.MULTI;
8
+ this.name = options.name || '批量修改';
9
+ this.commands = commands;
10
+ }
11
+
12
+ execute() {
13
+ this.commands.forEach(command => {
14
+ command.execute();
15
+ });
16
+ }
17
+
18
+ undo() {
19
+ for (let index = this.commands.length - 1; index >= 0; index -= 1) {
20
+ this.commands[index].undo();
21
+ }
22
+ }
23
+
24
+ getChangedKeys() {
25
+ return this.commands.reduce((result, command) => {
26
+ return result.concat(command.getChangedKeys());
27
+ }, []);
28
+ }
29
+ }
@@ -0,0 +1,46 @@
1
+ import Command from '../command';
2
+ import { COMMAND_TYPE } from '../constants';
3
+ import { getObjectIndex, insertObjectAt, removeObjectFromParent } from '../scene-helpers';
4
+
5
+ export default class RemoveElementCommand extends Command {
6
+ constructor(service, uuid) {
7
+ super(service);
8
+ this.type = COMMAND_TYPE.REMOVE_ELEMENT;
9
+ this.name = '删除元素';
10
+ this.object = service.getObjectByUuid(uuid);
11
+ this.parentUuid = this.object && this.object.parent ? this.object.parent.uuid : '';
12
+ this.index = getObjectIndex(this.object);
13
+ }
14
+
15
+ execute() {
16
+ if (!this.object) return;
17
+ if (this.object.parent) {
18
+ this.parentUuid = this.object.parent.uuid;
19
+ this.index = getObjectIndex(this.object);
20
+ }
21
+ removeObjectFromParent(this.object);
22
+ }
23
+
24
+ undo() {
25
+ if (!this.object) return;
26
+ const parent = this.service.getObjectByUuid(this.parentUuid) || this.service.getCustomRoot();
27
+ insertObjectAt(parent, this.object, this.index);
28
+ this.object.updateMatrixWorld(true);
29
+ }
30
+
31
+ toJSON() {
32
+ return {
33
+ ...super.toJSON(),
34
+ uuid: this.object ? this.object.uuid : '',
35
+ parentUuid: this.parentUuid,
36
+ index: this.index,
37
+ object: this.object ? this.object.toJSON() : null,
38
+ };
39
+ }
40
+
41
+ fromJSON(json) {
42
+ this.parentUuid = json.parentUuid;
43
+ this.index = json.index;
44
+ this.object = json.object ? this.service.parseObjectJSON(json.object) : null;
45
+ }
46
+ }
@@ -0,0 +1,30 @@
1
+ import Command from '../command';
2
+ import { COMMAND_TYPE } from '../constants';
3
+
4
+ export default class ResetOriginalModelStyleCommand extends Command {
5
+ constructor(service, uuid) {
6
+ super(service);
7
+ this.type = COMMAND_TYPE.RESET_ORIGINAL_MODEL_STYLE;
8
+ this.name = '还原原始模型样式';
9
+ this.object = service.getObjectByUuid(uuid);
10
+ this.uuid = uuid;
11
+ this.oldStyle = service.getOriginalModelStyleValue(uuid);
12
+ this.resetStyle = service.ensureOriginalModelBaseline(uuid);
13
+ }
14
+
15
+ execute() {
16
+ if (!this.object) return;
17
+ this.service.applyOriginalModelStyle(this.uuid, this.resetStyle);
18
+ this.service.syncOriginalModelStyleRecord(this.uuid);
19
+ }
20
+
21
+ undo() {
22
+ if (!this.object) return;
23
+ this.service.applyOriginalModelStyle(this.uuid, this.oldStyle);
24
+ this.service.syncOriginalModelStyleRecord(this.uuid);
25
+ }
26
+
27
+ getChangedKeys() {
28
+ return ['color', 'opacity', 'visible'];
29
+ }
30
+ }
@@ -0,0 +1,41 @@
1
+ import Command from '../command';
2
+ import { COMMAND_TYPE } from '../constants';
3
+ import { createGeometry, normalizeGeometryParams } from '../element-factory';
4
+
5
+ export default class SetGeometryParamsCommand extends Command {
6
+ constructor(service, uuid, geometryParams) {
7
+ super(service);
8
+ this.type = COMMAND_TYPE.SET_GEOMETRY_PARAMS;
9
+ this.name = '修改几何参数';
10
+ this.object = service.getObjectByUuid(uuid);
11
+ this.elementType = this.object && this.object.userData ? this.object.userData.elementType : '';
12
+ this.oldParams =
13
+ this.object && this.object.userData && this.object.userData.geometryParams
14
+ ? normalizeGeometryParams(this.elementType, this.object.userData.geometryParams)
15
+ : {};
16
+ this.newParams = normalizeGeometryParams(this.elementType, geometryParams);
17
+ this.oldGeometry = this.object ? this.object.geometry : null;
18
+ this.newGeometry = null;
19
+ }
20
+
21
+ execute() {
22
+ if (!this.object) return;
23
+ if (!this.newGeometry) {
24
+ this.newGeometry = createGeometry(this.service.THREE, this.elementType, this.newParams);
25
+ }
26
+ this.object.geometry = this.newGeometry;
27
+ this.object.userData.geometryParams = { ...this.newParams };
28
+ this.object.updateMatrixWorld(true);
29
+ }
30
+
31
+ undo() {
32
+ if (!this.object) return;
33
+ this.object.geometry = this.oldGeometry;
34
+ this.object.userData.geometryParams = { ...this.oldParams };
35
+ this.object.updateMatrixWorld(true);
36
+ }
37
+
38
+ getChangedKeys() {
39
+ return ['geometryParams'];
40
+ }
41
+ }
@@ -0,0 +1,56 @@
1
+ import Command from '../command';
2
+ import { COMMAND_TYPE } from '../constants';
3
+
4
+ function cloneStyle(style = {}) {
5
+ return JSON.parse(JSON.stringify(style || {}));
6
+ }
7
+
8
+ export default class SetOriginalModelStyleCommand extends Command {
9
+ constructor(service, uuid, style = {}) {
10
+ super(service);
11
+ this.type = COMMAND_TYPE.SET_ORIGINAL_MODEL_STYLE;
12
+ this.name = '修改原始模型样式';
13
+ this.updatable = true;
14
+ this.object = service.getObjectByUuid(uuid);
15
+ this.uuid = uuid;
16
+ // 首次修改前必须先固化原始样式基线,否则“还原”会回到已修改后的状态。
17
+ service.ensureOriginalModelBaseline(uuid);
18
+ this.oldStyle = service.getOriginalModelStyleValue(uuid);
19
+ this.newStyle = service.mergeOriginalModelStyle(this.oldStyle, style);
20
+ }
21
+
22
+ execute() {
23
+ if (!this.object) return;
24
+ this.service.applyOriginalModelStyle(this.uuid, this.newStyle);
25
+ this.service.syncOriginalModelStyleRecord(this.uuid);
26
+ }
27
+
28
+ undo() {
29
+ if (!this.object) return;
30
+ this.service.applyOriginalModelStyle(this.uuid, this.oldStyle);
31
+ this.service.syncOriginalModelStyleRecord(this.uuid);
32
+ }
33
+
34
+ canUpdate(command, deltaTime, mergeWindow) {
35
+ return (
36
+ command &&
37
+ command.type === this.type &&
38
+ command.uuid === this.uuid &&
39
+ deltaTime <= mergeWindow
40
+ );
41
+ }
42
+
43
+ update(command) {
44
+ this.newStyle = cloneStyle(command.newStyle);
45
+ }
46
+
47
+ getChangedKeys() {
48
+ const changedKeys = [];
49
+ ['color', 'opacity', 'visible'].forEach(key => {
50
+ if (this.oldStyle[key] !== this.newStyle[key]) {
51
+ changedKeys.push(key);
52
+ }
53
+ });
54
+ return changedKeys;
55
+ }
56
+ }