@inweb/viewer-three 26.10.6 → 26.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/README.md +7 -4
  2. package/dist/{plugins → extensions}/components/AxesHelperComponent.js +23 -1
  3. package/dist/extensions/components/AxesHelperComponent.js.map +1 -0
  4. package/dist/extensions/components/AxesHelperComponent.min.js +24 -0
  5. package/dist/{plugins → extensions}/components/AxesHelperComponent.module.js +24 -2
  6. package/dist/extensions/components/AxesHelperComponent.module.js.map +1 -0
  7. package/dist/{plugins → extensions}/components/ExtentsHelperComponent.js +18 -0
  8. package/dist/extensions/components/ExtentsHelperComponent.js.map +1 -0
  9. package/dist/{plugins/components/AxesHelperComponent.min.js → extensions/components/ExtentsHelperComponent.min.js} +1 -1
  10. package/dist/{plugins → extensions}/components/ExtentsHelperComponent.module.js +19 -1
  11. package/dist/extensions/components/ExtentsHelperComponent.module.js.map +1 -0
  12. package/dist/extensions/components/GridHelperComponent.js.map +1 -0
  13. package/dist/extensions/components/GridHelperComponent.module.js.map +1 -0
  14. package/dist/extensions/components/InfoPanelComponent.js +170 -0
  15. package/dist/extensions/components/InfoPanelComponent.js.map +1 -0
  16. package/dist/extensions/components/InfoPanelComponent.min.js +24 -0
  17. package/dist/extensions/components/InfoPanelComponent.module.js +164 -0
  18. package/dist/extensions/components/InfoPanelComponent.module.js.map +1 -0
  19. package/dist/extensions/components/LightHelperComponent.js.map +1 -0
  20. package/dist/extensions/components/LightHelperComponent.module.js.map +1 -0
  21. package/dist/extensions/components/RoomEnvironmentComponent.js.map +1 -0
  22. package/dist/extensions/components/RoomEnvironmentComponent.module.js.map +1 -0
  23. package/dist/{plugins → extensions}/components/StatsPanelComponent.js +9 -3
  24. package/dist/extensions/components/StatsPanelComponent.js.map +1 -0
  25. package/dist/extensions/components/StatsPanelComponent.min.js +24 -0
  26. package/dist/{plugins → extensions}/components/StatsPanelComponent.module.js +9 -3
  27. package/dist/extensions/components/StatsPanelComponent.module.js.map +1 -0
  28. package/dist/{plugins → extensions}/loaders/GLTFCloudLoader.js +2 -3
  29. package/dist/extensions/loaders/GLTFCloudLoader.js.map +1 -0
  30. package/dist/{plugins → extensions}/loaders/GLTFCloudLoader.min.js +1 -1
  31. package/dist/{plugins → extensions}/loaders/GLTFCloudLoader.module.js +2 -3
  32. package/dist/extensions/loaders/GLTFCloudLoader.module.js.map +1 -0
  33. package/dist/extensions/loaders/GLTFFileLoader.js +2499 -0
  34. package/dist/extensions/loaders/GLTFFileLoader.js.map +1 -0
  35. package/dist/extensions/loaders/GLTFFileLoader.min.js +24 -0
  36. package/dist/extensions/loaders/GLTFFileLoader.module.js +74 -0
  37. package/dist/extensions/loaders/GLTFFileLoader.module.js.map +1 -0
  38. package/dist/{plugins → extensions}/loaders/IFCXLoader.js +5 -7
  39. package/dist/extensions/loaders/IFCXLoader.js.map +1 -0
  40. package/dist/{plugins → extensions}/loaders/IFCXLoader.min.js +1 -1
  41. package/dist/{plugins → extensions}/loaders/IFCXLoader.module.js +5 -7
  42. package/dist/extensions/loaders/IFCXLoader.module.js.map +1 -0
  43. package/dist/{plugins → extensions}/loaders/PotreeLoader.js +56 -6
  44. package/dist/extensions/loaders/PotreeLoader.js.map +1 -0
  45. package/dist/extensions/loaders/PotreeLoader.min.js +24 -0
  46. package/dist/{plugins → extensions}/loaders/PotreeLoader.module.js +53 -2
  47. package/dist/extensions/loaders/PotreeLoader.module.js.map +1 -0
  48. package/dist/viewer-three.js +1416 -2930
  49. package/dist/viewer-three.js.map +1 -1
  50. package/dist/viewer-three.min.js +8 -3
  51. package/dist/viewer-three.module.js +1205 -363
  52. package/dist/viewer-three.module.js.map +1 -1
  53. package/{plugins → extensions}/components/AxesHelperComponent.ts +31 -2
  54. package/{plugins → extensions}/components/ExtentsHelperComponent.ts +25 -0
  55. package/extensions/components/InfoPanelComponent.ts +197 -0
  56. package/{plugins → extensions}/components/StatsPanelComponent.ts +10 -3
  57. package/{plugins → extensions}/loaders/GLTFCloudLoader.ts +2 -3
  58. package/{src/Viewer → extensions}/loaders/GLTFFileLoader.ts +21 -12
  59. package/{plugins → extensions}/loaders/IFCX/IFCXCloudLoader.ts +5 -5
  60. package/{plugins → extensions}/loaders/IFCX/IFCXFileLoader.ts +3 -4
  61. package/{plugins → extensions}/loaders/Potree/PotreeFileLoader.ts +3 -4
  62. package/extensions/loaders/Potree/PotreeModelImpl.ts +108 -0
  63. package/lib/Viewer/Viewer.d.ts +28 -20
  64. package/lib/Viewer/commands/GetSelected2.d.ts +2 -0
  65. package/lib/Viewer/commands/SelectModel.d.ts +1 -1
  66. package/lib/Viewer/commands/SetSelected2.d.ts +2 -0
  67. package/lib/Viewer/components/InfoComponent.d.ts +22 -0
  68. package/lib/Viewer/components/SelectionComponent.d.ts +1 -3
  69. package/lib/Viewer/components/index.d.ts +6 -6
  70. package/lib/Viewer/draggers/MeasureLineDragger.d.ts +7 -1
  71. package/lib/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.d.ts +2 -1
  72. package/lib/Viewer/loaders/GLTFBinaryExtension.d.ts +5 -0
  73. package/lib/Viewer/loaders/GLTFCloudDynamicLoader.d.ts +2 -2
  74. package/lib/Viewer/loaders/{GLTFFileLoader.d.ts → GLTFFileDynamicLoader.d.ts} +7 -1
  75. package/lib/Viewer/loaders/GLTFLoadingManager.d.ts +4 -3
  76. package/lib/Viewer/loaders/RangesLoader.d.ts +15 -0
  77. package/lib/Viewer/loaders/index.d.ts +22 -14
  78. package/lib/Viewer/measurement/Snapper.d.ts +15 -0
  79. package/lib/Viewer/measurement/UnitConverter.d.ts +63 -0
  80. package/lib/Viewer/measurement/UnitFormatter.d.ts +4 -0
  81. package/lib/Viewer/models/IModelImpl.d.ts +11 -8
  82. package/lib/Viewer/models/ModelImpl.d.ts +9 -5
  83. package/package.json +11 -11
  84. package/src/Viewer/Viewer.ts +127 -88
  85. package/src/Viewer/commands/ClearSelected.ts +3 -1
  86. package/src/Viewer/commands/GetModels.ts +1 -1
  87. package/src/Viewer/commands/GetSelected.ts +2 -2
  88. package/{plugins/loaders/Potree/PotreeModelImpl.ts → src/Viewer/commands/GetSelected2.ts} +7 -9
  89. package/src/Viewer/commands/HideSelected.ts +3 -1
  90. package/src/Viewer/commands/SelectModel.ts +5 -5
  91. package/src/Viewer/commands/SetSelected.ts +9 -10
  92. package/src/Viewer/commands/SetSelected2.ts +42 -0
  93. package/src/Viewer/commands/ZoomToObjects.ts +5 -6
  94. package/src/Viewer/commands/ZoomToSelected.ts +3 -1
  95. package/src/Viewer/commands/index.ts +4 -0
  96. package/src/Viewer/components/CameraComponent.ts +6 -1
  97. package/src/Viewer/components/ExtentsComponent.ts +4 -1
  98. package/src/Viewer/components/InfoComponent.ts +187 -0
  99. package/src/Viewer/components/SelectionComponent.ts +7 -30
  100. package/src/Viewer/components/index.ts +8 -6
  101. package/src/Viewer/draggers/MeasureLineDragger.ts +84 -226
  102. package/src/Viewer/loaders/DynamicGltfLoader/DynamicGltfLoader.js +276 -39
  103. package/src/Viewer/loaders/DynamicGltfLoader/DynamicModelImpl.ts +45 -10
  104. package/src/Viewer/loaders/DynamicGltfLoader/GltfStructure.js +71 -2
  105. package/src/Viewer/loaders/GLTFBinaryExtension.ts +91 -0
  106. package/src/Viewer/loaders/GLTFCloudDynamicLoader.ts +13 -19
  107. package/src/Viewer/loaders/GLTFFileDynamicLoader.ts +145 -0
  108. package/src/Viewer/loaders/GLTFLoadingManager.ts +5 -4
  109. package/src/Viewer/loaders/RangesLoader.ts +105 -0
  110. package/src/Viewer/loaders/index.ts +24 -16
  111. package/src/Viewer/measurement/Snapper.ts +208 -0
  112. package/src/Viewer/measurement/UnitConverter.ts +47 -0
  113. package/src/Viewer/measurement/UnitFormatter.ts +95 -0
  114. package/src/Viewer/models/IModelImpl.ts +17 -8
  115. package/src/Viewer/models/ModelImpl.ts +205 -16
  116. package/src/index-umd.ts +1 -1
  117. package/dist/plugins/components/AxesHelperComponent.js.map +0 -1
  118. package/dist/plugins/components/AxesHelperComponent.module.js.map +0 -1
  119. package/dist/plugins/components/ExtentsHelperComponent.js.map +0 -1
  120. package/dist/plugins/components/ExtentsHelperComponent.min.js +0 -24
  121. package/dist/plugins/components/ExtentsHelperComponent.module.js.map +0 -1
  122. package/dist/plugins/components/GridHelperComponent.js.map +0 -1
  123. package/dist/plugins/components/GridHelperComponent.module.js.map +0 -1
  124. package/dist/plugins/components/LightHelperComponent.js.map +0 -1
  125. package/dist/plugins/components/LightHelperComponent.module.js.map +0 -1
  126. package/dist/plugins/components/RoomEnvironmentComponent.js.map +0 -1
  127. package/dist/plugins/components/RoomEnvironmentComponent.module.js.map +0 -1
  128. package/dist/plugins/components/StatsPanelComponent.js.map +0 -1
  129. package/dist/plugins/components/StatsPanelComponent.min.js +0 -24
  130. package/dist/plugins/components/StatsPanelComponent.module.js.map +0 -1
  131. package/dist/plugins/loaders/GLTFCloudLoader.js.map +0 -1
  132. package/dist/plugins/loaders/GLTFCloudLoader.module.js.map +0 -1
  133. package/dist/plugins/loaders/IFCXLoader.js.map +0 -1
  134. package/dist/plugins/loaders/IFCXLoader.module.js.map +0 -1
  135. package/dist/plugins/loaders/PotreeLoader.js.map +0 -1
  136. package/dist/plugins/loaders/PotreeLoader.min.js +0 -24
  137. package/dist/plugins/loaders/PotreeLoader.module.js.map +0 -1
  138. /package/dist/{plugins → extensions}/components/GridHelperComponent.js +0 -0
  139. /package/dist/{plugins → extensions}/components/GridHelperComponent.min.js +0 -0
  140. /package/dist/{plugins → extensions}/components/GridHelperComponent.module.js +0 -0
  141. /package/dist/{plugins → extensions}/components/LightHelperComponent.js +0 -0
  142. /package/dist/{plugins → extensions}/components/LightHelperComponent.min.js +0 -0
  143. /package/dist/{plugins → extensions}/components/LightHelperComponent.module.js +0 -0
  144. /package/dist/{plugins → extensions}/components/RoomEnvironmentComponent.js +0 -0
  145. /package/dist/{plugins → extensions}/components/RoomEnvironmentComponent.min.js +0 -0
  146. /package/dist/{plugins → extensions}/components/RoomEnvironmentComponent.module.js +0 -0
  147. /package/{plugins → extensions}/components/GridHelperComponent.ts +0 -0
  148. /package/{plugins → extensions}/components/LightHelperComponent.ts +0 -0
  149. /package/{plugins → extensions}/components/RoomEnvironmentComponent.ts +0 -0
  150. /package/{plugins → extensions}/loaders/IFCX/IFCXLoader.ts +0 -0
  151. /package/{plugins → extensions}/loaders/IFCX/index.ts +0 -0
  152. /package/{plugins → extensions}/loaders/IFCX/render.js +0 -0
  153. /package/{plugins → extensions}/loaders/Potree/index.ts +0 -0
