@inweb/viewer-three 26.12.5 → 26.12.7
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/loaders/GLTFFileLoader.js +1 -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 +1 -1
- package/dist/extensions/loaders/GLTFFileLoader.module.js.map +1 -1
- package/dist/extensions/loaders/IFCXLoader.js +2 -2
- 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 +2 -2
- package/dist/extensions/loaders/IFCXLoader.module.js.map +1 -1
- package/dist/viewer-three.js +467 -164
- package/dist/viewer-three.js.map +1 -1
- package/dist/viewer-three.min.js +3 -3
- package/dist/viewer-three.module.js +465 -162
- package/dist/viewer-three.module.js.map +1 -1
- package/extensions/loaders/GLTFFileLoader.ts +1 -1
- package/extensions/loaders/IFCX/IFCXCloudLoader.ts +1 -1
- package/extensions/loaders/IFCX/IFCXFileLoader.ts +1 -1
- package/lib/Viewer/components/ExtentsComponent.d.ts +1 -1
- package/lib/Viewer/components/ResetComponent.d.ts +1 -1
- package/lib/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.d.ts +3 -3
- package/lib/Viewer/measurement/Snapper.d.ts +1 -0
- package/lib/Viewer/measurement/UnitConverter.d.ts +20 -13
- package/lib/Viewer/models/IModelImpl.d.ts +2 -6
- package/lib/Viewer/models/ModelImpl.d.ts +4 -6
- package/package.json +5 -5
- package/src/Viewer/Viewer.ts +15 -11
- package/src/Viewer/commands/GetSelected2.ts +2 -2
- package/src/Viewer/commands/ResetView.ts +0 -5
- package/src/Viewer/commands/ZoomTo.ts +3 -3
- package/src/Viewer/components/CameraComponent.ts +4 -4
- package/src/Viewer/components/ExtentsComponent.ts +3 -3
- package/src/Viewer/components/HighlighterComponent.ts +11 -17
- package/src/Viewer/components/ResetComponent.ts +5 -2
- package/src/Viewer/components/SelectionComponent.ts +14 -13
- package/src/Viewer/draggers/MeasureLineDragger.ts +1 -0
- package/src/Viewer/draggers/OrbitDragger.ts +2 -0
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +285 -10
- package/src/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.ts +94 -18
- package/src/Viewer/loaders/DynamicGltfLoader/GltfStructure.js +1 -1
- package/src/Viewer/measurement/Snapper.ts +6 -3
- package/src/Viewer/measurement/UnitConverter.ts +19 -12
- package/src/Viewer/measurement/UnitFormatter.ts +2 -2
- package/src/Viewer/models/IModelImpl.ts +2 -10
- package/src/Viewer/models/ModelImpl.ts +111 -61
|
@@ -102,6 +102,10 @@ export class DynamicGltfLoader {
|
|
|
102
102
|
this.newOptimizedObjects = new Set();
|
|
103
103
|
this.oldOptimizeObjects = new Set();
|
|
104
104
|
|
|
105
|
+
// Transform system for exploded view - works directly with original objects
|
|
106
|
+
this.objectTransforms = new Map(); // originalObject -> Matrix4
|
|
107
|
+
this.transformedGeometries = new Map(); // mergedObject.uuid -> original position data
|
|
108
|
+
|
|
105
109
|
this.activeChunkLoads = 0;
|
|
106
110
|
this.chunkQueue = [];
|
|
107
111
|
|
|
@@ -311,7 +315,6 @@ export class DynamicGltfLoader {
|
|
|
311
315
|
const uniqueTextureIds = new Set();
|
|
312
316
|
if (Array.isArray(this.structures)) {
|
|
313
317
|
for (const structure of this.structures) {
|
|
314
|
-
// console.log(structure.materialCache.values());
|
|
315
318
|
try {
|
|
316
319
|
for (const entry of structure.materialCache.values()) {
|
|
317
320
|
if (entry?.mesh?.uuid) uniqueMaterialIds.add(entry.mesh.uuid);
|
|
@@ -1006,17 +1009,40 @@ export class DynamicGltfLoader {
|
|
|
1006
1009
|
if (node.object && this.hiddenHandles.has(node.object.userData.handle)) continue;
|
|
1007
1010
|
|
|
1008
1011
|
const transformedBox = node.geometryExtents.clone();
|
|
1012
|
+
const structureRoot = node.structure ? this.structureRoots.get(node.structure.id) : null;
|
|
1013
|
+
|
|
1014
|
+
if (node.group) {
|
|
1015
|
+
const matrices = [];
|
|
1016
|
+
let currentGroup = node.group;
|
|
1009
1017
|
|
|
1010
|
-
|
|
1011
|
-
|
|
1018
|
+
while (currentGroup && currentGroup !== structureRoot) {
|
|
1019
|
+
if (currentGroup.matrix && currentGroup.matrixAutoUpdate === false) {
|
|
1020
|
+
matrices.unshift(currentGroup.matrix);
|
|
1021
|
+
}
|
|
1022
|
+
currentGroup = currentGroup.parent;
|
|
1023
|
+
}
|
|
1012
1024
|
|
|
1013
|
-
|
|
1014
|
-
transformedBox.applyMatrix4(
|
|
1025
|
+
for (const matrix of matrices) {
|
|
1026
|
+
transformedBox.applyMatrix4(matrix);
|
|
1015
1027
|
}
|
|
1016
1028
|
}
|
|
1029
|
+
|
|
1030
|
+
if (structureRoot && structureRoot.matrix) {
|
|
1031
|
+
transformedBox.applyMatrix4(structureRoot.matrix);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
const transform = this.objectTransforms.get(node.object);
|
|
1035
|
+
if (transform) {
|
|
1036
|
+
transformedBox.applyMatrix4(transform);
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1017
1039
|
totalExtent.union(transformedBox);
|
|
1018
1040
|
}
|
|
1019
1041
|
|
|
1042
|
+
if (this.scene && this.scene.matrix && !totalExtent.isEmpty()) {
|
|
1043
|
+
totalExtent.applyMatrix4(this.scene.matrix);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1020
1046
|
return totalExtent;
|
|
1021
1047
|
}
|
|
1022
1048
|
|
|
@@ -1101,7 +1127,6 @@ export class DynamicGltfLoader {
|
|
|
1101
1127
|
for (let i = 0; i < this.maxObjectId; i++) {
|
|
1102
1128
|
this.objectVisibility[i] = 1.0;
|
|
1103
1129
|
}
|
|
1104
|
-
console.log(`Initialized object visibility array: ${this.maxObjectId} objects`);
|
|
1105
1130
|
}
|
|
1106
1131
|
}
|
|
1107
1132
|
|
|
@@ -1255,6 +1280,9 @@ export class DynamicGltfLoader {
|
|
|
1255
1280
|
this.oldOptimizeObjects.clear();
|
|
1256
1281
|
this.isolatedObjects = [];
|
|
1257
1282
|
|
|
1283
|
+
this.objectTransforms.clear();
|
|
1284
|
+
this.transformedGeometries.clear();
|
|
1285
|
+
|
|
1258
1286
|
this.totalLoadedObjects = 0;
|
|
1259
1287
|
this.lastUpdateTime = 0;
|
|
1260
1288
|
this.currentMemoryUsage = 0;
|
|
@@ -2114,6 +2142,231 @@ export class DynamicGltfLoader {
|
|
|
2114
2142
|
}
|
|
2115
2143
|
}
|
|
2116
2144
|
|
|
2145
|
+
applyObjectTransforms(objectTransformMap) {
|
|
2146
|
+
if (this.mergedObjectMap.size === 0) {
|
|
2147
|
+
console.warn("No merged objects to transform");
|
|
2148
|
+
return;
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
// Store transform map directly
|
|
2152
|
+
this.objectTransforms = new Map(objectTransformMap);
|
|
2153
|
+
|
|
2154
|
+
// Apply transforms to all merged meshes
|
|
2155
|
+
for (const mesh of this.mergedMesh) {
|
|
2156
|
+
this._applyTransformToMergedObject(mesh);
|
|
2157
|
+
}
|
|
2158
|
+
for (const line of this.mergedLines) {
|
|
2159
|
+
this._applyTransformToMergedObject(line);
|
|
2160
|
+
}
|
|
2161
|
+
for (const lineSegment of this.mergedLineSegments) {
|
|
2162
|
+
this._applyTransformToMergedObject(lineSegment);
|
|
2163
|
+
}
|
|
2164
|
+
for (const point of this.mergedPoints) {
|
|
2165
|
+
this._applyTransformToMergedObject(point);
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
createExplodeTransforms(objects = null, explodeCenter = null, explodeFactor = 1.5) {
|
|
2170
|
+
const transformMap = new Map();
|
|
2171
|
+
|
|
2172
|
+
if (!explodeCenter) {
|
|
2173
|
+
explodeCenter = new Vector3();
|
|
2174
|
+
const extent = this.getTotalGeometryExtent();
|
|
2175
|
+
if (!extent.isEmpty()) {
|
|
2176
|
+
extent.getCenter(explodeCenter);
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
const objectsArray = objects
|
|
2181
|
+
? Array.isArray(objects)
|
|
2182
|
+
? objects
|
|
2183
|
+
: Array.from(objects)
|
|
2184
|
+
: Array.from(this.originalObjects);
|
|
2185
|
+
|
|
2186
|
+
for (const obj of objectsArray) {
|
|
2187
|
+
if (!obj.geometry || !obj.geometry.attributes.position) continue;
|
|
2188
|
+
|
|
2189
|
+
const boundingBox = new Box3().setFromBufferAttribute(obj.geometry.attributes.position);
|
|
2190
|
+
|
|
2191
|
+
if (obj.matrixWorld) {
|
|
2192
|
+
boundingBox.applyMatrix4(obj.matrixWorld);
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
if (boundingBox.isEmpty()) continue;
|
|
2196
|
+
|
|
2197
|
+
const objectCenter = new Vector3();
|
|
2198
|
+
boundingBox.getCenter(objectCenter);
|
|
2199
|
+
|
|
2200
|
+
const direction = objectCenter.clone().sub(explodeCenter);
|
|
2201
|
+
const distance = direction.length();
|
|
2202
|
+
|
|
2203
|
+
if (distance > 0) {
|
|
2204
|
+
direction.normalize();
|
|
2205
|
+
const offset = direction.multiplyScalar(distance * (explodeFactor - 1.0));
|
|
2206
|
+
|
|
2207
|
+
const matrix = new Matrix4().makeTranslation(offset.x, offset.y, offset.z);
|
|
2208
|
+
transformMap.set(obj, matrix);
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
return transformMap;
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
clearTransforms() {
|
|
2216
|
+
this.objectTransforms.clear();
|
|
2217
|
+
|
|
2218
|
+
for (const mesh of this.mergedMesh) {
|
|
2219
|
+
this._restoreOriginalGeometry(mesh);
|
|
2220
|
+
}
|
|
2221
|
+
for (const line of this.mergedLines) {
|
|
2222
|
+
this._restoreOriginalGeometry(line);
|
|
2223
|
+
}
|
|
2224
|
+
for (const lineSegment of this.mergedLineSegments) {
|
|
2225
|
+
this._restoreOriginalGeometry(lineSegment);
|
|
2226
|
+
}
|
|
2227
|
+
for (const point of this.mergedPoints) {
|
|
2228
|
+
this._restoreOriginalGeometry(point);
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
clearHandleTransforms() {
|
|
2233
|
+
this.clearTransforms();
|
|
2234
|
+
}
|
|
2235
|
+
|
|
2236
|
+
_applyTransformToMergedObject(mergedObject) {
|
|
2237
|
+
const objectData = this.mergedObjectMap.get(mergedObject.uuid);
|
|
2238
|
+
if (!objectData || !objectData.objectMapping) return;
|
|
2239
|
+
|
|
2240
|
+
const geometry = mergedObject.geometry;
|
|
2241
|
+
if (!geometry || !geometry.attributes.position) return;
|
|
2242
|
+
|
|
2243
|
+
const positionAttr = geometry.attributes.position;
|
|
2244
|
+
const positions = positionAttr.array;
|
|
2245
|
+
|
|
2246
|
+
if (!this.transformedGeometries.has(mergedObject.uuid)) {
|
|
2247
|
+
this.transformedGeometries.set(mergedObject.uuid, new Float32Array(positions));
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
const originalPositions = this.transformedGeometries.get(mergedObject.uuid);
|
|
2251
|
+
const tempVector = new Vector3();
|
|
2252
|
+
|
|
2253
|
+
for (const [originalMesh, mappingData] of objectData.objectMapping) {
|
|
2254
|
+
const transform = this.objectTransforms.get(originalMesh);
|
|
2255
|
+
|
|
2256
|
+
if (!transform) {
|
|
2257
|
+
const startIdx = mappingData.startVertexIndex * 3;
|
|
2258
|
+
const endIdx = (mappingData.startVertexIndex + mappingData.vertexCount) * 3;
|
|
2259
|
+
for (let i = startIdx; i < endIdx; i++) {
|
|
2260
|
+
positions[i] = originalPositions[i];
|
|
2261
|
+
}
|
|
2262
|
+
continue;
|
|
2263
|
+
}
|
|
2264
|
+
|
|
2265
|
+
const startVertex = mappingData.startVertexIndex;
|
|
2266
|
+
const vertexCount = mappingData.vertexCount;
|
|
2267
|
+
|
|
2268
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
2269
|
+
const idx = (startVertex + i) * 3;
|
|
2270
|
+
|
|
2271
|
+
tempVector.set(originalPositions[idx], originalPositions[idx + 1], originalPositions[idx + 2]);
|
|
2272
|
+
|
|
2273
|
+
tempVector.applyMatrix4(transform);
|
|
2274
|
+
|
|
2275
|
+
positions[idx] = tempVector.x;
|
|
2276
|
+
positions[idx + 1] = tempVector.y;
|
|
2277
|
+
positions[idx + 2] = tempVector.z;
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
if (geometry.attributes.normal) {
|
|
2282
|
+
this._updateNormalsForTransform(geometry, objectData, originalPositions);
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2285
|
+
positionAttr.needsUpdate = true;
|
|
2286
|
+
geometry.computeBoundingSphere();
|
|
2287
|
+
geometry.computeBoundingBox();
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2290
|
+
_updateNormalsForTransform(geometry, objectData, originalPositions) {
|
|
2291
|
+
const normalAttr = geometry.attributes.normal;
|
|
2292
|
+
if (!normalAttr) return;
|
|
2293
|
+
|
|
2294
|
+
const normals = normalAttr.array;
|
|
2295
|
+
const tempVector = new Vector3();
|
|
2296
|
+
const normalMatrix = new Matrix4();
|
|
2297
|
+
|
|
2298
|
+
// Store original normals if not already stored
|
|
2299
|
+
const normalsKey = `${geometry.uuid}_normals`;
|
|
2300
|
+
if (!this.transformedGeometries.has(normalsKey)) {
|
|
2301
|
+
this.transformedGeometries.set(normalsKey, new Float32Array(normals));
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
const originalNormals = this.transformedGeometries.get(normalsKey);
|
|
2305
|
+
|
|
2306
|
+
for (const [originalMesh, mappingData] of objectData.objectMapping) {
|
|
2307
|
+
// Direct lookup by object reference - NO HANDLE LOOKUP!
|
|
2308
|
+
const transform = this.objectTransforms.get(originalMesh);
|
|
2309
|
+
|
|
2310
|
+
if (!transform) {
|
|
2311
|
+
// Restore original normals
|
|
2312
|
+
const startIdx = mappingData.startVertexIndex * 3;
|
|
2313
|
+
const endIdx = (mappingData.startVertexIndex + mappingData.vertexCount) * 3;
|
|
2314
|
+
for (let i = startIdx; i < endIdx; i++) {
|
|
2315
|
+
normals[i] = originalNormals[i];
|
|
2316
|
+
}
|
|
2317
|
+
continue;
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2320
|
+
// Create normal matrix (inverse transpose of transform)
|
|
2321
|
+
normalMatrix.copy(transform).invert().transpose();
|
|
2322
|
+
|
|
2323
|
+
const startVertex = mappingData.startVertexIndex;
|
|
2324
|
+
const vertexCount = mappingData.vertexCount;
|
|
2325
|
+
|
|
2326
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
2327
|
+
const idx = (startVertex + i) * 3;
|
|
2328
|
+
|
|
2329
|
+
// Get original normal
|
|
2330
|
+
tempVector.set(originalNormals[idx], originalNormals[idx + 1], originalNormals[idx + 2]);
|
|
2331
|
+
|
|
2332
|
+
// Apply normal transformation
|
|
2333
|
+
tempVector.applyMatrix4(normalMatrix).normalize();
|
|
2334
|
+
|
|
2335
|
+
// Write back transformed normal
|
|
2336
|
+
normals[idx] = tempVector.x;
|
|
2337
|
+
normals[idx + 1] = tempVector.y;
|
|
2338
|
+
normals[idx + 2] = tempVector.z;
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
|
|
2342
|
+
normalAttr.needsUpdate = true;
|
|
2343
|
+
}
|
|
2344
|
+
|
|
2345
|
+
_restoreOriginalGeometry(mergedObject) {
|
|
2346
|
+
const geometry = mergedObject.geometry;
|
|
2347
|
+
if (!geometry || !geometry.attributes.position) return;
|
|
2348
|
+
|
|
2349
|
+
// Restore original positions
|
|
2350
|
+
const originalPositions = this.transformedGeometries.get(mergedObject.uuid);
|
|
2351
|
+
if (originalPositions) {
|
|
2352
|
+
const positions = geometry.attributes.position.array;
|
|
2353
|
+
positions.set(originalPositions);
|
|
2354
|
+
geometry.attributes.position.needsUpdate = true;
|
|
2355
|
+
}
|
|
2356
|
+
|
|
2357
|
+
// Restore original normals
|
|
2358
|
+
const normalsKey = `${geometry.uuid}_normals`;
|
|
2359
|
+
const originalNormals = this.transformedGeometries.get(normalsKey);
|
|
2360
|
+
if (originalNormals && geometry.attributes.normal) {
|
|
2361
|
+
const normals = geometry.attributes.normal.array;
|
|
2362
|
+
normals.set(originalNormals);
|
|
2363
|
+
geometry.attributes.normal.needsUpdate = true;
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
geometry.computeBoundingSphere();
|
|
2367
|
+
geometry.computeBoundingBox();
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2117
2370
|
syncHiddenObjects() {
|
|
2118
2371
|
if (this.mergedObjectMap.size === 0) {
|
|
2119
2372
|
console.log("No merged objects to sync");
|
|
@@ -2149,19 +2402,41 @@ export class DynamicGltfLoader {
|
|
|
2149
2402
|
|
|
2150
2403
|
getStructureGeometryExtent(structureId) {
|
|
2151
2404
|
const extent = new Box3();
|
|
2405
|
+
const structureRoot = this.structureRoots.get(structureId);
|
|
2406
|
+
|
|
2152
2407
|
for (const [nodeId, node] of this.nodes.entries()) {
|
|
2153
2408
|
if (!node.geometryExtents) continue;
|
|
2154
2409
|
if (!nodeId.startsWith(structureId + "_")) continue;
|
|
2155
2410
|
if (node.object && this.hiddenHandles && this.hiddenHandles.has(node.object.userData.handle)) continue;
|
|
2156
2411
|
const transformedBox = node.geometryExtents.clone();
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2412
|
+
|
|
2413
|
+
if (node.group) {
|
|
2414
|
+
const matrices = [];
|
|
2415
|
+
let currentGroup = node.group;
|
|
2416
|
+
|
|
2417
|
+
while (currentGroup && currentGroup !== structureRoot) {
|
|
2418
|
+
if (currentGroup.matrix && currentGroup.matrixAutoUpdate === false) {
|
|
2419
|
+
matrices.unshift(currentGroup.matrix);
|
|
2420
|
+
}
|
|
2421
|
+
currentGroup = currentGroup.parent;
|
|
2422
|
+
}
|
|
2423
|
+
|
|
2424
|
+
for (const matrix of matrices) {
|
|
2425
|
+
transformedBox.applyMatrix4(matrix);
|
|
2161
2426
|
}
|
|
2162
2427
|
}
|
|
2428
|
+
|
|
2429
|
+
if (structureRoot && structureRoot.matrix) {
|
|
2430
|
+
transformedBox.applyMatrix4(structureRoot.matrix);
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2163
2433
|
extent.union(transformedBox);
|
|
2164
2434
|
}
|
|
2435
|
+
|
|
2436
|
+
if (this.scene && this.scene.matrix && !extent.isEmpty()) {
|
|
2437
|
+
extent.applyMatrix4(this.scene.matrix);
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2165
2440
|
return extent;
|
|
2166
2441
|
}
|
|
2167
2442
|
|
|
@@ -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
|
-
|
|
81
|
-
if (ownHandles.length === 0) return [];
|
|
76
|
+
if (!Array.isArray(handles)) handles = [handles];
|
|
82
77
|
|
|
83
|
-
const handlesSet = new Set(
|
|
78
|
+
const handlesSet = new Set(handles);
|
|
84
79
|
|
|
85
80
|
const objects = [];
|
|
86
81
|
handlesSet.forEach((handle) => {
|
|
87
|
-
objects.push(
|
|
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
|
-
|
|
95
|
-
if (ownObjects.length === 0) return [];
|
|
89
|
+
if (!Array.isArray(objects)) objects = [objects];
|
|
96
90
|
|
|
97
91
|
const handleSet = new Set<string>();
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
|
124
|
+
override highlightObjects(objects: Object3D | Object3D[]): this {
|
|
130
125
|
this.gltfLoader.showOriginalObjects(objects);
|
|
131
126
|
return this;
|
|
132
127
|
}
|
|
133
128
|
|
|
134
|
-
override
|
|
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
|
}
|
|
@@ -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:
|
|
95
|
-
Line2: { threshold:
|
|
97
|
+
Line: { threshold: this.threshold },
|
|
98
|
+
Line2: { threshold: this.threshold },
|
|
96
99
|
LOD: {},
|
|
97
|
-
Points: { threshold:
|
|
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
|
-
|
|
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).
|
|
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].
|
|
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
|
-
|
|
63
|
+
highlightObjects(objects: Object3D | Object3D[]): this;
|
|
72
64
|
|
|
73
|
-
|
|
65
|
+
unhighlightObjects(objects: Object3D | Object3D[]): this;
|
|
74
66
|
|
|
75
67
|
explode(scale: number, coeff?: number): this;
|
|
76
68
|
}
|