@inweb/viewer-three 27.4.7 → 27.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extensions/components/AxesHelperComponent.js +3 -0
- package/dist/extensions/components/AxesHelperComponent.js.map +1 -1
- package/dist/extensions/components/AxesHelperComponent.min.js +1 -1
- package/dist/extensions/components/AxesHelperComponent.module.js +3 -0
- package/dist/extensions/components/AxesHelperComponent.module.js.map +1 -1
- package/dist/extensions/components/ExtentsHelperComponent.js +6 -2
- package/dist/extensions/components/ExtentsHelperComponent.js.map +1 -1
- package/dist/extensions/components/ExtentsHelperComponent.min.js +1 -1
- package/dist/extensions/components/ExtentsHelperComponent.module.js +6 -2
- package/dist/extensions/components/ExtentsHelperComponent.module.js.map +1 -1
- package/dist/extensions/components/GridHelperComponent.js +1 -0
- package/dist/extensions/components/GridHelperComponent.js.map +1 -1
- package/dist/extensions/components/GridHelperComponent.min.js +1 -1
- package/dist/extensions/components/GridHelperComponent.module.js +1 -0
- package/dist/extensions/components/GridHelperComponent.module.js.map +1 -1
- package/dist/extensions/components/LightHelperComponent.js +2 -1
- package/dist/extensions/components/LightHelperComponent.js.map +1 -1
- package/dist/extensions/components/LightHelperComponent.min.js +1 -1
- package/dist/extensions/components/LightHelperComponent.module.js +2 -1
- package/dist/extensions/components/LightHelperComponent.module.js.map +1 -1
- package/dist/extensions/components/StatsPanelComponent.js +0 -1
- package/dist/extensions/components/StatsPanelComponent.js.map +1 -1
- package/dist/extensions/components/StatsPanelComponent.min.js +1 -1
- package/dist/extensions/components/StatsPanelComponent.module.js +0 -1
- package/dist/extensions/components/StatsPanelComponent.module.js.map +1 -1
- package/dist/extensions/loaders/GLTFCloudLoader.js +7 -2
- package/dist/extensions/loaders/GLTFCloudLoader.js.map +1 -1
- package/dist/extensions/loaders/GLTFCloudLoader.min.js +1 -1
- package/dist/extensions/loaders/GLTFCloudLoader.module.js +7 -2
- package/dist/extensions/loaders/GLTFCloudLoader.module.js.map +1 -1
- package/dist/extensions/loaders/GLTFFileLoader.js +2 -1
- package/dist/extensions/loaders/GLTFFileLoader.js.map +1 -1
- package/dist/extensions/loaders/GLTFFileLoader.min.js +1 -1
- package/dist/extensions/loaders/GLTFFileLoader.module.js +2 -1
- package/dist/extensions/loaders/GLTFFileLoader.module.js.map +1 -1
- package/dist/extensions/loaders/IFCXLoader.js +10 -5
- package/dist/extensions/loaders/IFCXLoader.js.map +1 -1
- package/dist/extensions/loaders/IFCXLoader.min.js +1 -1
- package/dist/extensions/loaders/IFCXLoader.module.js +10 -5
- package/dist/extensions/loaders/IFCXLoader.module.js.map +1 -1
- package/dist/viewer-three.js +1901 -569
- package/dist/viewer-three.js.map +1 -1
- package/dist/viewer-three.min.js +4 -4
- package/dist/viewer-three.module.js +1366 -451
- package/dist/viewer-three.module.js.map +1 -1
- package/extensions/components/AxesHelperComponent.ts +3 -0
- package/extensions/components/ExtentsHelperComponent.ts +5 -2
- package/extensions/components/GridHelperComponent.ts +1 -0
- package/extensions/components/LightHelperComponent.ts +2 -1
- package/extensions/components/StatsPanelComponent.ts +0 -1
- package/extensions/loaders/GLTFCloudLoader.ts +8 -2
- package/extensions/loaders/GLTFFileLoader.ts +3 -2
- package/extensions/loaders/IFCX/IFCXFileLoader.ts +11 -5
- package/lib/Viewer/Viewer.d.ts +6 -8
- package/lib/Viewer/components/CameraComponent.d.ts +1 -1
- package/lib/Viewer/components/ClippingPlaneComponent.d.ts +8 -0
- package/lib/Viewer/components/HighlighterComponent.d.ts +2 -2
- package/lib/Viewer/components/InfoComponent.d.ts +1 -1
- package/lib/Viewer/components/SectionsComponent.d.ts +15 -0
- package/lib/Viewer/components/WCSHelperComponent.d.ts +2 -2
- package/lib/Viewer/draggers/CuttingPlaneDragger.d.ts +6 -6
- package/lib/Viewer/draggers/OrbitDragger.d.ts +1 -1
- package/lib/Viewer/measurement/Snapper.d.ts +4 -4
- package/package.json +5 -5
- package/src/Viewer/Viewer.ts +59 -48
- package/src/Viewer/commands/GetSelected2.ts +1 -1
- package/src/Viewer/commands/SetSelected.ts +1 -1
- package/src/Viewer/commands/index.ts +1 -1
- package/src/Viewer/components/BackgroundComponent.ts +2 -1
- package/src/Viewer/components/CameraComponent.ts +6 -7
- package/src/Viewer/components/CanvasRemoveComponent.ts +0 -1
- package/src/Viewer/{scenes/Helpers.ts → components/ClippingPlaneComponent.ts} +22 -12
- package/src/Viewer/components/HighlighterComponent.ts +9 -5
- package/src/Viewer/components/HighlighterUtils.ts +2 -2
- package/src/Viewer/components/InfoComponent.ts +4 -4
- package/src/Viewer/components/SectionsComponent.ts +119 -0
- package/src/Viewer/components/SelectionComponent.ts +5 -3
- package/src/Viewer/components/WCSHelperComponent.ts +8 -6
- package/src/Viewer/components/index.ts +4 -0
- package/src/Viewer/draggers/CuttingPlaneDragger.ts +57 -34
- package/src/Viewer/draggers/MeasureLineDragger.ts +1 -1
- package/src/Viewer/draggers/OrbitDragger.ts +3 -3
- package/src/Viewer/helpers/SectionsHelper.js +1061 -0
- package/src/Viewer/helpers/WCSHelper.ts +31 -5
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +417 -92
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.ts +19 -14
- package/src/Viewer/loaders/DynamicGltfLoader/GltfStructure.js +76 -9
- package/src/Viewer/loaders/GLTFBinaryParser.ts +2 -2
- package/src/Viewer/loaders/GLTFCloudDynamicLoader.ts +3 -2
- package/src/Viewer/loaders/GLTFFileDynamicLoader.ts +6 -4
- package/src/Viewer/measurement/Snapper.ts +6 -7
- package/src/Viewer/models/ModelImpl.ts +65 -28
- package/lib/Viewer/scenes/Helpers.d.ts +0 -7
- package/src/Viewer/postprocessing/SSAARenderPass.js +0 -245
|
@@ -62,7 +62,7 @@ export class DynamicModelImpl extends ModelImpl {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
override getObjects(): Object3D[] {
|
|
65
|
-
const objects = [];
|
|
65
|
+
const objects: Object3D[] = [];
|
|
66
66
|
this.gltfLoader.originalObjects.forEach((object: Object3D) => {
|
|
67
67
|
objects.push(object);
|
|
68
68
|
});
|
|
@@ -78,7 +78,7 @@ export class DynamicModelImpl extends ModelImpl {
|
|
|
78
78
|
|
|
79
79
|
const handlesSet = new Set(handles);
|
|
80
80
|
|
|
81
|
-
const objects = [];
|
|
81
|
+
const objects: Object3D[] = [];
|
|
82
82
|
handlesSet.forEach((handle) => {
|
|
83
83
|
objects.push(this.gltfLoader.getObjectsByHandle(handle));
|
|
84
84
|
});
|
|
@@ -153,6 +153,13 @@ export class DynamicModelImpl extends ModelImpl {
|
|
|
153
153
|
return target;
|
|
154
154
|
};
|
|
155
155
|
|
|
156
|
+
const calcObjectOffset = (object: Object3D, target: Vector3): Vector3 => {
|
|
157
|
+
const parent = object.parent;
|
|
158
|
+
if (!parent || parent.userData.originalCenter === undefined) return target;
|
|
159
|
+
|
|
160
|
+
return target.subVectors(object.userData.originalCenter, parent.userData.originalCenter);
|
|
161
|
+
};
|
|
162
|
+
|
|
156
163
|
const calcObjectDepth = (object: Object3D): number => {
|
|
157
164
|
if (object.userData.depth !== undefined) return object.userData.depth;
|
|
158
165
|
|
|
@@ -162,17 +169,19 @@ export class DynamicModelImpl extends ModelImpl {
|
|
|
162
169
|
object.userData.depth = depth;
|
|
163
170
|
object.userData.originalPosition = object.position.clone();
|
|
164
171
|
object.userData.originalCenter = calcObjectCenter(object, new Vector3());
|
|
172
|
+
object.userData.originalOffset = calcObjectOffset(object, new Vector3());
|
|
165
173
|
|
|
166
174
|
return depth;
|
|
167
175
|
};
|
|
168
176
|
|
|
169
177
|
const explodeScale = scale / 100;
|
|
170
178
|
const explodeRoot = this.scene.children[0];
|
|
179
|
+
const explodeObjects = this.getObjects();
|
|
171
180
|
|
|
172
|
-
if (
|
|
181
|
+
if (explodeRoot.userData.explodeDepth === undefined) {
|
|
173
182
|
let maxDepth = 0;
|
|
174
183
|
|
|
175
|
-
|
|
184
|
+
explodeObjects.forEach((object: Object3D) => {
|
|
176
185
|
const depth = calcObjectDepth(object);
|
|
177
186
|
if (depth > maxDepth) maxDepth = depth;
|
|
178
187
|
});
|
|
@@ -187,11 +196,11 @@ export class DynamicModelImpl extends ModelImpl {
|
|
|
187
196
|
|
|
188
197
|
const offsetCache = new Map();
|
|
189
198
|
|
|
190
|
-
const
|
|
199
|
+
const calcExplodeOffset = (object: Object3D, target: Vector3): Vector3 => {
|
|
191
200
|
if (offsetCache.has(object)) return target.copy(offsetCache.get(object));
|
|
192
201
|
|
|
193
202
|
const parent = object.parent;
|
|
194
|
-
if (parent && object !== explodeRoot)
|
|
203
|
+
if (parent && object !== explodeRoot) calcExplodeOffset(parent, target);
|
|
195
204
|
|
|
196
205
|
const depth = object.userData.depth;
|
|
197
206
|
|
|
@@ -199,11 +208,7 @@ export class DynamicModelImpl extends ModelImpl {
|
|
|
199
208
|
let objectScale = explodeScale * coeff;
|
|
200
209
|
if (depth === explodeDepth) objectScale *= currentSegmentFraction;
|
|
201
210
|
|
|
202
|
-
|
|
203
|
-
const objectCenter = object.userData.originalCenter;
|
|
204
|
-
const localOffset = objectCenter.clone().sub(parentCenter).multiplyScalar(objectScale);
|
|
205
|
-
|
|
206
|
-
target.add(localOffset);
|
|
211
|
+
target.addScaledVector(object.userData.originalOffset, objectScale);
|
|
207
212
|
}
|
|
208
213
|
|
|
209
214
|
offsetCache.set(object, target.clone());
|
|
@@ -213,9 +218,9 @@ export class DynamicModelImpl extends ModelImpl {
|
|
|
213
218
|
|
|
214
219
|
const transformMap = new Map();
|
|
215
220
|
|
|
216
|
-
|
|
217
|
-
const
|
|
218
|
-
transformMap.set(object, new Matrix4().makeTranslation(
|
|
221
|
+
explodeObjects.forEach((object: Object3D) => {
|
|
222
|
+
const offset = calcExplodeOffset(object, new Vector3());
|
|
223
|
+
transformMap.set(object, new Matrix4().makeTranslation(offset));
|
|
219
224
|
});
|
|
220
225
|
|
|
221
226
|
this.gltfLoader.applyObjectTransforms(transformMap);
|
|
@@ -88,6 +88,8 @@ export class GltfStructure {
|
|
|
88
88
|
this._nextObjectId = 1;
|
|
89
89
|
this.loadingAborted = false;
|
|
90
90
|
this.criticalError = null;
|
|
91
|
+
// Holds decoded base64 buffer when the .gltf has an embedded data URI
|
|
92
|
+
this.embeddedBinaryChunk = null;
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
async initialize(loader) {
|
|
@@ -99,21 +101,53 @@ export class GltfStructure {
|
|
|
99
101
|
|
|
100
102
|
this.json = json;
|
|
101
103
|
this.loader = loader;
|
|
102
|
-
|
|
104
|
+
|
|
105
|
+
const bufferUri = this.json.buffers?.[0]?.uri || "";
|
|
106
|
+
|
|
107
|
+
// Detect embedded data URI (e.g. "data:application/octet-stream;base64,...")
|
|
108
|
+
// and decode it once into memory. The buffer URI is then cleared so that
|
|
109
|
+
// scheduleRequest serves data from memory instead of doing network requests.
|
|
110
|
+
if (bufferUri.startsWith("data:")) {
|
|
111
|
+
this.embeddedBinaryChunk = await this._decodeDataUri(bufferUri);
|
|
112
|
+
this.uri = "";
|
|
113
|
+
} else {
|
|
114
|
+
this.uri = bufferUri;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Decode a data URI to ArrayBuffer using the browser's native fetch() —
|
|
119
|
+
// much faster than atob() + JS loop for large (10+ MB) base64 buffers.
|
|
120
|
+
async _decodeDataUri(dataUri) {
|
|
121
|
+
try {
|
|
122
|
+
const response = await fetch(dataUri);
|
|
123
|
+
return await response.arrayBuffer();
|
|
124
|
+
} catch (e) {
|
|
125
|
+
throw new Error(`DynamicLoader: Failed to decode embedded data URI: ${e.message}`);
|
|
126
|
+
}
|
|
103
127
|
}
|
|
104
128
|
|
|
105
129
|
clear() {
|
|
106
|
-
this.json = null;
|
|
107
|
-
this.loadController = null;
|
|
108
|
-
this.pendingRequests = [];
|
|
109
130
|
if (this.batchTimeout) {
|
|
110
131
|
clearTimeout(this.batchTimeout);
|
|
111
132
|
this.batchTimeout = null;
|
|
112
133
|
}
|
|
113
134
|
|
|
114
|
-
this.
|
|
115
|
-
|
|
116
|
-
this.
|
|
135
|
+
this._rejectPendingRequests();
|
|
136
|
+
|
|
137
|
+
if (this.disposeMaterials) {
|
|
138
|
+
try {
|
|
139
|
+
this.disposeMaterials();
|
|
140
|
+
} catch (e) {
|
|
141
|
+
console.warn("DynamicLoader: error during disposeMaterials in clear():", e);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (this.textureCache) this.textureCache.clear();
|
|
145
|
+
if (this.materials) this.materials.clear();
|
|
146
|
+
|
|
147
|
+
this.embeddedBinaryChunk = null;
|
|
148
|
+
this.json = null;
|
|
149
|
+
this.loadController = null;
|
|
150
|
+
this.uri = "";
|
|
117
151
|
|
|
118
152
|
this.activeChunkLoads = 0;
|
|
119
153
|
this.chunkQueue = [];
|
|
@@ -121,11 +155,38 @@ export class GltfStructure {
|
|
|
121
155
|
this.criticalError = null;
|
|
122
156
|
}
|
|
123
157
|
|
|
158
|
+
_rejectPendingRequests() {
|
|
159
|
+
const pending = this.pendingRequests;
|
|
160
|
+
this.pendingRequests = [];
|
|
161
|
+
if (!pending || pending.length === 0) return;
|
|
162
|
+
|
|
163
|
+
const cancelError = new Error("DynamicLoader: Structure cleared while requests pending");
|
|
164
|
+
for (let i = 0; i < pending.length; i++) {
|
|
165
|
+
const item = pending[i];
|
|
166
|
+
if (item && typeof item._reject === "function") {
|
|
167
|
+
try {
|
|
168
|
+
item._reject(cancelError);
|
|
169
|
+
} catch {
|
|
170
|
+
// Consumer may have already been disposed
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
124
176
|
getJson() {
|
|
125
177
|
return this.json;
|
|
126
178
|
}
|
|
127
179
|
|
|
128
180
|
scheduleRequest(request) {
|
|
181
|
+
// Fast path: buffer was an embedded data URI, decoded once into memory
|
|
182
|
+
if (this.embeddedBinaryChunk && !this.uri) {
|
|
183
|
+
return Promise.resolve({
|
|
184
|
+
buffer: this.embeddedBinaryChunk,
|
|
185
|
+
relOffset: request.offset,
|
|
186
|
+
length: request.length,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
129
190
|
return new Promise((resolve, reject) => {
|
|
130
191
|
if (this.loadingAborted) {
|
|
131
192
|
reject(
|
|
@@ -441,8 +502,13 @@ export class GltfStructure {
|
|
|
441
502
|
return await this.textureLoader.loadAsync(fullUrl);
|
|
442
503
|
} else if (image.bufferView !== undefined) {
|
|
443
504
|
const bufferView = this.json.bufferViews[image.bufferView];
|
|
444
|
-
const
|
|
445
|
-
|
|
505
|
+
const { buffer, relOffset } = await this.getBufferView(
|
|
506
|
+
bufferView.byteOffset || 0,
|
|
507
|
+
bufferView.byteLength,
|
|
508
|
+
5121
|
|
509
|
+
);
|
|
510
|
+
const imageBytes = new Uint8Array(buffer, relOffset, bufferView.byteLength);
|
|
511
|
+
const blob = new Blob([imageBytes], { type: image.mimeType });
|
|
446
512
|
const url = URL.createObjectURL(blob);
|
|
447
513
|
const texture = await this.textureLoader.loadAsync(url);
|
|
448
514
|
URL.revokeObjectURL(url);
|
|
@@ -479,6 +545,7 @@ export class GltfStructure {
|
|
|
479
545
|
})
|
|
480
546
|
);
|
|
481
547
|
}
|
|
548
|
+
await this.flushBufferRequests();
|
|
482
549
|
await Promise.all(texturePromises);
|
|
483
550
|
}
|
|
484
551
|
|
|
@@ -86,10 +86,10 @@ export class GLTFBinaryParser {
|
|
|
86
86
|
offset += chunkLength;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
if (
|
|
89
|
+
if (this.content === undefined) {
|
|
90
90
|
throw new Error("GLTFBinaryParser: JSON content not found.");
|
|
91
91
|
}
|
|
92
|
-
if (
|
|
92
|
+
if (this.body === undefined) {
|
|
93
93
|
throw new Error("GLTFBinaryParser: Binary buffer chunk not found or type not supported.");
|
|
94
94
|
}
|
|
95
95
|
}
|
|
@@ -58,9 +58,10 @@ export class GLTFCloudDynamicLoader extends Loader {
|
|
|
58
58
|
const scene = new Group();
|
|
59
59
|
|
|
60
60
|
this.gltfLoader = new DynamicGltfLoader(this.viewer.camera, scene, this.viewer.renderer);
|
|
61
|
-
this.gltfLoader.
|
|
61
|
+
this.gltfLoader.setMemoryLimit(this.viewer.options.memoryLimit);
|
|
62
62
|
this.gltfLoader.setVisibleEdges(this.viewer.options.edgeModel);
|
|
63
|
-
|
|
63
|
+
this.gltfLoader.setMaxConcurrentChunks(params.maxConcurrentChunks);
|
|
64
|
+
this.gltfLoader.setDracoLoader(params.dracoLoader);
|
|
64
65
|
|
|
65
66
|
const modelImpl = new DynamicModelImpl(scene);
|
|
66
67
|
modelImpl.id = model.file.id;
|
|
@@ -49,21 +49,23 @@ export class GLTFFileDynamicLoader extends Loader {
|
|
|
49
49
|
if (this.manager) this.manager.dispose();
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
override isSupport(file: any, format
|
|
52
|
+
override isSupport(file: any, format = ""): boolean {
|
|
53
53
|
return (
|
|
54
54
|
(typeof file === "string" || file instanceof globalThis.File || file instanceof ArrayBuffer) &&
|
|
55
55
|
/(gltf|glb)$/i.test(format)
|
|
56
56
|
);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
override async load(file: any, format?: string, params
|
|
59
|
+
override async load(file: any, format?: string, params: GLTFLoadParams = {}): Promise<this> {
|
|
60
60
|
this.manager = new GLTFLoadingManager(file, params);
|
|
61
61
|
|
|
62
62
|
const scene = new Group();
|
|
63
63
|
|
|
64
64
|
this.gltfLoader = new DynamicGltfLoader(this.viewer.camera, scene, this.viewer.renderer);
|
|
65
|
-
this.gltfLoader.
|
|
66
|
-
this.gltfLoader.
|
|
65
|
+
this.gltfLoader.setMemoryLimit(this.viewer.options.memoryLimit);
|
|
66
|
+
this.gltfLoader.setVisibleEdges(this.viewer.options.edgeModel);
|
|
67
|
+
this.gltfLoader.setMaxConcurrentChunks(params.maxConcurrentChunks);
|
|
68
|
+
this.gltfLoader.setDracoLoader(params.dracoLoader);
|
|
67
69
|
|
|
68
70
|
const modelImpl = new DynamicModelImpl(scene);
|
|
69
71
|
modelImpl.id = params.modelId || this.extractFileName(file);
|
|
@@ -32,7 +32,6 @@ import {
|
|
|
32
32
|
Raycaster,
|
|
33
33
|
Vector2,
|
|
34
34
|
Vector3,
|
|
35
|
-
WebGLRenderer,
|
|
36
35
|
} from "three";
|
|
37
36
|
|
|
38
37
|
const DESKTOP_SNAP_DISTANCE = 10;
|
|
@@ -47,16 +46,16 @@ const _projection = new Vector3();
|
|
|
47
46
|
|
|
48
47
|
export class Snapper {
|
|
49
48
|
public camera: Camera;
|
|
50
|
-
public
|
|
49
|
+
public clippingPlanes: Plane[];
|
|
51
50
|
public canvas: HTMLCanvasElement;
|
|
52
51
|
public threshold: number;
|
|
53
52
|
private raycaster: Raycaster;
|
|
54
53
|
private detectRadiusInPixels: number;
|
|
55
54
|
private edgesCache: WeakMap<any, EdgesGeometry>;
|
|
56
55
|
|
|
57
|
-
constructor(camera: Camera,
|
|
56
|
+
constructor(camera: Camera, clippingPlanes: Plane[], canvas: HTMLCanvasElement) {
|
|
58
57
|
this.camera = camera;
|
|
59
|
-
this.
|
|
58
|
+
this.clippingPlanes = clippingPlanes;
|
|
60
59
|
this.canvas = canvas;
|
|
61
60
|
|
|
62
61
|
this.threshold = 0.0001;
|
|
@@ -109,7 +108,7 @@ export class Snapper {
|
|
|
109
108
|
let intersects = this.raycaster.intersectObjects(objects, recursive);
|
|
110
109
|
|
|
111
110
|
if (clip) {
|
|
112
|
-
const clippingPlanes = this.
|
|
111
|
+
const clippingPlanes = this.clippingPlanes;
|
|
113
112
|
clippingPlanes.forEach((plane: Plane) => {
|
|
114
113
|
intersects = intersects.filter((intersect) => plane.distanceToPoint(intersect.point) >= 0);
|
|
115
114
|
});
|
|
@@ -152,7 +151,7 @@ export class Snapper {
|
|
|
152
151
|
return 0.1;
|
|
153
152
|
}
|
|
154
153
|
|
|
155
|
-
getSnapPoint(mouse: Vector2, objects: Object3D[]): Vector3 {
|
|
154
|
+
getSnapPoint(mouse: Vector2, objects: Object3D[]): Vector3 | undefined {
|
|
156
155
|
const intersections = this.getPointerIntersects(mouse, objects);
|
|
157
156
|
if (intersections.length === 0) return undefined;
|
|
158
157
|
|
|
@@ -167,7 +166,7 @@ export class Snapper {
|
|
|
167
166
|
const intersectionPoint = intersections[0].point;
|
|
168
167
|
const localPoint = object.worldToLocal(intersectionPoint.clone());
|
|
169
168
|
|
|
170
|
-
let snapPoint: Vector3;
|
|
169
|
+
let snapPoint: Vector3 | undefined = undefined;
|
|
171
170
|
let snapDistance = this.getDetectRadius(intersectionPoint);
|
|
172
171
|
|
|
173
172
|
const geometry = object.geometry;
|
|
@@ -36,6 +36,7 @@ export class ModelImpl implements IModelImpl {
|
|
|
36
36
|
private originalObjects: Set<Object3D>;
|
|
37
37
|
|
|
38
38
|
constructor(scene: Object3D) {
|
|
39
|
+
this.id = "";
|
|
39
40
|
this.scene = scene;
|
|
40
41
|
|
|
41
42
|
this.handleToObjects = new Map();
|
|
@@ -94,8 +95,8 @@ export class ModelImpl implements IModelImpl {
|
|
|
94
95
|
if (object.material) disposeMaterials(object.material);
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
this.handleToObjects
|
|
98
|
-
this.originalObjects
|
|
98
|
+
this.handleToObjects.clear();
|
|
99
|
+
this.originalObjects.clear();
|
|
99
100
|
|
|
100
101
|
this.scene.traverse(disposeObject);
|
|
101
102
|
this.scene.clear();
|
|
@@ -275,7 +276,30 @@ export class ModelImpl implements IModelImpl {
|
|
|
275
276
|
}
|
|
276
277
|
|
|
277
278
|
getExtents(target: Box3): Box3 {
|
|
278
|
-
|
|
279
|
+
const _box = new Box3();
|
|
280
|
+
|
|
281
|
+
function expandByObject(object: any, target: Box3) {
|
|
282
|
+
if (!object.geometry) return;
|
|
283
|
+
|
|
284
|
+
object.updateWorldMatrix(false, false);
|
|
285
|
+
|
|
286
|
+
// Use object bounds for the BatchedMesh, InstancedMesh and SkinnedMesh, use geometry
|
|
287
|
+
// bounds for others. See Box3.expandByObject() for more details.
|
|
288
|
+
|
|
289
|
+
if (object.boundingBox !== undefined) {
|
|
290
|
+
if (object.boundingBox === null) object.computeBoundingBox();
|
|
291
|
+
_box.copy(object.boundingBox);
|
|
292
|
+
} else {
|
|
293
|
+
if (object.geometry.boundingBox === null) object.geometry.computeBoundingBox();
|
|
294
|
+
_box.copy(object.geometry.boundingBox);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
_box.applyMatrix4(object.matrixWorld);
|
|
298
|
+
|
|
299
|
+
target.union(_box);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
this.scene.traverseVisible((object) => expandByObject(object, target));
|
|
279
303
|
return target;
|
|
280
304
|
}
|
|
281
305
|
|
|
@@ -284,15 +308,15 @@ export class ModelImpl implements IModelImpl {
|
|
|
284
308
|
}
|
|
285
309
|
|
|
286
310
|
getVisibleObjects(): Object3D[] {
|
|
287
|
-
const objects = [];
|
|
288
|
-
this.scene.traverseVisible((object) => objects.push(object));
|
|
289
|
-
return objects
|
|
311
|
+
const objects: Object3D[] = [];
|
|
312
|
+
this.scene.traverseVisible((object) => object.userData.handle && objects.push(object));
|
|
313
|
+
return objects;
|
|
290
314
|
}
|
|
291
315
|
|
|
292
316
|
getObjectsByHandles(handles: string | string[]): Object3D[] {
|
|
293
317
|
if (!Array.isArray(handles)) handles = [handles];
|
|
294
318
|
|
|
295
|
-
const ownHandles = [];
|
|
319
|
+
const ownHandles: string[] = [];
|
|
296
320
|
handles.forEach((handle) => {
|
|
297
321
|
const index = handle.indexOf(":");
|
|
298
322
|
if (index !== -1) {
|
|
@@ -304,7 +328,7 @@ export class ModelImpl implements IModelImpl {
|
|
|
304
328
|
|
|
305
329
|
const handlesSet = new Set<string>(ownHandles);
|
|
306
330
|
|
|
307
|
-
const objects = [];
|
|
331
|
+
const objects: Object3D[][] = [];
|
|
308
332
|
handlesSet.forEach((handle) => {
|
|
309
333
|
objects.push(Array.from(this.handleToObjects.get(handle) || []));
|
|
310
334
|
});
|
|
@@ -403,25 +427,40 @@ export class ModelImpl implements IModelImpl {
|
|
|
403
427
|
return target;
|
|
404
428
|
};
|
|
405
429
|
|
|
406
|
-
|
|
407
|
-
|
|
430
|
+
const calcObjectOffset = (object: Object3D, target: Vector3): Vector3 => {
|
|
431
|
+
const parent = object.parent;
|
|
432
|
+
if (!parent || parent.userData.originalCenter === undefined) return target;
|
|
408
433
|
|
|
409
|
-
object.
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
434
|
+
return target.subVectors(object.userData.originalCenter, parent.userData.originalCenter);
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
const calcObjectDepth = (object: Object3D): number => {
|
|
438
|
+
if (object.userData.depth !== undefined) return object.userData.depth;
|
|
413
439
|
|
|
440
|
+
const parent = object.parent;
|
|
441
|
+
const depth = parent && object !== explodeRoot ? calcObjectDepth(parent) + 1 : 0;
|
|
442
|
+
|
|
443
|
+
object.userData.depth = depth;
|
|
414
444
|
object.userData.originalPosition = object.position.clone();
|
|
415
445
|
object.userData.originalCenter = calcObjectCenter(object, new Vector3());
|
|
446
|
+
object.userData.originalOffset = calcObjectOffset(object, new Vector3());
|
|
416
447
|
|
|
417
|
-
return
|
|
418
|
-
}
|
|
448
|
+
return depth;
|
|
449
|
+
};
|
|
419
450
|
|
|
420
451
|
const explodeScale = scale / 100;
|
|
421
452
|
const explodeRoot = this.scene;
|
|
453
|
+
const explodeObjects = this.getObjects();
|
|
454
|
+
|
|
455
|
+
if (explodeRoot.userData.explodeDepth === undefined) {
|
|
456
|
+
let maxDepth = 0;
|
|
457
|
+
|
|
458
|
+
explodeObjects.forEach((object: Object3D) => {
|
|
459
|
+
const depth = calcObjectDepth(object);
|
|
460
|
+
if (depth > maxDepth) maxDepth = depth;
|
|
461
|
+
});
|
|
422
462
|
|
|
423
|
-
|
|
424
|
-
explodeRoot.userData.explodeDepth = calcExplodeDepth(explodeRoot, 1);
|
|
463
|
+
explodeRoot.userData.explodeDepth = maxDepth;
|
|
425
464
|
}
|
|
426
465
|
|
|
427
466
|
const maxDepth = explodeRoot.userData.explodeDepth;
|
|
@@ -429,27 +468,25 @@ export class ModelImpl implements IModelImpl {
|
|
|
429
468
|
const explodeDepth = 0 | scaledExplodeDepth;
|
|
430
469
|
const currentSegmentFraction = scaledExplodeDepth - explodeDepth;
|
|
431
470
|
|
|
432
|
-
|
|
471
|
+
const explodeObject = (object: any) => {
|
|
433
472
|
if (object.isCamera) return;
|
|
434
|
-
if (object.userData.isHighlightWireframe) return;
|
|
435
473
|
|
|
436
474
|
object.position.copy(object.userData.originalPosition);
|
|
437
475
|
|
|
476
|
+
const depth = object.userData.depth;
|
|
477
|
+
|
|
438
478
|
if (depth > 0 && depth <= explodeDepth) {
|
|
439
479
|
let objectScale = explodeScale * coeff;
|
|
440
480
|
if (depth === explodeDepth) objectScale *= currentSegmentFraction;
|
|
441
481
|
|
|
442
|
-
|
|
443
|
-
const objectCenter = object.userData.originalCenter;
|
|
444
|
-
const localOffset = objectCenter.clone().sub(parentCenter).multiplyScalar(objectScale);
|
|
445
|
-
|
|
446
|
-
object.position.add(localOffset);
|
|
482
|
+
object.position.addScaledVector(object.userData.originalOffset, objectScale);
|
|
447
483
|
}
|
|
484
|
+
};
|
|
448
485
|
|
|
449
|
-
|
|
450
|
-
|
|
486
|
+
explodeObjects.forEach((object: Object3D) => {
|
|
487
|
+
explodeObject(object);
|
|
488
|
+
});
|
|
451
489
|
|
|
452
|
-
explodeObject(explodeRoot, 0);
|
|
453
490
|
this.scene.updateMatrixWorld();
|
|
454
491
|
|
|
455
492
|
return this;
|