@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.
Files changed (45) 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 +467 -164
  12. package/dist/viewer-three.js.map +1 -1
  13. package/dist/viewer-three.min.js +3 -3
  14. package/dist/viewer-three.module.js +465 -162
  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/components/ExtentsComponent.d.ts +1 -1
  20. package/lib/Viewer/components/ResetComponent.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 +15 -11
  28. package/src/Viewer/commands/GetSelected2.ts +2 -2
  29. package/src/Viewer/commands/ResetView.ts +0 -5
  30. package/src/Viewer/commands/ZoomTo.ts +3 -3
  31. package/src/Viewer/components/CameraComponent.ts +4 -4
  32. package/src/Viewer/components/ExtentsComponent.ts +3 -3
  33. package/src/Viewer/components/HighlighterComponent.ts +11 -17
  34. package/src/Viewer/components/ResetComponent.ts +5 -2
  35. package/src/Viewer/components/SelectionComponent.ts +14 -13
  36. package/src/Viewer/draggers/MeasureLineDragger.ts +1 -0
  37. package/src/Viewer/draggers/OrbitDragger.ts +2 -0
  38. package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +285 -10
  39. package/src/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.ts +94 -18
  40. package/src/Viewer/loaders/DynamicGltfLoader/GltfStructure.js +1 -1
  41. package/src/Viewer/measurement/Snapper.ts +6 -3
  42. package/src/Viewer/measurement/UnitConverter.ts +19 -12
  43. package/src/Viewer/measurement/UnitFormatter.ts +2 -2
  44. package/src/Viewer/models/IModelImpl.ts +2 -10
  45. package/src/Viewer/models/ModelImpl.ts +111 -61
