fl-web-component 2.0.8 → 2.0.10
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 +4 -0
- package/dist/fl-web-component.common.2.js +126 -48
- package/dist/fl-web-component.common.2.js.map +1 -1
- package/dist/fl-web-component.common.js +7366 -1277
- package/dist/fl-web-component.common.js.map +1 -1
- package/dist/fl-web-component.css +1 -1
- package/package.json +1 -1
- package/packages/components/com-graphics/component/context.js +123 -0
- package/packages/components/com-graphics/index.vue +1243 -85
- package/packages/utils/StreamLoader.js +73 -16
- package/src/utils/threejs/editor/command.js +36 -0
- package/src/utils/threejs/editor/commands/add-element-command.js +41 -0
- package/src/utils/threejs/editor/commands/add-group-command.js +10 -0
- package/src/utils/threejs/editor/commands/clone-element-command.js +100 -0
- package/src/utils/threejs/editor/commands/move-element-command.js +57 -0
- package/src/utils/threejs/editor/commands/multi-command.js +29 -0
- package/src/utils/threejs/editor/commands/remove-element-command.js +46 -0
- package/src/utils/threejs/editor/commands/reset-original-model-style-command.js +30 -0
- package/src/utils/threejs/editor/commands/set-geometry-params-command.js +41 -0
- package/src/utils/threejs/editor/commands/set-original-model-style-command.js +56 -0
- package/src/utils/threejs/editor/commands/set-position-command.js +54 -0
- package/src/utils/threejs/editor/commands/set-rotation-command.js +53 -0
- package/src/utils/threejs/editor/commands/set-scale-command.js +54 -0
- package/src/utils/threejs/editor/commands/set-value-command.js +107 -0
- package/src/utils/threejs/editor/constants.js +107 -0
- package/src/utils/threejs/editor/element-factory.js +163 -0
- package/src/utils/threejs/editor/event-bus.js +34 -0
- package/src/utils/threejs/editor/history.js +80 -0
- package/src/utils/threejs/editor/scene-command-service.js +1529 -0
- package/src/utils/threejs/editor/scene-event-bridge.js +32 -0
- package/src/utils/threejs/editor/scene-helpers.js +415 -0
- package/src/utils/threejs/measure-angle.js +22 -2
- package/src/utils/threejs/measure-area.js +23 -3
- package/src/utils/threejs/measure-distance.js +20 -1
- package/src/utils/threejs/measure-height.js +17 -1
|
@@ -0,0 +1,1529 @@
|
|
|
1
|
+
import { COMMAND_TYPE, EDITOR_EVENT, SCENE_NODE_TYPE, TRANSFORM_MODE } from './constants';
|
|
2
|
+
import SceneEventBus from './event-bus';
|
|
3
|
+
import History from './history';
|
|
4
|
+
import SceneEventBridge from './scene-event-bridge';
|
|
5
|
+
import {
|
|
6
|
+
buildObjectSnapshot,
|
|
7
|
+
buildCustomSaveTree,
|
|
8
|
+
buildCustomTree,
|
|
9
|
+
buildTreeSnapshot,
|
|
10
|
+
clampInsertIndex,
|
|
11
|
+
createDefaultElementName,
|
|
12
|
+
createDefaultGroupName,
|
|
13
|
+
disposeObjectResources,
|
|
14
|
+
ensureCustomRoot,
|
|
15
|
+
findSelectableSceneObject,
|
|
16
|
+
getSceneNodeType,
|
|
17
|
+
getObjectByUuid,
|
|
18
|
+
getObjectIndex,
|
|
19
|
+
insertObjectAt,
|
|
20
|
+
isAncestor,
|
|
21
|
+
isCloneableObject,
|
|
22
|
+
isCustomElement,
|
|
23
|
+
isCustomGroup,
|
|
24
|
+
isCustomRoot,
|
|
25
|
+
isDeletableObject,
|
|
26
|
+
isEditableObject,
|
|
27
|
+
isOriginalModelObject,
|
|
28
|
+
isObjectVisible,
|
|
29
|
+
isSelectableObject,
|
|
30
|
+
} from './scene-helpers';
|
|
31
|
+
import {
|
|
32
|
+
createElementByType,
|
|
33
|
+
normalizeGeometryParams,
|
|
34
|
+
createGroupElement,
|
|
35
|
+
} from './element-factory';
|
|
36
|
+
import AddElementCommand from './commands/add-element-command';
|
|
37
|
+
import AddGroupCommand from './commands/add-group-command';
|
|
38
|
+
import RemoveElementCommand from './commands/remove-element-command';
|
|
39
|
+
import CloneElementCommand from './commands/clone-element-command';
|
|
40
|
+
import MoveElementCommand from './commands/move-element-command';
|
|
41
|
+
import SetPositionCommand from './commands/set-position-command';
|
|
42
|
+
import SetRotationCommand from './commands/set-rotation-command';
|
|
43
|
+
import SetScaleCommand from './commands/set-scale-command';
|
|
44
|
+
import SetValueCommand from './commands/set-value-command';
|
|
45
|
+
import SetGeometryParamsCommand from './commands/set-geometry-params-command';
|
|
46
|
+
import SetOriginalModelStyleCommand from './commands/set-original-model-style-command';
|
|
47
|
+
import ResetOriginalModelStyleCommand from './commands/reset-original-model-style-command';
|
|
48
|
+
import MultiCommand from './commands/multi-command';
|
|
49
|
+
|
|
50
|
+
export default class SceneCommandService {
|
|
51
|
+
constructor(options = {}) {
|
|
52
|
+
this.THREE = options.THREE;
|
|
53
|
+
this.getScene = options.getScene;
|
|
54
|
+
this.requestRender = options.requestRender || function () {};
|
|
55
|
+
this.selectedUuid = '';
|
|
56
|
+
this.selectedNodeType = SCENE_NODE_TYPE.UNKNOWN;
|
|
57
|
+
this.transformMode = TRANSFORM_MODE.TRANSLATE;
|
|
58
|
+
this.objectLoader = new this.THREE.ObjectLoader();
|
|
59
|
+
this.eventBus = new SceneEventBus();
|
|
60
|
+
this.history = new History(this, options.historyOptions);
|
|
61
|
+
this.originalModelBaselines = new Map();
|
|
62
|
+
this.originalModelStyleChanges = new Map();
|
|
63
|
+
this.originalModelNodeNames = new Map();
|
|
64
|
+
this.eventBridge = new SceneEventBridge({
|
|
65
|
+
eventBus: this.eventBus,
|
|
66
|
+
getTreeSnapshot: () => this.getCustomTree(),
|
|
67
|
+
getOriginalModelStyleChanges: () => this.getOriginalModelStyleChanges(),
|
|
68
|
+
});
|
|
69
|
+
this.ensureReady();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
ensureReady() {
|
|
73
|
+
const root = ensureCustomRoot(this.getSceneInstance(), this.THREE);
|
|
74
|
+
this.normalizeCustomObjectFlags(root);
|
|
75
|
+
this.normalizeCustomElementGeometryParams(root);
|
|
76
|
+
return root;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
destroy() {
|
|
80
|
+
this.history.clear();
|
|
81
|
+
this.eventBridge.destroy();
|
|
82
|
+
this.selectedUuid = '';
|
|
83
|
+
this.selectedNodeType = SCENE_NODE_TYPE.UNKNOWN;
|
|
84
|
+
this.originalModelBaselines.clear();
|
|
85
|
+
this.originalModelStyleChanges.clear();
|
|
86
|
+
this.originalModelNodeNames.clear();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
getSceneInstance() {
|
|
90
|
+
return typeof this.getScene === 'function' ? this.getScene() : null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
getCustomRoot() {
|
|
94
|
+
const root = ensureCustomRoot(this.getSceneInstance(), this.THREE);
|
|
95
|
+
this.normalizeCustomObjectFlags(root);
|
|
96
|
+
this.normalizeCustomElementGeometryParams(root);
|
|
97
|
+
return root;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
normalizeCustomObjectFlags(root) {
|
|
101
|
+
const targetRoot = root || ensureCustomRoot(this.getSceneInstance(), this.THREE);
|
|
102
|
+
if (!targetRoot) return;
|
|
103
|
+
targetRoot.traverse(object => {
|
|
104
|
+
if (!object.userData) return;
|
|
105
|
+
if (object.userData.nodeType === 'custom-root') {
|
|
106
|
+
object.userData.rootType = 'custom-root';
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (
|
|
110
|
+
(object.userData.nodeType === 'custom-element' ||
|
|
111
|
+
object.userData.nodeType === 'custom-group') &&
|
|
112
|
+
object.userData.rootType === 'custom-root'
|
|
113
|
+
) {
|
|
114
|
+
delete object.userData.rootType;
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
normalizeCustomElementGeometryParams(root) {
|
|
120
|
+
const targetRoot = root || ensureCustomRoot(this.getSceneInstance(), this.THREE);
|
|
121
|
+
if (!targetRoot) return;
|
|
122
|
+
targetRoot.traverse(object => {
|
|
123
|
+
if (!isCustomElement(object)) return;
|
|
124
|
+
const elementType = object.userData ? object.userData.elementType : '';
|
|
125
|
+
if (!elementType) return;
|
|
126
|
+
object.userData.geometryParams = normalizeGeometryParams(
|
|
127
|
+
elementType,
|
|
128
|
+
object.userData.geometryParams || {}
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
parseObjectJSON(json) {
|
|
134
|
+
return this.objectLoader.parse(json);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
subscribe(eventName, handler) {
|
|
138
|
+
return this.eventBus.on(eventName, handler);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
emitEvent(eventName, payload) {
|
|
142
|
+
this.eventBridge.emit(eventName, payload);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
getObjectByUuid(uuid) {
|
|
146
|
+
return getObjectByUuid(this.getSceneInstance(), uuid);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
getSceneObjectDepth(object) {
|
|
150
|
+
let depth = 0;
|
|
151
|
+
let current = object;
|
|
152
|
+
while (current && current.parent) {
|
|
153
|
+
depth += 1;
|
|
154
|
+
current = current.parent;
|
|
155
|
+
}
|
|
156
|
+
return depth;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
getOriginalModelResolvePriority(object) {
|
|
160
|
+
if (!object || !isOriginalModelObject(object)) return -1;
|
|
161
|
+
let priority = 0;
|
|
162
|
+
if (object.isInstancedMesh === true) {
|
|
163
|
+
priority += 8;
|
|
164
|
+
}
|
|
165
|
+
if (object.type && object.type !== 'Group') {
|
|
166
|
+
priority += 4;
|
|
167
|
+
}
|
|
168
|
+
if (this.getObjectMaterials(object).length > 0) {
|
|
169
|
+
priority += 2;
|
|
170
|
+
}
|
|
171
|
+
return priority;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
compareOriginalModelResolveTargets(left, right) {
|
|
175
|
+
const leftPriority = this.getOriginalModelResolvePriority(left);
|
|
176
|
+
const rightPriority = this.getOriginalModelResolvePriority(right);
|
|
177
|
+
if (leftPriority !== rightPriority) {
|
|
178
|
+
return leftPriority - rightPriority;
|
|
179
|
+
}
|
|
180
|
+
return this.getSceneObjectDepth(left) - this.getSceneObjectDepth(right);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
findOriginalModelObjectByName(name) {
|
|
184
|
+
const scene = this.getSceneInstance();
|
|
185
|
+
if (!scene || !name) return null;
|
|
186
|
+
const candidates = [];
|
|
187
|
+
scene.traverse(object => {
|
|
188
|
+
if (!isOriginalModelObject(object)) return;
|
|
189
|
+
if ((object.name || '') === name) {
|
|
190
|
+
candidates.push(object);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
if (candidates.length === 0) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
candidates.sort((left, right) => this.compareOriginalModelResolveTargets(right, left));
|
|
197
|
+
return candidates[0] || null;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
resolveOriginalModelObject(locator = {}) {
|
|
201
|
+
if (!locator || typeof locator !== 'object') return null;
|
|
202
|
+
const { uuid = '', name = '' } = locator;
|
|
203
|
+
const objectByUuid = uuid ? this.getObjectByUuid(uuid) : null;
|
|
204
|
+
const objectByName = name ? this.findOriginalModelObjectByName(name) : null;
|
|
205
|
+
if (objectByUuid && isOriginalModelObject(objectByUuid)) {
|
|
206
|
+
if (objectByName && this.compareOriginalModelResolveTargets(objectByName, objectByUuid) > 0) {
|
|
207
|
+
return objectByName;
|
|
208
|
+
}
|
|
209
|
+
return objectByUuid;
|
|
210
|
+
}
|
|
211
|
+
return objectByName;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
getOriginalModelChangeLocatorKey(locator = {}) {
|
|
215
|
+
if (!locator || typeof locator !== 'object') return '';
|
|
216
|
+
const name = typeof locator.name === 'string' ? locator.name : '';
|
|
217
|
+
const uuid = typeof locator.uuid === 'string' ? locator.uuid : '';
|
|
218
|
+
return name || uuid;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
setOriginalModelNodeName(locator = {}) {
|
|
222
|
+
if (!locator || typeof locator !== 'object') return '';
|
|
223
|
+
const nodeName = locator.nodeName !== undefined && locator.nodeName !== null
|
|
224
|
+
? String(locator.nodeName)
|
|
225
|
+
: '';
|
|
226
|
+
const keys = [locator.uuid, locator.name]
|
|
227
|
+
.filter(key => key !== undefined && key !== null && String(key));
|
|
228
|
+
if (keys.length === 0) return '';
|
|
229
|
+
keys.forEach(key => {
|
|
230
|
+
if (nodeName) {
|
|
231
|
+
this.originalModelNodeNames.set(String(key), nodeName);
|
|
232
|
+
} else {
|
|
233
|
+
this.originalModelNodeNames.delete(String(key));
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
if (nodeName) {
|
|
237
|
+
const record = this.findOriginalModelStoredRecord(this.originalModelStyleChanges, locator);
|
|
238
|
+
if (record) {
|
|
239
|
+
record.nodeName = nodeName;
|
|
240
|
+
this.eventBridge.emitOriginalModelChangeListChanged();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return nodeName;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
getOriginalModelNodeName(locator = {}) {
|
|
247
|
+
if (!locator || typeof locator !== 'object') return '';
|
|
248
|
+
const keys = [locator.uuid, locator.name]
|
|
249
|
+
.filter(key => key !== undefined && key !== null && String(key));
|
|
250
|
+
for (let index = 0; index < keys.length; index += 1) {
|
|
251
|
+
const nodeName = this.originalModelNodeNames.get(String(keys[index]));
|
|
252
|
+
if (nodeName) return nodeName;
|
|
253
|
+
}
|
|
254
|
+
return locator.nodeName || '';
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
cloneOriginalModelStyle(style = {}) {
|
|
258
|
+
return this.mergeOriginalModelStyle({}, style || {});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
isSameOriginalModelStyle(left = {}, right = {}) {
|
|
262
|
+
return ['color', 'opacity', 'visible'].every(key => left[key] === right[key]);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
buildOriginalModelStyleRecord(record = {}, options = {}) {
|
|
266
|
+
const styleSource = this.getOriginalModelStyleSource(record);
|
|
267
|
+
if (!record || !styleSource) return null;
|
|
268
|
+
const key = this.getOriginalModelChangeLocatorKey(record);
|
|
269
|
+
if (!key) return null;
|
|
270
|
+
const changedKeys =
|
|
271
|
+
Array.isArray(record.changedKeys) && record.changedKeys.length > 0
|
|
272
|
+
? record.changedKeys.slice()
|
|
273
|
+
: ['color', 'opacity', 'visible'].filter(styleKey =>
|
|
274
|
+
Object.prototype.hasOwnProperty.call(styleSource, styleKey)
|
|
275
|
+
);
|
|
276
|
+
return {
|
|
277
|
+
uuid: record.uuid || '',
|
|
278
|
+
name: record.name || '',
|
|
279
|
+
nodeName: this.getOriginalModelNodeName(record),
|
|
280
|
+
nodeType: record.nodeType || SCENE_NODE_TYPE.ORIGINAL_MODEL,
|
|
281
|
+
changedKeys,
|
|
282
|
+
before: record.before ? this.cloneOriginalModelStyle(record.before) : null,
|
|
283
|
+
after: this.cloneOriginalModelStyle(styleSource),
|
|
284
|
+
updatedAt: Number.isFinite(options.updatedAt)
|
|
285
|
+
? Number(options.updatedAt)
|
|
286
|
+
: Number.isFinite(record.updatedAt)
|
|
287
|
+
? Number(record.updatedAt)
|
|
288
|
+
: Date.now(),
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
getOriginalModelStyleSource(record = {}) {
|
|
293
|
+
if (!record || typeof record !== 'object') return null;
|
|
294
|
+
if (record.after && typeof record.after === 'object') {
|
|
295
|
+
return record.after;
|
|
296
|
+
}
|
|
297
|
+
if (record.style && typeof record.style === 'object') {
|
|
298
|
+
return record.style;
|
|
299
|
+
}
|
|
300
|
+
const flatStyle = {};
|
|
301
|
+
['color', 'opacity', 'visible'].forEach(key => {
|
|
302
|
+
if (Object.prototype.hasOwnProperty.call(record, key)) {
|
|
303
|
+
flatStyle[key] = record[key];
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
return Object.keys(flatStyle).length > 0 ? flatStyle : null;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
normalizeCustomSaveNode(node) {
|
|
310
|
+
if (!node || typeof node !== 'object') return node;
|
|
311
|
+
const nextNode = {
|
|
312
|
+
...node,
|
|
313
|
+
children: Array.isArray(node.children)
|
|
314
|
+
? node.children.map(child => this.normalizeCustomSaveNode(child))
|
|
315
|
+
: [],
|
|
316
|
+
};
|
|
317
|
+
if (
|
|
318
|
+
!Object.prototype.hasOwnProperty.call(nextNode, 'material') &&
|
|
319
|
+
(Object.prototype.hasOwnProperty.call(nextNode, 'color') ||
|
|
320
|
+
Object.prototype.hasOwnProperty.call(nextNode, 'opacity'))
|
|
321
|
+
) {
|
|
322
|
+
nextNode.material = {};
|
|
323
|
+
if (Object.prototype.hasOwnProperty.call(nextNode, 'color')) {
|
|
324
|
+
nextNode.material.color = nextNode.color;
|
|
325
|
+
}
|
|
326
|
+
if (Object.prototype.hasOwnProperty.call(nextNode, 'opacity')) {
|
|
327
|
+
nextNode.material.opacity = nextNode.opacity;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return nextNode;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
normalizeOriginalModelSaveItem(item = {}) {
|
|
334
|
+
if (!item || typeof item !== 'object') return item;
|
|
335
|
+
const nextItem = { ...item };
|
|
336
|
+
if (!nextItem.style || typeof nextItem.style !== 'object') {
|
|
337
|
+
nextItem.style = {};
|
|
338
|
+
['color', 'opacity', 'visible'].forEach(key => {
|
|
339
|
+
if (Object.prototype.hasOwnProperty.call(item, key)) {
|
|
340
|
+
nextItem.style[key] = item[key];
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
return nextItem;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
normalizeSaveSnapshot(snapshot = {}) {
|
|
348
|
+
const payload = snapshot && typeof snapshot === 'object' ? snapshot : {};
|
|
349
|
+
const nextSnapshot = { ...payload };
|
|
350
|
+
if (Object.prototype.hasOwnProperty.call(payload, 'customModelTree')) {
|
|
351
|
+
nextSnapshot.customModelTree = this.normalizeCustomSaveNode(payload.customModelTree);
|
|
352
|
+
}
|
|
353
|
+
if (Object.prototype.hasOwnProperty.call(payload, 'originalModelList')) {
|
|
354
|
+
nextSnapshot.originalModelList = Array.isArray(payload.originalModelList)
|
|
355
|
+
? payload.originalModelList.map(item => this.normalizeOriginalModelSaveItem(item))
|
|
356
|
+
: [];
|
|
357
|
+
}
|
|
358
|
+
return nextSnapshot;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
findOriginalModelStoredRecord(map, locator = {}) {
|
|
362
|
+
if (!(map instanceof Map)) return null;
|
|
363
|
+
const key = this.getOriginalModelChangeLocatorKey(locator);
|
|
364
|
+
if (key && map.has(key)) {
|
|
365
|
+
return map.get(key);
|
|
366
|
+
}
|
|
367
|
+
const { uuid = '', name = '' } = locator || {};
|
|
368
|
+
let matchedRecord = null;
|
|
369
|
+
map.forEach((record, recordKey) => {
|
|
370
|
+
if (matchedRecord) return;
|
|
371
|
+
if (uuid && (recordKey === uuid || (record && record.uuid === uuid))) {
|
|
372
|
+
matchedRecord = record;
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
if (name && record && record.name === name) {
|
|
376
|
+
matchedRecord = record;
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
return matchedRecord;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
deleteOriginalModelStoredRecord(map, locator = {}, excludeKey = '') {
|
|
383
|
+
if (!(map instanceof Map)) return;
|
|
384
|
+
const key = this.getOriginalModelChangeLocatorKey(locator);
|
|
385
|
+
const { uuid = '', name = '' } = locator || {};
|
|
386
|
+
Array.from(map.entries()).forEach(([recordKey, record]) => {
|
|
387
|
+
if (excludeKey && recordKey === excludeKey) return;
|
|
388
|
+
const matchByKey = !!key && recordKey === key;
|
|
389
|
+
const matchByUuid = !!uuid && record && record.uuid === uuid;
|
|
390
|
+
const matchByName = !!name && record && record.name === name;
|
|
391
|
+
if (matchByKey || matchByUuid || matchByName) {
|
|
392
|
+
map.delete(recordKey);
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
getOriginalModelBaseline(locator = {}) {
|
|
398
|
+
const baseline = this.findOriginalModelStoredRecord(this.originalModelBaselines, locator);
|
|
399
|
+
return baseline ? JSON.parse(JSON.stringify(baseline)) : null;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
setOriginalModelBaseline(locator = {}, baseline = {}) {
|
|
403
|
+
const key = this.getOriginalModelChangeLocatorKey(locator);
|
|
404
|
+
if (!key) return null;
|
|
405
|
+
const nextBaseline = {
|
|
406
|
+
color:
|
|
407
|
+
Object.prototype.hasOwnProperty.call(baseline, 'color') && baseline.color !== undefined
|
|
408
|
+
? baseline.color
|
|
409
|
+
: null,
|
|
410
|
+
opacity:
|
|
411
|
+
Object.prototype.hasOwnProperty.call(baseline, 'opacity') && baseline.opacity !== undefined
|
|
412
|
+
? Number(baseline.opacity)
|
|
413
|
+
: 1,
|
|
414
|
+
visible:
|
|
415
|
+
Object.prototype.hasOwnProperty.call(baseline, 'visible') && baseline.visible !== undefined
|
|
416
|
+
? !!baseline.visible
|
|
417
|
+
: true,
|
|
418
|
+
name: locator.name || baseline.name || '',
|
|
419
|
+
uuid: locator.uuid || baseline.uuid || '',
|
|
420
|
+
nodeName: this.getOriginalModelNodeName(locator) || baseline.nodeName || '',
|
|
421
|
+
};
|
|
422
|
+
this.deleteOriginalModelStoredRecord(this.originalModelBaselines, locator, key);
|
|
423
|
+
this.originalModelBaselines.set(key, nextBaseline);
|
|
424
|
+
return JSON.parse(JSON.stringify(nextBaseline));
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
setOriginalModelStyleRecord(record = {}, options = {}) {
|
|
428
|
+
const nextRecord = this.buildOriginalModelStyleRecord(record, options);
|
|
429
|
+
if (!nextRecord) return null;
|
|
430
|
+
const key = this.getOriginalModelChangeLocatorKey(nextRecord);
|
|
431
|
+
if (nextRecord.nodeName) {
|
|
432
|
+
this.setOriginalModelNodeName(nextRecord);
|
|
433
|
+
}
|
|
434
|
+
this.deleteOriginalModelStoredRecord(this.originalModelStyleChanges, nextRecord, key);
|
|
435
|
+
this.originalModelStyleChanges.set(key, nextRecord);
|
|
436
|
+
return JSON.parse(JSON.stringify(nextRecord));
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
getOriginalModelStyleRecords() {
|
|
440
|
+
return Array.from(this.originalModelStyleChanges.values())
|
|
441
|
+
.sort((left, right) => right.updatedAt - left.updatedAt)
|
|
442
|
+
.map(item => JSON.parse(JSON.stringify(item)));
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
findSelectableObject(target) {
|
|
446
|
+
return findSelectableSceneObject(target);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
getNodeType(uuid) {
|
|
450
|
+
return getSceneNodeType(this.getObjectByUuid(uuid));
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
getSelectedNodeType() {
|
|
454
|
+
return this.selectedNodeType;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
getObjectMaterials(object) {
|
|
458
|
+
if (!object || !object.material) return [];
|
|
459
|
+
return Array.isArray(object.material)
|
|
460
|
+
? object.material.filter(Boolean)
|
|
461
|
+
: [object.material].filter(Boolean);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
getOriginalModelStyleValue(uuid) {
|
|
465
|
+
const object = this.getObjectByUuid(uuid);
|
|
466
|
+
const materials = this.getObjectMaterials(object);
|
|
467
|
+
const primaryMaterial = materials[0] || null;
|
|
468
|
+
return {
|
|
469
|
+
color:
|
|
470
|
+
primaryMaterial && primaryMaterial.color && primaryMaterial.color.isColor
|
|
471
|
+
? `#${primaryMaterial.color.getHexString()}`
|
|
472
|
+
: null,
|
|
473
|
+
opacity:
|
|
474
|
+
primaryMaterial && typeof primaryMaterial.opacity === 'number'
|
|
475
|
+
? primaryMaterial.opacity
|
|
476
|
+
: 1,
|
|
477
|
+
visible: object ? object.visible !== false : true,
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
mergeOriginalModelStyle(baseStyle = {}, patchStyle = {}) {
|
|
482
|
+
return {
|
|
483
|
+
color:
|
|
484
|
+
patchStyle.color !== undefined
|
|
485
|
+
? patchStyle.color
|
|
486
|
+
: Object.prototype.hasOwnProperty.call(baseStyle, 'color')
|
|
487
|
+
? baseStyle.color
|
|
488
|
+
: null,
|
|
489
|
+
opacity:
|
|
490
|
+
patchStyle.opacity !== undefined
|
|
491
|
+
? Number(patchStyle.opacity)
|
|
492
|
+
: Object.prototype.hasOwnProperty.call(baseStyle, 'opacity')
|
|
493
|
+
? Number(baseStyle.opacity)
|
|
494
|
+
: 1,
|
|
495
|
+
visible:
|
|
496
|
+
patchStyle.visible !== undefined
|
|
497
|
+
? !!patchStyle.visible
|
|
498
|
+
: Object.prototype.hasOwnProperty.call(baseStyle, 'visible')
|
|
499
|
+
? !!baseStyle.visible
|
|
500
|
+
: true,
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// buildOriginalModelPath(object) {
|
|
505
|
+
// const path = [];
|
|
506
|
+
// let current = object;
|
|
507
|
+
// while (current && current.parent) {
|
|
508
|
+
// path.unshift(current.name || current.type || current.uuid);
|
|
509
|
+
// current = current.parent;
|
|
510
|
+
// }
|
|
511
|
+
// return path;
|
|
512
|
+
// }
|
|
513
|
+
|
|
514
|
+
ensureOriginalModelBaseline(uuid) {
|
|
515
|
+
const object = this.getObjectByUuid(uuid);
|
|
516
|
+
if (!object || !isOriginalModelObject(object)) {
|
|
517
|
+
throw new Error('当前节点不是原始模型节点');
|
|
518
|
+
}
|
|
519
|
+
const locator = {
|
|
520
|
+
uuid,
|
|
521
|
+
name: object.name || object.type || '未命名节点',
|
|
522
|
+
};
|
|
523
|
+
const nodeName = this.getOriginalModelNodeName(locator);
|
|
524
|
+
const baseline = this.getOriginalModelBaseline(locator);
|
|
525
|
+
if (baseline) {
|
|
526
|
+
if (!this.originalModelBaselines.has(this.getOriginalModelChangeLocatorKey(locator))) {
|
|
527
|
+
this.setOriginalModelBaseline(locator, baseline);
|
|
528
|
+
}
|
|
529
|
+
return baseline;
|
|
530
|
+
}
|
|
531
|
+
return this.setOriginalModelBaseline(locator, {
|
|
532
|
+
...this.getOriginalModelStyleValue(uuid),
|
|
533
|
+
name: locator.name,
|
|
534
|
+
uuid,
|
|
535
|
+
nodeName,
|
|
536
|
+
// path: this.buildOriginalModelPath(object),
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
applyOriginalModelStyle(uuid, style = {}) {
|
|
541
|
+
const object = this.getObjectByUuid(uuid);
|
|
542
|
+
if (!object || !isOriginalModelObject(object)) {
|
|
543
|
+
throw new Error('当前节点不是原始模型节点');
|
|
544
|
+
}
|
|
545
|
+
const nextStyle = this.mergeOriginalModelStyle(this.getOriginalModelStyleValue(uuid), style);
|
|
546
|
+
object.visible = nextStyle.visible !== false;
|
|
547
|
+
this.getObjectMaterials(object).forEach(material => {
|
|
548
|
+
if (nextStyle.color && material.color && material.color.isColor) {
|
|
549
|
+
material.color.set(nextStyle.color);
|
|
550
|
+
}
|
|
551
|
+
if (Number.isFinite(nextStyle.opacity)) {
|
|
552
|
+
material.opacity = Math.min(Math.max(Number(nextStyle.opacity), 0), 1);
|
|
553
|
+
material.transparent = material.opacity < 1;
|
|
554
|
+
}
|
|
555
|
+
material.needsUpdate = true;
|
|
556
|
+
});
|
|
557
|
+
object.updateMatrixWorld(true);
|
|
558
|
+
return this.getOriginalModelStyleValue(uuid);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
getOriginalModelChangedKeys(baselineStyle = {}, currentStyle = {}) {
|
|
562
|
+
return ['color', 'opacity', 'visible'].filter(key => baselineStyle[key] !== currentStyle[key]);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// buildOriginalModelChangeSummary(name, changedKeys = []) {
|
|
566
|
+
// const labelMap = {
|
|
567
|
+
// color: '变色',
|
|
568
|
+
// opacity: '透明',
|
|
569
|
+
// visible: '隐藏',
|
|
570
|
+
// };
|
|
571
|
+
// return `${name || '未命名节点'}【${changedKeys.map(key => labelMap[key] || key).join('、')}】`;
|
|
572
|
+
// }
|
|
573
|
+
|
|
574
|
+
syncOriginalModelStyleRecord(uuid, options = {}) {
|
|
575
|
+
const object = this.getObjectByUuid(uuid);
|
|
576
|
+
if (!object || !isOriginalModelObject(object)) {
|
|
577
|
+
return null;
|
|
578
|
+
}
|
|
579
|
+
const baseline = this.ensureOriginalModelBaseline(uuid);
|
|
580
|
+
const currentStyle = this.getOriginalModelStyleValue(uuid);
|
|
581
|
+
const changedKeys = this.getOriginalModelChangedKeys(baseline, currentStyle);
|
|
582
|
+
const locator = {
|
|
583
|
+
uuid,
|
|
584
|
+
name: object.name || object.type || '未命名节点',
|
|
585
|
+
};
|
|
586
|
+
const nodeName = this.getOriginalModelNodeName(locator);
|
|
587
|
+
if (changedKeys.length === 0) {
|
|
588
|
+
this.deleteOriginalModelStoredRecord(this.originalModelStyleChanges, locator);
|
|
589
|
+
return null;
|
|
590
|
+
}
|
|
591
|
+
const updatedAt = Number.isFinite(options.updatedAt) ? Number(options.updatedAt) : Date.now();
|
|
592
|
+
return this.setOriginalModelStyleRecord(
|
|
593
|
+
{
|
|
594
|
+
uuid,
|
|
595
|
+
name: locator.name,
|
|
596
|
+
nodeName,
|
|
597
|
+
// path: baseline.path || this.buildOriginalModelPath(object),
|
|
598
|
+
changedKeys,
|
|
599
|
+
before: {
|
|
600
|
+
color: baseline.color,
|
|
601
|
+
opacity: baseline.opacity,
|
|
602
|
+
visible: baseline.visible,
|
|
603
|
+
},
|
|
604
|
+
after: currentStyle,
|
|
605
|
+
// summary: this.buildOriginalModelChangeSummary(object.name || object.type, changedKeys),
|
|
606
|
+
nodeType: SCENE_NODE_TYPE.ORIGINAL_MODEL,
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
updatedAt,
|
|
610
|
+
}
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
getOriginalModelStyle(locatorKey) {
|
|
615
|
+
if (!locatorKey) return null;
|
|
616
|
+
const object = this.getObjectByUuid(locatorKey);
|
|
617
|
+
const locator =
|
|
618
|
+
object && isOriginalModelObject(object)
|
|
619
|
+
? {
|
|
620
|
+
uuid: locatorKey,
|
|
621
|
+
name: object.name || object.type || '未命名节点',
|
|
622
|
+
}
|
|
623
|
+
: {
|
|
624
|
+
uuid: locatorKey,
|
|
625
|
+
name: locatorKey,
|
|
626
|
+
};
|
|
627
|
+
const record = this.findOriginalModelStoredRecord(this.originalModelStyleChanges, locator);
|
|
628
|
+
return record ? JSON.parse(JSON.stringify(record)) : null;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
getOriginalModelChangeRecord(locatorKey) {
|
|
632
|
+
return this.getOriginalModelStyle(locatorKey);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
buildOriginalModelSnapshot(object) {
|
|
636
|
+
if (!object) return null;
|
|
637
|
+
const snapshot = buildObjectSnapshot(object);
|
|
638
|
+
if (!snapshot) return null;
|
|
639
|
+
const nodeName = this.getOriginalModelNodeName({
|
|
640
|
+
uuid: object.uuid,
|
|
641
|
+
name: object.name || object.type || '',
|
|
642
|
+
});
|
|
643
|
+
return {
|
|
644
|
+
...snapshot,
|
|
645
|
+
nodeName: nodeName || snapshot.nodeName || '',
|
|
646
|
+
nodeNameReady: !!(nodeName || snapshot.nodeName),
|
|
647
|
+
nodeType: SCENE_NODE_TYPE.ORIGINAL_MODEL,
|
|
648
|
+
elementType: '',
|
|
649
|
+
geometryParams: null,
|
|
650
|
+
material: snapshot.material || {
|
|
651
|
+
color: null,
|
|
652
|
+
opacity: 1,
|
|
653
|
+
},
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
buildOriginalModelSelectionPayload(uuid) {
|
|
658
|
+
const object = this.getObjectByUuid(uuid);
|
|
659
|
+
if (!object || !isOriginalModelObject(object)) {
|
|
660
|
+
return {
|
|
661
|
+
uuid: '',
|
|
662
|
+
nodeType: SCENE_NODE_TYPE.UNKNOWN,
|
|
663
|
+
data: null,
|
|
664
|
+
changedKeys: [],
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
const currentRecord = this.getOriginalModelChangeRecord(uuid);
|
|
668
|
+
const nodeName = this.getOriginalModelNodeName({
|
|
669
|
+
uuid,
|
|
670
|
+
name: object.name || object.type || '',
|
|
671
|
+
});
|
|
672
|
+
return {
|
|
673
|
+
uuid,
|
|
674
|
+
nodeType: SCENE_NODE_TYPE.ORIGINAL_MODEL,
|
|
675
|
+
data: this.buildOriginalModelSnapshot(object),
|
|
676
|
+
nodeName,
|
|
677
|
+
nodeNameReady: !!nodeName,
|
|
678
|
+
changedKeys:
|
|
679
|
+
currentRecord && Array.isArray(currentRecord.changedKeys)
|
|
680
|
+
? currentRecord.changedKeys.slice()
|
|
681
|
+
: [],
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
buildOriginalModelChangeListItem(record) {
|
|
686
|
+
if (!record) return null;
|
|
687
|
+
const recordKey =
|
|
688
|
+
record.uuid || record.name || `original-model-change-${record.updatedAt || 0}`;
|
|
689
|
+
return {
|
|
690
|
+
key: recordKey,
|
|
691
|
+
uuid: record.uuid || record.name || '',
|
|
692
|
+
name: record.name || '',
|
|
693
|
+
nodeName: record.nodeName || record.name || '',
|
|
694
|
+
nodeNameReady: true,
|
|
695
|
+
nodeType: SCENE_NODE_TYPE.ORIGINAL_MODEL,
|
|
696
|
+
changedKeys: Array.isArray(record.changedKeys) ? record.changedKeys.slice() : [],
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
getOriginalModelStyleChanges() {
|
|
701
|
+
return this.getOriginalModelStyleRecords()
|
|
702
|
+
.map(item => this.buildOriginalModelChangeListItem(item))
|
|
703
|
+
.filter(Boolean);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
buildSelectionPayload(object) {
|
|
707
|
+
const nodeType = getSceneNodeType(object);
|
|
708
|
+
if (nodeType === SCENE_NODE_TYPE.ORIGINAL_MODEL) {
|
|
709
|
+
return this.buildOriginalModelSelectionPayload(object ? object.uuid : '');
|
|
710
|
+
}
|
|
711
|
+
return {
|
|
712
|
+
uuid: object ? object.uuid : '',
|
|
713
|
+
nodeType,
|
|
714
|
+
data: object ? buildObjectSnapshot(object) : null,
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
buildObjectChangedPayload(object, changedKeys = []) {
|
|
719
|
+
if (!object) {
|
|
720
|
+
return {
|
|
721
|
+
uuid: '',
|
|
722
|
+
nodeType: '',
|
|
723
|
+
data: null,
|
|
724
|
+
changedKeys: Array.isArray(changedKeys) ? changedKeys.slice() : [],
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
const snapshot = buildObjectSnapshot(object);
|
|
728
|
+
const nodeType =
|
|
729
|
+
snapshot && Object.prototype.hasOwnProperty.call(snapshot, 'nodeType')
|
|
730
|
+
? snapshot.nodeType || ''
|
|
731
|
+
: '';
|
|
732
|
+
return {
|
|
733
|
+
uuid: object.uuid,
|
|
734
|
+
nodeType,
|
|
735
|
+
data: snapshot,
|
|
736
|
+
changedKeys: Array.isArray(changedKeys) ? changedKeys.slice() : [],
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
selectElement(uuid, options = {}) {
|
|
741
|
+
const object = this.getObjectByUuid(uuid);
|
|
742
|
+
if (
|
|
743
|
+
!object ||
|
|
744
|
+
!(isSelectableObject(object) || isOriginalModelObject(object))
|
|
745
|
+
// || !isObjectVisible(object)
|
|
746
|
+
) {
|
|
747
|
+
this.clearSelection(options);
|
|
748
|
+
return null;
|
|
749
|
+
}
|
|
750
|
+
return this.selectObject(uuid, options);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
selectObject(uuid, options = {}) {
|
|
754
|
+
const object = this.getObjectByUuid(uuid);
|
|
755
|
+
const nodeType = getSceneNodeType(object);
|
|
756
|
+
if (
|
|
757
|
+
object &&
|
|
758
|
+
// isObjectVisible(object) &&
|
|
759
|
+
(isSelectableObject(object) || isOriginalModelObject(object))
|
|
760
|
+
) {
|
|
761
|
+
this.selectedUuid = uuid;
|
|
762
|
+
this.selectedNodeType = nodeType;
|
|
763
|
+
const selectionPayload = this.buildSelectionPayload(object);
|
|
764
|
+
if (options.emitEvent !== false) {
|
|
765
|
+
this.eventBridge.emit(EDITOR_EVENT.OBJECT_SELECTED, selectionPayload);
|
|
766
|
+
if (nodeType === SCENE_NODE_TYPE.ORIGINAL_MODEL) {
|
|
767
|
+
this.eventBridge.emit(EDITOR_EVENT.ORIGINAL_MODEL_SELECTED, selectionPayload);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
return nodeType === SCENE_NODE_TYPE.ORIGINAL_MODEL ? selectionPayload : object;
|
|
771
|
+
}
|
|
772
|
+
this.clearSelection(options);
|
|
773
|
+
return null;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
clearSelection(options = {}) {
|
|
777
|
+
const previousNodeType = this.selectedNodeType;
|
|
778
|
+
this.selectedUuid = '';
|
|
779
|
+
this.selectedNodeType = SCENE_NODE_TYPE.UNKNOWN;
|
|
780
|
+
if (options.emitEvent !== false) {
|
|
781
|
+
this.eventBridge.emit(EDITOR_EVENT.OBJECT_SELECTED, {
|
|
782
|
+
uuid: '',
|
|
783
|
+
nodeType: SCENE_NODE_TYPE.UNKNOWN,
|
|
784
|
+
data: null,
|
|
785
|
+
});
|
|
786
|
+
if (previousNodeType === SCENE_NODE_TYPE.ORIGINAL_MODEL) {
|
|
787
|
+
this.eventBridge.emit(EDITOR_EVENT.ORIGINAL_MODEL_SELECTED, {
|
|
788
|
+
uuid: '',
|
|
789
|
+
data: null,
|
|
790
|
+
changedKeys: [],
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
setTransformMode(mode, options = {}) {
|
|
797
|
+
const nextMode = Object.values(TRANSFORM_MODE).indexOf(mode) !== -1 ? mode : this.transformMode;
|
|
798
|
+
if (!nextMode) return this.transformMode;
|
|
799
|
+
const changed = nextMode !== this.transformMode;
|
|
800
|
+
this.transformMode = nextMode;
|
|
801
|
+
if ((changed || options.forceEmit === true) && options.emitEvent !== false) {
|
|
802
|
+
this.eventBridge.emit(EDITOR_EVENT.TRANSFORM_MODE_CHANGED, {
|
|
803
|
+
mode: this.transformMode,
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
return this.transformMode;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
getTransformMode() {
|
|
810
|
+
return this.transformMode;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
getObjectSnapshot(uuid) {
|
|
814
|
+
return buildObjectSnapshot(this.getObjectByUuid(uuid));
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
getSelectedObjectSnapshot() {
|
|
818
|
+
if (!this.selectedUuid) return null;
|
|
819
|
+
return this.getObjectSnapshot(this.selectedUuid);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
getSelectedOriginalModelStyleSnapshot() {
|
|
823
|
+
if (!this.selectedUuid || this.selectedNodeType !== SCENE_NODE_TYPE.ORIGINAL_MODEL) {
|
|
824
|
+
return null;
|
|
825
|
+
}
|
|
826
|
+
return this.buildOriginalModelSelectionPayload(this.selectedUuid);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
resolveInsertTarget(selectedUuid = this.selectedUuid) {
|
|
830
|
+
const root = this.getCustomRoot();
|
|
831
|
+
const selected = this.getObjectByUuid(selectedUuid);
|
|
832
|
+
if (!selected || !isSelectableObject(selected)) {
|
|
833
|
+
return {
|
|
834
|
+
parentUuid: root.uuid,
|
|
835
|
+
insertMode: 'append',
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
if (isCustomRoot(selected)) {
|
|
840
|
+
return {
|
|
841
|
+
parentUuid: selected.uuid,
|
|
842
|
+
insertMode: 'append',
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
if (isCustomGroup(selected)) {
|
|
847
|
+
return {
|
|
848
|
+
parentUuid: selected.uuid,
|
|
849
|
+
insertMode: 'append',
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
if (isCustomElement(selected)) {
|
|
854
|
+
return {
|
|
855
|
+
parentUuid: selected.parent ? selected.parent.uuid : root.uuid,
|
|
856
|
+
referenceUuid: selected.uuid,
|
|
857
|
+
insertMode: 'after',
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
return {
|
|
862
|
+
parentUuid: root.uuid,
|
|
863
|
+
insertMode: 'append',
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
resolveTargetParent(parentUuid) {
|
|
868
|
+
const root = this.getCustomRoot();
|
|
869
|
+
const parent = this.getObjectByUuid(parentUuid);
|
|
870
|
+
if (!parent) return root;
|
|
871
|
+
if (isCustomRoot(parent) || isCustomGroup(parent)) {
|
|
872
|
+
return parent;
|
|
873
|
+
}
|
|
874
|
+
return root;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
resolveInsertIndex(parent, referenceUuid, insertMode = 'append') {
|
|
878
|
+
if (!parent) return 0;
|
|
879
|
+
if (!referenceUuid || insertMode === 'append') {
|
|
880
|
+
return parent.children.length;
|
|
881
|
+
}
|
|
882
|
+
const referenceObject = this.getObjectByUuid(referenceUuid);
|
|
883
|
+
if (!referenceObject || referenceObject.parent !== parent) {
|
|
884
|
+
return parent.children.length;
|
|
885
|
+
}
|
|
886
|
+
const referenceIndex = parent.children.indexOf(referenceObject);
|
|
887
|
+
if (referenceIndex === -1) {
|
|
888
|
+
return parent.children.length;
|
|
889
|
+
}
|
|
890
|
+
if (insertMode === 'before') {
|
|
891
|
+
return referenceIndex;
|
|
892
|
+
}
|
|
893
|
+
if (insertMode === 'after') {
|
|
894
|
+
return referenceIndex + 1;
|
|
895
|
+
}
|
|
896
|
+
return parent.children.length;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
executeCommand(command, options = {}) {
|
|
900
|
+
const result = this.history.execute(command);
|
|
901
|
+
this.requestRender();
|
|
902
|
+
this.emitCommandEvents(result.command, options.action || 'execute', {
|
|
903
|
+
merged: result.merged,
|
|
904
|
+
transformMode: options.transformMode,
|
|
905
|
+
});
|
|
906
|
+
return result.command.getObject ? result.command.getObject() : null;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
emitCommandEvents(command, action, options = {}) {
|
|
910
|
+
if (!command) return;
|
|
911
|
+
|
|
912
|
+
if (command.type === COMMAND_TYPE.MULTI) {
|
|
913
|
+
command.commands.forEach(childCommand => {
|
|
914
|
+
this.emitSingleCommandEvents(childCommand, action, {
|
|
915
|
+
...options,
|
|
916
|
+
skipHistoryChanged: true,
|
|
917
|
+
});
|
|
918
|
+
});
|
|
919
|
+
this.emitHistoryChanged(action, command, options.merged);
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
this.emitSingleCommandEvents(command, action, options);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
emitSingleCommandEvents(command, action, options = {}) {
|
|
927
|
+
const object = command.getObject ? command.getObject() : null;
|
|
928
|
+
const parentUuid = object && object.parent ? object.parent.uuid : command.parentUuid || '';
|
|
929
|
+
if (
|
|
930
|
+
command.type === COMMAND_TYPE.ADD_ELEMENT ||
|
|
931
|
+
command.type === COMMAND_TYPE.ADD_GROUP ||
|
|
932
|
+
command.type === COMMAND_TYPE.CLONE_ELEMENT
|
|
933
|
+
) {
|
|
934
|
+
if (action === 'execute' || action === 'redo') {
|
|
935
|
+
this.selectedUuid = object ? object.uuid : '';
|
|
936
|
+
this.eventBridge.emit(EDITOR_EVENT.OBJECT_ADDED, {
|
|
937
|
+
uuid: object ? object.uuid : '',
|
|
938
|
+
parentUuid,
|
|
939
|
+
nodeType: object && object.userData ? object.userData.nodeType : '',
|
|
940
|
+
elementType: object && object.userData ? object.userData.elementType : '',
|
|
941
|
+
});
|
|
942
|
+
this.eventBridge.emit(EDITOR_EVENT.OBJECT_SELECTED, {
|
|
943
|
+
uuid: object ? object.uuid : '',
|
|
944
|
+
nodeType: object ? getSceneNodeType(object) : SCENE_NODE_TYPE.UNKNOWN,
|
|
945
|
+
data: object ? this.getObjectSnapshot(object.uuid) : null,
|
|
946
|
+
});
|
|
947
|
+
this.eventBridge.emitSceneGraphChanged();
|
|
948
|
+
this.setTransformMode(options.transformMode || TRANSFORM_MODE.TRANSLATE, {
|
|
949
|
+
forceEmit: true,
|
|
950
|
+
});
|
|
951
|
+
} else {
|
|
952
|
+
if (this.selectedUuid === (object && object.uuid)) {
|
|
953
|
+
this.clearSelection();
|
|
954
|
+
}
|
|
955
|
+
this.eventBridge.emit(EDITOR_EVENT.OBJECT_REMOVED, {
|
|
956
|
+
uuid: object ? object.uuid : '',
|
|
957
|
+
parentUuid: command.parentUuid || '',
|
|
958
|
+
removedTree: buildTreeSnapshot(object),
|
|
959
|
+
});
|
|
960
|
+
this.eventBridge.emitSceneGraphChanged();
|
|
961
|
+
}
|
|
962
|
+
this.emitHistoryChanged(action, command, options.merged);
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
if (command.type === COMMAND_TYPE.REMOVE_ELEMENT) {
|
|
967
|
+
if (action === 'execute' || action === 'redo') {
|
|
968
|
+
if (
|
|
969
|
+
object &&
|
|
970
|
+
this.selectedUuid &&
|
|
971
|
+
(this.selectedUuid === object.uuid ||
|
|
972
|
+
object.getObjectByProperty('uuid', this.selectedUuid))
|
|
973
|
+
) {
|
|
974
|
+
this.clearSelection();
|
|
975
|
+
}
|
|
976
|
+
this.eventBridge.emit(EDITOR_EVENT.OBJECT_REMOVED, {
|
|
977
|
+
uuid: object ? object.uuid : '',
|
|
978
|
+
parentUuid: command.parentUuid || '',
|
|
979
|
+
removedTree: buildTreeSnapshot(object),
|
|
980
|
+
});
|
|
981
|
+
} else {
|
|
982
|
+
this.eventBridge.emit(EDITOR_EVENT.OBJECT_ADDED, {
|
|
983
|
+
uuid: object ? object.uuid : '',
|
|
984
|
+
parentUuid,
|
|
985
|
+
nodeType: object && object.userData ? object.userData.nodeType : '',
|
|
986
|
+
elementType: object && object.userData ? object.userData.elementType : '',
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
this.eventBridge.emitSceneGraphChanged();
|
|
990
|
+
this.emitHistoryChanged(action, command, options.merged);
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
if (command.type === COMMAND_TYPE.MOVE_ELEMENT) {
|
|
995
|
+
this.eventBridge.emit(
|
|
996
|
+
EDITOR_EVENT.OBJECT_CHANGED,
|
|
997
|
+
this.buildObjectChangedPayload(object, ['parentUuid'])
|
|
998
|
+
);
|
|
999
|
+
this.eventBridge.emitSceneGraphChanged();
|
|
1000
|
+
this.emitHistoryChanged(action, command, options.merged);
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
if (
|
|
1005
|
+
command.type === COMMAND_TYPE.SET_ORIGINAL_MODEL_STYLE ||
|
|
1006
|
+
command.type === COMMAND_TYPE.RESET_ORIGINAL_MODEL_STYLE
|
|
1007
|
+
) {
|
|
1008
|
+
const currentObject = object || this.getObjectByUuid(command.uuid);
|
|
1009
|
+
const originalModelPayload =
|
|
1010
|
+
currentObject && currentObject.uuid
|
|
1011
|
+
? this.buildOriginalModelSelectionPayload(currentObject.uuid)
|
|
1012
|
+
: {
|
|
1013
|
+
uuid: command.uuid || '',
|
|
1014
|
+
data: null,
|
|
1015
|
+
changedKeys: [],
|
|
1016
|
+
};
|
|
1017
|
+
const payload = {
|
|
1018
|
+
uuid: originalModelPayload.uuid,
|
|
1019
|
+
nodeType: SCENE_NODE_TYPE.ORIGINAL_MODEL,
|
|
1020
|
+
data: originalModelPayload.data,
|
|
1021
|
+
changedKeys: Array.isArray(originalModelPayload.changedKeys)
|
|
1022
|
+
? originalModelPayload.changedKeys.slice()
|
|
1023
|
+
: [],
|
|
1024
|
+
};
|
|
1025
|
+
this.eventBridge.emit(EDITOR_EVENT.OBJECT_CHANGED, payload);
|
|
1026
|
+
if (command.type === COMMAND_TYPE.RESET_ORIGINAL_MODEL_STYLE) {
|
|
1027
|
+
this.eventBridge.emit(EDITOR_EVENT.ORIGINAL_MODEL_STYLE_RESET, payload);
|
|
1028
|
+
// name: currentObject ? currentObject.name || currentObject.type || '未命名节点' : '',
|
|
1029
|
+
} else {
|
|
1030
|
+
this.eventBridge.emit(EDITOR_EVENT.ORIGINAL_MODEL_STYLE_CHANGED, payload);
|
|
1031
|
+
}
|
|
1032
|
+
this.eventBridge.emitOriginalModelChangeListChanged();
|
|
1033
|
+
if (
|
|
1034
|
+
currentObject &&
|
|
1035
|
+
this.selectedUuid === currentObject.uuid &&
|
|
1036
|
+
this.selectedNodeType === SCENE_NODE_TYPE.ORIGINAL_MODEL
|
|
1037
|
+
) {
|
|
1038
|
+
this.eventBridge.emit(
|
|
1039
|
+
EDITOR_EVENT.ORIGINAL_MODEL_SELECTED,
|
|
1040
|
+
this.buildOriginalModelSelectionPayload(currentObject.uuid)
|
|
1041
|
+
);
|
|
1042
|
+
}
|
|
1043
|
+
this.emitHistoryChanged(action, command, options.merged);
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
this.eventBridge.emit(
|
|
1048
|
+
EDITOR_EVENT.OBJECT_CHANGED,
|
|
1049
|
+
this.buildObjectChangedPayload(object, command.getChangedKeys())
|
|
1050
|
+
);
|
|
1051
|
+
if (command.getChangedKeys().indexOf('name') !== -1) {
|
|
1052
|
+
this.eventBridge.emitSceneGraphChanged();
|
|
1053
|
+
}
|
|
1054
|
+
this.emitHistoryChanged(action, command, options.merged);
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
emitHistoryChanged(action, command, merged) {
|
|
1058
|
+
this.eventBridge.emit(EDITOR_EVENT.HISTORY_CHANGED, {
|
|
1059
|
+
action,
|
|
1060
|
+
commandType: command.type,
|
|
1061
|
+
commandName: command.name,
|
|
1062
|
+
merged: !!merged,
|
|
1063
|
+
...this.history.getState(),
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
addElement(options = {}) {
|
|
1068
|
+
this.ensureReady();
|
|
1069
|
+
const target =
|
|
1070
|
+
options.parentUuid || options.referenceUuid
|
|
1071
|
+
? {
|
|
1072
|
+
parentUuid:
|
|
1073
|
+
options.parentUuid ||
|
|
1074
|
+
(() => {
|
|
1075
|
+
const referenceObject = this.getObjectByUuid(options.referenceUuid);
|
|
1076
|
+
return referenceObject && referenceObject.parent ? referenceObject.parent.uuid : '';
|
|
1077
|
+
})(),
|
|
1078
|
+
referenceUuid: options.referenceUuid,
|
|
1079
|
+
insertMode: options.insertMode || 'append',
|
|
1080
|
+
}
|
|
1081
|
+
: this.resolveInsertTarget(options.selectedUuid);
|
|
1082
|
+
const parent = this.resolveTargetParent(target.parentUuid);
|
|
1083
|
+
const object = createElementByType(this.THREE, options.type, parent, {
|
|
1084
|
+
name: options.name || createDefaultElementName(parent, options.type),
|
|
1085
|
+
transform: options.transform,
|
|
1086
|
+
geometryParams: options.geometryParams,
|
|
1087
|
+
});
|
|
1088
|
+
const index = this.resolveInsertIndex(parent, target.referenceUuid, target.insertMode);
|
|
1089
|
+
const command = new AddElementCommand(this, object, {
|
|
1090
|
+
parentUuid: parent.uuid,
|
|
1091
|
+
index,
|
|
1092
|
+
});
|
|
1093
|
+
return this.executeCommand(command, {
|
|
1094
|
+
transformMode: TRANSFORM_MODE.TRANSLATE,
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
addGroup(options = {}) {
|
|
1099
|
+
this.ensureReady();
|
|
1100
|
+
const target =
|
|
1101
|
+
options.parentUuid || options.referenceUuid
|
|
1102
|
+
? {
|
|
1103
|
+
parentUuid:
|
|
1104
|
+
options.parentUuid ||
|
|
1105
|
+
(() => {
|
|
1106
|
+
const referenceObject = this.getObjectByUuid(options.referenceUuid);
|
|
1107
|
+
return referenceObject && referenceObject.parent ? referenceObject.parent.uuid : '';
|
|
1108
|
+
})(),
|
|
1109
|
+
referenceUuid: options.referenceUuid,
|
|
1110
|
+
insertMode: options.insertMode || 'append',
|
|
1111
|
+
}
|
|
1112
|
+
: this.resolveInsertTarget(options.selectedUuid);
|
|
1113
|
+
const parent = this.resolveTargetParent(target.parentUuid);
|
|
1114
|
+
const object = createGroupElement(this.THREE, parent, {
|
|
1115
|
+
name: options.name || createDefaultGroupName(parent),
|
|
1116
|
+
transform: options.transform,
|
|
1117
|
+
});
|
|
1118
|
+
const index = this.resolveInsertIndex(parent, target.referenceUuid, target.insertMode);
|
|
1119
|
+
const command = new AddGroupCommand(this, object, {
|
|
1120
|
+
parentUuid: parent.uuid,
|
|
1121
|
+
index,
|
|
1122
|
+
});
|
|
1123
|
+
return this.executeCommand(command, {
|
|
1124
|
+
transformMode: TRANSFORM_MODE.TRANSLATE,
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
removeElement(uuid) {
|
|
1129
|
+
const object = this.getObjectByUuid(uuid);
|
|
1130
|
+
if (!object || !isDeletableObject(object)) {
|
|
1131
|
+
throw new Error('当前节点不允许删除');
|
|
1132
|
+
}
|
|
1133
|
+
const command = new RemoveElementCommand(this, uuid);
|
|
1134
|
+
this.executeCommand(command);
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
cloneElement(uuid, options = {}) {
|
|
1138
|
+
const source = this.getObjectByUuid(uuid);
|
|
1139
|
+
if (!source || !isCloneableObject(source)) {
|
|
1140
|
+
throw new Error('当前节点不允许复制');
|
|
1141
|
+
}
|
|
1142
|
+
const referenceObject = options.referenceUuid
|
|
1143
|
+
? this.getObjectByUuid(options.referenceUuid)
|
|
1144
|
+
: null;
|
|
1145
|
+
const targetParent = this.resolveTargetParent(
|
|
1146
|
+
options.parentUuid ||
|
|
1147
|
+
(referenceObject && referenceObject.parent ? referenceObject.parent.uuid : '') ||
|
|
1148
|
+
(source.parent ? source.parent.uuid : '')
|
|
1149
|
+
);
|
|
1150
|
+
const index =
|
|
1151
|
+
typeof options.referenceUuid !== 'undefined' || typeof options.insertMode !== 'undefined'
|
|
1152
|
+
? this.resolveInsertIndex(
|
|
1153
|
+
targetParent,
|
|
1154
|
+
options.referenceUuid,
|
|
1155
|
+
options.insertMode || 'append'
|
|
1156
|
+
)
|
|
1157
|
+
: clampInsertIndex(targetParent, getObjectIndex(source) + 1);
|
|
1158
|
+
const command = new CloneElementCommand(this, uuid, {
|
|
1159
|
+
parentUuid: targetParent.uuid,
|
|
1160
|
+
index,
|
|
1161
|
+
positionOffset: options.positionOffset,
|
|
1162
|
+
disableAutoOffset: options.disableAutoOffset,
|
|
1163
|
+
});
|
|
1164
|
+
return this.executeCommand(command, {
|
|
1165
|
+
transformMode: TRANSFORM_MODE.TRANSLATE,
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
moveElement(uuid, targetParentUuid, referenceUuid, insertMode = 'append') {
|
|
1170
|
+
const object = this.getObjectByUuid(uuid);
|
|
1171
|
+
if (!object || !isEditableObject(object)) {
|
|
1172
|
+
throw new Error('当前节点不允许移动');
|
|
1173
|
+
}
|
|
1174
|
+
const targetParent = this.resolveTargetParent(targetParentUuid);
|
|
1175
|
+
if (isAncestor(object, targetParent)) {
|
|
1176
|
+
throw new Error('不允许把节点移动到自己的后代节点下');
|
|
1177
|
+
}
|
|
1178
|
+
const index = this.resolveInsertIndex(targetParent, referenceUuid, insertMode);
|
|
1179
|
+
const command = new MoveElementCommand(this, uuid, {
|
|
1180
|
+
targetParentUuid: targetParent.uuid,
|
|
1181
|
+
index,
|
|
1182
|
+
});
|
|
1183
|
+
return this.executeCommand(command);
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
updateElement(options = {}) {
|
|
1187
|
+
const object = this.getObjectByUuid(options.uuid);
|
|
1188
|
+
if (!object || !isEditableObject(object)) {
|
|
1189
|
+
throw new Error('当前节点不允许编辑');
|
|
1190
|
+
}
|
|
1191
|
+
const commands = [];
|
|
1192
|
+
if (options.name !== undefined) {
|
|
1193
|
+
commands.push(new SetValueCommand(this, options.uuid, 'name', options.name));
|
|
1194
|
+
}
|
|
1195
|
+
if (options.visible !== undefined) {
|
|
1196
|
+
commands.push(new SetValueCommand(this, options.uuid, 'visible', !!options.visible));
|
|
1197
|
+
}
|
|
1198
|
+
if (Array.isArray(options.position)) {
|
|
1199
|
+
commands.push(new SetPositionCommand(this, options.uuid, options.position));
|
|
1200
|
+
}
|
|
1201
|
+
if (Array.isArray(options.rotation)) {
|
|
1202
|
+
commands.push(new SetRotationCommand(this, options.uuid, options.rotation));
|
|
1203
|
+
}
|
|
1204
|
+
if (Array.isArray(options.scale)) {
|
|
1205
|
+
commands.push(new SetScaleCommand(this, options.uuid, options.scale));
|
|
1206
|
+
}
|
|
1207
|
+
if (options.material && options.material.color !== undefined) {
|
|
1208
|
+
commands.push(
|
|
1209
|
+
new SetValueCommand(this, options.uuid, 'material.color', options.material.color)
|
|
1210
|
+
);
|
|
1211
|
+
}
|
|
1212
|
+
if (options.material && options.material.opacity !== undefined) {
|
|
1213
|
+
commands.push(
|
|
1214
|
+
new SetValueCommand(
|
|
1215
|
+
this,
|
|
1216
|
+
options.uuid,
|
|
1217
|
+
'material.opacity',
|
|
1218
|
+
Number(options.material.opacity)
|
|
1219
|
+
)
|
|
1220
|
+
);
|
|
1221
|
+
}
|
|
1222
|
+
if (!object.isGroup && options.geometryParams) {
|
|
1223
|
+
commands.push(new SetGeometryParamsCommand(this, options.uuid, options.geometryParams));
|
|
1224
|
+
}
|
|
1225
|
+
if (commands.length === 0) {
|
|
1226
|
+
return object;
|
|
1227
|
+
}
|
|
1228
|
+
const command =
|
|
1229
|
+
commands.length === 1
|
|
1230
|
+
? commands[0]
|
|
1231
|
+
: new MultiCommand(this, commands, { name: '批量更新元素' });
|
|
1232
|
+
return this.executeCommand(command);
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
updateOriginalModelStyle(options = {}) {
|
|
1236
|
+
const object = this.getObjectByUuid(options.uuid);
|
|
1237
|
+
if (!object || !isOriginalModelObject(object)) {
|
|
1238
|
+
throw new Error('当前节点不是原始模型节点');
|
|
1239
|
+
}
|
|
1240
|
+
const nextStyle = {};
|
|
1241
|
+
if (options.color !== undefined) {
|
|
1242
|
+
nextStyle.color = options.color;
|
|
1243
|
+
}
|
|
1244
|
+
if (options.opacity !== undefined) {
|
|
1245
|
+
nextStyle.opacity = Number(options.opacity);
|
|
1246
|
+
}
|
|
1247
|
+
if (options.visible !== undefined) {
|
|
1248
|
+
nextStyle.visible = !!options.visible;
|
|
1249
|
+
}
|
|
1250
|
+
if (Object.keys(nextStyle).length === 0) {
|
|
1251
|
+
return object;
|
|
1252
|
+
}
|
|
1253
|
+
const currentStyle = this.getOriginalModelStyleValue(options.uuid);
|
|
1254
|
+
const mergedStyle = this.mergeOriginalModelStyle(currentStyle, nextStyle);
|
|
1255
|
+
if (this.getOriginalModelChangedKeys(currentStyle, mergedStyle).length === 0) {
|
|
1256
|
+
return object;
|
|
1257
|
+
}
|
|
1258
|
+
return this.executeCommand(new SetOriginalModelStyleCommand(this, options.uuid, nextStyle));
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
resetOriginalModelStyle(uuid) {
|
|
1262
|
+
const object = this.getObjectByUuid(uuid);
|
|
1263
|
+
if (!object || !isOriginalModelObject(object)) {
|
|
1264
|
+
throw new Error('当前节点不是原始模型节点');
|
|
1265
|
+
}
|
|
1266
|
+
const baseline = this.ensureOriginalModelBaseline(uuid);
|
|
1267
|
+
const currentStyle = this.getOriginalModelStyleValue(uuid);
|
|
1268
|
+
if (this.getOriginalModelChangedKeys(baseline, currentStyle).length === 0) {
|
|
1269
|
+
return object;
|
|
1270
|
+
}
|
|
1271
|
+
return this.executeCommand(new ResetOriginalModelStyleCommand(this, uuid));
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
setElementPosition(uuid, position, oldPosition) {
|
|
1275
|
+
return this.executeCommand(new SetPositionCommand(this, uuid, position, oldPosition));
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
setElementRotation(uuid, rotation, oldRotation) {
|
|
1279
|
+
return this.executeCommand(new SetRotationCommand(this, uuid, rotation, oldRotation));
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
setElementScale(uuid, scale, oldScale) {
|
|
1283
|
+
return this.executeCommand(new SetScaleCommand(this, uuid, scale, oldScale));
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
renameElement(uuid, name) {
|
|
1287
|
+
return this.executeCommand(new SetValueCommand(this, uuid, 'name', name));
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
setElementVisible(uuid, visible) {
|
|
1291
|
+
return this.executeCommand(new SetValueCommand(this, uuid, 'visible', !!visible));
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
undo() {
|
|
1295
|
+
const command = this.history.undo();
|
|
1296
|
+
if (!command) return null;
|
|
1297
|
+
this.requestRender();
|
|
1298
|
+
this.emitCommandEvents(command, 'undo');
|
|
1299
|
+
return command.getObject ? command.getObject() : null;
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
redo() {
|
|
1303
|
+
const command = this.history.redo();
|
|
1304
|
+
if (!command) return null;
|
|
1305
|
+
this.requestRender();
|
|
1306
|
+
this.emitCommandEvents(command, 'redo');
|
|
1307
|
+
return command.getObject ? command.getObject() : null;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
getCustomTree() {
|
|
1311
|
+
return buildCustomTree(this.getCustomRoot());
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
getCustomSaveTree() {
|
|
1315
|
+
return buildCustomSaveTree(this.getCustomRoot());
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
buildOriginalModelSaveItem(record) {
|
|
1319
|
+
const styleRecord = this.buildOriginalModelStyleRecord(record);
|
|
1320
|
+
if (!styleRecord) return null;
|
|
1321
|
+
return {
|
|
1322
|
+
name: styleRecord.name || '',
|
|
1323
|
+
nodeName: styleRecord.nodeName || styleRecord.name || '',
|
|
1324
|
+
nodeNameReady: true,
|
|
1325
|
+
nodeType: SCENE_NODE_TYPE.ORIGINAL_MODEL,
|
|
1326
|
+
changedKeys: styleRecord.changedKeys.slice(),
|
|
1327
|
+
color: styleRecord.after.color,
|
|
1328
|
+
opacity: styleRecord.after.opacity,
|
|
1329
|
+
visible: styleRecord.after.visible,
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
getOriginalModelSaveChanges() {
|
|
1334
|
+
return this.getOriginalModelStyleRecords()
|
|
1335
|
+
.map(item => this.buildOriginalModelSaveItem(item))
|
|
1336
|
+
.filter(Boolean);
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
getSaveSnapshot() {
|
|
1340
|
+
return {
|
|
1341
|
+
customModelTree: this.getCustomSaveTree(),
|
|
1342
|
+
originalModelList: this.getOriginalModelSaveChanges(),
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
createCustomObjectFromSaveNode(node, parent) {
|
|
1347
|
+
if (!node || !parent) return null;
|
|
1348
|
+
const normalizedNode = this.normalizeCustomSaveNode(node);
|
|
1349
|
+
const nodeType = normalizedNode.nodeType || '';
|
|
1350
|
+
if (nodeType === SCENE_NODE_TYPE.CUSTOM_GROUP) {
|
|
1351
|
+
const group = createGroupElement(this.THREE, parent, {
|
|
1352
|
+
name: normalizedNode.name || '',
|
|
1353
|
+
transform: {
|
|
1354
|
+
position: normalizedNode.position,
|
|
1355
|
+
rotation: normalizedNode.rotation,
|
|
1356
|
+
scale: normalizedNode.scale,
|
|
1357
|
+
},
|
|
1358
|
+
source: 'load',
|
|
1359
|
+
});
|
|
1360
|
+
group.visible = normalizedNode.visible !== false;
|
|
1361
|
+
return group;
|
|
1362
|
+
}
|
|
1363
|
+
if (nodeType === SCENE_NODE_TYPE.CUSTOM_ELEMENT) {
|
|
1364
|
+
const element = createElementByType(this.THREE, node.elementType, parent, {
|
|
1365
|
+
name: normalizedNode.name || '',
|
|
1366
|
+
transform: {
|
|
1367
|
+
position: normalizedNode.position,
|
|
1368
|
+
rotation: normalizedNode.rotation,
|
|
1369
|
+
scale: normalizedNode.scale,
|
|
1370
|
+
},
|
|
1371
|
+
geometryParams: normalizedNode.geometryParams || {},
|
|
1372
|
+
material: normalizedNode.material || {},
|
|
1373
|
+
source: 'load',
|
|
1374
|
+
});
|
|
1375
|
+
element.visible = normalizedNode.visible !== false;
|
|
1376
|
+
return element;
|
|
1377
|
+
}
|
|
1378
|
+
return null;
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
restoreCustomSaveTree(snapshot) {
|
|
1382
|
+
const root = this.getCustomRoot();
|
|
1383
|
+
while (root.children.length > 0) {
|
|
1384
|
+
const child = root.children[0];
|
|
1385
|
+
root.remove(child);
|
|
1386
|
+
disposeObjectResources(child);
|
|
1387
|
+
}
|
|
1388
|
+
const sourceChildren =
|
|
1389
|
+
snapshot && Array.isArray(snapshot.children)
|
|
1390
|
+
? snapshot.children
|
|
1391
|
+
: Array.isArray(snapshot)
|
|
1392
|
+
? snapshot
|
|
1393
|
+
: [];
|
|
1394
|
+
const appendChildren = (children, parent) => {
|
|
1395
|
+
children.forEach(childNode => {
|
|
1396
|
+
const object = this.createCustomObjectFromSaveNode(childNode, parent);
|
|
1397
|
+
if (!object) return;
|
|
1398
|
+
insertObjectAt(parent, object, parent.children.length);
|
|
1399
|
+
if (Array.isArray(childNode.children) && childNode.children.length > 0) {
|
|
1400
|
+
appendChildren(childNode.children, object);
|
|
1401
|
+
}
|
|
1402
|
+
});
|
|
1403
|
+
};
|
|
1404
|
+
appendChildren(sourceChildren, root);
|
|
1405
|
+
this.normalizeCustomObjectFlags(root);
|
|
1406
|
+
this.normalizeCustomElementGeometryParams(root);
|
|
1407
|
+
return root;
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
restoreOriginalModelSaveChanges(changes = []) {
|
|
1411
|
+
const nextChanges = Array.isArray(changes)
|
|
1412
|
+
? changes.map(item => this.normalizeOriginalModelSaveItem(item))
|
|
1413
|
+
: [];
|
|
1414
|
+
this.getOriginalModelStyleRecords().forEach(record => {
|
|
1415
|
+
const object = this.resolveOriginalModelObject(record);
|
|
1416
|
+
const baseline = this.getOriginalModelBaseline(record);
|
|
1417
|
+
if (!baseline || !object || !isOriginalModelObject(object)) {
|
|
1418
|
+
return;
|
|
1419
|
+
}
|
|
1420
|
+
this.applyOriginalModelStyle(object.uuid, baseline);
|
|
1421
|
+
});
|
|
1422
|
+
this.originalModelStyleChanges.clear();
|
|
1423
|
+
nextChanges.forEach(item => {
|
|
1424
|
+
this.setOriginalModelStyleRecord(item);
|
|
1425
|
+
});
|
|
1426
|
+
this.retryPendingOriginalModelSaveChanges();
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
retryPendingOriginalModelSaveChanges() {
|
|
1430
|
+
const retryChanges = this.getOriginalModelStyleRecords();
|
|
1431
|
+
if (retryChanges.length === 0) {
|
|
1432
|
+
return [];
|
|
1433
|
+
}
|
|
1434
|
+
const unresolved = [];
|
|
1435
|
+
let shouldRender = false;
|
|
1436
|
+
retryChanges.forEach(item => {
|
|
1437
|
+
if (!item || !item.after) return;
|
|
1438
|
+
const object = this.resolveOriginalModelObject(item);
|
|
1439
|
+
if (!object || !isOriginalModelObject(object)) {
|
|
1440
|
+
unresolved.push(item);
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1443
|
+
const targetStyle = this.cloneOriginalModelStyle(item.after);
|
|
1444
|
+
const currentStyle = this.getOriginalModelStyleValue(object.uuid);
|
|
1445
|
+
this.ensureOriginalModelBaseline(object.uuid);
|
|
1446
|
+
if (!this.isSameOriginalModelStyle(currentStyle, targetStyle)) {
|
|
1447
|
+
this.applyOriginalModelStyle(object.uuid, targetStyle);
|
|
1448
|
+
shouldRender = true;
|
|
1449
|
+
}
|
|
1450
|
+
this.syncOriginalModelStyleRecord(object.uuid, {
|
|
1451
|
+
updatedAt: item.updatedAt,
|
|
1452
|
+
});
|
|
1453
|
+
});
|
|
1454
|
+
if (shouldRender) {
|
|
1455
|
+
this.requestRender();
|
|
1456
|
+
}
|
|
1457
|
+
return unresolved.slice();
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
applySaveSnapshot(snapshot = {}) {
|
|
1461
|
+
const payload = this.normalizeSaveSnapshot(snapshot || {});
|
|
1462
|
+
const hasCustomModelTree = Object.prototype.hasOwnProperty.call(payload, 'customModelTree');
|
|
1463
|
+
const hasOriginalModelChanges = Object.prototype.hasOwnProperty.call(
|
|
1464
|
+
payload,
|
|
1465
|
+
'originalModelList'
|
|
1466
|
+
);
|
|
1467
|
+
if (hasCustomModelTree) {
|
|
1468
|
+
this.restoreCustomSaveTree(payload.customModelTree);
|
|
1469
|
+
}
|
|
1470
|
+
if (hasOriginalModelChanges) {
|
|
1471
|
+
this.restoreOriginalModelSaveChanges(payload.originalModelList);
|
|
1472
|
+
}
|
|
1473
|
+
this.history.clear();
|
|
1474
|
+
this.clearSelection();
|
|
1475
|
+
this.eventBridge.emitSceneGraphChanged();
|
|
1476
|
+
this.eventBridge.emitOriginalModelChangeListChanged();
|
|
1477
|
+
this.emitHistoryChanged('reset', {
|
|
1478
|
+
type: 'ApplySaveSnapshot',
|
|
1479
|
+
name: '回放技改方案',
|
|
1480
|
+
});
|
|
1481
|
+
this.requestRender();
|
|
1482
|
+
return this.getSaveSnapshot();
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
serializeCustomElements() {
|
|
1486
|
+
return this.getCustomRoot().toJSON();
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
deserializeCustomElements(json) {
|
|
1490
|
+
if (!json) return null;
|
|
1491
|
+
const root = this.getCustomRoot();
|
|
1492
|
+
while (root.children.length > 0) {
|
|
1493
|
+
const child = root.children[0];
|
|
1494
|
+
root.remove(child);
|
|
1495
|
+
disposeObjectResources(child);
|
|
1496
|
+
}
|
|
1497
|
+
const parsedRoot = this.parseObjectJSON(json);
|
|
1498
|
+
while (parsedRoot.children.length > 0) {
|
|
1499
|
+
const child = parsedRoot.children.shift();
|
|
1500
|
+
insertObjectAt(root, child, root.children.length);
|
|
1501
|
+
}
|
|
1502
|
+
this.normalizeCustomObjectFlags(root);
|
|
1503
|
+
this.normalizeCustomElementGeometryParams(root);
|
|
1504
|
+
this.history.clear();
|
|
1505
|
+
this.clearSelection();
|
|
1506
|
+
this.eventBridge.emitSceneGraphChanged();
|
|
1507
|
+
this.emitHistoryChanged('reset', {
|
|
1508
|
+
type: 'DeserializeCustomElements',
|
|
1509
|
+
name: '反序列化自定义元素',
|
|
1510
|
+
});
|
|
1511
|
+
return root;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
removeAllCustomElements() {
|
|
1515
|
+
const root = this.getCustomRoot();
|
|
1516
|
+
while (root.children.length > 0) {
|
|
1517
|
+
const child = root.children[0];
|
|
1518
|
+
root.remove(child);
|
|
1519
|
+
disposeObjectResources(child);
|
|
1520
|
+
}
|
|
1521
|
+
this.history.clear();
|
|
1522
|
+
this.clearSelection();
|
|
1523
|
+
this.eventBridge.emitSceneGraphChanged();
|
|
1524
|
+
this.emitHistoryChanged('reset', {
|
|
1525
|
+
type: 'ClearCustomElements',
|
|
1526
|
+
name: '清空自定义元素',
|
|
1527
|
+
});
|
|
1528
|
+
}
|
|
1529
|
+
}
|