@inweb/viewer-visualize 26.5.0 → 26.5.2

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 (34) hide show
  1. package/dist/viewer-visualize.js +18183 -18012
  2. package/dist/viewer-visualize.js.map +1 -1
  3. package/dist/viewer-visualize.min.js +1 -1
  4. package/dist/viewer-visualize.module.js +335 -297
  5. package/dist/viewer-visualize.module.js.map +1 -1
  6. package/lib/Viewer/Loaders/{UpdaterController.d.ts → UpdateController.d.ts} +1 -1
  7. package/lib/Viewer/Loaders/VSFBufferLoader.d.ts +8 -0
  8. package/lib/Viewer/Loaders/VSFModelLoader.d.ts +8 -0
  9. package/lib/Viewer/Loaders/VSFXBufferLoader.d.ts +8 -0
  10. package/lib/Viewer/Loaders/VSFXModelLoader.d.ts +8 -0
  11. package/lib/Viewer/Loaders/VSFXPartialLoader.d.ts +10 -0
  12. package/lib/Viewer/Loaders/VSFXStreamingLoader.d.ts +8 -0
  13. package/lib/Viewer/Loaders/index.d.ts +67 -0
  14. package/lib/Viewer/Viewer.d.ts +7 -59
  15. package/lib/index.d.ts +3 -2
  16. package/package.json +5 -5
  17. package/src/Viewer/Loaders/{BaseLoader.ts → UpdateController.ts} +30 -10
  18. package/src/Viewer/Loaders/{LoaderFactory.ts → VSFBufferLoader.ts} +28 -19
  19. package/src/Viewer/Loaders/VSFModelLoader.ts +87 -0
  20. package/src/Viewer/Loaders/VSFXBufferLoader.ts +58 -0
  21. package/src/Viewer/Loaders/{VsfXLoader.ts → VSFXModelLoader.ts} +35 -35
  22. package/src/Viewer/Loaders/{VsfXPartialLoader.ts → VSFXPartialLoader.ts} +55 -45
  23. package/src/Viewer/Loaders/{VsfXStreamingLoader.ts → VSFXStreamingLoader.ts} +35 -25
  24. package/src/Viewer/Loaders/index.ts +108 -0
  25. package/src/Viewer/Viewer.ts +53 -103
  26. package/src/index.ts +6 -3
  27. package/lib/Viewer/Loaders/BaseLoader.d.ts +0 -10
  28. package/lib/Viewer/Loaders/LoaderFactory.d.ts +0 -10
  29. package/lib/Viewer/Loaders/TCSLoader.d.ts +0 -4
  30. package/lib/Viewer/Loaders/VsfXLoader.d.ts +0 -4
  31. package/lib/Viewer/Loaders/VsfXPartialLoader.d.ts +0 -4
  32. package/lib/Viewer/Loaders/VsfXStreamingLoader.d.ts +0 -4
  33. package/src/Viewer/Loaders/TCSLoader.ts +0 -83
  34. package/src/Viewer/Loaders/UpdaterController.ts +0 -37
@@ -21,20 +21,37 @@
21
21
  // acknowledge and accept the above terms.
22
22
  ///////////////////////////////////////////////////////////////////////////////
23
23
 
24
- import { BaseLoader } from "./BaseLoader";
25
- import { UpdaterController, UpdateType } from "./UpdaterController";
24
+ import { Loader } from "@inweb/viewer-core";
25
+ import { Viewer } from "../Viewer";
26
+ import { UpdateController, UpdateType } from "./UpdateController";
26
27
 
27
28
  const PENDING_REQUESTS_SIZE = 50;
28
29
  const PENDING_REQUESTS_TIMEOUT = 250;
29
30
 