@@ -50,9 +50,11 @@ import {
50
50
  FileSource,
51
51
  IClippingPlane,
52
52
  IComponent,
53
- IEntity,
54
53
  IDragger,
54
+ IEntity,
55
+ IInfo,
55
56
  ILoader,
57
+ Info,
56
58
  IOptions,
57
59
  IOrthogonalCamera,
58
60
  IPerspectiveCamera,
@@ -80,12 +82,14 @@ export class Viewer
80
82
  implements IViewer, IWorldTransform
81
83
  {
82
84
  public client: Client | undefined;
83
- protected _options: Options;
84
-
85
- private canvaseventlistener: (event: any) => void;
86
-
85
+ public options: IOptions;
87
86
  public canvas: HTMLCanvasElement | undefined;
88
87
  public canvasEvents: string[];
88
+ public loaders: ILoader[];
89
+ public models: IModelImpl[];
90
+ public info: IInfo;
91
+
92
+ private canvaseventlistener: (event: any) => void;
89
93
 
90
94
  public scene: Scene | undefined;
91
95
  public helpers: Helpers | undefined;
@@ -98,9 +102,6 @@ export class Viewer
98
102
  public ssaaRenderPass: SSAARenderPass | undefined;
99
103
  public outputPass: OutputPass | undefined;
100
104
  public composer: EffectComposer | undefined;
101
-
102
- public loaders: ILoader[];
103
- public models: IModelImpl[];
104
105
  public selected: Object3D[];
105
106
  public extents: Box3;
106
107
  public target: Vector3;
@@ -120,22 +121,23 @@ export class Viewer
120
121
  */
121
122
  constructor(client?: Client) {
122
123
  super();
123
- this._options = new Options(this);
124
-
125
124
  this.client = client;
125
+ this.options = new Options(this);
126
+ this.loaders = [];
127
+ this.models = [];
128
+ this.info = new Info();
126
129
 
127
- this.canvasEvents = CANVAS_EVENTS;
130
+ this.canvasEvents = CANVAS_EVENTS.slice();
128
131
  this.canvaseventlistener = (event: Event) => this.emit(event);
129
132
 
130
- this.loaders = [];
131
- this.models = [];
132
133
  this.selected = [];
133
134
  this.extents = new Box3();
134
- this.target = new Vector3();
135
+ this.target = new Vector3(0, 0, 0);
135
136
 
136
137
  this._activeDragger = null;
137
138
  this._components = [];
138
139
 
140
+ this._renderNeeded = false;
139
141
  this._renderTime = 0;
140
142
 
141
143
  this.render = this.render.bind(this);
@@ -144,10 +146,17 @@ export class Viewer
144
146
  this._markup = new Markup();
145
147
  }
146
148
 
147
- get options(): IOptions {
148
- return this._options;
149
+ /**
150
+ * 2D markup core instance used to create markups.
151
+ *
152
+ * @readonly
153
+ */
154
+ get markup(): IMarkup {
155
+ return this._markup;
149
156
  }
150
157
 
158
+ // IViewer
159
+
151
160
  get draggers(): string[] {
152
161
  return [...draggers.getDraggers().keys()];
153
162
  }
@@ -156,21 +165,11 @@ export class Viewer
156
165
  return [...components.getComponents().keys()];
157
166
  }
158
167
 
159
- /**
160
- * 2D markup core instance used to create markups.
161
- *
162
- * @readonly
163
- */
164
- get markup(): IMarkup {
165
- return this._markup;
166
- }
167
-
168
168
  initialize(canvas: HTMLCanvasElement, onProgress?: (event: ProgressEvent<EventTarget>) => void): Promise<this> {
169
169
  this.addEventListener("optionschange", (event) => this.syncOptions(event.data));
170
170
 
171
171
  this.scene = new Scene();
172
172
  this.helpers = new Helpers();
173
- this.target = new Vector3(0, 0, 0);
174
173
 
175
174
  const pixelRatio = window.devicePixelRatio;
176
175
  const rect = canvas.parentElement.getBoundingClientRect();
@@ -319,6 +318,8 @@ export class Viewer
319
318
  this.emitEvent({ type: "update", data: force });
320
319
  }
321
320
 
321
+ // Internal render routines
322
+
322
323
  render(time?: DOMHighResTimeStamp, force = false): void {
323
324
  if (!this.renderer) return;
324
325
  if (!this._renderNeeded && !force) return;
@@ -329,6 +330,9 @@ export class Viewer
329
330
  this._renderTime = time;
330
331
  this._renderNeeded = false;
331
332
 
333
+ this.renderer.info.autoReset = false;
334
+ this.renderer.info.reset();
335
+
332
336
  if (this.options.antialiasing === true || this.options.antialiasing === "msaa") {
333
337
  this.renderer.render(this.scene, this.camera);
334
338
  this.renderer.render(this.helpers, this.camera);
@@ -341,6 +345,8 @@ export class Viewer
341
345
  this.emitEvent({ type: "render", time, deltaTime });
342
346
  }
343
347
 
348
+ // Internal loading routines
349
+
344
350
  loadReferences(model: Model | File | Assembly): Promise<this> {
345
351
  // todo: load reference as text fonts
346
352
  return Promise.resolve(this);
@@ -357,18 +363,20 @@ export class Viewer
357
363
  * exception will be thrown.
358
364
  *
359
365
  * For files from Open Cloud Server, the default model will be loaded. If there is no default model,
360
- * first availiable model will be loaded. If no models are found in the file, an exception will be
366
+ * the first available model will be loaded. If no models are found in the file, an exception will be
361
367
  * thrown.
362
368
  *
363
369
  * For URLs, the file extension is used to determine the file format. For a `ArrayBuffer` and `Data
364
- * URL`, a file format must be specified using `params.format` parameter (see below). If no appropriate
365
- * loader is found for the specified format, an exception will be thrown.
370
+ * URL`, a file format must be specified using `params.format` parameter. If no appropriate loader is
371
+ * found for the specified format, an exception will be thrown.
366
372
  *
367
373
  * If there was an active dragger before opening the file, it will be deactivated. After opening the
368
374
  * file, you must manually activate the required dragger.
369
375
  *
370
376
  * Fires:
371
377
  *
378
+ * - {@link CancelEvent | cancel}
379
+ * - {@link ClearEvent | clear}
372
380
  * - {@link OpenEvent | open}
373
381
  * - {@link GeometryStartEvent | geometrystart}
374
382
  * - {@link GeometryProgressEvent | geometryprogress}
@@ -377,23 +385,26 @@ export class Viewer
377
385
  * - {@link GeometryEndEvent | geometryend}
378
386
  * - {@link GeometryErrorEvent | geometryerror}
379
387
  *
380
- * @param file - File to load. Can be one of:
388
+ * @param file - File to load. Can be:
381
389
  *
382
390
  * - `File`, `Assembly` or `Model` instance from the Open Cloud Server
383
- * - File `URL` string
391
+ * - `URL` string
384
392
  * - {@link https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/Data_URIs | Data URL} string
385
393
  * - {@link https://developer.mozilla.org/docs/Web/API/File | Web API File} object
386
394
  * - {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer | ArrayBuffer}
387
395
  * object
388
396
  *
389
397
  * @param params - Loading parameters.
390
- * @param params.format - File format string. Required when loading a file as `ArrayBuffer` or `Data
391
- * URL`.
398
+ * @param params.format - File format. Can be `gltf`, `glb` or format from an extension. Required when
399
+ * loading a file as `ArrayBuffer` or `Data URL`.
392
400
  * @param params.mode - File opening mode. Can be one of:
393
401
  *
394
- * - `open` - Unloads an open file and opens a new one. This is default mode.
395
- * - `append` - Appends a file to an already open file.
402
+ * - `file` - Single file mode. Unloads an open file and opens a new one. This is default mode.
403
+ * - `assembly` - Assembly mode. Appends a file to an already open file.
396
404
  *
405
+ * @param params.modelId - Unique model ID in the assembly (multi-model scene). Used as a model prefix
406
+ * when selecting objects (see {@link getSelected2}). Must not contain the ":" (colon). Required when
407
+ * loading a file as `ArrayBuffer` or `Data URL`.
397
408
  * @param params.requestHeader - The
398
409
  * {@link https://developer.mozilla.org/docs/Glossary/Request_header | request header} used in HTTP
399
410
  * request.
@@ -415,6 +426,7 @@ export class Viewer
415
426
  params: {
416
427
  format?: string;
417
428
  mode?: string;
429
+ modelId?: string;
418
430
  requestHeader?: HeadersInit;
419
431
  withCredentials?: boolean;
420
432
  path?: string;
@@ -424,22 +436,27 @@ export class Viewer
424
436
  ): Promise<this> {
425
437
  if (!this.renderer) return this;
426
438
 
427
- if (params.mode !== "a" && params.mode !== "append") {
439
+ const mode = params.mode || "file";
440
+
441
+ if (mode !== "assembly" && mode !== "a" && mode !== "append") {
428
442
  this.cancel();
429
443
  this.clear();
430
444
  }
431
445
 
432
- this.emitEvent({ type: "open", file });
446
+ this.emitEvent({ type: "open", mode, file });
433
447
 
434
448
  let model: any = file;
435
449
  if (model && typeof model.getModels === "function") {
436
450
  const models = await model.getModels();
437
451
  model = models.find((model: Model) => model.default) || models[0] || file;
438
452
  }
453
+ if (model && typeof model.database === "string") {
454
+ file = model.file;
455
+ }
439
456
  if (!model) throw new Error(`Format not supported`);
440
457
 
441
458
  let format = params.format;
442
- if (!format && typeof model.type === "string") format = model.type.split(".").pop();
459
+ if (!format && typeof file["type"] === "string") format = file["type"].split(".").pop();
443
460
  if (!format && typeof file === "string") format = file.split(".").pop();
444
461
  if (!format && file instanceof globalThis.File) format = file.name.split(".").pop();
445
462
 
@@ -483,7 +500,7 @@ export class Viewer
483
500
  "Viewer.loadGltfFile() has been deprecated since 26.4 and will be removed in a future release, use Viewer.open() instead."
484
501
  );
485
502
 
486
- return this.open(file, { ...params, format: "gltf", externalFiles, mode: "append" });
503
+ return this.open(file, { ...params, format: "gltf", externalFiles, mode: "assembly" });
487
504
  }
488
505
 
489
506
  cancel(): this {
@@ -510,6 +527,9 @@ export class Viewer
510
527
  this.scene.clear();
511
528
  this.helpers.clear();
512
529
 
530
+ this.extents.makeEmpty();
531
+ this.target.set(0, 0, 0);
532
+
513
533
  this.syncOptions();
514
534
  this.syncOverlay();
515
535
  this.update(true);
@@ -519,6 +539,10 @@ export class Viewer
519
539
  return this;
520
540
  }
521
541
 
542
+ is3D(): boolean {
543
+ return true;
544
+ }
545
+
522
546
  syncOptions(options: IOptions = this.options): void {
523
547
  if (!this.renderer) return;
524
548
 
@@ -557,10 +581,18 @@ export class Viewer
557
581
  return this.executeCommand("getSelected");
558
582
  }
559
583
 
584
+ getSelected2(): string[] {
585
+ return this.executeCommand("getSelected2");
586
+ }
587
+
560
588
  setSelected(handles?: string[]): void {
561
589
  this.executeCommand("setSelected", handles);
562
590
  }
563
591
 
592
+ setSelected2(handles?: string[]): void {
593
+ this.executeCommand("setSelected2", handles);
594
+ }
595
+
564
596
  clearSelected(): void {
565
597
  this.executeCommand("clearSelected");
566
598
  }
@@ -630,44 +662,6 @@ export class Viewer
630
662
  return this._components.find((component) => component.name === name);
631
663
  }
632
664
 
633
- is3D(): boolean {
634
- return true;
635
- }
636
-
637
- screenToWorld(position: { x: number; y: number }): { x: number; y: number; z: number } {
638
- if (!this.renderer) return { x: position.x, y: position.y, z: 0 };
639
-
640
- const rect = this.canvas.getBoundingClientRect();
641
- const x = position.x / (rect.width / 2) - 1;
642
- const y = -position.y / (rect.height / 2) + 1;
643
-
644
- const point = new Vector3(x, y, -1);
645
- point.unproject(this.camera);
646
-
647
- return { x: point.x, y: point.y, z: point.z };
648
- }
649
-
650
- worldToScreen(position: { x: number; y: number; z: number }): { x: number; y: number } {
651
- if (!this.renderer) return { x: position.x, y: position.y };
652
-
653
- const point = new Vector3(position.x, position.y, position.z);
654
- point.project(this.camera);
655
-
656
- const rect = this.canvas.getBoundingClientRect();
657
- const x = (point.x + 1) * (rect.width / 2);
658
- const y = (-point.y + 1) * (rect.height / 2);
659
-
660
- return { x, y };
661
- }
662
-
663
- getScale(): { x: number; y: number; z: number } {
664
- return { x: 1, y: 1, z: 1 };
665
- }
666
-
667
- executeCommand(id: string, ...args: any[]): any {
668
- return commands.executeCommand(id, this, ...args);
669
- }
670
-
671
665
  drawViewpoint(viewpoint: IViewpoint): void {
672
666
  if (!this.renderer) return;
673
667
 
@@ -731,15 +725,17 @@ export class Viewer
731
725
  };
732
726
 
733
727
  const setClippingPlanes = (clipping_planes: IClippingPlane[]) => {
734
- clipping_planes?.forEach((clipping_plane) => {
735
- const plane = new Plane();
736
- plane.setFromNormalAndCoplanarPoint(
737
- getVector3FromPoint3d(clipping_plane.direction),
738
- getVector3FromPoint3d(clipping_plane.location)
739
- );
740
-
741
- this.renderer.clippingPlanes.push(plane);
742
- });
728
+ if (clipping_planes) {
729
+ clipping_planes.forEach((clipping_plane) => {
730
+ const plane = new Plane();
731
+ plane.setFromNormalAndCoplanarPoint(
732
+ getVector3FromPoint3d(clipping_plane.direction),
733
+ getVector3FromPoint3d(clipping_plane.location)
734
+ );
735
+
736
+ this.renderer.clippingPlanes.push(plane);
737
+ });
738
+ }
743
739
  };
744
740
 
745
741
  const setSelection = (selection: IEntity[]) => {
@@ -759,10 +755,10 @@ export class Viewer
759
755
  setOrthogonalCamera(viewpoint.orthogonal_camera);
760
756
  setPerspectiveCamera(viewpoint.perspective_camera);
761
757
  setClippingPlanes(viewpoint.clipping_planes);
762
- setSelection(viewpoint.selection);
758
+ setSelection(viewpoint.custom_fields?.selection2 || viewpoint.selection);
763
759
  this._markup.setViewpoint(viewpoint);
764
760
 
765
- this.target = getVector3FromPoint3d(viewpoint.custom_fields?.camera_target ?? this.target);
761
+ this.target.copy(getVector3FromPoint3d(viewpoint.custom_fields?.camera_target ?? this.target));
766
762
 
767
763
  this.setActiveDragger(draggerName);
768
764
  this.emitEvent({ type: "drawviewpoint", data: viewpoint });
@@ -814,6 +810,10 @@ export class Viewer
814
810
  return this.getSelected().map((handle) => ({ handle }));
815
811
  };
816
812
 
813
+ const getSelection2 = (): IEntity[] => {
814
+ return this.getSelected2().map((handle) => ({ handle }));
815
+ };
816
+
817
817
  const viewpoint: IViewpoint = { custom_fields: {} };
818
818
 
819
819
  viewpoint.orthogonal_camera = getOrthogonalCamera();
@@ -824,9 +824,48 @@ export class Viewer
824
824
  this._markup.getViewpoint(viewpoint);
825
825
 
826
826
  viewpoint.custom_fields.camera_target = getPoint3dFromVector3(this.target);
827
+ viewpoint.custom_fields.selection2 = getSelection2();
827
828
 
828
829
  this.emitEvent({ type: "createviewpoint", data: viewpoint });
829
830
 
830
831
  return viewpoint;
831
832
  }
833
+
834
+ // IWorldTransform
835
+
836
+ screenToWorld(position: { x: number; y: number }): { x: number; y: number; z: number } {
837
+ if (!this.renderer) return { x: position.x, y: position.y, z: 0 };
838
+
839
+ const rect = this.canvas.getBoundingClientRect();
840
+ const x = position.x / (rect.width / 2) - 1;
841
+ const y = -position.y / (rect.height / 2) + 1;
842
+
843
+ const point = new Vector3(x, y, -1);
844
+ point.unproject(this.camera);
845
+
846
+ return { x: point.x, y: point.y, z: point.z };
847
+ }
848
+
849
+ worldToScreen(position: { x: number; y: number; z: number }): { x: number; y: number } {
850
+ if (!this.renderer) return { x: position.x, y: position.y };
851
+
852
+ const point = new Vector3(position.x, position.y, position.z);
853
+ point.project(this.camera);
854
+
855
+ const rect = this.canvas.getBoundingClientRect();
856
+ const x = (point.x + 1) * (rect.width / 2);
857
+ const y = (-point.y + 1) * (rect.height / 2);
858
+
859
+ return { x, y };
860
+ }
861
+
862
+ getScale(): { x: number; y: number; z: number } {
863
+ return { x: 1, y: 1, z: 1 };
864
+ }
865
+
866
+ // ICommandService
867
+
868
+ executeCommand(id: string, ...args: any[]): any {
869
+ return commands.executeCommand(id, this, ...args);
870
+ }
832
871
  }
@@ -29,5 +29,7 @@ export function clearSelected(viewer: Viewer): void {
29
29
  selection.clearSelection();
30
30
 
31
31
  viewer.update();
32
- viewer.emitEvent({ type: "select", data: undefined, handles: [] });
32
+
33
+ viewer.emitEvent({ type: "select", handles: [] });
34
+ viewer.emitEvent({ type: "select2", handles: [] });
33
35
  }
@@ -24,5 +24,5 @@
24
24
  import type { Viewer } from "../Viewer";
25
25
 
26
26
  export function getModels(viewer: Viewer): string[] {
27
- return viewer.models.map((model) => model.handle);
27
+ return viewer.models.map((model) => model.id);
28
28
  }
@@ -24,7 +24,7 @@
24
24
  import type { Viewer } from "../Viewer";
25
25
 
26
26
  export function getSelected(viewer: Viewer): string[] {
27
- const handles = [];
28
- viewer.models.forEach((model) => handles.push(...model.getHandlesByObjects(viewer.selected)));
27
+ const handles2 = viewer.executeCommand("getSelected2");
28
+ const handles = handles2.map((handle) => handle.slice(handle.indexOf(":") + 1));
29
29
  return handles;
30
30
  }
@@ -21,16 +21,14 @@
21
21
  // acknowledge and accept the above terms.
22
22
  ///////////////////////////////////////////////////////////////////////////////
23
23
 
24
- import { Box3 } from "three";
25
- import { PointCloudOctree } from "potree-core";
26
- import { ModelImpl } from "@inweb/viewer-three";
24
+ import type { Viewer } from "../Viewer";
27
25
 
28
- // Potree model implementation.
26
+ export function getSelected2(viewer: Viewer): string[] {
27
+ const handles2 = [];
29
28
 
30
- export class PotreeModelImpl extends ModelImpl {
31
- public pco: PointCloudOctree;
29
+ viewer.models.forEach((model) => {
30
+ handles2.push(...model.getHandlesByObjects(viewer.selected));
31
+ });
32
32
 
33
- override getExtents(target: Box3): Box3 {
34
- return target.union(this.pco.pcoGeometry.boundingBox);
35
- }
33
+ return handles2;
36
34
  }
@@ -31,6 +31,8 @@ export function hideSelected(viewer: Viewer): void {
31
31
  selection.clearSelection();
32
32
 
33
33
  viewer.update();
34
+
34
35
  viewer.emitEvent({ type: "hide" });
35
- viewer.emitEvent({ type: "select", data: undefined, handles: [] });
36
+ viewer.emitEvent({ type: "select", handles: [] });
37
+ viewer.emitEvent({ type: "select2", handles: [] });
36
38
  }
@@ -24,14 +24,14 @@
24
24
  import type { Viewer } from "../Viewer";
25
25
  import type { SelectionComponent } from "../components/SelectionComponent";
26
26
 
27
- export function selectModel(viewer: Viewer, handle: string): void {
27
+ export function selectModel(viewer: Viewer, id: string): void {
28
28
  const selection = viewer.getComponent("SelectionComponent") as SelectionComponent;
29
29
  selection.clearSelection();
30
30
 
31
- viewer.models
32
- .filter((model) => model.handle === handle)
33
- .forEach((model) => selection.select(model.getObjects(), model));
31
+ viewer.models.filter((model) => model.id === id).forEach((model) => selection.select(model.getObjects(), model));
34
32
 
35
33
  viewer.update();
36
- viewer.emit({ type: "select", data: [] });
34
+
35
+ viewer.emitEvent({ type: "select", handles: viewer.getSelected() });
36
+ viewer.emitEvent({ type: "select2", handles: viewer.getSelected2() });
37
37
  }
@@ -22,19 +22,18 @@
22
22
  ///////////////////////////////////////////////////////////////////////////////
23
23
 
24
24
  import type { Viewer } from "../Viewer";
25
- import type { SelectionComponent } from "../components/SelectionComponent";
26
25
 
27
26
  export function setSelected(viewer: Viewer, handles: string[] = []): void {
28
- const selection = viewer.getComponent("SelectionComponent") as SelectionComponent;
29
- selection.clearSelection();
27
+ const handles2 = [];
30
28
 
31
- viewer.models.forEach((model) => {
32
- const objects = model.getObjectsByHandles(handles);
33
- model.showObjects(objects);
34
- selection.select(objects, model);
29
+ handles.forEach((handle) => {
30
+ if (handle.includes(":")) {
31
+ handles2.push(handle);
32
+ } else
33
+ viewer.models.forEach((model) => {
34
+ handles2.push(`${model.id}:${handle}`);
35
+ });
35
36
  });
36
37
 
37
- viewer.update();
38
- viewer.emitEvent({ type: "show" });
39
- viewer.emitEvent({ type: "select", data: undefined, handles });
38
+ viewer.executeCommand("setSelected2", handles2);
40
39
  }
@@ -0,0 +1,42 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2002-2025, Open Design Alliance (the "Alliance").
3
+ // All rights reserved.
4
+ //
5
+ // This software and its documentation and related materials are owned by
6
+ // the Alliance. The software may only be incorporated into application
7
+ // programs owned by members of the Alliance, subject to a signed
8
+ // Membership Agreement and Supplemental Software License Agreement with the
9
+ // Alliance. The structure and organization of this software are the valuable
10
+ // trade secrets of the Alliance and its suppliers. The software is also
11
+ // protected by copyright law and international treaty provisions. Application
12
+ // programs incorporating this software must include the following statement
13
+ // with their copyright notices:
14
+ //
15
+ // This application incorporates Open Design Alliance software pursuant to a
16
+ // license agreement with Open Design Alliance.
17
+ // Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance.
18
+ // All rights reserved.
19
+ //
20
+ // By use of this software, its documentation or related materials, you
21
+ // acknowledge and accept the above terms.
22
+ ///////////////////////////////////////////////////////////////////////////////
23
+
24
+ import type { Viewer } from "../Viewer";
25
+ import type { SelectionComponent } from "../components/SelectionComponent";
26
+
27
+ export function setSelected2(viewer: Viewer, handles: string[] = []): void {
28
+ const selectionComponent = viewer.getComponent("SelectionComponent") as SelectionComponent;
29
+ selectionComponent.clearSelection();
30
+
31
+ viewer.models.forEach((model) => {
32
+ const objects = model.getObjectsByHandles(handles);
33
+ model.showObjects(objects);
34
+ selectionComponent.select(objects, model);
35
+ });
36
+
37
+ viewer.update();
38
+
39
+ viewer.emitEvent({ type: "show" });
40
+ viewer.emitEvent({ type: "select", data: undefined, handles: viewer.getSelected() });
41
+ viewer.emitEvent({ type: "select2", data: undefined, handles });
42
+ }
@@ -26,13 +26,12 @@ import type { Viewer } from "../Viewer";
26
26
  import { zoomTo } from "./ZoomTo";
27
27
 
28
28
  export function zoomToObjects(viewer: Viewer, handles: string[] = []): void {
29
- const handleSet = new Set(handles);
30
- const objects = [];
31
- viewer.scene.traverseVisible((child) => {
32
- if (handleSet.has(child.userData?.handle)) objects.push(child);
33
- });
29
+ const extents = new Box3();
34
30
 
35
- const extents = objects.reduce((result: Box3, object) => result.expandByObject(object), new Box3());
31
+ viewer.models.forEach((model) => {
32
+ const objects = model.getObjectsByHandles(handles);
33
+ objects.forEach((object) => extents.expandByObject(object));
34
+ });
36
35
  if (extents.isEmpty()) extents.copy(viewer.extents);
37
36
 
38
37
  zoomTo(viewer, extents);
@@ -26,7 +26,9 @@ import type { Viewer } from "../Viewer";
26
26
  import { zoomTo } from "./ZoomTo";
27
27
 
28
28
  export function zoomToSelected(viewer: Viewer): void {
29
- const extents = viewer.selected.reduce((result: Box3, object) => result.expandByObject(object), new Box3());
29
+ const extents = new Box3();
30
+
31
+ viewer.selected.forEach((object) => extents.expandByObject(object));
30
32
  if (extents.isEmpty()) extents.copy(viewer.extents);
31
33
 
32
34
  zoomTo(viewer, extents);
@@ -32,6 +32,7 @@ import { explode, collect } from "./Explode";
32
32
  import { getDefaultViewPositions } from "./GetDefaultViewPositions";
33
33
  import { getModels } from "./GetModels";
34
34
  import { getSelected } from "./GetSelected";
35
+ import { getSelected2 } from "./GetSelected2";
35
36
  import { hideSelected } from "./HideSelected";
36
37
  import { isolateSelected } from "./IsolateSelected";
37
38
  import { regenerateAll } from "./RegenerateAll";
@@ -41,6 +42,7 @@ import { setActiveDragger } from "./SetActiveDragger";
41
42
  import { setDefaultViewPosition } from "./SetDefaultViewPosition";
42
43
  import { setMarkupColor } from "./SetMarkupColor";
43
44
  import { setSelected } from "./SetSelected";
45
+ import { setSelected2 } from "./SetSelected2";
44
46
  import { showAll } from "./ShowAll";
45
47
  import { zoomToExtents } from "./ZoomToExtents";
46
48
  import { zoomToObjects } from "./ZoomToObjects";
@@ -86,6 +88,7 @@ commands.registerCommand("explode", explode);
86
88
  commands.registerCommand("getDefaultViewPositions", getDefaultViewPositions);
87
89
  commands.registerCommand("getModels", getModels);
88
90
  commands.registerCommand("getSelected", getSelected);
91
+ commands.registerCommand("getSelected2", getSelected2);
89
92
  commands.registerCommand("hideSelected", hideSelected);
90
93
  commands.registerCommand("isolateSelected", isolateSelected);
91
94
  commands.registerCommand("regenerateAll", regenerateAll);
@@ -95,6 +98,7 @@ commands.registerCommand("setActiveDragger", setActiveDragger);
95
98
  commands.registerCommand("setDefaultViewPosition", setDefaultViewPosition);
96
99
  commands.registerCommand("setMarkupColor", setMarkupColor);
97
100
  commands.registerCommand("setSelected", setSelected);
101
+ commands.registerCommand("setSelected2", setSelected2);
98
102
  commands.registerCommand("showAll", showAll);
99
103
  commands.registerCommand("zoomToExtents", zoomToExtents);
100
104
  commands.registerCommand("zoomToObjects", zoomToObjects);
@@ -119,9 +119,14 @@ export class CameraComponent implements IComponent {
119
119
  };
120
120
 
121
121
  geometryEnd = () => {
122
+ // do not change the camera after opening the second file in assembly
123
+ if (this.viewer.models.length > 1) {
124
+ this.switchCamera(this.viewer.camera);
125
+ return;
126
+ }
127
+
122
128
  let camera: any;
123
129
 
124
- // TODO: do not change the camera and target after opening the second model in "append" mode
125
130
  this.viewer.scene.traverse((object: any) => {
126
131
  if (object.isCamera)
127
132
  if (!camera) camera = object;