@inweb/viewer-three 26.10.6 → 26.11.1

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 (84) hide show
  1. package/dist/plugins/components/AxesHelperComponent.js +23 -1
  2. package/dist/plugins/components/AxesHelperComponent.js.map +1 -1
  3. package/dist/plugins/components/AxesHelperComponent.min.js +1 -1
  4. package/dist/plugins/components/AxesHelperComponent.module.js +24 -2
  5. package/dist/plugins/components/AxesHelperComponent.module.js.map +1 -1
  6. package/dist/plugins/components/ExtentsHelperComponent.js +18 -0
  7. package/dist/plugins/components/ExtentsHelperComponent.js.map +1 -1
  8. package/dist/plugins/components/ExtentsHelperComponent.min.js +1 -1
  9. package/dist/plugins/components/ExtentsHelperComponent.module.js +19 -1
  10. package/dist/plugins/components/ExtentsHelperComponent.module.js.map +1 -1
  11. package/dist/plugins/loaders/GLTFCloudLoader.js +2 -3
  12. package/dist/plugins/loaders/GLTFCloudLoader.js.map +1 -1
  13. package/dist/plugins/loaders/GLTFCloudLoader.min.js +1 -1
  14. package/dist/plugins/loaders/GLTFCloudLoader.module.js +2 -3
  15. package/dist/plugins/loaders/GLTFCloudLoader.module.js.map +1 -1
  16. package/dist/plugins/loaders/GLTFFileLoader.js +2499 -0
  17. package/dist/plugins/loaders/GLTFFileLoader.js.map +1 -0
  18. package/dist/plugins/loaders/GLTFFileLoader.min.js +24 -0
  19. package/dist/plugins/loaders/GLTFFileLoader.module.js +74 -0
  20. package/dist/plugins/loaders/GLTFFileLoader.module.js.map +1 -0
  21. package/dist/plugins/loaders/IFCXLoader.js +5 -7
  22. package/dist/plugins/loaders/IFCXLoader.js.map +1 -1
  23. package/dist/plugins/loaders/IFCXLoader.min.js +1 -1
  24. package/dist/plugins/loaders/IFCXLoader.module.js +5 -7
  25. package/dist/plugins/loaders/IFCXLoader.module.js.map +1 -1
  26. package/dist/plugins/loaders/PotreeLoader.js +1 -2
  27. package/dist/plugins/loaders/PotreeLoader.js.map +1 -1
  28. package/dist/plugins/loaders/PotreeLoader.min.js +1 -1
  29. package/dist/plugins/loaders/PotreeLoader.module.js +1 -2
  30. package/dist/plugins/loaders/PotreeLoader.module.js.map +1 -1
  31. package/dist/viewer-three.js +770 -2777
  32. package/dist/viewer-three.js.map +1 -1
  33. package/dist/viewer-three.min.js +3 -3
  34. package/dist/viewer-three.module.js +609 -206
  35. package/dist/viewer-three.module.js.map +1 -1
  36. package/lib/Viewer/Viewer.d.ts +27 -20
  37. package/lib/Viewer/commands/GetSelected2.d.ts +2 -0
  38. package/lib/Viewer/commands/SelectModel.d.ts +1 -1
  39. package/lib/Viewer/commands/SetSelected2.d.ts +2 -0
  40. package/lib/Viewer/components/index.d.ts +6 -6
  41. package/lib/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.d.ts +0 -1
  42. package/lib/Viewer/loaders/GLTFBinaryExtension.d.ts +5 -0
  43. package/lib/Viewer/loaders/GLTFCloudDynamicLoader.d.ts +2 -2
  44. package/lib/Viewer/loaders/{GLTFFileLoader.d.ts → GLTFFileDynamicLoader.d.ts} +7 -1
  45. package/lib/Viewer/loaders/GLTFLoadingManager.d.ts +4 -3
  46. package/lib/Viewer/loaders/RangesLoader.d.ts +15 -0
  47. package/lib/Viewer/loaders/index.d.ts +22 -14
  48. package/lib/Viewer/models/IModelImpl.d.ts +6 -8
  49. package/lib/Viewer/models/ModelImpl.d.ts +3 -5
  50. package/package.json +5 -5
  51. package/plugins/components/AxesHelperComponent.ts +31 -2
  52. package/plugins/components/ExtentsHelperComponent.ts +25 -0
  53. package/plugins/loaders/GLTFCloudLoader.ts +2 -3
  54. package/{src/Viewer → plugins}/loaders/GLTFFileLoader.ts +21 -12
  55. package/plugins/loaders/IFCX/IFCXCloudLoader.ts +5 -5
  56. package/plugins/loaders/IFCX/IFCXFileLoader.ts +3 -4
  57. package/plugins/loaders/Potree/PotreeFileLoader.ts +3 -4
  58. package/src/Viewer/Viewer.ts +120 -88
  59. package/src/Viewer/commands/ClearSelected.ts +3 -1
  60. package/src/Viewer/commands/GetModels.ts +1 -1
  61. package/src/Viewer/commands/GetSelected.ts +2 -2
  62. package/src/Viewer/commands/GetSelected2.ts +34 -0
  63. package/src/Viewer/commands/HideSelected.ts +3 -1
  64. package/src/Viewer/commands/SelectModel.ts +5 -5
  65. package/src/Viewer/commands/SetSelected.ts +9 -10
  66. package/src/Viewer/commands/SetSelected2.ts +42 -0
  67. package/src/Viewer/commands/ZoomToObjects.ts +5 -6
  68. package/src/Viewer/commands/ZoomToSelected.ts +3 -1
  69. package/src/Viewer/commands/index.ts +4 -0
  70. package/src/Viewer/components/CameraComponent.ts +6 -1
  71. package/src/Viewer/components/ExtentsComponent.ts +4 -1
  72. package/src/Viewer/components/SelectionComponent.ts +2 -0
  73. package/src/Viewer/components/index.ts +6 -6
  74. package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +253 -22
  75. package/src/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.ts +20 -10
  76. package/src/Viewer/loaders/DynamicGltfLoader/GltfStructure.js +3 -1
  77. package/src/Viewer/loaders/GLTFBinaryExtension.ts +91 -0
  78. package/src/Viewer/loaders/GLTFCloudDynamicLoader.ts +13 -19
  79. package/src/Viewer/loaders/GLTFFileDynamicLoader.ts +145 -0
  80. package/src/Viewer/loaders/GLTFLoadingManager.ts +5 -4
  81. package/src/Viewer/loaders/RangesLoader.ts +95 -0
  82. package/src/Viewer/loaders/index.ts +24 -16
  83. package/src/Viewer/models/IModelImpl.ts +8 -9
  84. package/src/Viewer/models/ModelImpl.ts +30 -17
@@ -23,12 +23,11 @@
23
23
 
24
24
  import { draggersRegistry, commandsRegistry, Options, componentsRegistry, Loader, loadersRegistry, CANVAS_EVENTS } from '@inweb/viewer-core';
25
25
  export * from '@inweb/viewer-core';
26
- import { Line, Vector3, BufferGeometry, Float32BufferAttribute, LineBasicMaterial, Mesh, MeshBasicMaterial, DoubleSide, EventDispatcher, MOUSE, TOUCH, Spherical, Quaternion, Vector2, Plane, Object3D, Line3, Matrix4, Vector4, MathUtils, Raycaster, EdgesGeometry, Controls, Clock, Sphere, Box3, Color, PerspectiveCamera, OrthographicCamera, AmbientLight, DirectionalLight, HemisphereLight, MeshPhongMaterial, WebGLRenderTarget, UnsignedByteType, RGBAFormat, CylinderGeometry, Sprite, CanvasTexture, SRGBColorSpace, SpriteMaterial, LoadingManager, LoaderUtils, TextureLoader, BufferAttribute, PointsMaterial, Points, TriangleStripDrawMode, TriangleFanDrawMode, LineSegments, LineLoop, Group, NormalBlending, UniformsUtils, ShaderMaterial, AdditiveBlending, HalfFloatType, Scene, WebGLRenderer, LinearSRGBColorSpace } from 'three';
26
+ import { Line, Vector3, BufferGeometry, Float32BufferAttribute, LineBasicMaterial, Mesh, MeshBasicMaterial, DoubleSide, EventDispatcher, MOUSE, TOUCH, Spherical, Quaternion, Vector2, Plane, Object3D, Line3, Matrix4, Vector4, MathUtils, Raycaster, EdgesGeometry, Controls, Clock, Sphere, Box3, Color, PerspectiveCamera, OrthographicCamera, AmbientLight, DirectionalLight, HemisphereLight, MeshPhongMaterial, WebGLRenderTarget, UnsignedByteType, RGBAFormat, CylinderGeometry, Sprite, CanvasTexture, SRGBColorSpace, SpriteMaterial, TextureLoader, BufferAttribute, PointsMaterial, Points, TriangleStripDrawMode, TriangleFanDrawMode, LineSegments, LineLoop, Group, NormalBlending, LoadingManager, LoaderUtils, FileLoader, UniformsUtils, ShaderMaterial, AdditiveBlending, HalfFloatType, Scene, WebGLRenderer, LinearSRGBColorSpace } from 'three';
27
27
  import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js';
28
28
  import { LineSegmentsGeometry } from 'three/examples/jsm/lines/LineSegmentsGeometry.js';
29
29
  import { Wireframe } from 'three/examples/jsm/lines/Wireframe.js';
30
30
  import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js';
31
- import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
32
31
  import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
33
32
  import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
34
33
  import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