30
- export class VsfXPartialLoader extends BaseLoader {
31
- override async load(): Promise<void> {
32
- if (!this.viewer.visualizeJs) return;
31
+ export class VSFXPartialLoader extends Loader {
32
+ public viewer: Viewer;
33
+ public abortControllerForRequestMap: Map<number, AbortController>;
34
+
35
+ constructor(viewer: Viewer) {
36
+ super();
37
+ this.viewer = viewer;
38
+ this.abortControllerForRequestMap = new Map();
39
+ }
40
+
41
+ override isSupport(file: any): boolean {
42
+ return (
43
+ typeof file === "object" &&
44
+ typeof file.downloadResource === "function" &&
45
+ /.vsfx$/i.test(file.database) &&
46
+ (this.viewer.options.enablePartialMode === true || /.rcs$/i.test(file.name))
47
+ );
48
+ }
49
+
50
+ override async load(model: any, format?: string): Promise<this> {
51
+ if (!this.viewer.visualizeJs) return this;
33
52
 
34
53
  const visLib = this.viewer.visLib();
35
54
  const visViewer = visLib.getViewer();
36
- const abortController = new AbortController();
37
- const abortControllerForRequestMap = new Map();
38
55
  let servicePartAborted = false;
39
56
 
40
57
  const pendingRequestsMap = new Map();
@@ -42,49 +59,46 @@ export class VsfXPartialLoader extends BaseLoader {
42
59
  const pendingRequestsAbortHandler = () => clearTimeout(pendingRequestsTimerId);
43
60
 
44
61
  const pendingRequestsAbortController = new AbortController();
45
- abortControllerForRequestMap.set(0, pendingRequestsAbortController);
46
-
47
- const updaterController = new UpdaterController();
48
- updaterController.initialize(this.viewer);
62
+ this.abortControllerForRequestMap.set(0, pendingRequestsAbortController);
49
63
 
50
- this.viewer._abortController = abortController;
51
- this.viewer._abortControllerForRequestMap = abortControllerForRequestMap;
64
+ const updateController = new UpdateController();
65
+ updateController.initialize(this.viewer);
52
66
 
53
- visViewer.memoryLimit = this.options.memoryLimit;
67
+ visViewer.memoryLimit = this.viewer.options.memoryLimit;
54
68
 
55
69
  const chunkLoadHandler = (progress: number, chunk: Uint8Array, requestId = 0) => {
56
70
  if (!this.viewer.visualizeJs) return;
57
71
 
58
72
  const state = visViewer.parseVsfxInPartialMode(requestId, chunk);
59
- updaterController.update(UpdateType.kDelay);
73
+ updateController.update(UpdateType.kDelay);
60
74
 
61
- this.viewer.emitEvent({ type: "geometryprogress", data: progress, model: this.model });
75
+ this.viewer.emitEvent({ type: "geometryprogress", data: progress, file: model.file, model });
62
76
 
63
77
  if (state) {
64
- updaterController.update(UpdateType.kForce);
78
+ updateController.update(UpdateType.kForce);
65
79
 
66
80
  this.viewer.syncOpenCloudVisualStyle(false);
67
81
  this.viewer.syncOptions();
68
82
  this.viewer.syncOverlay();
69
83
  this.viewer.resize();
70
84
 
71
- this.viewer.emitEvent({ type: "databasechunk", data: chunk, model: this.model });
85
+ this.viewer.emitEvent({ type: "databasechunk", data: chunk, file: model.file, model });
72
86
  } else {
73
- this.viewer.emitEvent({ type: "geometrychunk", data: chunk, model: this.model });
87
+ this.viewer.emitEvent({ type: "geometrychunk", data: chunk, file: model.file, model });
74
88
  }
75
89
  };
76
90
 
77
91
  const downloadResourceRange = async (dataId: string, requestId: number, ranges: any) => {
78
92
  const abortCtrl = new AbortController();
79
- abortControllerForRequestMap.set(requestId, abortCtrl);
93
+ this.abortControllerForRequestMap.set(requestId, abortCtrl);
80
94
  try {
81
- await this.model.downloadResourceRange(dataId, requestId, ranges, chunkLoadHandler, abortCtrl.signal);
95
+ await model.downloadResourceRange(dataId, requestId, ranges, chunkLoadHandler, abortCtrl.signal);
82
96
  } catch (error: any) {
83
- this.viewer.emitEvent({ type: "geometryerror", data: error, model: this.model });
97
+ this.viewer.emitEvent({ type: "geometryerror", data: error, file: model.file, model });
84
98
  } finally {
85
99
  ranges.forEach((range) => visViewer.onRequestResponseComplete(range.requestId));
86
- abortControllerForRequestMap.delete(requestId);
87
- updaterController.update(UpdateType.kNormal);
100
+ this.abortControllerForRequestMap.delete(requestId);
101
+ updateController.update(UpdateType.kNormal);
88
102
  }
89
103
  };
90
104
 
@@ -106,31 +120,31 @@ export class VsfXPartialLoader extends BaseLoader {
106
120
  onServicePartReceived: (bHasIndex: boolean) => {
107
121
  if (bHasIndex) {
108
122
  servicePartAborted = true;
109
- abortController.abort();
123
+ this.abortController.abort();
110
124
  }
111
125
  },
112
126
 
113
127
  onRequest: (requestId: number, records: any) => {
114
128
  const ranges = requestRecordsToRanges(requestId, records);
115
- downloadResourceRange(this.model.database, requestId, ranges);
129
+ downloadResourceRange(model.database, requestId, ranges);
116
130
  },
117
131
 
118
132
  onFullLoaded: () => {
119
- updaterController.update(UpdateType.kNormal);
133
+ updateController.update(UpdateType.kNormal);
120
134
  },
121
135
 
122
136
  onRequestResponseParsed: (requestId: number) => {
123
- abortControllerForRequestMap.delete(requestId);
124
- updaterController.update(UpdateType.kNormal);
137
+ this.abortControllerForRequestMap.delete(requestId);
138
+ updateController.update(UpdateType.kNormal);
125
139
  },
126
140
 
127
141
  onRequestAborted: (requestId: number) => {
128
- const abortCtrl = abortControllerForRequestMap.get(requestId);
142
+ const abortCtrl = this.abortControllerForRequestMap.get(requestId);
129
143
  if (abortCtrl) abortCtrl.abort();
130
144
  },
131
145
 
132
146
  onRequestResourceFile: (requestId: number, _: string, records: any) => {
133
- const dataId = `${this.model.fileId}${this.model.file.type}`;
147
+ const dataId = `${model.fileId}${model.file.type}`;
134
148
  const ranges = requestRecordsToRanges(requestId, records);
135
149
 
136
150
  let pendingRanges = [];
@@ -186,21 +200,17 @@ export class VsfXPartialLoader extends BaseLoader {
186
200
  visViewer.attachPartialResolver(objectHandler);
187
201
 
188
202
  try {
189
- this.viewer.emitEvent({ type: "geometrystart", model: this.model });
190
-
191
- await this.model.downloadResource(this.model.database, chunkLoadHandler, abortController.signal).catch((e) => {
192
- if (!servicePartAborted) throw e;
193
- });
203
+ await model.downloadResource(model.database, chunkLoadHandler, this.abortController.signal);
204
+ } catch (e) {
205
+ window.clearTimeout(pendingRequestsTimerId);
206
+ if (!servicePartAborted) throw e;
207
+ }
194
208
 
195
- this.viewer.emitEvent({ type: "geometryend", model: this.model });
196
- } catch (e: any) {
197
- if (pendingRequestsTimerId) {
198
- window.clearTimeout(pendingRequestsTimerId);
199
- pendingRequestsTimerId = 0;
200
- }
209
+ return this;
210
+ }
201
211
 
202
- this.viewer.emitEvent({ type: "geometryerror", data: e, model: this.model });
203
- throw e;
204
- }
212
+ override cancel(): void {
213
+ super.cancel();
214
+ this.abortControllerForRequestMap.forEach((controller) => controller.abort());
205
215
  }
206
216
  }
@@ -21,21 +21,37 @@
21
21
  // acknowledge and accept the above terms.
22
22
  ///////////////////////////////////////////////////////////////////////////////
23
23
 
24
- import { BaseLoader } from "./BaseLoader";
25
- import { UpdaterController, UpdateType } from "./UpdaterController";
24
+ import { Loader } from "@inweb/viewer-core";
25
+ import { Viewer } from "../Viewer";
26
+ import { UpdateController, UpdateType } from "./UpdateController";
26
27
 
27
- export class VsfXStreamingLoader extends BaseLoader {
28
- override async load(): Promise<void> {
29
- if (!this.viewer.visualizeJs) return;
28
+ export class VSFXStreamingLoader extends Loader {
29
+ public viewer: Viewer;
30
+
31
+ constructor(viewer: Viewer) {
32
+ super();
33
+ this.viewer = viewer;
34
+ }
35
+
36
+ override isSupport(file: any): boolean {
37
+ return (
38
+ typeof file === "object" &&
39
+ typeof file.downloadResource === "function" &&
40
+ /.vsfx$/i.test(file.database) &&
41
+ this.viewer.options.enableStreamingMode === true &&
42
+ this.viewer.options.enablePartialMode === false &&
43
+ !/.rcs$/i.test(file.name)
44
+ );
45
+ }
46
+
47
+ override async load(model: any): Promise<this> {
48
+ if (!this.viewer.visualizeJs) return this;
30
49
 
31
50
  const visLib = this.viewer.visLib();
32
51
  const visViewer = visLib.getViewer();
33
- const abortController = new AbortController();
34
-
35
- const updaterController = new UpdaterController();
36
- updaterController.initialize(this.viewer);
37
52
 
38
- this.viewer._abortController = abortController;
53
+ const updateController = new UpdateController();
54
+ updateController.initialize(this.viewer);
39
55
 
40
56
  let isFireDatabaseChunk = false;
41
57
 
@@ -43,9 +59,9 @@ export class VsfXStreamingLoader extends BaseLoader {
43
59
  if (!this.viewer.visualizeJs) return;
44
60
 
45
61
  const status = visViewer.parseVsfx(chunk);
46
- updaterController.update(UpdateType.kDelay);
62
+ updateController.update(UpdateType.kDelay);
47
63
 
48
- this.viewer.emitEvent({ type: "geometryprogress", data: progress, model: this.model });
64
+ this.viewer.emitEvent({ type: "geometryprogress", data: progress, file: model.file, model });
49
65
 
50
66
  let state = false;
51
67
  if (
@@ -57,32 +73,26 @@ export class VsfXStreamingLoader extends BaseLoader {
57
73
  }
58
74
 
59
75
  if (state) {
60
- updaterController.update(UpdateType.kForce);
76
+ updateController.update(UpdateType.kForce);
61
77
 
62
78
  this.viewer.syncOpenCloudVisualStyle(false);
63
79
  this.viewer.syncOptions();
64
80
  this.viewer.syncOverlay();
65
81
  this.viewer.resize();
66
82
 
67
- this.viewer.emitEvent({ type: "databasechunk", data: chunk, model: this.model });
83
+ this.viewer.emitEvent({ type: "databasechunk", data: chunk, file: model.file, model });
68
84
  } else {
69
- this.viewer.emitEvent({ type: "geometrychunk", data: chunk, model: this.model });
85
+ this.viewer.emitEvent({ type: "geometrychunk", data: chunk, file: model.file, model });
70
86
  }
71
87
  };
72
88
 
73
89
  console.time("File load time");
74
- try {
75
- this.viewer.emitEvent({ type: "geometrystart", model: this.model });
76
90
 
77
- await this.model.downloadResource(this.model.database, chunkLoadHandler, abortController.signal);
78
- console.timeEnd("File load time");
91
+ await model.downloadResource(model.database, chunkLoadHandler, this.abortController.signal);
92
+ updateController.update(UpdateType.kNormal);
79
93
 
80
- updaterController.update(UpdateType.kNormal);
94
+ console.timeEnd("File load time");
81
95
 
82
- this.viewer.emitEvent({ type: "geometryend", model: this.model });
83
- } catch (error: any) {
84
- this.viewer.emitEvent({ type: "geometryerror", data: error, model: this.model });
85
- throw error;
86
- }
96
+ return Promise.resolve(this);
87
97
  }
88
98
  }
@@ -0,0 +1,108 @@
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 { ILoadersRegistry, loadersRegistry } from "@inweb/viewer-core";
25
+
26
+ import { VSFModelLoader } from "./VSFModelLoader";
27
+ import { VSFBufferLoader } from "./VSFBufferLoader";
28
+ import { VSFXModelLoader } from "./VSFXModelLoader";
29
+ import { VSFXStreamingLoader } from "./VSFXStreamingLoader";
30
+ import { VSFXPartialLoader } from "./VSFXPartialLoader";
31
+ import { VSFXBufferLoader } from "./VSFXBufferLoader";
32
+
33
+ /**
34
+ * Viewer loaders registry. Use this registry to register custom loaders.
35
+ *
36
+ * To implement custom loader:
37
+ *
38
+ * 1. Define a loader class implements {@link ILoader}.
39
+ * 2. Define a constructor with a `viewer` parameter.
40
+ * 3. Override {@link ILoader.isSupport} and сheck if the loader can load the specified file.
41
+ * 4. Override {@link ILoader.load} and define the logic for loading the model from the file.
42
+ *
43
+ * The loader should do:
44
+ *
45
+ * - Load model data from file. The model data must be a `VSFX` data buffer of `VSFX` data chunk.
46
+ * - Parse model data with the `VisualizeJS` viewer instance.
47
+ * - Synchronize viewer styles, options and overlay.
48
+ * - Update the viewer.
49
+ *
50
+ * The loader must emit events:
51
+ *
52
+ * - `geometryprogress` - during loading. If progress is not supported, emit it once with a value of 100%
53
+ * after the load is complete.
54
+ * - `databasechunk` - when model is loaded and ready to render.
55
+ * 5. Override {@link ILoader.dispose} and release loader resources, if required.
56
+ * 6. Register loader provider in the loaders registry by calling the {@link loaders.registerLoader}.
57
+ *
58
+ * @example Implementing a custom loader.
59
+ *
60
+ * ```javascript
61
+ * import { Loader } from "@inweb/viewer-core";
62
+ * import { loaders, Viewer } from "@inweb/viewer-visualize";
63
+ *
64
+ * class MyLoader extends Loader {
65
+ * public viewer: Viewer;
66
+ *
67
+ * constructor(viewer: Viewer) {
68
+ * super();
69
+ * this.viewer = viewer;
70
+ * }
71
+ *
72
+ * override isSupport(file, format): Boolean {
73
+ * // place custom logic here
74
+ * return ...;
75
+ * }
76
+ *
77
+ * override load(file, format, params): Promise<this> {
78
+ *
79
+ * // place custom loading logic here
80
+ * const data = ...
81
+ *
82
+ * this.viewer.visualizeJs.getViewer().parseVsfx(data);
83
+ *
84
+ * this.viewer.syncOpenCloudVisualStyle(false);
85
+ * this.viewer.syncOptions();
86
+ * this.viewer.syncOverlay();
87
+ * this.viewer.resize();
88
+ *
89
+ * this.viewer.emitEvent({ type: "databasechunk", data: model, file });
90
+ *
91
+ * return Promise.resove(this);
92
+ * };
93
+ * }
94
+ *
95
+ * loaders.registerLoader("MyLoader", (viewer) => new MyLoader(viewer));
96
+ * ```
97
+ */
98
+ export const loaders: ILoadersRegistry = loadersRegistry("visualizejs");
99
+
100
+ // build-in loaders
101
+
102
+ loaders.registerLoader("vsf", (viewer: any) => new VSFModelLoader(viewer));
103
+ loaders.registerLoader("vsf-buffer", (viewer: any) => new VSFBufferLoader(viewer));
104
+
105
+ loaders.registerLoader("vsfx", (viewer: any) => new VSFXModelLoader(viewer));
106
+ loaders.registerLoader("vsfx-streaming", (viewer: any) => new VSFXStreamingLoader(viewer));
107
+ loaders.registerLoader("vsfx-partial", (viewer: any) => new VSFXPartialLoader(viewer));
108
+ loaders.registerLoader("vsfx-buffer", (viewer: any) => new VSFXBufferLoader(viewer));
@@ -27,10 +27,12 @@ import {
27
27
  CANVAS_EVENTS,
28
28
  CanvasEventMap,
29
29
  Dragger,
30
+ FileSource,
30
31
  IClippingPlane,
31
32
  IComponent,
32
33
  IEntity,
33
34
  IDragger,
35
+ ILoader,
34
36
  IOrthogonalCamera,
35
37
  IOptions,
36
38
  IPoint,
@@ -45,9 +47,9 @@ import { IMarkup, IWorldTransform } from "@inweb/markup";
45
47
  import { draggers } from "./Draggers";
46
48
  import { commands } from "./Commands";
47
49
  import { components } from "./Components";
50
+ import { loaders } from "./Loaders";
48
51
 
49
52
  import { loadVisualizeJs } from "./utils";
50
- import { LoaderFactory } from "./Loaders/LoaderFactory";
51
53
  import { MarkupFactory, MarkupType } from "./Markup/MarkupFactory";
52
54
 
53
55
  const OVERLAY_VIEW_NAME = "$OVERLAY_VIEW_NAME";
@@ -81,10 +83,9 @@ export class Viewer
81
83
  private _markup: IMarkup;
82
84
  public canvas: HTMLCanvasElement | undefined;
83
85
 
84
- public _abortController: AbortController | undefined;
85
- public _abortControllerForRequestMap: Map<string, AbortController> | undefined;
86
86
  public _abortControllerForReferences: AbortController | undefined;
87
87
  public client: Client | undefined;
88
+ public loaders: Array<ILoader>;
88
89
 
89
90
  /**
90
91
  * @param client - The `Client` instance that is used to load model reference files from the Open Cloud
@@ -114,6 +115,7 @@ export class Viewer
114
115
  this._options = new Options(this);
115
116
 
116
117
  this.client = client;
118
+ this.loaders = [];
117
119
 
118
120
  this._activeDragger = null;
119
121
  this._components = [];
@@ -802,6 +804,7 @@ export class Viewer
802
804
  async loadReferences(model: Model | File | Assembly): Promise<this> {
803
805
  if (!this.visualizeJs) return this;
804
806
  if (!this.client) return this;
807
+ if (!model.getReferences) return this;
805
808
 
806
809
  const abortController = new AbortController();
807
810
  this._abortControllerForReferences?.abort();
@@ -845,71 +848,39 @@ export class Viewer
845
848
  this.update();
846
849
  }
847
850
 
848
- /**
849
- * Loads a file from Open Cloud Server into the viewer.
850
- *
851
- * The file geometry data on the server must be converted to `VSFX` format.
852
- *
853
- * To open a large file, enable {@link IOptions.enablePartialMode | partial streaming} mode before
854
- * opening (see example below).
855
- *
856
- * This method requires a `Client` instance to be specified when creating the viewer to load model
857
- * reference files from the Open Cloud Server. For a standalone viewer instance use
858
- * {@link openVsfFile | openVsfFile()} or {@link openVsfxFile | openVsfxFile()}.
859
- *
860
- * If there was an active dragger before opening the file, it will be deactivated. After opening the
861
- * file, you must manually activate the required dragger.
862
- *
863
- * Fires:
864
- *
865
- * - {@link OpenEvent | open}
866
- * - {@link GeometryStartEvent | geometrystart}
867
- * - {@link GeometryProgressEvent | geometryprogress}
868
- * - {@link DatabaseChunkEvent | databasechunk}
869
- * - {@link GeometryChunkEvent | geometrychunk}
870
- * - {@link GeometryEndEvent | geometryend}
871
- * - {@link GeometryErrorEvent | geometryerror}
872
- *
873
- * @example Using partial streaming mode to open a large file from a server.
874
- *
875
- * ```javascript
876
- * viewer.options.enableStreamingMode = true;
877
- * viewer.options.enablePartialMode = true;
878
- * await viewer.open(file);
879
- * ```
880
- *
881
- * @param file - File, assembly or specific model to load. If a `File` instance with multiple models is
882
- * specified, the default model will be loaded. If there is no default model, first availiable model
883
- * will be loaded.
884
- */
885
- async open(file: File | Assembly | Model): Promise<this> {
851
+ async open(file: FileSource, params: { format?: string; mode?: string } = {}): Promise<this> {
886
852
  if (!this.visualizeJs) return this;
887
853
 
888
854
  this.cancel();
889
855
  this.clear();
890
856
 
891
- this.emitEvent({ type: "open", file, model: file });
857
+ this.emitEvent({ type: "open", file });
892
858
 
893
- let model: Model | undefined = undefined;
894
- if (file) {
895
- const models = (await file.getModels()) || [];
896
- model = models.find((model: Model) => model.default) || models[0];
897
- }
898
- if (!model) throw new Error("No default model found");
899
-
900
- const overrideOptions = new Options();
901
- overrideOptions.data = this._options.data;
902
- if (file.type === ".rcs" && !overrideOptions.enablePartialMode) {
903
- console.log("Partial streaming mode is forced for RCS file");
904
- overrideOptions.enableStreamingMode = true;
905
- overrideOptions.enablePartialMode = true;
859
+ let model: any = file;
860
+ if (model && typeof model.getModels === "function") {
861
+ const models = await model.getModels();
862
+ model = models.find((model: Model) => model.default) || models[0] || file;
906
863
  }
864
+ if (!model) throw new Error(`Format not supported`);
907
865
 
908
- const loaderFactory = new LoaderFactory();
909
- const loader = loaderFactory.create(this, model, overrideOptions);
866
+ let format = params.format;
867
+ if (!format && typeof model.type === "string") format = model.type.split(".").pop();
868
+ if (!format && typeof file === "string") format = file.split(".").pop();
869
+ if (!format && file instanceof globalThis.File) format = file.name.split(".").pop();
910
870
 
911
- await this.loadReferences(model);
912
- await loader.load();
871
+ const loader = loaders.createLoader(this, model, format);
872
+ if (!loader) throw new Error(`Format not supported`);
873
+ this.loaders.push(loader);
874
+
875
+ this.emitEvent({ type: "geometrystart", file, model });
876
+ try {
877
+ await this.loadReferences(model);
878
+ await loader.load(model, format, params);
879
+ } catch (error: any) {
880
+ this.emitEvent({ type: "geometryerror", data: error, file, model });
881
+ throw error;
882
+ }
883
+ this.emitEvent({ type: "geometryend", file, model });
913
884
 
914
885
  if (this.visualizeJs) {
915
886
  this.applyModelTransformMatrix(model);
@@ -920,24 +891,7 @@ export class Viewer
920
891
  }
921
892
 
922
893
  /**
923
- * Loads a `VSF` file into the viewer.
924
- *
925
- * This method does not support {@link IOptions.enableStreamingMode | streaming} or
926
- * {@link IOptions.enablePartialMode | partial streaming} mode.
927
- *
928
- * If there was an active dragger before opening the file, it will be deactivated. After opening the
929
- * file, you must manually activate the required dragger.
930
- *
931
- * Fires:
932
- *
933
- * - {@link OpenEvent | open}
934
- * - {@link GeometryStartEvent | geometrystart}
935
- * - {@link GeometryProgressEvent | geometryprogress}
936
- * - {@link DatabaseChunkEvent | databasechunk}
937
- * - {@link GeometryEndEvent | geometryend}
938
- * - {@link GeometryErrorEvent | geometryerror}
939
- *
940
- * @param buffer - Binary data buffer to load.
894
+ * Deprecated since `26.4`. Use {@link open | open()} instead.
941
895
  */
942
896
  openVsfFile(buffer: Uint8Array | ArrayBuffer): this {
943
897
  if (!this.visualizeJs) return this;
@@ -945,14 +899,13 @@ export class Viewer
945
899
  this.cancel();
946
900
  this.clear();
947
901
 
948
- this.emitEvent({ type: "open", buffer });
949
-
950
- try {
951
- this.emitEvent({ type: "geometrystart", buffer });
902
+ this.emitEvent({ type: "open", file: buffer });
952
903
 
953
- const visLib = this.visLib();
954
- const visViewer = visLib.getViewer();
904
+ const visLib = this.visLib();
905
+ const visViewer = visLib.getViewer();
955
906
 
907
+ this.emitEvent({ type: "geometrystart", file: buffer });
908
+ try {
956
909
  const data = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
957
910
  visViewer.parseFile(data);
958
911
 
@@ -961,13 +914,13 @@ export class Viewer
961
914
  this.syncOverlay();
962
915
  this.resize();
963
916
 
964
- this.emitEvent({ type: "geometryprogress", data: 1, buffer });
965
- this.emitEvent({ type: "databasechunk", data, buffer });
966
- this.emitEvent({ type: "geometryend", buffer });
917
+ this.emitEvent({ type: "geometryprogress", data: 1, file: buffer });
918
+ this.emitEvent({ type: "databasechunk", data, file: buffer });
967
919
  } catch (error: any) {
968
- this.emitEvent({ type: "geometryerror", data: error, buffer });
920
+ this.emitEvent({ type: "geometryerror", data: error, file: buffer });
969
921
  throw error;
970
922
  }
923
+ this.emitEvent({ type: "geometryend", file: buffer });
971
924
 
972
925
  return this;
973
926
  }
@@ -998,14 +951,13 @@ export class Viewer
998
951
  this.cancel();
999
952
  this.clear();
1000
953
 
1001
- this.emitEvent({ type: "open", buffer });
1002
-
1003
- try {
1004
- this.emitEvent({ type: "geometrystart", buffer });
954
+ this.emitEvent({ type: "open", file: buffer });
1005
955
 
1006
- const visLib = this.visLib();
1007
- const visViewer = visLib.getViewer();
956
+ const visLib = this.visLib();
957
+ const visViewer = visLib.getViewer();
1008
958
 
959
+ this.emitEvent({ type: "geometrystart", file: buffer });
960
+ try {
1009
961
  const data = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
1010
962
  visViewer.parseVsfx(data);
1011
963
 
@@ -1014,13 +966,13 @@ export class Viewer
1014
966
  this.syncOverlay();
1015
967
  this.resize();
1016
968
 
1017
- this.emitEvent({ type: "geometryprogress", data: 1, buffer });
1018
- this.emitEvent({ type: "databasechunk", data, buffer });
1019
- this.emitEvent({ type: "geometryend", buffer });
969
+ this.emitEvent({ type: "geometryprogress", data: 1, file: buffer });
970
+ this.emitEvent({ type: "databasechunk", data, file: buffer });
1020
971
  } catch (error: any) {
1021
- this.emitEvent({ type: "geometryerror", data: error, buffer });
972
+ this.emitEvent({ type: "geometryerror", data: error, file: buffer });
1022
973
  throw error;
1023
974
  }
975
+ this.emitEvent({ type: "geometryend", file: buffer });
1024
976
 
1025
977
  return this;
1026
978
  }
@@ -1029,14 +981,9 @@ export class Viewer
1029
981
  this._abortControllerForReferences?.abort();
1030
982
  this._abortControllerForReferences = undefined;
1031
983
 
1032
- this._abortController?.abort();
1033
- this._abortController = undefined;
1034
-
1035
- this._abortControllerForRequestMap?.forEach((controller) => controller.abort());
1036
- this._abortControllerForRequestMap = undefined;
984
+ this.loaders.forEach((loader) => loader.cancel());
1037
985
 
1038
986
  this.emitEvent({ type: "cancel" });
1039
-
1040
987
  return this;
1041
988
  }
1042
989
 
@@ -1054,6 +1001,9 @@ export class Viewer
1054
1001
  visViewer.clear();
1055
1002
  visViewer.createLocalDatabase();
1056
1003
 
1004
+ this.loaders.forEach((loader) => loader.dispose());
1005
+ this.loaders = [];
1006
+
1057
1007
  this.syncOpenCloudVisualStyle(true);
1058
1008
  this.syncOptions();
1059
1009
  this.syncOverlay();