@inweb/viewer-three 26.12.6 → 27.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/extensions/loaders/GLTFFileLoader.js +1 -1
  2. package/dist/extensions/loaders/GLTFFileLoader.js.map +1 -1
  3. package/dist/extensions/loaders/GLTFFileLoader.min.js +1 -1
  4. package/dist/extensions/loaders/GLTFFileLoader.module.js +1 -1
  5. package/dist/extensions/loaders/GLTFFileLoader.module.js.map +1 -1
  6. package/dist/extensions/loaders/IFCXLoader.js +2 -2
  7. package/dist/extensions/loaders/IFCXLoader.js.map +1 -1
  8. package/dist/extensions/loaders/IFCXLoader.min.js +1 -1
  9. package/dist/extensions/loaders/IFCXLoader.module.js +2 -2
  10. package/dist/extensions/loaders/IFCXLoader.module.js.map +1 -1
  11. package/dist/viewer-three.js +466 -170
  12. package/dist/viewer-three.js.map +1 -1
  13. package/dist/viewer-three.min.js +2 -2
  14. package/dist/viewer-three.module.js +464 -168
  15. package/dist/viewer-three.module.js.map +1 -1
  16. package/extensions/loaders/GLTFFileLoader.ts +1 -1
  17. package/extensions/loaders/IFCX/IFCXCloudLoader.ts +1 -1
  18. package/extensions/loaders/IFCX/IFCXFileLoader.ts +1 -1
  19. package/lib/Viewer/Viewer.d.ts +2 -1
  20. package/lib/Viewer/components/ExtentsComponent.d.ts +1 -1
  21. package/lib/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.d.ts +3 -3
  22. package/lib/Viewer/measurement/Snapper.d.ts +1 -0
  23. package/lib/Viewer/measurement/UnitConverter.d.ts +20 -13
  24. package/lib/Viewer/models/IModelImpl.d.ts +2 -6
  25. package/lib/Viewer/models/ModelImpl.d.ts +4 -6
  26. package/package.json +5 -5
  27. package/src/Viewer/Viewer.ts +25 -15
  28. package/src/Viewer/commands/GetSelected2.ts +2 -2
  29. package/src/Viewer/commands/ZoomTo.ts +3 -3
  30. package/src/Viewer/components/CameraComponent.ts +4 -4
  31. package/src/Viewer/components/ExtentsComponent.ts +3 -3
  32. package/src/Viewer/components/HighlighterComponent.ts +11 -17
  33. package/src/Viewer/components/SelectionComponent.ts +14 -13
  34. package/src/Viewer/draggers/MeasureLineDragger.ts +1 -0
  35. package/src/Viewer/draggers/OrbitDragger.ts +2 -0
  36. package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +287 -22
  37. package/src/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.ts +94 -18
  38. package/src/Viewer/loaders/DynamicGltfLoader/GltfStructure.js +1 -1
  39. package/src/Viewer/measurement/Snapper.ts +6 -3
  40. package/src/Viewer/measurement/UnitConverter.ts +19 -12
  41. package/src/Viewer/measurement/UnitFormatter.ts +2 -2
  42. package/src/Viewer/models/IModelImpl.ts +2 -10
  43. package/src/Viewer/models/ModelImpl.ts +111 -61