@@ -805,6 +805,7 @@ class OrbitDragger {
805
805
  this.changed = false;
806
806
  this.viewer = viewer;
807
807
  this.viewer.addEventListener("databasechunk", this.updateControls);
808
+ this.viewer.addEventListener("clear", this.updateControls);
808
809
  this.viewer.on("viewposition", this.updateControls);
809
810
  this.viewer.addEventListener("zoom", this.updateControls);
810
811
  this.viewer.addEventListener("drawviewpoint", this.updateControls);
@@ -816,6 +817,7 @@ class OrbitDragger {
816
817
  initialize() { }
817
818
  dispose() {
818
819
  this.viewer.removeEventListener("databasechunk", this.updateControls);
820
+ this.viewer.removeEventListener("clear", this.updateControls);
819
821
  this.viewer.off("viewposition", this.updateControls);
820
822
  this.viewer.removeEventListener("zoom", this.updateControls);
821
823
  this.viewer.removeEventListener("drawviewpoint", this.updateControls);
@@ -913,20 +915,25 @@ class CuttingPlaneZAxisDragger extends CuttingPlaneDragger {
913
915
  }
914
916
  }
915
917
 
918
+ const DisplayUnits = {
919
+ Meters: { name: "Meters", symbol: "m", scale: 1.0 },
920
+ Centimeters: { name: "Centimeters", symbol: "cm", scale: 0.01 },
921
+ Millimeters: { name: "Millimeters", symbol: "mm", scale: 0.001 },
922
+ Feet: { name: "Feet", symbol: "ft", scale: 0.3048 },
923
+ Inches: { name: "Inches", symbol: "in", scale: 0.0254 },
924
+ Yards: { name: "Yards", symbol: "yd", scale: 0.9144 },
925
+ Kilometers: { name: "Kilometers", symbol: "km", scale: 1000.0 },
926
+ Miles: { name: "Miles", symbol: "mi", scale: 1609.344 },
927
+ Micrometers: { name: "Micrometers", symbol: "µm", scale: 0.000001 },
928
+ Mils: { name: "Mils", symbol: "mil", scale: 0.0000254 },
929
+ MicroInches: { name: "Micro-inches", symbol: "µin", scale: 0.0000000254 },
930
+ Default: { name: "File units", symbol: "", scale: 1.0 },
931
+ };
916
932
  const ModelUnits = {
917
- Meters: { name: "Meters", type: "m", scale: 1.0 },
918
- Centimeters: { name: "Centimeters", type: "cm", scale: 0.01 },
919
- Millimeters: { name: "Millimeters", type: "mm", scale: 0.001 },
920
- Feet: { name: "Feet", type: "ft", scale: 0.3048 },
921
- Inches: { name: "Inches", type: "in", scale: 0.0254 },
922
- Yards: { name: "Yards", type: "yd", scale: 0.9144 },
923
- Kilometers: { name: "Kilometers", type: "km", scale: 1000.0 },
924
- Miles: { name: "Miles", type: "mi", scale: 1609.344 },
925
- Micrometers: { name: "Micrometers", type: "µm", scale: 0.000001 },
926
- Mils: { name: "Mils", type: "mil", scale: 0.0000254 },
927
- MicroInches: { name: "Micro-inches", type: "µin", scale: 0.0000000254 },
928
- Default: { name: "File units", type: "unit", scale: 1.0 },
933
+ Default: { name: "", symbol: "", scale: 1.0 },
929
934
  };
935
+ Object.keys(DisplayUnits).forEach((key) => (ModelUnits[key] = DisplayUnits[key]));
936
+ Object.keys(DisplayUnits).forEach((key) => (ModelUnits[DisplayUnits[key].symbol] = DisplayUnits[key]));
930
937
  function convertUnits(fromUnits, toUnits, distance) {
931
938
  const fromFactor = 1 / (ModelUnits[fromUnits] || ModelUnits.Default).scale;
932
939
  const toFactor = (ModelUnits[toUnits] || ModelUnits.Default).scale || 1;
@@ -934,7 +941,7 @@ function convertUnits(fromUnits, toUnits, distance) {
934
941
  }
935
942
 
936
943
  function getDisplayUnit(units) {
937
- return (ModelUnits[units] || ModelUnits.Default).type;
944
+ return (ModelUnits[units] || ModelUnits.Default).symbol;
938
945
  }
939
946
  function calculatePrecision(value) {
940
947
  const distance = Math.abs(value);
@@ -971,7 +978,7 @@ function formatDistance(distance, units, precision = 2) {
971
978
  else if (digits > 10)
972
979
  digits = 10;
973
980
  if (ModelUnits[units]) {
974
- return formatNumber(distance, digits, precision) + " " + ModelUnits[units].type;
981
+ return formatNumber(distance, digits, precision) + " " + ModelUnits[units].symbol;
975
982
  }
976
983
  else if (units) {
977
984
  return formatNumber(distance, digits, precision) + " " + units;
@@ -994,6 +1001,7 @@ class Snapper {
994
1001
  this.camera = camera;
995
1002
  this.renderer = renderer;
996
1003
  this.canvas = canvas;
1004
+ this.threshold = 0.0001;
997
1005
  this.raycaster = new Raycaster();
998
1006
  this.detectRadiusInPixels = this.isMobile() ? MOBILE_SNAP_DISTANCE : DESKTOP_SNAP_DISTANCE;
999
1007
  this.edgesCache = new WeakMap();
@@ -1014,10 +1022,10 @@ class Snapper {
1014
1022
  this.raycaster.setFromCamera(coords, this.camera);
1015
1023
  this.raycaster.params = {
1016
1024
  Mesh: {},
1017
- Line: { threshold: 0.05 },
1018
- Line2: { threshold: 0.05 },
1025
+ Line: { threshold: this.threshold },
1026
+ Line2: { threshold: this.threshold },
1019
1027
  LOD: {},
1020
- Points: { threshold: 0.01 },
1028
+ Points: { threshold: this.threshold },
1021
1029
  Sprite: {},
1022
1030
  };
1023
1031
  let intersects = this.raycaster.intersectObjects(objects, false);
@@ -1186,6 +1194,7 @@ class MeasureLineDragger extends OrbitDragger {
1186
1194
  this.line = new MeasureLine(this.overlay, this.scale, this.units, this.precision);
1187
1195
  this.overlay.addLine(this.line);
1188
1196
  this.snapper = new Snapper(viewer.camera, viewer.renderer, viewer.canvas);
1197
+ this.snapper.threshold = viewer.extents.getSize(new Vector3()).length() / 10000;
1189
1198
  this.objects = [];
1190
1199
  this.updateObjects();
1191
1200
  this.updateUnits();
@@ -2189,7 +2198,7 @@ function zoomTo(viewer, box) {
2189
2198
  const boxCenter = box.getCenter(new Vector3());
2190
2199
  const boxSize = box.getBoundingSphere(new Sphere()).radius;
2191
2200
  const rendererSize = viewer.renderer.getSize(new Vector2());
2192
- const aspect = rendererSize.x / rendererSize.y;
2201
+ const aspectRatio = rendererSize.x / rendererSize.y;
2193
2202
  const camera = viewer.camera;
2194
2203
  if (camera.isPerspectiveCamera) {
2195
2204
  const offset = new Vector3(0, 0, 1)
@@ -2201,8 +2210,8 @@ function zoomTo(viewer, box) {
2201
2210
  if (camera.isOrthographicCamera) {
2202
2211
  camera.top = boxSize;
2203
2212
  camera.bottom = -boxSize;
2204
- camera.left = camera.bottom * aspect;
2205
- camera.right = camera.top * aspect;
2213
+ camera.left = camera.bottom * aspectRatio;
2214
+ camera.right = camera.top * aspectRatio;
2206
2215
  camera.zoom = 1;
2207
2216
  camera.updateProjectionMatrix();
2208
2217
  const offset = new Vector3(0, 0, 1)
@@ -2263,9 +2272,9 @@ function getSelected(viewer) {
2263
2272
  function getSelected2(viewer) {
2264
2273
  const handles2 = [];
2265
2274
  viewer.models.forEach((model) => {
2266
- handles2.push(...model.getHandlesByObjects(viewer.selected));
2275
+ handles2.push(model.getHandlesByObjects(viewer.selected));
2267
2276
  });
2268
- return handles2;
2277
+ return handles2.flat();
2269
2278
  }
2270
2279
 
2271
2280
  function hideSelected(viewer) {
@@ -2289,7 +2298,6 @@ function regenerateAll(viewer) {
2289
2298
  }
2290
2299
 
2291
2300
  function resetView(viewer) {
2292
- const reset = viewer.getComponent("ResetComponent");
2293
2301
  viewer.executeCommand("setActiveDragger");
2294
2302
  viewer.executeCommand("clearSlices");
2295
2303
  viewer.executeCommand("clearOverlay");
@@ -2298,7 +2306,6 @@ function resetView(viewer) {
2298
2306
  viewer.executeCommand("showAll");
2299
2307
  viewer.executeCommand("explode", 0);
2300
2308
  viewer.executeCommand("zoomToExtents", true);
2301
- reset.resetCameraPosition();
2302
2309
  viewer.emit({ type: "resetview" });
2303
2310
  }
2304
2311
 
@@ -2491,15 +2498,15 @@ class CameraComponent {
2491
2498
  switchCamera(camera) {
2492
2499
  const extentsSize = this.viewer.extents.getBoundingSphere(new Sphere()).radius * 2 || 1;
2493
2500
  const rendererSize = this.viewer.renderer.getSize(new Vector2());
2494
- const aspect = rendererSize.x / rendererSize.y;
2501
+ const aspectRatio = rendererSize.x / rendererSize.y;
2495
2502
  if (camera.isPerspectiveCamera) {
2496
- camera.aspect = aspect;
2503
+ camera.aspect = aspectRatio;
2497
2504
  camera.near = extentsSize / 1000;
2498
2505
  camera.far = extentsSize * 1000;
2499
2506
  }
2500
2507
  if (camera.isOrthographicCamera) {
2501
- camera.left = camera.bottom * aspect;
2502
- camera.right = camera.top * aspect;
2508
+ camera.left = camera.bottom * aspectRatio;
2509
+ camera.right = camera.top * aspectRatio;
2503
2510
  camera.near = 0;
2504
2511
  camera.far = extentsSize * 1000;
2505
2512
  }
@@ -2547,10 +2554,12 @@ class CameraComponent {
2547
2554
 
2548
2555
  class ExtentsComponent {
2549
2556
  constructor(viewer) {
2550
- this.syncExtents = () => {
2557
+ this.syncExtents = (event) => {
2551
2558
  const extents = new Box3();
2552
2559
  this.viewer.models.forEach((model) => model.getExtents(extents));
2553
2560
  this.viewer.extents.copy(extents);
2561
+ if (event.type !== "databasechunk" && event.target !== "clear")
2562
+ return;
2554
2563
  if (this.viewer.models.length > 1)
2555
2564
  return;
2556
2565
  this.viewer.extents.getCenter(this.viewer.target);
@@ -2917,12 +2926,12 @@ class HighlighterComponent {
2917
2926
  const { edgesVisibility } = this.viewer.options;
2918
2927
  if (!Array.isArray(objects))
2919
2928
  objects = [objects];
2920
- if (!objects.length)
2929
+ if (objects.length === 0)
2921
2930
  return;
2922
2931
  objects
2923
2932
  .filter((object) => !object.userData.isEdge)
2924
2933
  .forEach((object) => {
2925
- if (object.isHighlighted)
2934
+ if (object.userData.isHighlighted)
2926
2935
  return;
2927
2936
  if (object.isLine || object.isLineSegments) {
2928
2937
  const positions = object.geometry.attributes.position.array;
@@ -2931,43 +2940,39 @@ class HighlighterComponent {
2931
2940
  ? HighlighterUtils.fromIndexedLine(positions, indices)
2932
2941
  : HighlighterUtils.fromNonIndexedLine(positions, object.isLineSegments);
2933
2942
  const wireframe = new Wireframe(lineGeometry, this.lineGlowMaterial);
2934
- wireframe.position.copy(object.position);
2935
- wireframe.rotation.copy(object.rotation);
2936
- wireframe.scale.copy(object.scale);
2937
2943
  wireframe.visible = edgesVisibility;
2938
- object.parent.add(wireframe);
2944
+ wireframe.userData.isHighlightWireframe = true;
2945
+ object.add(wireframe);
2939
2946
  object.userData.highlightWireframe = wireframe;
2940
2947
  object.userData.originalMaterial = object.material;
2941
2948
  object.material = this.lineMaterial;
2942
- object.isHighlighted = true;
2949
+ object.userData.isHighlighted = true;
2943
2950
  }
2944
2951
  else if (object.isMesh) {
2945
2952
  const edgesGeometry = new EdgesGeometry(object.geometry, 89);
2946
2953
  const lineGeometry = new LineSegmentsGeometry().fromEdgesGeometry(edgesGeometry);
2947
2954
  const wireframe = new Wireframe(lineGeometry, this.edgesMaterial);
2948
- wireframe.position.copy(object.position);
2949
- wireframe.rotation.copy(object.rotation);
2950
- wireframe.scale.copy(object.scale);
2951
2955
  wireframe.visible = edgesVisibility;
2952
- object.parent.add(wireframe);
2956
+ wireframe.userData.isHighlightWireframe = true;
2957
+ object.add(wireframe);
2953
2958
  object.userData.highlightWireframe = wireframe;
2954
2959
  object.userData.originalMaterial = object.material;
2955
2960
  object.material = this.facesMaterial;
2956
- object.isHighlighted = true;
2961
+ object.userData.isHighlighted = true;
2957
2962
  }
2958
2963
  });
2959
2964
  }
2960
2965
  unhighlight(objects) {
2961
2966
  if (!Array.isArray(objects))
2962
2967
  objects = [objects];
2963
- if (!objects.length)
2968
+ if (objects.length === 0)
2964
2969
  return;
2965
2970
  objects.forEach((object) => {
2966
- if (!object.isHighlighted)
2971
+ if (!object.userData.isHighlighted)
2967
2972
  return;
2968
- object.isHighlighted = false;
2969
2973
  object.material = object.userData.originalMaterial;
2970
2974
  object.userData.highlightWireframe.removeFromParent();
2975
+ delete object.userData.isHighlighted;
2971
2976
  delete object.userData.originalMaterial;
2972
2977
  delete object.userData.highlightWireframe;
2973
2978
  });
@@ -3015,6 +3020,7 @@ class SelectionComponent {
3015
3020
  if (upPosition.distanceTo(this.downPosition) !== 0)
3016
3021
  return;
3017
3022
  const snapper = new Snapper(this.viewer.camera, this.viewer.renderer, this.viewer.canvas);
3023
+ snapper.threshold = this.viewer.extents.getSize(new Vector3()).length() / 10000;
3018
3024
  let intersections = [];
3019
3025
  this.viewer.models.forEach((model) => {
3020
3026
  const objects = model.getVisibleObjects();
@@ -3069,12 +3075,12 @@ class SelectionComponent {
3069
3075
  }
3070
3076
  if (!Array.isArray(objects))
3071
3077
  objects = [objects];
3072
- if (!objects.length)
3078
+ if (objects.length === 0)
3073
3079
  return;
3074
- model.showOriginalObjects(objects);
3080
+ model.highlightObjects(objects);
3075
3081
  this.highlighter.highlight(objects);
3076
3082
  objects.forEach((object) => this.viewer.selected.push(object));
3077
- objects.forEach((object) => (object.isSelected = true));
3083
+ objects.forEach((object) => (object.userData.isSelected = true));
3078
3084
  }
3079
3085
  deselect(objects, model) {
3080
3086
  if (!model) {
@@ -3083,29 +3089,29 @@ class SelectionComponent {
3083
3089
  }
3084
3090
  if (!Array.isArray(objects))
3085
3091
  objects = [objects];
3086
- if (!objects.length)
3092
+ if (objects.length === 0)
3087
3093
  return;
3094
+ model.unhighlightObjects(objects);
3088
3095
  this.highlighter.unhighlight(objects);
3089
- model.hideOriginalObjects(objects);
3090
3096
  this.viewer.selected = this.viewer.selected.filter((x) => !objects.includes(x));
3091
- objects.forEach((object) => (object.isSelected = false));
3097
+ objects.forEach((object) => (object.userData.isSelected = false));
3092
3098
  }
3093
3099
  toggleSelection(objects, model) {
3094
3100
  if (!Array.isArray(objects))
3095
3101
  objects = [objects];
3096
- if (!objects.length)
3102
+ if (objects.length === 0)
3097
3103
  return;
3098
- if (objects[0].isSelected)
3104
+ if (objects[0].userData.isSelected)
3099
3105
  this.deselect(objects, model);
3100
3106
  else
3101
3107
  this.select(objects, model);
3102
3108
  }
3103
3109
  clearSelection() {
3104
- if (!this.viewer.selected.length)
3110
+ if (this.viewer.selected.length === 0)
3105
3111
  return;
3112
+ this.viewer.models.forEach((model) => model.unhighlightObjects(this.viewer.selected));
3106
3113
  this.highlighter.unhighlight(this.viewer.selected);
3107
- this.viewer.models.forEach((model) => model.hideOriginalObjects(this.viewer.selected));
3108
- this.viewer.selected.forEach((object) => (object.isSelected = false));
3114
+ this.viewer.selected.forEach((object) => (object.userData.isSelected = false));
3109
3115
  this.viewer.selected.length = 0;
3110
3116
  }
3111
3117
  }
@@ -3233,19 +3239,22 @@ class ResetComponent {
3233
3239
  direction: this.viewer.camera.getWorldDirection(new Vector3()),
3234
3240
  };
3235
3241
  };
3242
+ this.resetCameraPosition = () => {
3243
+ if (this.savedCameraPosition) {
3244
+ this.viewer.camera.position.copy(this.savedCameraPosition.position);
3245
+ this.viewer.camera.up.copy(this.savedCameraPosition.up);
3246
+ this.viewer.camera.lookAt(this.savedCameraPosition.position.clone().add(this.savedCameraPosition.direction));
3247
+ this.viewer.camera.updateProjectionMatrix();
3248
+ }
3249
+ };
3236
3250
  this.viewer = viewer;
3237
3251
  this.viewer.addEventListener("databasechunk", this.onDatabaseChunk);
3252
+ this.viewer.on("resetview", this.resetCameraPosition);
3238
3253
  }
3239
3254
  dispose() {
3255
+ this.viewer.off("resetview", this.resetCameraPosition);
3240
3256
  this.viewer.removeEventListener("databasechunk", this.onDatabaseChunk);
3241
3257
  }
3242
- resetCameraPosition() {
3243
- if (this.savedCameraPosition) {
3244
- this.viewer.camera.position.copy(this.savedCameraPosition.position);
3245
- this.viewer.camera.up.copy(this.savedCameraPosition.up);
3246
- this.viewer.camera.lookAt(this.savedCameraPosition.position.clone().add(this.savedCameraPosition.direction));
3247
- }
3248
- }
3249
3258
  }
3250
3259
 
3251
3260
  const components = componentsRegistry("threejs");
@@ -3264,6 +3273,20 @@ components.registerComponent("ResetComponent", (viewer) => new ResetComponent(vi
3264
3273
  class ModelImpl {
3265
3274
  constructor(scene) {
3266
3275
  this.scene = scene;
3276
+ this.handleToObjects = new Map();
3277
+ this.originalObjects = new Set();
3278
+ this.scene.traverse((object) => {
3279
+ this.originalObjects.add(object);
3280
+ const handle = object.userData.handle;
3281
+ if (!handle)
3282
+ return;
3283
+ let objects = this.handleToObjects.get(handle);
3284
+ if (!objects) {
3285
+ objects = new Set();
3286
+ this.handleToObjects.set(handle, objects);
3287
+ }
3288
+ objects.add(object);
3289
+ });
3267
3290
  }
3268
3291
  dispose() {
3269
3292
  function disposeMaterial(material) {
@@ -3279,6 +3302,8 @@ class ModelImpl {
3279
3302
  if (object.material)
3280
3303
  disposeMaterials(object.material);
3281
3304
  }
3305
+ this.handleToObjects = undefined;
3306
+ this.originalObjects = undefined;
3282
3307
  this.scene.traverse(disposeObject);
3283
3308
  this.scene.clear();
3284
3309
  }
@@ -3421,67 +3446,56 @@ class ModelImpl {
3421
3446
  return info;
3422
3447
  }
3423
3448
  getExtents(target) {
3424
- this.scene.traverseVisible((object) => !object.children.length && target.expandByObject(object));
3449
+ this.scene.traverseVisible((object) => target.expandByObject(object));
3425
3450
  return target;
3426
3451
  }
3427
3452
  getObjects() {
3428
- const objects = [];
3429
- this.scene.traverse((object) => objects.push(object));
3430
- return objects;
3453
+ return Array.from(this.originalObjects);
3431
3454
  }
3432
3455
  getVisibleObjects() {
3433
3456
  const objects = [];
3434
3457
  this.scene.traverseVisible((object) => objects.push(object));
3435
- return objects;
3436
- }
3437
- hasObject(object) {
3438
- while (object) {
3439
- if (object === this.scene)
3440
- return true;
3441
- object = object.parent;
3442
- }
3443
- return false;
3444
- }
3445
- hasHandle(handle) {
3446
- return !handle.includes(":") || handle.split(":", 1)[0] === this.id + "";
3458
+ return objects.filter((object) => object.userData.handle);
3447
3459
  }
3448
- getOwnObjects(objects) {
3449
- if (!Array.isArray(objects))
3450
- objects = [objects];
3451
- return objects.filter((object) => this.hasObject(object));
3452
- }
3453
- getOwnHandles(handles) {
3460
+ getObjectsByHandles(handles) {
3454
3461
  if (!Array.isArray(handles))
3455
3462
  handles = [handles];
3456
- return handles.filter((handle) => this.hasHandle(handle));
3457
- }
3458
- getObjectsByHandles(handles) {
3459
- const ownHandles = this.getOwnHandles(handles);
3460
- if (ownHandles.length === 0)
3461
- return [];
3462
- const handleSet = new Set(ownHandles.map((handle) => handle.slice(handle.indexOf(":") + 1)));
3463
+ const ownHandles = [];
3464
+ handles.forEach((handle) => {
3465
+ const index = handle.indexOf(":");
3466
+ if (index !== -1) {
3467
+ if (handle.slice(0, index) !== this.id)
3468
+ return;
3469
+ handle = handle.slice(index + 1);
3470
+ }
3471
+ ownHandles.push(handle);
3472
+ });
3473
+ const handlesSet = new Set(ownHandles);
3463
3474
  const objects = [];
3464
- this.scene.traverse((object) => {
3465
- const handle = object.userData.handle;
3466
- if (handle && handleSet.has(handle))
3467
- objects.push(object);
3475
+ handlesSet.forEach((handle) => {
3476
+ objects.push(Array.from(this.handleToObjects.get(handle) || []));
3468
3477
  });
3469
- return objects;
3478
+ return objects.flat();
3470
3479
  }
3471
3480
  getHandlesByObjects(objects) {
3472
- const ownObjects = this.getOwnObjects(objects);
3473
- if (ownObjects.length === 0)
3474
- return [];
3481
+ if (!Array.isArray(objects))
3482
+ objects = [objects];
3475
3483
  const handleSet = new Set();
3476
- ownObjects.forEach((object) => {
3477
- const handle = object.userData.handle;
3478
- if (handle)
3479
- handleSet.add(`${this.id}:${handle}`);
3484
+ objects
3485
+ .filter((object) => this.originalObjects.has(object))
3486
+ .forEach((object) => {
3487
+ handleSet.add(`${this.id}:${object.userData.handle}`);
3480
3488
  });
3481
3489
  return Array.from(handleSet);
3482
3490
  }
3483
3491
  hideObjects(objects) {
3484
- this.getOwnObjects(objects).forEach((object) => (object.visible = false));
3492
+ if (!Array.isArray(objects))
3493
+ objects = [objects];
3494
+ objects
3495
+ .filter((object) => this.originalObjects.has(object))
3496
+ .forEach((object) => {
3497
+ object.visible = false;
3498
+ });
3485
3499
  return this;
3486
3500
  }
3487
3501
  hideAllObjects() {
@@ -3491,12 +3505,20 @@ class ModelImpl {
3491
3505
  if (!Array.isArray(objects))
3492
3506
  objects = [objects];
3493
3507
  const visibleSet = new Set(objects);
3494
- this.getOwnObjects(objects).forEach((object) => object.traverseAncestors((parent) => visibleSet.add(parent)));
3508
+ objects
3509
+ .filter((object) => this.originalObjects.has(object))
3510
+ .forEach((object) => {
3511
+ object.traverseAncestors((parent) => visibleSet.add(parent));
3512
+ });
3495
3513
  this.scene.traverse((object) => (object.visible = visibleSet.has(object)));
3496
3514
  return this;
3497
3515
  }
3498
3516
  showObjects(objects) {
3499
- this.getOwnObjects(objects).forEach((object) => {
3517
+ if (!Array.isArray(objects))
3518
+ objects = [objects];
3519
+ objects
3520
+ .filter((object) => this.originalObjects.has(object))
3521
+ .forEach((object) => {
3500
3522
  object.visible = true;
3501
3523
  object.traverseAncestors((parent) => (parent.visible = true));
3502
3524
  });
@@ -3506,36 +3528,55 @@ class ModelImpl {
3506
3528
  this.scene.traverse((object) => (object.visible = true));
3507
3529
  return this;
3508
3530
  }
3509
- showOriginalObjects(objects) {
3531
+ highlightObjects(objects) {
3510
3532
  return this;
3511
3533
  }
3512
- hideOriginalObjects(objects) {
3534
+ unhighlightObjects(objects) {
3513
3535
  return this;
3514
3536
  }
3515
3537
  explode(scale = 0, coeff = 4) {
3538
+ const centers = new Map();
3539
+ const getObjectCenter = (object, target) => {
3540
+ const extents = new Box3().setFromObject(object);
3541
+ const handle = object.userData.handle;
3542
+ if (!handle)
3543
+ return extents.getCenter(target);
3544
+ const center = centers.get(handle);
3545
+ if (center)
3546
+ return target.copy(center);
3547
+ const objects = this.getObjectsByHandles(handle);
3548
+ objects.forEach((x) => extents.expandByObject(x));
3549
+ extents.getCenter(target);
3550
+ centers.set(handle, target.clone());
3551
+ return target;
3552
+ };
3516
3553
  function calcExplodeDepth(object, depth) {
3517
- let res = depth;
3554
+ let result = depth;
3518
3555
  object.children.forEach((x) => {
3519
3556
  const objectDepth = calcExplodeDepth(x, depth + 1);
3520
- if (res < objectDepth)
3521
- res = objectDepth;
3557
+ if (result < objectDepth)
3558
+ result = objectDepth;
3522
3559
  });
3523
3560
  object.userData.originalPosition = object.position.clone();
3524
- object.userData.originalCenter = new Box3().setFromObject(object).getCenter(new Vector3());
3525
- object.userData.isExplodeLocked = depth > 2 && object.children.length === 0;
3526
- return res;
3527
- }
3528
- scale /= 100;
3529
- if (!this.scene.userData.explodeDepth)
3530
- this.scene.userData.explodeDepth = calcExplodeDepth(this.scene, 1);
3531
- const maxDepth = this.scene.userData.explodeDepth;
3532
- const scaledExplodeDepth = scale * maxDepth + 1;
3561
+ object.userData.originalCenter = getObjectCenter(object, new Vector3());
3562
+ return result;
3563
+ }
3564
+ const explodeScale = scale / 100;
3565
+ const explodeRoot = this.scene;
3566
+ if (!explodeRoot.userData.explodeDepth)
3567
+ explodeRoot.userData.explodeDepth = calcExplodeDepth(explodeRoot, 1);
3568
+ const maxDepth = explodeRoot.userData.explodeDepth;
3569
+ const scaledExplodeDepth = explodeScale * maxDepth + 1;
3533
3570
  const explodeDepth = 0 | scaledExplodeDepth;
3534
3571
  const currentSegmentFraction = scaledExplodeDepth - explodeDepth;
3535
3572
  function explodeObject(object, depth) {
3573
+ if (object.isCamera)
3574
+ return;
3575
+ if (object.userData.isHighlightWireframe)
3576
+ return;
3536
3577
  object.position.copy(object.userData.originalPosition);
3537
- if (depth > 0 && depth <= explodeDepth && !object.userData.isExplodeLocked) {
3538
- let objectScale = scale * coeff;
3578
+ if (depth > 0 && depth <= explodeDepth) {
3579
+ let objectScale = explodeScale * coeff;
3539
3580
  if (depth === explodeDepth)
3540
3581
  objectScale *= currentSegmentFraction;
3541
3582
  const parentCenter = object.parent.userData.originalCenter;
@@ -3545,7 +3586,7 @@ class ModelImpl {
3545
3586
  }
3546
3587
  object.children.forEach((x) => explodeObject(x, depth + 1));
3547
3588
  }
3548
- explodeObject(this.scene, 0);
3589
+ explodeObject(explodeRoot, 0);
3549
3590
  this.scene.updateMatrixWorld();
3550
3591
  return this;
3551
3592
  }
@@ -3583,29 +3624,24 @@ class DynamicModelImpl extends ModelImpl {
3583
3624
  getVisibleObjects() {
3584
3625
  return this.gltfLoader.getOriginalObjectForSelect();
3585
3626
  }
3586
- hasObject(object) {
3587
- return this.gltfLoader.originalObjects.has(object);
3588
- }
3589
3627
  getObjectsByHandles(handles) {
3590
- const ownHandles = this.getOwnHandles(handles);
3591
- if (ownHandles.length === 0)
3592
- return [];
3593
- const handlesSet = new Set(ownHandles);
3628
+ if (!Array.isArray(handles))
3629
+ handles = [handles];
3630
+ const handlesSet = new Set(handles);
3594
3631
  const objects = [];
3595
3632
  handlesSet.forEach((handle) => {
3596
- objects.push(...this.gltfLoader.getObjectsByHandle(handle));
3633
+ objects.push(this.gltfLoader.getObjectsByHandle(handle));
3597
3634
  });
3598
- return objects;
3635
+ return objects.flat();
3599
3636
  }
3600
3637
  getHandlesByObjects(objects) {
3601
- const ownObjects = this.getOwnObjects(objects);
3602
- if (ownObjects.length === 0)
3603
- return [];
3638
+ if (!Array.isArray(objects))
3639
+ objects = [objects];
3604
3640
  const handleSet = new Set();
3605
- ownObjects.forEach((object) => {
3606
- const handle = object.userData.handle;
3607
- if (handle)
3608
- handleSet.add(handle);
3641
+ objects
3642
+ .filter((object) => this.gltfLoader.originalObjects.has(object))
3643
+ .forEach((object) => {
3644
+ handleSet.add(object.userData.handle);
3609
3645
  });
3610
3646
  return Array.from(handleSet);
3611
3647
  }
@@ -3628,14 +3664,78 @@ class DynamicModelImpl extends ModelImpl {
3628
3664
  this.gltfLoader.showAllHiddenObjects();
3629
3665
  return this;
3630
3666
  }
3631
- showOriginalObjects(objects) {
3667
+ highlightObjects(objects) {
3632
3668
  this.gltfLoader.showOriginalObjects(objects);
3633
3669
  return this;
3634
3670
  }
3635
- hideOriginalObjects(objects) {
3671
+ unhighlightObjects(objects) {
3636
3672
  this.gltfLoader.hideOriginalObjects(objects);
3637
3673
  return this;
3638
3674
  }
3675
+ explode(scale = 0, coeff = 4) {
3676
+ const centers = new Map();
3677
+ const calcObjectCenter = (object, target) => {
3678
+ const extents = new Box3().setFromObject(object);
3679
+ const handle = object.userData.handle;
3680
+ if (!handle)
3681
+ return extents.getCenter(target);
3682
+ const center = centers.get(handle);
3683
+ if (center)
3684
+ return target.copy(center);
3685
+ const objects = this.getObjectsByHandles(handle);
3686
+ objects.forEach((x) => extents.expandByObject(x));
3687
+ extents.getCenter(target);
3688
+ centers.set(handle, target.clone());
3689
+ return target;
3690
+ };
3691
+ function calcExplodeDepth(object, depth) {
3692
+ let result = depth;
3693
+ object.children
3694
+ .filter((x) => !x.userData.isOptimized)
3695
+ .forEach((x) => {
3696
+ const objectDepth = calcExplodeDepth(x, depth + 1);
3697
+ if (result < objectDepth)
3698
+ result = objectDepth;
3699
+ });
3700
+ object.userData.originalPosition = object.position.clone();
3701
+ object.userData.originalCenter = calcObjectCenter(object, new Vector3());
3702
+ return result;
3703
+ }
3704
+ const explodeScale = scale / 100;
3705
+ const explodeRoot = this.scene.children[0];
3706
+ if (!explodeRoot.userData.explodeDepth)
3707
+ explodeRoot.userData.explodeDepth = calcExplodeDepth(explodeRoot, 1);
3708
+ const maxDepth = explodeRoot.userData.explodeDepth;
3709
+ const scaledExplodeDepth = explodeScale * maxDepth + 1;
3710
+ const explodeDepth = 0 | scaledExplodeDepth;
3711
+ const currentSegmentFraction = scaledExplodeDepth - explodeDepth;
3712
+ const transformMap = new Map();
3713
+ function explodeObject(object, depth) {
3714
+ if (object.isCamera)
3715
+ return;
3716
+ if (object.userData.isHighlightWireframe)
3717
+ return;
3718
+ object.position.copy(object.userData.originalPosition);
3719
+ if (depth > 0 && depth <= explodeDepth && !object.userData.isExplodeLocked) {
3720
+ let objectScale = explodeScale * coeff;
3721
+ if (depth === explodeDepth)
3722
+ objectScale *= currentSegmentFraction;
3723
+ const parentCenter = object.parent.userData.originalCenter;
3724
+ const objectCenter = object.userData.originalCenter;
3725
+ const objectOffset = objectCenter.clone().sub(parentCenter).multiplyScalar(objectScale);
3726
+ object.position.add(objectOffset);
3727
+ const matrix = new Matrix4().makeTranslation(objectOffset.x, objectOffset.y, objectOffset.z);
3728
+ transformMap.set(object, matrix);
3729
+ }
3730
+ object.children
3731
+ .filter((x) => !x.userData.isOptimized)
3732
+ .forEach((x) => explodeObject(x, depth + 1));
3733
+ }
3734
+ explodeObject(explodeRoot, 0);
3735
+ this.scene.updateMatrixWorld();
3736
+ this.gltfLoader.applyObjectTransforms(transformMap);
3737
+ return this;
3738
+ }
3639
3739
  }
3640
3740
 
3641
3741
  const GL_COMPONENT_TYPES = {
@@ -3684,7 +3784,7 @@ class GltfStructure {
3684
3784
  this.textureCache = new Map();
3685
3785
  this.materialCache = new Map();
3686
3786
  this.uri = "";
3687
- this._nextObjectId = 0;
3787
+ this._nextObjectId = 1;
3688
3788
  this.loadingAborted = false;
3689
3789
  this.criticalError = null;
3690
3790
  }
@@ -4203,6 +4303,8 @@ class DynamicGltfLoader {
4203
4303
  this.hiddenHandles = new Set();
4204
4304
  this.newOptimizedObjects = new Set();
4205
4305
  this.oldOptimizeObjects = new Set();
4306
+ this.objectTransforms = new Map();
4307
+ this.transformedGeometries = new Map();
4206
4308
  this.activeChunkLoads = 0;
4207
4309
  this.chunkQueue = [];
4208
4310
  this.objectIdToIndex = new Map();
@@ -4964,14 +5066,32 @@ class DynamicGltfLoader {
4964
5066
  if (!node.geometryExtents) continue;
4965
5067
  if (node.object && this.hiddenHandles.has(node.object.userData.handle)) continue;
4966
5068
  const transformedBox = node.geometryExtents.clone();
4967
- if (node.group && node.group.matrix) {
4968
- transformedBox.applyMatrix4(node.group.matrix);
4969
- if (node.group.parent && node.group.parent.matrix) {
4970
- transformedBox.applyMatrix4(node.group.parent.matrix);
5069
+ const structureRoot = node.structure ? this.structureRoots.get(node.structure.id) : null;
5070
+ if (node.group) {
5071
+ const matrices = [];
5072
+ let currentGroup = node.group;
5073
+ while (currentGroup && currentGroup !== structureRoot) {
5074
+ if (currentGroup.matrix && currentGroup.matrixAutoUpdate === false) {
5075
+ matrices.unshift(currentGroup.matrix);
5076
+ }
5077
+ currentGroup = currentGroup.parent;
5078
+ }
5079
+ for (const matrix of matrices) {
5080
+ transformedBox.applyMatrix4(matrix);
4971
5081
  }
4972
5082
  }
5083
+ if (structureRoot && structureRoot.matrix) {
5084
+ transformedBox.applyMatrix4(structureRoot.matrix);
5085
+ }
5086
+ const transform = this.objectTransforms.get(node.object);
5087
+ if (transform) {
5088
+ transformedBox.applyMatrix4(transform);
5089
+ }
4973
5090
  totalExtent.union(transformedBox);
4974
5091
  }
5092
+ if (this.scene && this.scene.matrix && !totalExtent.isEmpty()) {
5093
+ totalExtent.applyMatrix4(this.scene.matrix);
5094
+ }
4975
5095
  return totalExtent;
4976
5096
  }
4977
5097
  loadCamera(structure, cameraIndex, nodeDef) {
@@ -5047,7 +5167,6 @@ class DynamicGltfLoader {
5047
5167
  for (let i = 0; i < this.maxObjectId; i++) {
5048
5168
  this.objectVisibility[i] = 1.0;
5049
5169
  }
5050
- console.log(`Initialized object visibility array: ${this.maxObjectId} objects`);
5051
5170
  }
5052
5171
  }
5053
5172
  createVisibilityMaterial(material) {
@@ -5184,6 +5303,8 @@ class DynamicGltfLoader {
5184
5303
  this.newOptimizedObjects.clear();
5185
5304
  this.oldOptimizeObjects.clear();
5186
5305
  this.isolatedObjects = [];
5306
+ this.objectTransforms.clear();
5307
+ this.transformedGeometries.clear();
5187
5308
  this.totalLoadedObjects = 0;
5188
5309
  this.lastUpdateTime = 0;
5189
5310
  this.currentMemoryUsage = 0;
@@ -5881,6 +6002,171 @@ class DynamicGltfLoader {
5881
6002
  mergedObject.geometry.attributes.visibility.needsUpdate = true;
5882
6003
  }
5883
6004
  }
6005
+ applyObjectTransforms(objectTransformMap) {
6006
+ if (this.mergedObjectMap.size === 0) {
6007
+ console.warn("No merged objects to transform");
6008
+ return;
6009
+ }
6010
+ this.objectTransforms = new Map(objectTransformMap);
6011
+ for (const mesh of this.mergedMesh) {
6012
+ this._applyTransformToMergedObject(mesh);
6013
+ }
6014
+ for (const line of this.mergedLines) {
6015
+ this._applyTransformToMergedObject(line);
6016
+ }
6017
+ for (const lineSegment of this.mergedLineSegments) {
6018
+ this._applyTransformToMergedObject(lineSegment);
6019
+ }
6020
+ for (const point of this.mergedPoints) {
6021
+ this._applyTransformToMergedObject(point);
6022
+ }
6023
+ }
6024
+ createExplodeTransforms(objects = null, explodeCenter = null, explodeFactor = 1.5) {
6025
+ const transformMap = new Map();
6026
+ if (!explodeCenter) {
6027
+ explodeCenter = new Vector3();
6028
+ const extent = this.getTotalGeometryExtent();
6029
+ if (!extent.isEmpty()) {
6030
+ extent.getCenter(explodeCenter);
6031
+ }
6032
+ }
6033
+ const objectsArray = objects
6034
+ ? Array.isArray(objects)
6035
+ ? objects
6036
+ : Array.from(objects)
6037
+ : Array.from(this.originalObjects);
6038
+ for (const obj of objectsArray) {
6039
+ if (!obj.geometry || !obj.geometry.attributes.position) continue;
6040
+ const boundingBox = new Box3().setFromBufferAttribute(obj.geometry.attributes.position);
6041
+ if (obj.matrixWorld) {
6042
+ boundingBox.applyMatrix4(obj.matrixWorld);
6043
+ }
6044
+ if (boundingBox.isEmpty()) continue;
6045
+ const objectCenter = new Vector3();
6046
+ boundingBox.getCenter(objectCenter);
6047
+ const direction = objectCenter.clone().sub(explodeCenter);
6048
+ const distance = direction.length();
6049
+ if (distance > 0) {
6050
+ direction.normalize();
6051
+ const offset = direction.multiplyScalar(distance * (explodeFactor - 1.0));
6052
+ const matrix = new Matrix4().makeTranslation(offset.x, offset.y, offset.z);
6053
+ transformMap.set(obj, matrix);
6054
+ }
6055
+ }
6056
+ return transformMap;
6057
+ }
6058
+ clearTransforms() {
6059
+ this.objectTransforms.clear();
6060
+ for (const mesh of this.mergedMesh) {
6061
+ this._restoreOriginalGeometry(mesh);
6062
+ }
6063
+ for (const line of this.mergedLines) {
6064
+ this._restoreOriginalGeometry(line);
6065
+ }
6066
+ for (const lineSegment of this.mergedLineSegments) {
6067
+ this._restoreOriginalGeometry(lineSegment);
6068
+ }
6069
+ for (const point of this.mergedPoints) {
6070
+ this._restoreOriginalGeometry(point);
6071
+ }
6072
+ }
6073
+ clearHandleTransforms() {
6074
+ this.clearTransforms();
6075
+ }
6076
+ _applyTransformToMergedObject(mergedObject) {
6077
+ const objectData = this.mergedObjectMap.get(mergedObject.uuid);
6078
+ if (!objectData || !objectData.objectMapping) return;
6079
+ const geometry = mergedObject.geometry;
6080
+ if (!geometry || !geometry.attributes.position) return;
6081
+ const positionAttr = geometry.attributes.position;
6082
+ const positions = positionAttr.array;
6083
+ if (!this.transformedGeometries.has(mergedObject.uuid)) {
6084
+ this.transformedGeometries.set(mergedObject.uuid, new Float32Array(positions));
6085
+ }
6086
+ const originalPositions = this.transformedGeometries.get(mergedObject.uuid);
6087
+ const tempVector = new Vector3();
6088
+ for (const [originalMesh, mappingData] of objectData.objectMapping) {
6089
+ const transform = this.objectTransforms.get(originalMesh);
6090
+ if (!transform) {
6091
+ const startIdx = mappingData.startVertexIndex * 3;
6092
+ const endIdx = (mappingData.startVertexIndex + mappingData.vertexCount) * 3;
6093
+ for (let i = startIdx; i < endIdx; i++) {
6094
+ positions[i] = originalPositions[i];
6095
+ }
6096
+ continue;
6097
+ }
6098
+ const startVertex = mappingData.startVertexIndex;
6099
+ const vertexCount = mappingData.vertexCount;
6100
+ for (let i = 0; i < vertexCount; i++) {
6101
+ const idx = (startVertex + i) * 3;
6102
+ tempVector.set(originalPositions[idx], originalPositions[idx + 1], originalPositions[idx + 2]);
6103
+ tempVector.applyMatrix4(transform);
6104
+ positions[idx] = tempVector.x;
6105
+ positions[idx + 1] = tempVector.y;
6106
+ positions[idx + 2] = tempVector.z;
6107
+ }
6108
+ }
6109
+ if (geometry.attributes.normal) {
6110
+ this._updateNormalsForTransform(geometry, objectData, originalPositions);
6111
+ }
6112
+ positionAttr.needsUpdate = true;
6113
+ geometry.computeBoundingSphere();
6114
+ geometry.computeBoundingBox();
6115
+ }
6116
+ _updateNormalsForTransform(geometry, objectData, originalPositions) {
6117
+ const normalAttr = geometry.attributes.normal;
6118
+ if (!normalAttr) return;
6119
+ const normals = normalAttr.array;
6120
+ const tempVector = new Vector3();
6121
+ const normalMatrix = new Matrix4();
6122
+ const normalsKey = `${geometry.uuid}_normals`;
6123
+ if (!this.transformedGeometries.has(normalsKey)) {
6124
+ this.transformedGeometries.set(normalsKey, new Float32Array(normals));
6125
+ }
6126
+ const originalNormals = this.transformedGeometries.get(normalsKey);
6127
+ for (const [originalMesh, mappingData] of objectData.objectMapping) {
6128
+ const transform = this.objectTransforms.get(originalMesh);
6129
+ if (!transform) {
6130
+ const startIdx = mappingData.startVertexIndex * 3;
6131
+ const endIdx = (mappingData.startVertexIndex + mappingData.vertexCount) * 3;
6132
+ for (let i = startIdx; i < endIdx; i++) {
6133
+ normals[i] = originalNormals[i];
6134
+ }
6135
+ continue;
6136
+ }
6137
+ normalMatrix.copy(transform).invert().transpose();
6138
+ const startVertex = mappingData.startVertexIndex;
6139
+ const vertexCount = mappingData.vertexCount;
6140
+ for (let i = 0; i < vertexCount; i++) {
6141
+ const idx = (startVertex + i) * 3;
6142
+ tempVector.set(originalNormals[idx], originalNormals[idx + 1], originalNormals[idx + 2]);
6143
+ tempVector.applyMatrix4(normalMatrix).normalize();
6144
+ normals[idx] = tempVector.x;
6145
+ normals[idx + 1] = tempVector.y;
6146
+ normals[idx + 2] = tempVector.z;
6147
+ }
6148
+ }
6149
+ normalAttr.needsUpdate = true;
6150
+ }
6151
+ _restoreOriginalGeometry(mergedObject) {
6152
+ const geometry = mergedObject.geometry;
6153
+ if (!geometry || !geometry.attributes.position) return;
6154
+ const originalPositions = this.transformedGeometries.get(mergedObject.uuid);
6155
+ if (originalPositions) {
6156
+ const positions = geometry.attributes.position.array;
6157
+ positions.set(originalPositions);
6158
+ geometry.attributes.position.needsUpdate = true;
6159
+ }
6160
+ const normalsKey = `${geometry.uuid}_normals`;
6161
+ const originalNormals = this.transformedGeometries.get(normalsKey);
6162
+ if (originalNormals && geometry.attributes.normal) {
6163
+ const normals = geometry.attributes.normal.array;
6164
+ normals.set(originalNormals);
6165
+ geometry.attributes.normal.needsUpdate = true;
6166
+ }
6167
+ geometry.computeBoundingSphere();
6168
+ geometry.computeBoundingBox();
6169
+ }
5884
6170
  syncHiddenObjects() {
5885
6171
  if (this.mergedObjectMap.size === 0) {
5886
6172
  console.log("No merged objects to sync");
@@ -5912,19 +6198,33 @@ class DynamicGltfLoader {
5912
6198
  }
5913
6199
  getStructureGeometryExtent(structureId) {
5914
6200
  const extent = new Box3();
6201
+ const structureRoot = this.structureRoots.get(structureId);
5915
6202
  for (const [nodeId, node] of this.nodes.entries()) {
5916
6203
  if (!node.geometryExtents) continue;
5917
6204
  if (!nodeId.startsWith(structureId + "_")) continue;
5918
6205
  if (node.object && this.hiddenHandles && this.hiddenHandles.has(node.object.userData.handle)) continue;
5919
6206
  const transformedBox = node.geometryExtents.clone();
5920
- if (node.group && node.group.matrix) {
5921
- transformedBox.applyMatrix4(node.group.matrix);
5922
- if (node.group.parent && node.group.parent.matrix) {
5923
- transformedBox.applyMatrix4(node.group.parent.matrix);
6207
+ if (node.group) {
6208
+ const matrices = [];
6209
+ let currentGroup = node.group;
6210
+ while (currentGroup && currentGroup !== structureRoot) {
6211
+ if (currentGroup.matrix && currentGroup.matrixAutoUpdate === false) {
6212
+ matrices.unshift(currentGroup.matrix);
6213
+ }
6214
+ currentGroup = currentGroup.parent;
5924
6215
  }
6216
+ for (const matrix of matrices) {
6217
+ transformedBox.applyMatrix4(matrix);
6218
+ }
6219
+ }
6220
+ if (structureRoot && structureRoot.matrix) {
6221
+ transformedBox.applyMatrix4(structureRoot.matrix);
5925
6222
  }
5926
6223
  extent.union(transformedBox);
5927
6224
  }
6225
+ if (this.scene && this.scene.matrix && !extent.isEmpty()) {
6226
+ extent.applyMatrix4(this.scene.matrix);
6227
+ }
5928
6228
  return extent;
5929
6229
  }
5930
6230
  setMaxConcurrentChunks(maxChunks) {
@@ -6497,8 +6797,8 @@ class Viewer extends EventEmitter2 {
6497
6797
  const rect = canvas.parentElement.getBoundingClientRect();
6498
6798
  const width = rect.width || 1;
6499
6799
  const height = rect.height || 1;
6500
- const aspect = width / height;
6501
- this.camera = new PerspectiveCamera(45, aspect, 0.001, 1000);
6800
+ const aspectRatio = width / height;
6801
+ this.camera = new PerspectiveCamera(45, aspectRatio, 0.001, 1000);
6502
6802
  this.camera.up.set(0, 1, 0);
6503
6803
  this.camera.position.set(0, 0, 1);
6504
6804
  this.camera.lookAt(this.target);
@@ -6595,14 +6895,14 @@ class Viewer extends EventEmitter2 {
6595
6895
  if (!this.renderer)
6596
6896
  return;
6597
6897
  const camera = this.camera;
6598
- const aspect = width / height;
6898
+ const aspectRatio = width / height;
6599
6899
  if (camera.isPerspectiveCamera) {
6600
- camera.aspect = aspect;
6900
+ camera.aspect = aspectRatio;
6601
6901
  camera.updateProjectionMatrix();
6602
6902
  }
6603
6903
  if (camera.isOrthographicCamera) {
6604
- camera.left = camera.bottom * aspect;
6605
- camera.right = camera.top * aspect;
6904
+ camera.left = camera.bottom * aspectRatio;
6905
+ camera.right = camera.top * aspectRatio;
6606
6906
  camera.updateProjectionMatrix();
6607
6907
  }
6608
6908
  this.renderer.setSize(width, height, updateStyle);
@@ -6829,12 +7129,12 @@ class Viewer extends EventEmitter2 {
6829
7129
  if (orthogonal_camera) {
6830
7130
  const extentsSize = this.extents.getBoundingSphere(new Sphere()).radius * 2;
6831
7131
  const rendererSize = this.renderer.getSize(new Vector2());
6832
- const aspect = rendererSize.x / rendererSize.y;
7132
+ const aspectRatio = rendererSize.x / rendererSize.y;
6833
7133
  const camera = new OrthographicCamera();
6834
7134
  camera.top = orthogonal_camera.field_height / 2;
6835
7135
  camera.bottom = -orthogonal_camera.field_height / 2;
6836
- camera.left = camera.bottom * aspect;
6837
- camera.right = camera.top * aspect;
7136
+ camera.left = camera.bottom * aspectRatio;
7137
+ camera.right = camera.top * aspectRatio;
6838
7138
  camera.near = 0;
6839
7139
  camera.far = extentsSize * 1000;
6840
7140
  camera.zoom = orthogonal_camera.view_to_world_scale;
@@ -6847,6 +7147,7 @@ class Viewer extends EventEmitter2 {
6847
7147
  this.renderPass.camera = camera;
6848
7148
  this.helpersPass.camera = camera;
6849
7149
  this.ssaaRenderPass.camera = camera;
7150
+ this.options.cameraMode = "orthographic";
6850
7151
  this.emitEvent({ type: "changecameramode", mode: "orthographic" });
6851
7152
  }
6852
7153
  };
@@ -6854,10 +7155,10 @@ class Viewer extends EventEmitter2 {
6854
7155
  if (perspective_camera) {
6855
7156
  const extentsSize = this.extents.getBoundingSphere(new Sphere()).radius * 2;
6856
7157
  const rendererSize = this.renderer.getSize(new Vector2());
6857
- const aspect = rendererSize.x / rendererSize.y;
7158
+ const aspectRatio = rendererSize.x / rendererSize.y;
6858
7159
  const camera = new PerspectiveCamera();
6859
7160
  camera.fov = perspective_camera.field_of_view;
6860
- camera.aspect = aspect;
7161
+ camera.aspect = aspectRatio;
6861
7162
  camera.near = extentsSize / 1000;
6862
7163
  camera.far = extentsSize * 1000;
6863
7164
  camera.updateProjectionMatrix();
@@ -6869,6 +7170,7 @@ class Viewer extends EventEmitter2 {
6869
7170
  this.renderPass.camera = camera;
6870
7171
  this.helpersPass.camera = camera;
6871
7172
  this.ssaaRenderPass.camera = camera;
7173
+ this.options.cameraMode = "perspective";
6872
7174
  this.emitEvent({ type: "changecameramode", mode: "perspective" });
6873
7175
  }
6874
7176
  };
@@ -6898,6 +7200,7 @@ class Viewer extends EventEmitter2 {
6898
7200
  setSelection(((_b = viewpoint.custom_fields) === null || _b === void 0 ? void 0 : _b.selection2) || viewpoint.selection);
6899
7201
  this._markup.setViewpoint(viewpoint);
6900
7202
  this.target.copy(getVector3FromPoint3d((_d = (_c = viewpoint.custom_fields) === null || _c === void 0 ? void 0 : _c.camera_target) !== null && _d !== void 0 ? _d : this.target));
7203
+ this.syncOverlay();
6901
7204
  this.setActiveDragger(draggerName);
6902
7205
  this.emitEvent({ type: "drawviewpoint", data: viewpoint });
6903
7206
  this.update();