@@ -2060,7 +2059,8 @@ function clearSelected(viewer) {
2060
2059
  const selection = viewer.getComponent("SelectionComponent");
2061
2060
  selection.clearSelection();
2062
2061
  viewer.update();
2063
- viewer.emitEvent({ type: "select", data: undefined, handles: [] });
2062
+ viewer.emitEvent({ type: "select", handles: [] });
2063
+ viewer.emitEvent({ type: "select2", handles: [] });
2064
2064
  }
2065
2065
 
2066
2066
  function clearSlices(viewer) {
@@ -2149,22 +2149,31 @@ function getDefaultViewPositions() {
2149
2149
  }
2150
2150
 
2151
2151
  function getModels(viewer) {
2152
- return viewer.models.map((model) => model.handle);
2152
+ return viewer.models.map((model) => model.id);
2153
2153
  }
2154
2154
 
2155
2155
  function getSelected(viewer) {
2156
- const handles = [];
2157
- viewer.models.forEach((model) => handles.push(...model.getHandlesByObjects(viewer.selected)));
2156
+ const handles2 = viewer.executeCommand("getSelected2");
2157
+ const handles = handles2.map((handle) => handle.slice(handle.indexOf(":") + 1));
2158
2158
  return handles;
2159
2159
  }
2160
2160
 
2161
+ function getSelected2(viewer) {
2162
+ const handles2 = [];
2163
+ viewer.models.forEach((model) => {
2164
+ handles2.push(...model.getHandlesByObjects(viewer.selected));
2165
+ });
2166
+ return handles2;
2167
+ }
2168
+
2161
2169
  function hideSelected(viewer) {
2162
2170
  viewer.models.forEach((model) => model.hideObjects(viewer.selected));
2163
2171
  const selection = viewer.getComponent("SelectionComponent");
2164
2172
  selection.clearSelection();
2165
2173
  viewer.update();
2166
2174
  viewer.emitEvent({ type: "hide" });
2167
- viewer.emitEvent({ type: "select", data: undefined, handles: [] });
2175
+ viewer.emitEvent({ type: "select", handles: [] });
2176
+ viewer.emitEvent({ type: "select2", handles: [] });
2168
2177
  }
2169
2178
 
2170
2179
  function isolateSelected(viewer) {
@@ -2191,14 +2200,13 @@ function resetView(viewer) {
2191
2200
  viewer.emit({ type: "resetview" });
2192
2201
  }
2193
2202
 
2194
- function selectModel(viewer, handle) {
2203
+ function selectModel(viewer, id) {
2195
2204
  const selection = viewer.getComponent("SelectionComponent");
2196
2205
  selection.clearSelection();
2197
- viewer.models
2198
- .filter((model) => model.handle === handle)
2199
- .forEach((model) => selection.select(model.getObjects(), model));
2206
+ viewer.models.filter((model) => model.id === id).forEach((model) => selection.select(model.getObjects(), model));
2200
2207
  viewer.update();
2201
- viewer.emit({ type: "select", data: [] });
2208
+ viewer.emitEvent({ type: "select", handles: viewer.getSelected() });
2209
+ viewer.emitEvent({ type: "select2", handles: viewer.getSelected2() });
2202
2210
  }
2203
2211
 
2204
2212
  function setActiveDragger(viewer, dragger = "") {
@@ -2210,16 +2218,31 @@ function setMarkupColor(viewer, r = 255, g = 0, b = 0) {
2210
2218
  }
2211
2219
 
2212
2220
  function setSelected(viewer, handles = []) {
2213
- const selection = viewer.getComponent("SelectionComponent");
2214
- selection.clearSelection();
2221
+ const handles2 = [];
2222
+ handles.forEach((handle) => {
2223
+ if (handle.includes(":")) {
2224
+ handles2.push(handle);
2225
+ }
2226
+ else
2227
+ viewer.models.forEach((model) => {
2228
+ handles2.push(`${model.id}:${handle}`);
2229
+ });
2230
+ });
2231
+ viewer.executeCommand("setSelected2", handles2);
2232
+ }
2233
+
2234
+ function setSelected2(viewer, handles = []) {
2235
+ const selectionComponent = viewer.getComponent("SelectionComponent");
2236
+ selectionComponent.clearSelection();
2215
2237
  viewer.models.forEach((model) => {
2216
2238
  const objects = model.getObjectsByHandles(handles);
2217
2239
  model.showObjects(objects);
2218
- selection.select(objects, model);
2240
+ selectionComponent.select(objects, model);
2219
2241
  });
2220
2242
  viewer.update();
2221
2243
  viewer.emitEvent({ type: "show" });
2222
- viewer.emitEvent({ type: "select", data: undefined, handles });
2244
+ viewer.emitEvent({ type: "select", data: undefined, handles: viewer.getSelected() });
2245
+ viewer.emitEvent({ type: "select2", data: undefined, handles });
2223
2246
  }
2224
2247
 
2225
2248
  function showAll(viewer) {
@@ -2233,21 +2256,19 @@ function zoomToExtents(viewer) {
2233
2256
  }
2234
2257
 
2235
2258
  function zoomToObjects(viewer, handles = []) {
2236
- const handleSet = new Set(handles);
2237
- const objects = [];
2238
- viewer.scene.traverseVisible((child) => {
2239
- var _a;
2240
- if (handleSet.has((_a = child.userData) === null || _a === void 0 ? void 0 : _a.handle))
2241
- objects.push(child);
2259
+ const extents = new Box3();
2260
+ viewer.models.forEach((model) => {
2261
+ const objects = model.getObjectsByHandles(handles);
2262
+ objects.forEach((object) => extents.expandByObject(object));
2242
2263
  });
2243
- const extents = objects.reduce((result, object) => result.expandByObject(object), new Box3());
2244
2264
  if (extents.isEmpty())
2245
2265
  extents.copy(viewer.extents);
2246
2266
  zoomTo(viewer, extents);
2247
2267
  }
2248
2268
 
2249
2269
  function zoomToSelected(viewer) {
2250
- const extents = viewer.selected.reduce((result, object) => result.expandByObject(object), new Box3());
2270
+ const extents = new Box3();
2271
+ viewer.selected.forEach((object) => extents.expandByObject(object));
2251
2272
  if (extents.isEmpty())
2252
2273
  extents.copy(viewer.extents);
2253
2274
  zoomTo(viewer, extents);
@@ -2264,6 +2285,7 @@ commands.registerCommand("explode", explode);
2264
2285
  commands.registerCommand("getDefaultViewPositions", getDefaultViewPositions);
2265
2286
  commands.registerCommand("getModels", getModels);
2266
2287
  commands.registerCommand("getSelected", getSelected);
2288
+ commands.registerCommand("getSelected2", getSelected2);
2267
2289
  commands.registerCommand("hideSelected", hideSelected);
2268
2290
  commands.registerCommand("isolateSelected", isolateSelected);
2269
2291
  commands.registerCommand("regenerateAll", regenerateAll);
@@ -2273,6 +2295,7 @@ commands.registerCommand("setActiveDragger", setActiveDragger);
2273
2295
  commands.registerCommand("setDefaultViewPosition", setDefaultViewPosition);
2274
2296
  commands.registerCommand("setMarkupColor", setMarkupColor);
2275
2297
  commands.registerCommand("setSelected", setSelected);
2298
+ commands.registerCommand("setSelected2", setSelected2);
2276
2299
  commands.registerCommand("showAll", showAll);
2277
2300
  commands.registerCommand("zoomToExtents", zoomToExtents);
2278
2301
  commands.registerCommand("zoomToObjects", zoomToObjects);
@@ -2325,6 +2348,10 @@ class CameraComponent {
2325
2348
  this.switchCameraMode(this.viewer.options.cameraMode);
2326
2349
  };
2327
2350
  this.geometryEnd = () => {
2351
+ if (this.viewer.models.length > 1) {
2352
+ this.switchCamera(this.viewer.camera);
2353
+ return;
2354
+ }
2328
2355
  let camera;
2329
2356
  this.viewer.scene.traverse((object) => {
2330
2357
  if (object.isCamera)
@@ -2422,6 +2449,8 @@ class ExtentsComponent {
2422
2449
  const extents = new Box3();
2423
2450
  this.viewer.models.forEach((model) => model.getExtents(extents));
2424
2451
  this.viewer.extents.copy(extents);
2452
+ if (this.viewer.models.length > 1)
2453
+ return;
2425
2454
  this.viewer.extents.getCenter(this.viewer.target);
2426
2455
  };
2427
2456
  this.viewer = viewer;
@@ -2770,6 +2799,7 @@ class SelectionComponent {
2770
2799
  }
2771
2800
  this.viewer.update();
2772
2801
  this.viewer.emitEvent({ type: "select", data: undefined, handles: this.viewer.getSelected() });
2802
+ this.viewer.emitEvent({ type: "select2", data: undefined, handles: this.viewer.getSelected2() });
2773
2803
  };
2774
2804
  this.onDoubleClick = (event) => {
2775
2805
  if (event.button !== 0)
@@ -3014,48 +3044,8 @@ components.registerComponent("SelectionComponent", (viewer) => new SelectionComp
3014
3044
  components.registerComponent("WCSHelperComponent", (viewer) => new WCSHelperComponent(viewer));
3015
3045
  components.registerComponent("ResetComponent", (viewer) => new ResetComponent(viewer));
3016
3046
 
3017
- class GLTFLoadingManager extends LoadingManager {
3018
- constructor(file, params = {}) {
3019
- super();
3020
- this.path = "";
3021
- this.resourcePath = "";
3022
- this.fileURL = "";
3023
- this.dataURLs = new Map();
3024
- this.path = params.path || "";
3025
- const externalFiles = params.externalFiles || new Map();
3026
- if (typeof file === "string") {
3027
- this.fileURL = file;
3028
- this.resourcePath = LoaderUtils.extractUrlBase(file);
3029
- }
3030
- else {
3031
- externalFiles.forEach((value, key) => (this.fileURL = value === file ? key : this.fileURL));
3032
- externalFiles.set(this.fileURL, file);
3033
- }
3034
- externalFiles.forEach((value, key) => {
3035
- let dataURL;
3036
- if (typeof value === "string")
3037
- dataURL = value;
3038
- else
3039
- dataURL = URL.createObjectURL(new Blob([value]));
3040
- this.dataURLs.set(key, dataURL);
3041
- });
3042
- this.setURLModifier((url) => {
3043
- const key = decodeURI(url)
3044
- .replace(this.path, "")
3045
- .replace(this.resourcePath, "")
3046
- .replace(/^(\.?\/)/, "");
3047
- const dataURL = this.dataURLs.get(key);
3048
- return dataURL !== null && dataURL !== void 0 ? dataURL : url;
3049
- });
3050
- }
3051
- dispose() {
3052
- this.dataURLs.forEach(URL.revokeObjectURL);
3053
- }
3054
- }
3055
-
3056
3047
  class ModelImpl {
3057
3048
  constructor(scene) {
3058
- this.handle = "1";
3059
3049
  this.scene = scene;
3060
3050
  }
3061
3051
  dispose() {
@@ -3097,27 +3087,45 @@ class ModelImpl {
3097
3087
  }
3098
3088
  return false;
3099
3089
  }
3090
+ hasHandle(handle) {
3091
+ return !handle.includes(":") || handle.split(":", 1)[0] === this.id + "";
3092
+ }
3100
3093
  getOwnObjects(objects) {
3101
3094
  if (!Array.isArray(objects))
3102
3095
  objects = [objects];
3103
3096
  return objects.filter((object) => this.hasObject(object));
3104
3097
  }
3098
+ getOwnHandles(handles) {
3099
+ if (!Array.isArray(handles))
3100
+ handles = [handles];
3101
+ return handles.filter((handle) => this.hasHandle(handle));
3102
+ }
3105
3103
  getObjectsByHandles(handles) {
3106
- const handleSet = new Set(handles);
3104
+ const ownHandles = this.getOwnHandles(handles);
3105
+ if (ownHandles.length === 0)
3106
+ return [];
3107
+ const handleSet = new Set(ownHandles.map((handle) => handle.slice(handle.indexOf(":") + 1)));
3107
3108
  const objects = [];
3108
- this.scene.traverse((object) => handleSet.has(object.userData.handle) && objects.push(object));
3109
+ this.scene.traverse((object) => {
3110
+ const handle = object.userData.handle;
3111
+ if (handle && handleSet.has(handle))
3112
+ objects.push(object);
3113
+ });
3109
3114
  return objects;
3110
3115
  }
3111
3116
  getHandlesByObjects(objects) {
3112
- if (!Array.isArray(objects))
3113
- objects = [objects];
3114
- const handlesSet = new Set();
3115
- this.getOwnObjects(objects).forEach((object) => handlesSet.add(object.userData.handle));
3116
- return Array.from(handlesSet);
3117
+ const ownObjects = this.getOwnObjects(objects);
3118
+ if (ownObjects.length === 0)
3119
+ return [];
3120
+ const handleSet = new Set();
3121
+ ownObjects.forEach((object) => {
3122
+ const handle = object.userData.handle;
3123
+ if (handle)
3124
+ handleSet.add(`${this.id}:${handle}`);
3125
+ });
3126
+ return Array.from(handleSet);
3117
3127
  }
3118
3128
  hideObjects(objects) {
3119
- if (!Array.isArray(objects))
3120
- objects = [objects];
3121
3129
  this.getOwnObjects(objects).forEach((object) => (object.visible = false));
3122
3130
  return this;
3123
3131
  }
@@ -3133,8 +3141,6 @@ class ModelImpl {
3133
3141
  return this;
3134
3142
  }
3135
3143
  showObjects(objects) {
3136
- if (!Array.isArray(objects))
3137
- objects = [objects];
3138
3144
  this.getOwnObjects(objects).forEach((object) => {
3139
3145
  object.visible = true;
3140
3146
  object.traverseAncestors((parent) => (parent.visible = true));
@@ -3190,42 +3196,6 @@ class ModelImpl {
3190
3196
  }
3191
3197
  }
3192
3198
 
3193
- class GLTFFileLoader extends Loader {
3194
- constructor(viewer) {
3195
- super();
3196
- this.viewer = viewer;
3197
- }
3198
- isSupport(file, format) {
3199
- return ((typeof file === "string" || file instanceof globalThis.File || file instanceof ArrayBuffer) &&
3200
- /(gltf|glb)$/i.test(format));
3201
- }
3202
- async load(file, format, params) {
3203
- const manager = new GLTFLoadingManager(file, params);
3204
- const loader = new GLTFLoader(manager);
3205
- loader.setPath(manager.path);
3206
- loader.setCrossOrigin(params.crossOrigin || loader.crossOrigin);
3207
- loader.setWithCredentials(params.withCredentials || loader.withCredentials);
3208
- const progress = (event) => {
3209
- const { lengthComputable, loaded, total } = event;
3210
- const progress = lengthComputable ? loaded / total : 1;
3211
- this.viewer.emitEvent({ type: "geometryprogress", data: progress, file });
3212
- };
3213
- const gltf = await loader.loadAsync(manager.fileURL, progress);
3214
- if (!this.viewer.scene)
3215
- return this;
3216
- const modelImpl = new ModelImpl(gltf.scene);
3217
- modelImpl.loader = this;
3218
- modelImpl.viewer = this.viewer;
3219
- this.viewer.scene.add(gltf.scene);
3220
- this.viewer.models.push(modelImpl);
3221
- this.viewer.syncOptions();
3222
- this.viewer.syncOverlay();
3223
- this.viewer.update();
3224
- this.viewer.emitEvent({ type: "databasechunk", data: gltf.scene, file });
3225
- return this;
3226
- }
3227
- }
3228
-
3229
3199
  class DynamicModelImpl extends ModelImpl {
3230
3200
  getExtents(target) {
3231
3201
  return target.union(this.gltfLoader.getTotalGeometryExtent());
@@ -3244,31 +3214,40 @@ class DynamicModelImpl extends ModelImpl {
3244
3214
  return this.gltfLoader.originalObjects.has(object);
3245
3215
  }
3246
3216
  getObjectsByHandles(handles) {
3247
- const handlesSet = new Set(handles);
3217
+ const ownHandles = this.getOwnHandles(handles);
3218
+ if (ownHandles.length === 0)
3219
+ return [];
3220
+ const handlesSet = new Set(ownHandles);
3248
3221
  const objects = [];
3249
3222
  handlesSet.forEach((handle) => {
3250
- const handle2 = `${this.modelId}_${handle}`;
3251
- const handles = this.gltfLoader.handleToObjects.get(handle2) || [];
3252
- objects.push(...Array.from(handles));
3223
+ objects.push(...this.gltfLoader.getObjectsByHandle(handle));
3253
3224
  });
3254
3225
  return objects;
3255
3226
  }
3256
3227
  getHandlesByObjects(objects) {
3257
- const handles = super.getHandlesByObjects(objects);
3258
- return handles.map((x) => x.split("_").pop());
3228
+ const ownObjects = this.getOwnObjects(objects);
3229
+ if (ownObjects.length === 0)
3230
+ return [];
3231
+ const handleSet = new Set();
3232
+ ownObjects.forEach((object) => {
3233
+ const handle = object.userData.handle;
3234
+ if (handle)
3235
+ handleSet.add(handle);
3236
+ });
3237
+ return Array.from(handleSet);
3259
3238
  }
3260
3239
  hideObjects(objects) {
3261
- const handles = super.getHandlesByObjects(objects);
3240
+ const handles = this.getHandlesByObjects(objects);
3262
3241
  this.gltfLoader.hideObjects(handles);
3263
3242
  return this;
3264
3243
  }
3265
3244
  isolateObjects(objects) {
3266
- const handles = super.getHandlesByObjects(objects);
3245
+ const handles = this.getHandlesByObjects(objects);
3267
3246
  this.gltfLoader.isolateObjects(new Set(handles));
3268
3247
  return this;
3269
3248
  }
3270
3249
  showObjects(objects) {
3271
- const handles = super.getHandlesByObjects(objects);
3250
+ const handles = this.getHandlesByObjects(objects);
3272
3251
  this.gltfLoader.showObjects(handles);
3273
3252
  return this;
3274
3253
  }
@@ -3332,11 +3311,13 @@ class GltfStructure {
3332
3311
  this.materials = new Map();
3333
3312
  this.textureCache = new Map();
3334
3313
  this.materialCache = new Map();
3314
+ this.uri = "";
3335
3315
  }
3336
3316
  async initialize(loader) {
3337
3317
  this.json = await this.loadController.loadJson();
3338
3318
  this.baseUrl = await this.loadController.baseUrl();
3339
3319
  this.loader = loader;
3320
+ this.uri = this.json.buffers[0].uri || "";
3340
3321
  }
3341
3322
  clear() {
3342
3323
  this.json = null;
@@ -3430,7 +3411,7 @@ class GltfStructure {
3430
3411
  await this.loader.waitForChunkSlot();
3431
3412
  try {
3432
3413
  const length = range.end - range.start;
3433
- const buffer = await this.loadController.loadBinaryData([{ offset: range.start, length }]);
3414
+ const buffer = await this.loadController.loadBinaryData([{ offset: range.start, length }], this.uri);
3434
3415
  for (const req of range.requests) {
3435
3416
  const relOffset = req.offset - range.start;
3436
3417
  try {
@@ -3747,6 +3728,7 @@ class GltfStructure {
3747
3728
  }
3748
3729
  }
3749
3730
 
3731
+ const STRUCTURE_ID_SEPARATOR = ":";
3750
3732
  class DynamicGltfLoader {
3751
3733
  constructor(camera, scene, renderer) {
3752
3734
  this.camera = camera;
@@ -3759,6 +3741,7 @@ class DynamicGltfLoader {
3759
3741
  geometryerror: [],
3760
3742
  update: [],
3761
3743
  geometrymemory: [],
3744
+ optimizationprogress: [],
3762
3745
  };
3763
3746
  this.loadDistance = 100;
3764
3747
  this.unloadDistance = 150;
@@ -3800,7 +3783,6 @@ class DynamicGltfLoader {
3800
3783
  this.hiddenHandles = new Set();
3801
3784
  this.newOptimizedObjects = new Set();
3802
3785
  this.oldOptimizeObjects = new Set();
3803
- this.maxConcurrentChunks = 8;
3804
3786
  this.activeChunkLoads = 0;
3805
3787
  this.chunkQueue = [];
3806
3788
  this.objectIdToIndex = new Map();
@@ -3809,6 +3791,7 @@ class DynamicGltfLoader {
3809
3791
  this.maxConcurrentChunks = 6;
3810
3792
  this.mergedObjectMap = new Map();
3811
3793
  this.mergedGeometryVisibility = new Map();
3794
+ this._webglInfoCache = null;
3812
3795
  }
3813
3796
  setVisibleEdges(visible) {
3814
3797
  this.visibleEdges = visible;
@@ -3912,6 +3895,123 @@ class DynamicGltfLoader {
3912
3895
  this.updateMemoryIndicator();
3913
3896
  console.log(`Final memory usage: ${Math.round(currentMemoryUsage / (1024 * 1024))}MB`);
3914
3897
  }
3898
+ getStats() {
3899
+ let totalObjects = 0;
3900
+ let renderedObjects = 0;
3901
+ let totalTriangles = 0;
3902
+ let renderedTriangles = 0;
3903
+ let totalLines = 0;
3904
+ let renderedLines = 0;
3905
+ let totalEdges = 0;
3906
+ let renderedEdges = 0;
3907
+ this.scene.traverse((object) => {
3908
+ totalObjects++;
3909
+ const geometry = object.geometry;
3910
+ if (!geometry) return;
3911
+ let triCount = 0;
3912
+ if (geometry.index) {
3913
+ triCount = Math.floor(geometry.index.count / 3);
3914
+ } else if (geometry.attributes && geometry.attributes.position) {
3915
+ triCount = Math.floor(geometry.attributes.position.count / 3);
3916
+ }
3917
+ totalTriangles += triCount;
3918
+ let lineCount = 0;
3919
+ if (geometry.index) {
3920
+ lineCount = Math.floor(geometry.index.count / 2);
3921
+ } else if (geometry.attributes && geometry.attributes.position) {
3922
+ lineCount = Math.floor(geometry.attributes.position.count / 2);
3923
+ }
3924
+ if (object.type === "Line" || object.type === "LineSegments" || object.type === "LineLoop") {
3925
+ if (object.userData.isEdge) {
3926
+ totalEdges += lineCount;
3927
+ } else {
3928
+ totalLines += lineCount;
3929
+ }
3930
+ }
3931
+ if (object.visible !== false) {
3932
+ if (object.isMesh || object.isLine || object.isPoints) {
3933
+ renderedObjects++;
3934
+ if (object.isMesh) {
3935
+ renderedTriangles += triCount;
3936
+ } else if (object.type === "Line" || object.type === "LineSegments" || object.type === "LineLoop") {
3937
+ if (object.userData.isEdge) {
3938
+ renderedEdges += lineCount;
3939
+ } else {
3940
+ renderedLines += lineCount;
3941
+ }
3942
+ }
3943
+ }
3944
+ }
3945
+ });
3946
+ const geometryCount = this.geometryCache ? this.geometryCache.size : 0;
3947
+ const geometryMemoryBytes = Array.from(this.geometryCache?.values?.() || []).reduce((a, b) => a + b, 0);
3948
+ const uniqueMaterialIds = new Set();
3949
+ const uniqueTextureIds = new Set();
3950
+ if (Array.isArray(this.structures)) {
3951
+ for (const structure of this.structures) {
3952
+ console.log(structure.materialCache.values());
3953
+ try {
3954
+ for (const entry of structure.materialCache.values()) {
3955
+ if (entry?.mesh?.uuid) uniqueMaterialIds.add(entry.mesh.uuid);
3956
+ if (entry?.points?.uuid) uniqueMaterialIds.add(entry.points.uuid);
3957
+ if (entry?.lines?.uuid) uniqueMaterialIds.add(entry.lines.uuid);
3958
+ }
3959
+ } catch (exp) {
3960
+ console.error("Error adding material to uniqueMaterialIds", exp);
3961
+ }
3962
+ }
3963
+ }
3964
+ const materialCount = uniqueMaterialIds.size;
3965
+ const textureCount = uniqueTextureIds.size;
3966
+ const estimatedGpuMemoryBytes = geometryMemoryBytes;
3967
+ if (!this._webglInfoCache) {
3968
+ try {
3969
+ const gl = this.renderer.getContext();
3970
+ const dbgInfo = gl.getExtension("WEBGL_debug_renderer_info");
3971
+ if (dbgInfo) {
3972
+ const rendererStr = gl.getParameter(dbgInfo.UNMASKED_RENDERER_WEBGL);
3973
+ const vendorStr = gl.getParameter(dbgInfo.UNMASKED_VENDOR_WEBGL);
3974
+ this._webglInfoCache = { renderer: rendererStr, vendor: vendorStr };
3975
+ } else {
3976
+ this._webglInfoCache = { renderer: null, vendor: null };
3977
+ }
3978
+ } catch (e) {
3979
+ console.error("Error getting webgl info", e);
3980
+ this._webglInfoCache = { renderer: null, vendor: null };
3981
+ }
3982
+ }
3983
+ const size = new Vector2();
3984
+ if (this.renderer && this.renderer.getSize) {
3985
+ this.renderer.getSize(size);
3986
+ }
3987
+ return {
3988
+ scene: {
3989
+ beforeOptimization: {
3990
+ objects: totalObjects - renderedObjects,
3991
+ triangles: totalTriangles - renderedTriangles,
3992
+ lines: totalLines - renderedLines,
3993
+ edges: totalEdges - renderedEdges,
3994
+ },
3995
+ afterOptimization: {
3996
+ objects: renderedObjects,
3997
+ triangles: renderedTriangles,
3998
+ lines: renderedLines,
3999
+ edges: renderedEdges,
4000
+ },
4001
+ },
4002
+ memory: {
4003
+ geometries: { count: geometryCount, bytes: geometryMemoryBytes },
4004
+ textures: { count: textureCount },
4005
+ materials: { count: materialCount },
4006
+ totalEstimatedGpuBytes: estimatedGpuMemoryBytes,
4007
+ },
4008
+ system: {
4009
+ webglRenderer: this._webglInfoCache?.renderer || "",
4010
+ webglVendor: this._webglInfoCache?.vendor || "",
4011
+ viewport: { width: size.x || 0, height: size.y || 0 },
4012
+ },
4013
+ };
4014
+ }
3915
4015
  async loadNode(nodeId, onLoadFinishCb) {
3916
4016
  const node = this.nodes.get(nodeId);
3917
4017
  if (!node || node.loaded || node.loading) return;
@@ -4082,7 +4182,7 @@ class DynamicGltfLoader {
4082
4182
  if (node.handle) {
4083
4183
  mesh.userData.handle = node.handle;
4084
4184
  } else {
4085
- mesh.userData.handle = `${node.structure.id}_${mesh.userData.handle}`;
4185
+ mesh.userData.handle = `${node.structure.id}${STRUCTURE_ID_SEPARATOR}${mesh.userData.handle}`;
4086
4186
  }
4087
4187
  if (mesh.material.name === "edges") {
4088
4188
  mesh.userData.isEdge = true;
@@ -4228,7 +4328,7 @@ class DynamicGltfLoader {
4228
4328
  let nodeGroup = null;
4229
4329
  let handle = null;
4230
4330
  if (nodeDef.extras?.handle) {
4231
- handle = `${structure.id}_${nodeDef.extras.handle}`;
4331
+ handle = `${structure.id}${STRUCTURE_ID_SEPARATOR}${nodeDef.extras.handle}`;
4232
4332
  }
4233
4333
  if (nodeDef.camera !== undefined) {
4234
4334
  const camera = this.loadCamera(structure, nodeDef.camera, nodeDef);
@@ -4245,7 +4345,7 @@ class DynamicGltfLoader {
4245
4345
  if (nodeDef.extras) {
4246
4346
  nodeGroup.userData = { ...nodeDef.extras };
4247
4347
  if (nodeGroup.userData.handle) {
4248
- nodeGroup.userData.handle = `${structure.id}_${nodeGroup.userData.handle}`;
4348
+ nodeGroup.userData.handle = `${structure.id}${STRUCTURE_ID_SEPARATOR}${nodeGroup.userData.handle}`;
4249
4349
  }
4250
4350
  }
4251
4351
  if (nodeDef.matrix) {
@@ -4290,7 +4390,7 @@ class DynamicGltfLoader {
4290
4390
  this.edgeNodes.push(uniqueNodeId);
4291
4391
  }
4292
4392
  if (meshDef.extras && meshDef.extras.handle) {
4293
- handle = `${structure.id}_${meshDef.extras.handle}`;
4393
+ handle = `${structure.id}${STRUCTURE_ID_SEPARATOR}${meshDef.extras.handle}`;
4294
4394
  }
4295
4395
  this.nodes.set(uniqueNodeId, {
4296
4396
  position: nodeGroup ? nodeGroup.position.clone() : new Vector3().setFromMatrixPosition(nodeMatrix),
@@ -4376,12 +4476,12 @@ class DynamicGltfLoader {
4376
4476
  }
4377
4477
  }
4378
4478
  async loadNodes() {
4379
- console.time("process nodes");
4479
+ console.time("Process nodes");
4380
4480
  await this.processNodes();
4381
- console.timeEnd("process nodes");
4382
- console.time("optimize scene");
4481
+ console.timeEnd("Process nodes");
4482
+ console.time("Optimize scene");
4383
4483
  await this.optimizeScene();
4384
- console.timeEnd("optimize scene");
4484
+ console.timeEnd("Optimize scene");
4385
4485
  }
4386
4486
  cleanupPartialLoad() {
4387
4487
  this.nodesToLoad.forEach((nodeId) => {
@@ -4702,7 +4802,7 @@ class DynamicGltfLoader {
4702
4802
  this.handleToObjects.set(fullHandle, new Set());
4703
4803
  }
4704
4804
  this.handleToObjects.get(fullHandle).add(object);
4705
- object.userData.structureId = object.userData.handle.split("_")[0];
4805
+ object.userData.structureId = object.userData.handle.split(STRUCTURE_ID_SEPARATOR)[0];
4706
4806
  }
4707
4807
  getObjectsByHandle(handle) {
4708
4808
  if (!handle) return [];
@@ -4768,10 +4868,28 @@ class DynamicGltfLoader {
4768
4868
  }
4769
4869
  this.originalObjects.add(object);
4770
4870
  }
4771
- optimizeScene() {
4871
+ yieldToUI() {
4872
+ return new Promise((resolve) => {
4873
+ requestAnimationFrame(() => {
4874
+ setTimeout(resolve, 0);
4875
+ });
4876
+ });
4877
+ }
4878
+ async optimizeScene() {
4879
+ console.log("Starting scene optimization...");
4880
+ this.dispatchEvent("optimizationprogress", {
4881
+ phase: "start",
4882
+ progress: 0,
4883
+ message: "Starting optimization...",
4884
+ });
4772
4885
  this.originalObjects.clear();
4773
4886
  this.originalObjectsToSelection.clear();
4774
4887
  const structureGroups = new Map();
4888
+ this.dispatchEvent("optimizationprogress", {
4889
+ phase: "collecting",
4890
+ progress: 5,
4891
+ message: "Collecting scene objects...",
4892
+ });
4775
4893
  this.scene.traverse((object) => {
4776
4894
  if (object.userData.structureId) {
4777
4895
  const structureId = object.userData.structureId;
@@ -4800,16 +4918,44 @@ class DynamicGltfLoader {
4800
4918
  }
4801
4919
  }
4802
4920
  });
4921
+ let processedGroups = 0;
4922
+ const totalGroups = structureGroups.size;
4923
+ this.dispatchEvent("optimizationprogress", {
4924
+ phase: "merging",
4925
+ progress: 10,
4926
+ message: `Merging ${totalGroups} structure groups...`,
4927
+ current: 0,
4928
+ total: totalGroups,
4929
+ });
4803
4930
  for (const group of structureGroups.values()) {
4804
4931
  group.mapMeshes.clear();
4805
4932
  group.mapLines.clear();
4806
4933
  group.mapLineSegments.clear();
4807
4934
  group.mapPoints.clear();
4808
- this.mergeMeshGroups(group.meshes, group.rootGroup);
4809
- this.mergeLineGroups(group.lines, group.rootGroup);
4810
- this.mergeLineSegmentGroups(group.lineSegments, group.rootGroup);
4811
- this.mergePointsGroups(group.points, group.rootGroup);
4935
+ await this.mergeMeshGroups(group.meshes, group.rootGroup);
4936
+ await this.yieldToUI();
4937
+ await this.mergeLineGroups(group.lines, group.rootGroup);
4938
+ await this.yieldToUI();
4939
+ await this.mergeLineSegmentGroups(group.lineSegments, group.rootGroup);
4940
+ await this.yieldToUI();
4941
+ await this.mergePointsGroups(group.points, group.rootGroup);
4942
+ processedGroups++;
4943
+ const progress = 10 + Math.round((processedGroups / totalGroups) * 80);
4944
+ this.dispatchEvent("optimizationprogress", {
4945
+ phase: "merging",
4946
+ progress,
4947
+ message: `Processing structure ${processedGroups}/${totalGroups}...`,
4948
+ current: processedGroups,
4949
+ total: totalGroups,
4950
+ });
4951
+ console.log(`Optimization progress: ${processedGroups}/${totalGroups} structure groups processed (${progress}%)`);
4952
+ await this.yieldToUI();
4812
4953
  }
4954
+ this.dispatchEvent("optimizationprogress", {
4955
+ phase: "finalizing",
4956
+ progress: 95,
4957
+ message: "Finalizing optimization...",
4958
+ });
4813
4959
  this.originalObjects.forEach((obj) => {
4814
4960
  obj.visible = false;
4815
4961
  if (!(obj instanceof Points) && !obj.userData.isEdge) {
@@ -4818,9 +4964,15 @@ class DynamicGltfLoader {
4818
4964
  });
4819
4965
  this.initializeObjectVisibility();
4820
4966
  console.log(`Optimization complete. Total objects: ${this.maxObjectId}`);
4967
+ this.dispatchEvent("optimizationprogress", {
4968
+ phase: "complete",
4969
+ progress: 100,
4970
+ message: `Optimization complete! ${this.maxObjectId} objects processed.`,
4971
+ });
4821
4972
  this.dispatchEvent("update");
4822
4973
  }
4823
- mergeMeshGroups(materialGroups, rootGroup) {
4974
+ async mergeMeshGroups(materialGroups, rootGroup) {
4975
+ let processedGroups = 0;
4824
4976
  for (const group of materialGroups) {
4825
4977
  if (!group.material) {
4826
4978
  console.warn("Skipping mesh group with null material");
@@ -4894,6 +5046,10 @@ class DynamicGltfLoader {
4894
5046
  this.handleToOptimizedObjects.set(handle, mergedObjects);
4895
5047
  }
4896
5048
  });
5049
+ processedGroups++;
5050
+ if (processedGroups % 5 === 0) {
5051
+ await this.yieldToUI();
5052
+ }
4897
5053
  } catch (error) {
4898
5054
  console.error("Failed to merge meshes for material:", error);
4899
5055
  group.objects.forEach((mesh) => {
@@ -4902,7 +5058,8 @@ class DynamicGltfLoader {
4902
5058
  }
4903
5059
  }
4904
5060
  }
4905
- mergeLineGroups(materialGroups, rootGroup) {
5061
+ async mergeLineGroups(materialGroups, rootGroup) {
5062
+ let processedGroups = 0;
4906
5063
  for (const group of materialGroups) {
4907
5064
  if (group.objects.length === 0) continue;
4908
5065
  if (!group.material) {
@@ -4921,7 +5078,9 @@ class DynamicGltfLoader {
4921
5078
  let posOffset = 0;
4922
5079
  const indices = [];
4923
5080
  let vertexOffset = 0;
5081
+ let isEdge = false;
4924
5082
  group.objects.forEach((line) => {
5083
+ isEdge = line.userData.isEdge;
4925
5084
  const geometry = line.geometry;
4926
5085
  const positionAttr = geometry.attributes.position;
4927
5086
  const vertexCount = positionAttr.count;
@@ -4972,6 +5131,7 @@ class DynamicGltfLoader {
4972
5131
  geometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
4973
5132
  const visibilityMaterial = this.createVisibilityMaterial(group.material);
4974
5133
  const mergedLine = new LineSegments(geometry, visibilityMaterial);
5134
+ mergedLine.userData.isEdge = isEdge;
4975
5135
  const mergedObjects = [mergedLine];
4976
5136
  if (this.useVAO) {
4977
5137
  this.createVAO(mergedLine);
@@ -4994,9 +5154,14 @@ class DynamicGltfLoader {
4994
5154
  this.handleToOptimizedObjects.set(handle, mergedObjects);
4995
5155
  }
4996
5156
  });
5157
+ processedGroups++;
5158
+ if (processedGroups % 5 === 0) {
5159
+ await this.yieldToUI();
5160
+ }
4997
5161
  }
4998
5162
  }
4999
- mergeLineSegmentGroups(materialGroups, rootGroup) {
5163
+ async mergeLineSegmentGroups(materialGroups, rootGroup) {
5164
+ let processedGroups = 0;
5000
5165
  for (const group of materialGroups) {
5001
5166
  if (!group.material) {
5002
5167
  console.warn("Skipping line segment group with null material");
@@ -5008,7 +5173,9 @@ class DynamicGltfLoader {
5008
5173
  const handles = new Set();
5009
5174
  const objectMapping = new Map();
5010
5175
  let currentVertexOffset = 0;
5176
+ let isEdge = false;
5011
5177
  for (const line of group.objects) {
5178
+ isEdge = line.userData.isEdge;
5012
5179
  const geometry = line.geometry.clone();
5013
5180
  line.updateWorldMatrix(true, false);
5014
5181
  geometry.applyMatrix4(line.matrixWorld);
@@ -5044,6 +5211,7 @@ class DynamicGltfLoader {
5044
5211
  mergedGeometry.setAttribute("visibility", new BufferAttribute(visibilityArray, 1));
5045
5212
  const visibilityMaterial = this.createVisibilityMaterial(group.material);
5046
5213
  const mergedLine = new LineSegments(mergedGeometry, visibilityMaterial);
5214
+ mergedLine.userData.isEdge = isEdge;
5047
5215
  if (this.useVAO) {
5048
5216
  this.createVAO(mergedLine);
5049
5217
  }
@@ -5070,6 +5238,10 @@ class DynamicGltfLoader {
5070
5238
  this.handleToOptimizedObjects.set(handle, mergedObjects);
5071
5239
  }
5072
5240
  });
5241
+ processedGroups++;
5242
+ if (processedGroups % 5 === 0) {
5243
+ await this.yieldToUI();
5244
+ }
5073
5245
  } catch (error) {
5074
5246
  console.warn("Failed to merge line segments for material:", error);
5075
5247
  group.objects.forEach((line) => {
@@ -5078,7 +5250,8 @@ class DynamicGltfLoader {
5078
5250
  }
5079
5251
  }
5080
5252
  }
5081
- mergePointsGroups(materialGroups, rootGroup) {
5253
+ async mergePointsGroups(materialGroups, rootGroup) {
5254
+ let processedGroups = 0;
5082
5255
  for (const group of materialGroups) {
5083
5256
  if (!group.material) {
5084
5257
  console.warn("Skipping points group with null material");
@@ -5120,6 +5293,10 @@ class DynamicGltfLoader {
5120
5293
  this.handleToOptimizedObjects.set(handle, mergedObjects);
5121
5294
  }
5122
5295
  });
5296
+ processedGroups++;
5297
+ if (processedGroups % 5 === 0) {
5298
+ await this.yieldToUI();
5299
+ }
5123
5300
  } catch (error) {
5124
5301
  console.warn("Failed to merge points for material:", error);
5125
5302
  group.objects.forEach((points) => {
@@ -5355,8 +5532,225 @@ class DynamicGltfLoader {
5355
5532
  }
5356
5533
  }
5357
5534
 
5358
- class GLTFCloudDynamicLoader {
5535
+ class GLTFLoadingManager extends LoadingManager {
5536
+ constructor(file, params = {}) {
5537
+ super();
5538
+ this.path = "";
5539
+ this.resourcePath = "";
5540
+ this.fileURL = "";
5541
+ this.dataURLs = new Map();
5542
+ this.path = params.path || "";
5543
+ const externalFiles = params.externalFiles || new Map();
5544
+ if (typeof file === "string") {
5545
+ this.fileURL = file;
5546
+ this.resourcePath = LoaderUtils.extractUrlBase(file);
5547
+ }
5548
+ else {
5549
+ externalFiles.forEach((value, key) => (this.fileURL = value === file ? key : this.fileURL));
5550
+ externalFiles.set(this.fileURL, file);
5551
+ }
5552
+ externalFiles.forEach((value, key) => {
5553
+ let dataURL;
5554
+ if (typeof value === "string")
5555
+ dataURL = value;
5556
+ else
5557
+ dataURL = URL.createObjectURL(new Blob([value]));
5558
+ this.dataURLs.set(key, dataURL);
5559
+ });
5560
+ this.setURLModifier((url) => {
5561
+ const key = decodeURI(url)
5562
+ .replace(this.path, "")
5563
+ .replace(this.resourcePath, "")
5564
+ .replace(/^(\.?\/)/, "");
5565
+ const dataURL = this.dataURLs.get(key);
5566
+ return dataURL !== null && dataURL !== void 0 ? dataURL : url;
5567
+ });
5568
+ }
5569
+ dispose() {
5570
+ this.dataURLs.forEach((dataURL) => URL.revokeObjectURL(dataURL));
5571
+ }
5572
+ }
5573
+
5574
+ const BINARY_EXTENSION_HEADER_MAGIC = "glTF";
5575
+ const BINARY_EXTENSION_HEADER_LENGTH = 12;
5576
+ const BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4e4f534a, BIN: 0x004e4042 };
5577
+ class GLTFBinaryExtension {
5578
+ constructor(data) {
5579
+ const headerView = new DataView(data, 0, BINARY_EXTENSION_HEADER_LENGTH);
5580
+ const textDecoder = new TextDecoder();
5581
+ const magic = textDecoder.decode(new Uint8Array(data.slice(0, 4)));
5582
+ if (magic !== BINARY_EXTENSION_HEADER_MAGIC) {
5583
+ this.content = textDecoder.decode(data);
5584
+ return;
5585
+ }
5586
+ const header = {
5587
+ magic,
5588
+ version: headerView.getUint32(4, true),
5589
+ length: headerView.getUint32(8, true),
5590
+ };
5591
+ if (header.magic !== BINARY_EXTENSION_HEADER_MAGIC) {
5592
+ throw new Error("Unsupported glTF-Binary header.");
5593
+ }
5594
+ if (header.version < 2.0) {
5595
+ throw new Error("Legacy binary file detected.");
5596
+ }
5597
+ const chunkContentsLength = header.length - BINARY_EXTENSION_HEADER_LENGTH;
5598
+ const chunkView = new DataView(data, BINARY_EXTENSION_HEADER_LENGTH);
5599
+ let chunkIndex = 0;
5600
+ while (chunkIndex < chunkContentsLength) {
5601
+ const chunkLength = chunkView.getUint32(chunkIndex, true);
5602
+ chunkIndex += 4;
5603
+ const chunkType = chunkView.getUint32(chunkIndex, true);
5604
+ chunkIndex += 4;
5605
+ if (chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON) {
5606
+ const contentArray = new Uint8Array(data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength);
5607
+ this.content = textDecoder.decode(contentArray);
5608
+ }
5609
+ else if (chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN) {
5610
+ const byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex;
5611
+ this.body = data.slice(byteOffset, byteOffset + chunkLength);
5612
+ }
5613
+ chunkIndex += chunkLength;
5614
+ }
5615
+ if (typeof this.content === "undefined") {
5616
+ throw new Error("JSON content not found.");
5617
+ }
5618
+ }
5619
+ }
5620
+
5621
+ class RangesLoader {
5622
+ constructor() {
5623
+ this.requestHeader = {};
5624
+ this.withCredentials = false;
5625
+ this.abortSignal = undefined;
5626
+ }
5627
+ setRequestHeader(requestHeader) {
5628
+ this.requestHeader = requestHeader;
5629
+ }
5630
+ setWithCredentials(withCredentials) {
5631
+ this.withCredentials = withCredentials;
5632
+ }
5633
+ setAbortSignal(abortSignal) {
5634
+ this.abortSignal = abortSignal;
5635
+ }
5636
+ async load(url, ranges) {
5637
+ const init = {
5638
+ headers: {
5639
+ ...this.requestHeader,
5640
+ Range: "bytes=" + ranges.map((x) => `${x.offset}-${x.offset + x.length - 1}`).join(","),
5641
+ },
5642
+ credentials: this.withCredentials ? "include" : "same-origin",
5643
+ signal: this.abortSignal,
5644
+ };
5645
+ const response = await fetch(url, init);
5646
+ if (!response.ok) {
5647
+ throw new Error(`Failed to fetch "${url}", status ${response.status}`);
5648
+ }
5649
+ if (response.status !== 206) {
5650
+ const arrayBuffer = await response.arrayBuffer();
5651
+ return this.extractRanges(arrayBuffer, ranges);
5652
+ }
5653
+ return response.arrayBuffer();
5654
+ }
5655
+ extractRanges(arrayBuffer, ranges) {
5656
+ const totalLength = ranges.reduce((sum, range) => sum + range.length, 0);
5657
+ const result = new Uint8Array(totalLength);
5658
+ let offset = 0;
5659
+ for (const range of ranges) {
5660
+ const chunk = new Uint8Array(arrayBuffer, range.offset, range.length);
5661
+ result.set(chunk, offset);
5662
+ offset += range.length;
5663
+ }
5664
+ return result.buffer;
5665
+ }
5666
+ }
5667
+
5668
+ class GLTFFileDynamicLoader extends Loader {
5359
5669
  constructor(viewer) {
5670
+ super();
5671
+ this.viewer = viewer;
5672
+ }
5673
+ dispose() {
5674
+ if (this.gltfLoader)
5675
+ this.gltfLoader.clear();
5676
+ if (this.manager)
5677
+ this.manager.dispose();
5678
+ }
5679
+ isSupport(file, format) {
5680
+ return ((typeof file === "string" || file instanceof globalThis.File || file instanceof ArrayBuffer) &&
5681
+ /(gltf|glb)$/i.test(format));
5682
+ }
5683
+ async load(file, format, params) {
5684
+ this.manager = new GLTFLoadingManager(file, params);
5685
+ const scene = new Group();
5686
+ this.gltfLoader = new DynamicGltfLoader(this.viewer.camera, scene, this.viewer.renderer);
5687
+ this.gltfLoader.memoryLimit = this.viewer.options.memoryLimit;
5688
+ this.gltfLoader.visibleEdges = this.viewer.options.edgeModel;
5689
+ const modelImpl = new DynamicModelImpl(scene);
5690
+ modelImpl.id = params.modelId || this.extractFileName(file);
5691
+ modelImpl.gltfLoader = this.gltfLoader;
5692
+ this.gltfLoader.addEventListener("databasechunk", () => {
5693
+ this.viewer.scene.add(scene);
5694
+ this.viewer.models.push(modelImpl);
5695
+ this.viewer.syncOptions();
5696
+ this.viewer.syncOverlay();
5697
+ this.viewer.update();
5698
+ this.viewer.emitEvent({ type: "databasechunk", data: scene, file });
5699
+ });
5700
+ this.gltfLoader.addEventListener("geometryerror", (data) => {
5701
+ this.viewer.emitEvent({ type: "geometryerror", data, file });
5702
+ });
5703
+ this.gltfLoader.addEventListener("update", (data) => {
5704
+ this.viewer.update();
5705
+ });
5706
+ const loadController = {
5707
+ loadJson: async () => {
5708
+ const loader = new FileLoader(this.manager);
5709
+ loader.setPath(this.manager.path);
5710
+ loader.setRequestHeader(params.requestHeader || {});
5711
+ loader.setWithCredentials(params.withCredentials || loader.withCredentials);
5712
+ loader.setResponseType("arraybuffer");
5713
+ const progress = (event) => {
5714
+ const { lengthComputable, loaded, total } = event;
5715
+ const progress = lengthComputable ? loaded / total : 1;
5716
+ this.viewer.emitEvent({ type: "geometryprogress", data: progress, file });
5717
+ };
5718
+ const data = await loader.loadAsync(this.manager.fileURL, progress);
5719
+ const extension = new GLTFBinaryExtension(data);
5720
+ this.gltf = JSON.parse(extension.content);
5721
+ this.bin = extension.body;
5722
+ return this.gltf;
5723
+ },
5724
+ loadBinaryData: (ranges, uri = "") => {
5725
+ const loader = new RangesLoader();
5726
+ loader.setRequestHeader(params.requestHeader || {});
5727
+ loader.setWithCredentials(params.withCredentials || false);
5728
+ loader.setAbortSignal(this.gltfLoader.abortController.signal);
5729
+ if (this.bin)
5730
+ return loader.extractRanges(this.bin, ranges);
5731
+ const path = this.manager.path || this.manager.resourcePath;
5732
+ const url = LoaderUtils.resolveURL(uri, path);
5733
+ return loader.load(this.manager.resolveURL(url), ranges);
5734
+ },
5735
+ baseUrl: () => {
5736
+ const path = this.manager.path || this.manager.resourcePath;
5737
+ return Promise.resolve(path);
5738
+ },
5739
+ };
5740
+ const structure = new GltfStructure(modelImpl.id, loadController);
5741
+ await this.gltfLoader.loadStructure(structure);
5742
+ await this.gltfLoader.loadNodes();
5743
+ return this;
5744
+ }
5745
+ cancel() {
5746
+ if (this.gltfLoader)
5747
+ this.gltfLoader.abortLoading();
5748
+ }
5749
+ }
5750
+
5751
+ class GLTFCloudDynamicLoader extends Loader {
5752
+ constructor(viewer) {
5753
+ super();
5360
5754
  this.requestId = 0;
5361
5755
  this.viewer = viewer;
5362
5756
  }
@@ -5371,17 +5765,15 @@ class GLTFCloudDynamicLoader {
5371
5765
  typeof file.downloadResourceRange === "function" &&
5372
5766
  /.gltf$/i.test(file.database));
5373
5767
  }
5374
- async load(model, format, params) {
5768
+ async load(model, format, params = {}) {
5375
5769
  const scene = new Group();
5376
5770
  this.gltfLoader = new DynamicGltfLoader(this.viewer.camera, scene, this.viewer.renderer);
5377
5771
  this.gltfLoader.memoryLimit = this.viewer.options.memoryLimit;
5378
5772
  this.gltfLoader.setVisibleEdges(this.viewer.options.edgeModel);
5773
+ const modelImpl = new DynamicModelImpl(scene);
5774
+ modelImpl.id = model.file.id;
5775
+ modelImpl.gltfLoader = this.gltfLoader;
5379
5776
  this.gltfLoader.addEventListener("databasechunk", (data) => {
5380
- const modelImpl = new DynamicModelImpl(scene);
5381
- modelImpl.loader = this;
5382
- modelImpl.viewer = this.viewer;
5383
- modelImpl.gltfLoader = this.gltfLoader;
5384
- modelImpl.modelId = model.id;
5385
5777
  this.viewer.scene.add(scene);
5386
5778
  this.viewer.models.push(modelImpl);
5387
5779
  this.viewer.syncOptions();
@@ -5389,10 +5781,6 @@ class GLTFCloudDynamicLoader {
5389
5781
  this.viewer.update();
5390
5782
  this.viewer.emitEvent({ type: "databasechunk", data: scene, file: model.file, model });
5391
5783
  });
5392
- this.gltfLoader.addEventListener("geometryprogress", (data) => {
5393
- const progress = data.loaded / data.total;
5394
- this.viewer.emitEvent({ type: "geometryprogress", data: progress, file: model.file, model });
5395
- });
5396
5784
  this.gltfLoader.addEventListener("geometryerror", (data) => {
5397
5785
  this.viewer.emitEvent({ type: "geometryerror", data, file: model.file, model });
5398
5786
  });
@@ -5402,7 +5790,7 @@ class GLTFCloudDynamicLoader {
5402
5790
  const loadController = {
5403
5791
  loadJson: async () => {
5404
5792
  const progress = (progress) => {
5405
- this.viewer.emitEvent({ type: "geometryprogress", data: progress, file: model });
5793
+ this.viewer.emitEvent({ type: "geometryprogress", data: progress, file: model.file, model });
5406
5794
  };
5407
5795
  const arrayBuffer = await model.downloadResource(model.database, progress, this.gltfLoader.getAbortController().signal);
5408
5796
  const text = new TextDecoder().decode(arrayBuffer);
@@ -5419,7 +5807,7 @@ class GLTFCloudDynamicLoader {
5419
5807
  },
5420
5808
  baseUrl: () => Promise.resolve(`${model.httpClient.serverUrl}${model.path}/`),
5421
5809
  };
5422
- const structure = new GltfStructure(model.id, loadController);
5810
+ const structure = new GltfStructure(modelImpl.id, loadController);
5423
5811
  await this.gltfLoader.loadStructure(structure);
5424
5812
  await this.gltfLoader.loadNodes();
5425
5813
  return this;
@@ -5431,7 +5819,7 @@ class GLTFCloudDynamicLoader {
5431
5819
  }
5432
5820
 
5433
5821
  const loaders = loadersRegistry("threejs");
5434
- loaders.registerLoader("gltf-file", (viewer) => new GLTFFileLoader(viewer));
5822
+ loaders.registerLoader("gltf-file", (viewer) => new GLTFFileDynamicLoader(viewer));
5435
5823
  loaders.registerLoader("gltf-cloud", (viewer) => new GLTFCloudDynamicLoader(viewer));
5436
5824
 
5437
5825
  class SSAARenderPass extends Pass {
@@ -5638,24 +6026,25 @@ class Helpers extends Scene {
5638
6026
  class Viewer extends EventEmitter2 {
5639
6027
  constructor(client) {
5640
6028
  super();
5641
- this._options = new Options(this);
5642
6029
  this.client = client;
5643
- this.canvasEvents = CANVAS_EVENTS;
5644
- this.canvaseventlistener = (event) => this.emit(event);
6030
+ this.options = new Options(this);
5645
6031
  this.loaders = [];
5646
6032
  this.models = [];
6033
+ this.canvasEvents = CANVAS_EVENTS.slice();
6034
+ this.canvaseventlistener = (event) => this.emit(event);
5647
6035
  this.selected = [];
5648
6036
  this.extents = new Box3();
5649
- this.target = new Vector3();
6037
+ this.target = new Vector3(0, 0, 0);
5650
6038
  this._activeDragger = null;
5651
6039
  this._components = [];
6040
+ this._renderNeeded = false;
5652
6041
  this._renderTime = 0;
5653
6042
  this.render = this.render.bind(this);
5654
6043
  this.update = this.update.bind(this);
5655
6044
  this._markup = new Markup();
5656
6045
  }
5657
- get options() {
5658
- return this._options;
6046
+ get markup() {
6047
+ return this._markup;
5659
6048
  }
5660
6049
  get draggers() {
5661
6050
  return [...draggers.getDraggers().keys()];
@@ -5663,14 +6052,10 @@ class Viewer extends EventEmitter2 {
5663
6052
  get components() {
5664
6053
  return [...components.getComponents().keys()];
5665
6054
  }
5666
- get markup() {
5667
- return this._markup;
5668
- }
5669
6055
  initialize(canvas, onProgress) {
5670
6056
  this.addEventListener("optionschange", (event) => this.syncOptions(event.data));
5671
6057
  this.scene = new Scene();
5672
6058
  this.helpers = new Helpers();
5673
- this.target = new Vector3(0, 0, 0);
5674
6059
  const pixelRatio = window.devicePixelRatio;
5675
6060
  const rect = canvas.parentElement.getBoundingClientRect();
5676
6061
  const width = rect.width || 1;
@@ -5821,21 +6206,25 @@ class Viewer extends EventEmitter2 {
5821
6206
  async open(file, params = {}) {
5822
6207
  if (!this.renderer)
5823
6208
  return this;
5824
- if (params.mode !== "a" && params.mode !== "append") {
6209
+ const mode = params.mode || "file";
6210
+ if (mode !== "assembly" && mode !== "a" && mode !== "append") {
5825
6211
  this.cancel();
5826
6212
  this.clear();
5827
6213
  }
5828
- this.emitEvent({ type: "open", file });
6214
+ this.emitEvent({ type: "open", mode, file });
5829
6215
  let model = file;
5830
6216
  if (model && typeof model.getModels === "function") {
5831
6217
  const models = await model.getModels();
5832
6218
  model = models.find((model) => model.default) || models[0] || file;
5833
6219
  }
6220
+ if (model && typeof model.database === "string") {
6221
+ file = model.file;
6222
+ }
5834
6223
  if (!model)
5835
6224
  throw new Error(`Format not supported`);
5836
6225
  let format = params.format;
5837
- if (!format && typeof model.type === "string")
5838
- format = model.type.split(".").pop();
6226
+ if (!format && typeof file["type"] === "string")
6227
+ format = file["type"].split(".").pop();
5839
6228
  if (!format && typeof file === "string")
5840
6229
  format = file.split(".").pop();
5841
6230
  if (!format && file instanceof globalThis.File)
@@ -5862,7 +6251,7 @@ class Viewer extends EventEmitter2 {
5862
6251
  }
5863
6252
  loadGltfFile(file, externalFiles, params = {}) {
5864
6253
  console.warn("Viewer.loadGltfFile() has been deprecated since 26.4 and will be removed in a future release, use Viewer.open() instead.");
5865
- return this.open(file, { ...params, format: "gltf", externalFiles, mode: "append" });
6254
+ return this.open(file, { ...params, format: "gltf", externalFiles, mode: "assembly" });
5866
6255
  }
5867
6256
  cancel() {
5868
6257
  this.loaders.forEach((loader) => loader.cancel());
@@ -5882,12 +6271,17 @@ class Viewer extends EventEmitter2 {
5882
6271
  this.models = [];
5883
6272
  this.scene.clear();
5884
6273
  this.helpers.clear();
6274
+ this.extents.makeEmpty();
6275
+ this.target.set(0, 0, 0);
5885
6276
  this.syncOptions();
5886
6277
  this.syncOverlay();
5887
6278
  this.update(true);
5888
6279
  this.emitEvent({ type: "clear" });
5889
6280
  return this;
5890
6281
  }
6282
+ is3D() {
6283
+ return true;
6284
+ }
5891
6285
  syncOptions(options = this.options) {
5892
6286
  if (!this.renderer)
5893
6287
  return;
@@ -5919,9 +6313,15 @@ class Viewer extends EventEmitter2 {
5919
6313
  getSelected() {
5920
6314
  return this.executeCommand("getSelected");
5921
6315
  }
6316
+ getSelected2() {
6317
+ return this.executeCommand("getSelected2");
6318
+ }
5922
6319
  setSelected(handles) {
5923
6320
  this.executeCommand("setSelected", handles);
5924
6321
  }
6322
+ setSelected2(handles) {
6323
+ this.executeCommand("setSelected2", handles);
6324
+ }
5925
6325
  clearSelected() {
5926
6326
  this.executeCommand("clearSelected");
5927
6327
  }
@@ -5981,37 +6381,8 @@ class Viewer extends EventEmitter2 {
5981
6381
  getComponent(name) {
5982
6382
  return this._components.find((component) => component.name === name);
5983
6383
  }
5984
- is3D() {
5985
- return true;
5986
- }
5987
- screenToWorld(position) {
5988
- if (!this.renderer)
5989
- return { x: position.x, y: position.y, z: 0 };
5990
- const rect = this.canvas.getBoundingClientRect();
5991
- const x = position.x / (rect.width / 2) - 1;
5992
- const y = -position.y / (rect.height / 2) + 1;
5993
- const point = new Vector3(x, y, -1);
5994
- point.unproject(this.camera);
5995
- return { x: point.x, y: point.y, z: point.z };
5996
- }
5997
- worldToScreen(position) {
5998
- if (!this.renderer)
5999
- return { x: position.x, y: position.y };
6000
- const point = new Vector3(position.x, position.y, position.z);
6001
- point.project(this.camera);
6002
- const rect = this.canvas.getBoundingClientRect();
6003
- const x = (point.x + 1) * (rect.width / 2);
6004
- const y = (-point.y + 1) * (rect.height / 2);
6005
- return { x, y };
6006
- }
6007
- getScale() {
6008
- return { x: 1, y: 1, z: 1 };
6009
- }
6010
- executeCommand(id, ...args) {
6011
- return commands.executeCommand(id, this, ...args);
6012
- }
6013
6384
  drawViewpoint(viewpoint) {
6014
- var _a, _b, _c;
6385
+ var _a, _b, _c, _d;
6015
6386
  if (!this.renderer)
6016
6387
  return;
6017
6388
  const getVector3FromPoint3d = ({ x, y, z }) => new Vector3(x, y, z);
@@ -6063,11 +6434,13 @@ class Viewer extends EventEmitter2 {
6063
6434
  }
6064
6435
  };
6065
6436
  const setClippingPlanes = (clipping_planes) => {
6066
- clipping_planes === null || clipping_planes === void 0 ? void 0 : clipping_planes.forEach((clipping_plane) => {
6067
- const plane = new Plane();
6068
- plane.setFromNormalAndCoplanarPoint(getVector3FromPoint3d(clipping_plane.direction), getVector3FromPoint3d(clipping_plane.location));
6069
- this.renderer.clippingPlanes.push(plane);
6070
- });
6437
+ if (clipping_planes) {
6438
+ clipping_planes.forEach((clipping_plane) => {
6439
+ const plane = new Plane();
6440
+ plane.setFromNormalAndCoplanarPoint(getVector3FromPoint3d(clipping_plane.direction), getVector3FromPoint3d(clipping_plane.location));
6441
+ this.renderer.clippingPlanes.push(plane);
6442
+ });
6443
+ }
6071
6444
  };
6072
6445
  const setSelection = (selection) => {
6073
6446
  if (selection)
@@ -6083,9 +6456,9 @@ class Viewer extends EventEmitter2 {
6083
6456
  setOrthogonalCamera(viewpoint.orthogonal_camera);
6084
6457
  setPerspectiveCamera(viewpoint.perspective_camera);
6085
6458
  setClippingPlanes(viewpoint.clipping_planes);
6086
- setSelection(viewpoint.selection);
6459
+ setSelection(((_b = viewpoint.custom_fields) === null || _b === void 0 ? void 0 : _b.selection2) || viewpoint.selection);
6087
6460
  this._markup.setViewpoint(viewpoint);
6088
- this.target = getVector3FromPoint3d((_c = (_b = viewpoint.custom_fields) === null || _b === void 0 ? void 0 : _b.camera_target) !== null && _c !== void 0 ? _c : this.target);
6461
+ 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));
6089
6462
  this.setActiveDragger(draggerName);
6090
6463
  this.emitEvent({ type: "drawviewpoint", data: viewpoint });
6091
6464
  this.update();
@@ -6132,6 +6505,9 @@ class Viewer extends EventEmitter2 {
6132
6505
  const getSelection = () => {
6133
6506
  return this.getSelected().map((handle) => ({ handle }));
6134
6507
  };
6508
+ const getSelection2 = () => {
6509
+ return this.getSelected2().map((handle) => ({ handle }));
6510
+ };
6135
6511
  const viewpoint = { custom_fields: {} };
6136
6512
  viewpoint.orthogonal_camera = getOrthogonalCamera();
6137
6513
  viewpoint.perspective_camera = getPerspectiveCamera();
@@ -6140,9 +6516,36 @@ class Viewer extends EventEmitter2 {
6140
6516
  viewpoint.description = new Date().toDateString();
6141
6517
  this._markup.getViewpoint(viewpoint);
6142
6518
  viewpoint.custom_fields.camera_target = getPoint3dFromVector3(this.target);
6519
+ viewpoint.custom_fields.selection2 = getSelection2();
6143
6520
  this.emitEvent({ type: "createviewpoint", data: viewpoint });
6144
6521
  return viewpoint;
6145
6522
  }
6523
+ screenToWorld(position) {
6524
+ if (!this.renderer)
6525
+ return { x: position.x, y: position.y, z: 0 };
6526
+ const rect = this.canvas.getBoundingClientRect();
6527
+ const x = position.x / (rect.width / 2) - 1;
6528
+ const y = -position.y / (rect.height / 2) + 1;
6529
+ const point = new Vector3(x, y, -1);
6530
+ point.unproject(this.camera);
6531
+ return { x: point.x, y: point.y, z: point.z };
6532
+ }
6533
+ worldToScreen(position) {
6534
+ if (!this.renderer)
6535
+ return { x: position.x, y: position.y };
6536
+ const point = new Vector3(position.x, position.y, position.z);
6537
+ point.project(this.camera);
6538
+ const rect = this.canvas.getBoundingClientRect();
6539
+ const x = (point.x + 1) * (rect.width / 2);
6540
+ const y = (-point.y + 1) * (rect.height / 2);
6541
+ return { x, y };
6542
+ }
6543
+ getScale() {
6544
+ return { x: 1, y: 1, z: 1 };
6545
+ }
6546
+ executeCommand(id, ...args) {
6547
+ return commands.executeCommand(id, this, ...args);
6548
+ }
6146
6549
  }
6147
6550
 
6148
6551
  export { GLTFLoadingManager, ModelImpl, Viewer, commands, components, draggers, loaders };