@@ -77,9 +77,6 @@ export class DynamicGltfLoader {
77
77
  this.graphicsObjectLimit = 10000;
78
78
  this.totalLoadedObjects = 0;
79
79
 
80
- this.lastUpdateTime = 0;
81
- this.updateInterval = 1000;
82
-
83
80
  this.handleToObjects = new Map();
84
81
 
85
82
  this.originalObjects = new Set();
@@ -102,6 +99,10 @@ export class DynamicGltfLoader {
102
99
  this.newOptimizedObjects = new Set();
103
100
  this.oldOptimizeObjects = new Set();
104
101
 
102
+ // Transform system for exploded view - works directly with original objects
103
+ this.objectTransforms = new Map(); // originalObject -> Matrix4
104
+ this.transformedGeometries = new Map(); // mergedObject.uuid -> original position data
105
+
105
106
  this.activeChunkLoads = 0;
106
107
  this.chunkQueue = [];
107
108
 
@@ -311,7 +312,6 @@ export class DynamicGltfLoader {
311
312
  const uniqueTextureIds = new Set();
312
313
  if (Array.isArray(this.structures)) {
313
314
  for (const structure of this.structures) {
314
- // console.log(structure.materialCache.values());
315
315
  try {
316
316
  for (const entry of structure.materialCache.values()) {
317
317
  if (entry?.mesh?.uuid) uniqueMaterialIds.add(entry.mesh.uuid);
@@ -884,11 +884,7 @@ export class DynamicGltfLoader {
884
884
  total: totalNodes,
885
885
  });
886
886
 
887
- const currentTime = Date.now();
888
- if (currentTime - this.lastUpdateTime >= this.updateInterval) {
889
- this.dispatchEvent("update");
890
- this.lastUpdateTime = currentTime;
891
- }
887
+ this.dispatchEvent("update");
892
888
 
893
889
  await new Promise((resolve) => {
894
890
  setTimeout(resolve, 0);
@@ -1006,17 +1002,40 @@ export class DynamicGltfLoader {
1006
1002
  if (node.object && this.hiddenHandles.has(node.object.userData.handle)) continue;
1007
1003
 
1008
1004
  const transformedBox = node.geometryExtents.clone();
1005
+ const structureRoot = node.structure ? this.structureRoots.get(node.structure.id) : null;
1009
1006
 
1010
- if (node.group && node.group.matrix) {
1011
- transformedBox.applyMatrix4(node.group.matrix);
1007
+ if (node.group) {
1008
+ const matrices = [];
1009
+ let currentGroup = node.group;
1012
1010
 
1013
- if (node.group.parent && node.group.parent.matrix) {
1014
- transformedBox.applyMatrix4(node.group.parent.matrix);
1011
+ while (currentGroup && currentGroup !== structureRoot) {
1012
+ if (currentGroup.matrix && currentGroup.matrixAutoUpdate === false) {
1013
+ matrices.unshift(currentGroup.matrix);
1014
+ }
1015
+ currentGroup = currentGroup.parent;
1016
+ }
1017
+
1018
+ for (const matrix of matrices) {
1019
+ transformedBox.applyMatrix4(matrix);
1015
1020
  }
1016
1021
  }
1022
+
1023
+ if (structureRoot && structureRoot.matrix) {
1024
+ transformedBox.applyMatrix4(structureRoot.matrix);
1025
+ }
1026
+
1027
+ const transform = this.objectTransforms.get(node.object);
1028
+ if (transform) {
1029
+ transformedBox.applyMatrix4(transform);
1030
+ }
1031
+
1017
1032
  totalExtent.union(transformedBox);
1018
1033
  }
1019
1034
 
1035
+ if (this.scene && this.scene.matrix && !totalExtent.isEmpty()) {
1036
+ totalExtent.applyMatrix4(this.scene.matrix);
1037
+ }
1038
+
1020
1039
  return totalExtent;
1021
1040
  }
1022
1041
 
@@ -1101,7 +1120,6 @@ export class DynamicGltfLoader {
1101
1120
  for (let i = 0; i < this.maxObjectId; i++) {
1102
1121
  this.objectVisibility[i] = 1.0;
1103
1122
  }
1104
- console.log(`Initialized object visibility array: ${this.maxObjectId} objects`);
1105
1123
  }
1106
1124
  }
1107
1125
 
@@ -1255,8 +1273,10 @@ export class DynamicGltfLoader {
1255
1273
  this.oldOptimizeObjects.clear();
1256
1274
  this.isolatedObjects = [];
1257
1275
 
1276
+ this.objectTransforms.clear();
1277
+ this.transformedGeometries.clear();
1278
+
1258
1279
  this.totalLoadedObjects = 0;
1259
- this.lastUpdateTime = 0;
1260
1280
  this.currentMemoryUsage = 0;
1261
1281
  this.loadedGeometrySize = 0;
1262
1282
 
@@ -1386,9 +1406,7 @@ export class DynamicGltfLoader {
1386
1406
 
1387
1407
  yieldToUI() {
1388
1408
  return new Promise((resolve) => {
1389
- requestAnimationFrame(() => {
1390
- setTimeout(resolve, 0);
1391
- });
1409
+ setTimeout(resolve, 0);
1392
1410
  });
1393
1411
  }
1394
1412
 
@@ -2114,6 +2132,231 @@ export class DynamicGltfLoader {
2114
2132
  }
2115
2133
  }
2116
2134
 
2135
+ applyObjectTransforms(objectTransformMap) {
2136
+ if (this.mergedObjectMap.size === 0) {
2137
+ console.warn("No merged objects to transform");
2138
+ return;
2139
+ }
2140
+
2141
+ // Store transform map directly
2142
+ this.objectTransforms = new Map(objectTransformMap);
2143
+
2144
+ // Apply transforms to all merged meshes
2145
+ for (const mesh of this.mergedMesh) {
2146
+ this._applyTransformToMergedObject(mesh);
2147
+ }
2148
+ for (const line of this.mergedLines) {
2149
+ this._applyTransformToMergedObject(line);
2150
+ }
2151
+ for (const lineSegment of this.mergedLineSegments) {
2152
+ this._applyTransformToMergedObject(lineSegment);
2153
+ }
2154
+ for (const point of this.mergedPoints) {
2155
+ this._applyTransformToMergedObject(point);
2156
+ }
2157
+ }
2158
+
2159
+ createExplodeTransforms(objects = null, explodeCenter = null, explodeFactor = 1.5) {
2160
+ const transformMap = new Map();
2161
+
2162
+ if (!explodeCenter) {
2163
+ explodeCenter = new Vector3();
2164
+ const extent = this.getTotalGeometryExtent();
2165
+ if (!extent.isEmpty()) {
2166
+ extent.getCenter(explodeCenter);
2167
+ }
2168
+ }
2169
+
2170
+ const objectsArray = objects
2171
+ ? Array.isArray(objects)
2172
+ ? objects
2173
+ : Array.from(objects)
2174
+ : Array.from(this.originalObjects);
2175
+
2176
+ for (const obj of objectsArray) {
2177
+ if (!obj.geometry || !obj.geometry.attributes.position) continue;
2178
+
2179
+ const boundingBox = new Box3().setFromBufferAttribute(obj.geometry.attributes.position);
2180
+
2181
+ if (obj.matrixWorld) {
2182
+ boundingBox.applyMatrix4(obj.matrixWorld);
2183
+ }
2184
+
2185
+ if (boundingBox.isEmpty()) continue;
2186
+
2187
+ const objectCenter = new Vector3();
2188
+ boundingBox.getCenter(objectCenter);
2189
+
2190
+ const direction = objectCenter.clone().sub(explodeCenter);
2191
+ const distance = direction.length();
2192
+
2193
+ if (distance > 0) {
2194
+ direction.normalize();
2195
+ const offset = direction.multiplyScalar(distance * (explodeFactor - 1.0));
2196
+
2197
+ const matrix = new Matrix4().makeTranslation(offset.x, offset.y, offset.z);
2198
+ transformMap.set(obj, matrix);
2199
+ }
2200
+ }
2201
+
2202
+ return transformMap;
2203
+ }
2204
+
2205
+ clearTransforms() {
2206
+ this.objectTransforms.clear();
2207
+
2208
+ for (const mesh of this.mergedMesh) {
2209
+ this._restoreOriginalGeometry(mesh);
2210
+ }
2211
+ for (const line of this.mergedLines) {
2212
+ this._restoreOriginalGeometry(line);
2213
+ }
2214
+ for (const lineSegment of this.mergedLineSegments) {
2215
+ this._restoreOriginalGeometry(lineSegment);
2216
+ }
2217
+ for (const point of this.mergedPoints) {
2218
+ this._restoreOriginalGeometry(point);
2219
+ }
2220
+ }
2221
+
2222
+ clearHandleTransforms() {
2223
+ this.clearTransforms();
2224
+ }
2225
+
2226
+ _applyTransformToMergedObject(mergedObject) {
2227
+ const objectData = this.mergedObjectMap.get(mergedObject.uuid);
2228
+ if (!objectData || !objectData.objectMapping) return;
2229
+
2230
+ const geometry = mergedObject.geometry;
2231
+ if (!geometry || !geometry.attributes.position) return;
2232
+
2233
+ const positionAttr = geometry.attributes.position;
2234
+ const positions = positionAttr.array;
2235
+
2236
+ if (!this.transformedGeometries.has(mergedObject.uuid)) {
2237
+ this.transformedGeometries.set(mergedObject.uuid, new Float32Array(positions));
2238
+ }
2239
+
2240
+ const originalPositions = this.transformedGeometries.get(mergedObject.uuid);
2241
+ const tempVector = new Vector3();
2242
+
2243
+ for (const [originalMesh, mappingData] of objectData.objectMapping) {
2244
+ const transform = this.objectTransforms.get(originalMesh);
2245
+
2246
+ if (!transform) {
2247
+ const startIdx = mappingData.startVertexIndex * 3;
2248
+ const endIdx = (mappingData.startVertexIndex + mappingData.vertexCount) * 3;
2249
+ for (let i = startIdx; i < endIdx; i++) {
2250
+ positions[i] = originalPositions[i];
2251
+ }
2252
+ continue;
2253
+ }
2254
+
2255
+ const startVertex = mappingData.startVertexIndex;
2256
+ const vertexCount = mappingData.vertexCount;
2257
+
2258
+ for (let i = 0; i < vertexCount; i++) {
2259
+ const idx = (startVertex + i) * 3;
2260
+
2261
+ tempVector.set(originalPositions[idx], originalPositions[idx + 1], originalPositions[idx + 2]);
2262
+
2263
+ tempVector.applyMatrix4(transform);
2264
+
2265
+ positions[idx] = tempVector.x;
2266
+ positions[idx + 1] = tempVector.y;
2267
+ positions[idx + 2] = tempVector.z;
2268
+ }
2269
+ }
2270
+
2271
+ if (geometry.attributes.normal) {
2272
+ this._updateNormalsForTransform(geometry, objectData, originalPositions);
2273
+ }
2274
+
2275
+ positionAttr.needsUpdate = true;
2276
+ geometry.computeBoundingSphere();
2277
+ geometry.computeBoundingBox();
2278
+ }
2279
+
2280
+ _updateNormalsForTransform(geometry, objectData, originalPositions) {
2281
+ const normalAttr = geometry.attributes.normal;
2282
+ if (!normalAttr) return;
2283
+
2284
+ const normals = normalAttr.array;
2285
+ const tempVector = new Vector3();
2286
+ const normalMatrix = new Matrix4();
2287
+
2288
+ // Store original normals if not already stored
2289
+ const normalsKey = `${geometry.uuid}_normals`;
2290
+ if (!this.transformedGeometries.has(normalsKey)) {
2291
+ this.transformedGeometries.set(normalsKey, new Float32Array(normals));
2292
+ }
2293
+
2294
+ const originalNormals = this.transformedGeometries.get(normalsKey);
2295
+
2296
+ for (const [originalMesh, mappingData] of objectData.objectMapping) {
2297
+ // Direct lookup by object reference - NO HANDLE LOOKUP!
2298
+ const transform = this.objectTransforms.get(originalMesh);
2299
+
2300
+ if (!transform) {
2301
+ // Restore original normals
2302
+ const startIdx = mappingData.startVertexIndex * 3;
2303
+ const endIdx = (mappingData.startVertexIndex + mappingData.vertexCount) * 3;
2304
+ for (let i = startIdx; i < endIdx; i++) {
2305
+ normals[i] = originalNormals[i];
2306
+ }
2307
+ continue;
2308
+ }
2309
+
2310
+ // Create normal matrix (inverse transpose of transform)
2311
+ normalMatrix.copy(transform).invert().transpose();
2312
+
2313
+ const startVertex = mappingData.startVertexIndex;
2314
+ const vertexCount = mappingData.vertexCount;
2315
+
2316
+ for (let i = 0; i < vertexCount; i++) {
2317
+ const idx = (startVertex + i) * 3;
2318
+
2319
+ // Get original normal
2320
+ tempVector.set(originalNormals[idx], originalNormals[idx + 1], originalNormals[idx + 2]);
2321
+
2322
+ // Apply normal transformation
2323
+ tempVector.applyMatrix4(normalMatrix).normalize();
2324
+
2325
+ // Write back transformed normal
2326
+ normals[idx] = tempVector.x;
2327
+ normals[idx + 1] = tempVector.y;
2328
+ normals[idx + 2] = tempVector.z;
2329
+ }
2330
+ }
2331
+
2332
+ normalAttr.needsUpdate = true;
2333
+ }
2334
+
2335
+ _restoreOriginalGeometry(mergedObject) {
2336
+ const geometry = mergedObject.geometry;
2337
+ if (!geometry || !geometry.attributes.position) return;
2338
+
2339
+ // Restore original positions
2340
+ const originalPositions = this.transformedGeometries.get(mergedObject.uuid);
2341
+ if (originalPositions) {
2342
+ const positions = geometry.attributes.position.array;
2343
+ positions.set(originalPositions);
2344
+ geometry.attributes.position.needsUpdate = true;
2345
+ }
2346
+
2347
+ // Restore original normals
2348
+ const normalsKey = `${geometry.uuid}_normals`;
2349
+ const originalNormals = this.transformedGeometries.get(normalsKey);
2350
+ if (originalNormals && geometry.attributes.normal) {
2351
+ const normals = geometry.attributes.normal.array;
2352
+ normals.set(originalNormals);
2353
+ geometry.attributes.normal.needsUpdate = true;
2354
+ }
2355
+
2356
+ geometry.computeBoundingSphere();
2357
+ geometry.computeBoundingBox();
2358
+ }
2359
+
2117
2360
  syncHiddenObjects() {
2118
2361
  if (this.mergedObjectMap.size === 0) {
2119
2362
  console.log("No merged objects to sync");
@@ -2149,19 +2392,41 @@ export class DynamicGltfLoader {
2149
2392
 
2150
2393
  getStructureGeometryExtent(structureId) {
2151
2394
  const extent = new Box3();
2395
+ const structureRoot = this.structureRoots.get(structureId);
2396
+
2152
2397
  for (const [nodeId, node] of this.nodes.entries()) {
2153
2398
  if (!node.geometryExtents) continue;
2154
2399
  if (!nodeId.startsWith(structureId + "_")) continue;
2155
2400
  if (node.object && this.hiddenHandles && this.hiddenHandles.has(node.object.userData.handle)) continue;
2156
2401
  const transformedBox = node.geometryExtents.clone();
2157
- if (node.group && node.group.matrix) {
2158
- transformedBox.applyMatrix4(node.group.matrix);
2159
- if (node.group.parent && node.group.parent.matrix) {
2160
- transformedBox.applyMatrix4(node.group.parent.matrix);
2402
+
2403
+ if (node.group) {
2404
+ const matrices = [];
2405
+ let currentGroup = node.group;
2406
+
2407
+ while (currentGroup && currentGroup !== structureRoot) {
2408
+ if (currentGroup.matrix && currentGroup.matrixAutoUpdate === false) {
2409
+ matrices.unshift(currentGroup.matrix);
2410
+ }
2411
+ currentGroup = currentGroup.parent;
2161
2412
  }
2413
+
2414
+ for (const matrix of matrices) {
2415
+ transformedBox.applyMatrix4(matrix);
2416
+ }
2417
+ }
2418
+
2419
+ if (structureRoot && structureRoot.matrix) {
2420
+ transformedBox.applyMatrix4(structureRoot.matrix);
2162
2421
  }
2422
+
2163
2423
  extent.union(transformedBox);
2164
2424
  }
2425
+
2426
+ if (this.scene && this.scene.matrix && !extent.isEmpty()) {
2427
+ extent.applyMatrix4(this.scene.matrix);
2428
+ }
2429
+
2165
2430
  return extent;
2166
2431
  }
2167
2432
 
@@ -21,7 +21,7 @@
21
21
  // acknowledge and accept the above terms.
22
22
  ///////////////////////////////////////////////////////////////////////////////
23
23
 
24
- import { Box3, Object3D } from "three";
24
+ import { Box3, Matrix4, Object3D, Vector3 } from "three";
25
25
  import { IInfo, Info } from "@inweb/viewer-core";
26
26
 
27
27
  import { ModelImpl } from "../../models/ModelImpl";
@@ -72,33 +72,28 @@ export class DynamicModelImpl extends ModelImpl {
72
72
  return this.gltfLoader.getOriginalObjectForSelect();
73
73
  }
74
74
 
75
- override hasObject(object: any): boolean {
76
- return this.gltfLoader.originalObjects.has(object);
77
- }
78
-
79
75
  override getObjectsByHandles(handles: string | string[]): Object3D[] {
80
- const ownHandles = this.getOwnHandles(handles);
81
- if (ownHandles.length === 0) return [];
76
+ if (!Array.isArray(handles)) handles = [handles];
82
77
 
83
- const handlesSet = new Set(ownHandles);
78
+ const handlesSet = new Set(handles);
84
79
 
85
80
  const objects = [];
86
81
  handlesSet.forEach((handle) => {
87
- objects.push(...this.gltfLoader.getObjectsByHandle(handle));
82
+ objects.push(this.gltfLoader.getObjectsByHandle(handle));
88
83
  });
89
84
 
90
- return objects;
85
+ return objects.flat();
91
86
  }
92
87
 
93
88
  override getHandlesByObjects(objects: Object3D | Object3D[]): string[] {
94
- const ownObjects = this.getOwnObjects(objects);
95
- if (ownObjects.length === 0) return [];
89
+ if (!Array.isArray(objects)) objects = [objects];
96
90
 
97
91
  const handleSet = new Set<string>();
98
- ownObjects.forEach((object) => {
99
- const handle = object.userData.handle;
100
- if (handle) handleSet.add(handle);
101
- });
92
+ objects
93
+ .filter((object) => this.gltfLoader.originalObjects.has(object))
94
+ .forEach((object) => {
95
+ handleSet.add(object.userData.handle);
96
+ });
102
97
 
103
98
  return Array.from(handleSet);
104
99
  }
@@ -126,13 +121,94 @@ export class DynamicModelImpl extends ModelImpl {
126
121
  return this;
127
122
  }
128
123
 
129
- override showOriginalObjects(objects: Object3D | Object3D[]): this {
124
+ override highlightObjects(objects: Object3D | Object3D[]): this {
130
125
  this.gltfLoader.showOriginalObjects(objects);
131
126
  return this;
132
127
  }
133
128
 
134
- override hideOriginalObjects(objects: Object3D | Object3D[]): this {
129
+ override unhighlightObjects(objects: Object3D | Object3D[]): this {
135
130
  this.gltfLoader.hideOriginalObjects(objects);
136
131
  return this;
137
132
  }
133
+
134
+ override explode(scale = 0, coeff = 4): this {
135
+ const centers = new Map();
136
+
137
+ const calcObjectCenter = (object: Object3D, target: Vector3): Vector3 => {
138
+ const extents = new Box3().setFromObject(object);
139
+
140
+ const handle = object.userData.handle;
141
+ if (!handle) return extents.getCenter(target);
142
+
143
+ const center = centers.get(handle);
144
+ if (center) return target.copy(center);
145
+
146
+ const objects = this.getObjectsByHandles(handle);
147
+ objects.forEach((x: Object3D) => extents.expandByObject(x));
148
+ extents.getCenter(target);
149
+
150
+ centers.set(handle, target.clone());
151
+
152
+ return target;
153
+ };
154
+
155
+ function calcExplodeDepth(object: Object3D, depth: number): number {
156
+ let result = depth;
157
+ object.children
158
+ .filter((x: Object3D) => !x.userData.isOptimized)
159
+ .forEach((x: Object3D) => {
160
+ const objectDepth = calcExplodeDepth(x, depth + 1);
161
+ if (result < objectDepth) result = objectDepth;
162
+ });
163
+
164
+ object.userData.originalPosition = object.position.clone();
165
+ object.userData.originalCenter = calcObjectCenter(object, new Vector3());
166
+
167
+ return result;
168
+ }
169
+
170
+ const explodeScale = scale / 100;
171
+ const explodeRoot = this.scene.children[0];
172
+
173
+ if (!explodeRoot.userData.explodeDepth) explodeRoot.userData.explodeDepth = calcExplodeDepth(explodeRoot, 1);
174
+ const maxDepth = explodeRoot.userData.explodeDepth;
175
+
176
+ const scaledExplodeDepth = explodeScale * maxDepth + 1;
177
+ const explodeDepth = 0 | scaledExplodeDepth;
178
+ const currentSegmentFraction = scaledExplodeDepth - explodeDepth;
179
+
180
+ const transformMap = new Map();
181
+
182
+ function explodeObject(object, depth: number) {
183
+ if (object.isCamera) return;
184
+ if (object.userData.isHighlightWireframe) return;
185
+
186
+ object.position.copy(object.userData.originalPosition);
187
+
188
+ if (depth > 0 && depth <= explodeDepth && !object.userData.isExplodeLocked) {
189
+ let objectScale = explodeScale * coeff;
190
+ if (depth === explodeDepth) objectScale *= currentSegmentFraction;
191
+
192
+ const parentCenter = object.parent.userData.originalCenter;
193
+ const objectCenter = object.userData.originalCenter;
194
+ const objectOffset = objectCenter.clone().sub(parentCenter).multiplyScalar(objectScale);
195
+
196
+ object.position.add(objectOffset);
197
+
198
+ const matrix = new Matrix4().makeTranslation(objectOffset.x, objectOffset.y, objectOffset.z);
199
+ transformMap.set(object, matrix);
200
+ }
201
+
202
+ object.children
203
+ .filter((x: Object3D) => !x.userData.isOptimized)
204
+ .forEach((x: Object3D) => explodeObject(x, depth + 1));
205
+ }
206
+
207
+ explodeObject(explodeRoot, 0);
208
+ this.scene.updateMatrixWorld();
209
+
210
+ this.gltfLoader.applyObjectTransforms(transformMap);
211
+
212
+ return this;
213
+ }
138
214
  }
@@ -58,7 +58,7 @@ export class GltfStructure {
58
58
  this.textureCache = new Map();
59
59
  this.materialCache = new Map();
60
60
  this.uri = "";
61
- this._nextObjectId = 0;
61
+ this._nextObjectId = 1;
62
62
  this.loadingAborted = false;
63
63
  this.criticalError = null;
64
64
  }
@@ -49,6 +49,7 @@ export class Snapper {
49
49
  public camera: Camera;
50
50
  public renderer: WebGLRenderer;
51
51
  public canvas: HTMLCanvasElement;
52
+ public threshold: number;
52
53
  private raycaster: Raycaster;
53
54
  private detectRadiusInPixels: number;
54
55
  private edgesCache: WeakMap<any, EdgesGeometry>;
@@ -58,6 +59,8 @@ export class Snapper {
58
59
  this.renderer = renderer;
59
60
  this.canvas = canvas;
60
61
 
62
+ this.threshold = 0.0001;
63
+
61
64
  this.raycaster = new Raycaster();
62
65
  this.detectRadiusInPixels = this.isMobile() ? MOBILE_SNAP_DISTANCE : DESKTOP_SNAP_DISTANCE;
63
66
  this.edgesCache = new WeakMap();
@@ -91,10 +94,10 @@ export class Snapper {
91
94
 
92
95
  this.raycaster.params = {
93
96
  Mesh: {},
94
- Line: { threshold: 0.05 },
95
- Line2: { threshold: 0.05 },
97
+ Line: { threshold: this.threshold },
98
+ Line2: { threshold: this.threshold },
96
99
  LOD: {},
97
- Points: { threshold: 0.01 },
100
+ Points: { threshold: this.threshold },
98
101
  Sprite: {},
99
102
  };
100
103
 
@@ -23,21 +23,28 @@
23
23
 
24
24
  // Model units for the user to choose from.
25
25
 
26
+ export const DisplayUnits = {
27
+ Meters: { name: "Meters", symbol: "m", scale: 1.0 },
28
+ Centimeters: { name: "Centimeters", symbol: "cm", scale: 0.01 },
29
+ Millimeters: { name: "Millimeters", symbol: "mm", scale: 0.001 },
30
+ Feet: { name: "Feet", symbol: "ft", scale: 0.3048 },
31
+ Inches: { name: "Inches", symbol: "in", scale: 0.0254 },
32
+ Yards: { name: "Yards", symbol: "yd", scale: 0.9144 },
33
+ Kilometers: { name: "Kilometers", symbol: "km", scale: 1000.0 },
34
+ Miles: { name: "Miles", symbol: "mi", scale: 1609.344 },
35
+ Micrometers: { name: "Micrometers", symbol: "µm", scale: 0.000001 },
36
+ Mils: { name: "Mils", symbol: "mil", scale: 0.0000254 },
37
+ MicroInches: { name: "Micro-inches", symbol: "µin", scale: 0.0000000254 },
38
+ Default: { name: "File units", symbol: "", scale: 1.0 },
39
+ };
40
+
26
41
  export const ModelUnits = {
27
- Meters: { name: "Meters", type: "m", scale: 1.0 },
28
- Centimeters: { name: "Centimeters", type: "cm", scale: 0.01 },
29
- Millimeters: { name: "Millimeters", type: "mm", scale: 0.001 },
30
- Feet: { name: "Feet", type: "ft", scale: 0.3048 },
31
- Inches: { name: "Inches", type: "in", scale: 0.0254 },
32
- Yards: { name: "Yards", type: "yd", scale: 0.9144 },
33
- Kilometers: { name: "Kilometers", type: "km", scale: 1000.0 },
34
- Miles: { name: "Miles", type: "mi", scale: 1609.344 },
35
- Micrometers: { name: "Micrometers", type: "µm", scale: 0.000001 },
36
- Mils: { name: "Mils", type: "mil", scale: 0.0000254 },
37
- MicroInches: { name: "Micro-inches", type: "µin", scale: 0.0000000254 },
38
- Default: { name: "File units", type: "unit", scale: 1.0 },
42
+ Default: { name: "", symbol: "", scale: 1.0 },
39
43
  };
40
44
 
45
+ Object.keys(DisplayUnits).forEach((key) => (ModelUnits[key] = DisplayUnits[key]));
46
+ Object.keys(DisplayUnits).forEach((key) => (ModelUnits[DisplayUnits[key].symbol] = DisplayUnits[key]));
47
+
41
48
  // Convert distance from unit to unit.
42
49
 
43
50
  export function convertUnits(fromUnits: string, toUnits: string, distance: number) {
@@ -26,7 +26,7 @@ import { ModelUnits } from "./UnitConverter";
26
26
  // Returns a standard string representation of the display unit.
27
27
 
28
28
  export function getDisplayUnit(units: string) {
29
- return (ModelUnits[units] || ModelUnits.Default).type;
29
+ return (ModelUnits[units] || ModelUnits.Default).symbol;
30
30
  }
31
31
 
32
32
  // ===================== AI-CODE-START ======================
@@ -86,7 +86,7 @@ export function formatDistance(distance: number, units: string, precision: any =
86
86
  else if (digits > 10) digits = 10;
87
87
 
88
88
  if (ModelUnits[units]) {
89
- return formatNumber(distance, digits, precision) + " " + ModelUnits[units].type;
89
+ return formatNumber(distance, digits, precision) + " " + ModelUnits[units].symbol;
90
90
  } else if (units) {
91
91
  return formatNumber(distance, digits, precision) + " " + units;
92
92
  } else {
@@ -46,14 +46,6 @@ export interface IModelImpl extends IModel {
46
46
 
47
47
  getVisibleObjects(): Object3D[];
48
48
 
49
- hasObject(object: Object3D): boolean;
50
-
51
- hasHandle(handle: string): boolean;
52
-
53
- getOwnObjects(objects: Object3D | Object3D[]): Object3D[];
54
-
55
- getOwnHandles(handles: string | string[]): string[];
56
-
57
49
  getObjectsByHandles(handles: string | string[]): Object3D[];
58
50
 
59
51
  getHandlesByObjects(objects: Object3D | Object3D[]): string[];
@@ -68,9 +60,9 @@ export interface IModelImpl extends IModel {
68
60
 
69
61
  showAllObjects(): this;
70
62
 
71
- showOriginalObjects(objects: Object3D | Object3D[]): this;
63
+ highlightObjects(objects: Object3D | Object3D[]): this;
72
64
 
73
- hideOriginalObjects(objects: Object3D | Object3D[]): this;
65
+ unhighlightObjects(objects: Object3D | Object3D[]): this;
74
66
 
75
67
  explode(scale: number, coeff?: number): this;
76
68
  }