@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.
- package/dist/viewer-visualize.js +18183 -18012
- package/dist/viewer-visualize.js.map +1 -1
- package/dist/viewer-visualize.min.js +1 -1
- package/dist/viewer-visualize.module.js +335 -297
- package/dist/viewer-visualize.module.js.map +1 -1
- package/lib/Viewer/Loaders/{UpdaterController.d.ts → UpdateController.d.ts} +1 -1
- package/lib/Viewer/Loaders/VSFBufferLoader.d.ts +8 -0
- package/lib/Viewer/Loaders/VSFModelLoader.d.ts +8 -0
- package/lib/Viewer/Loaders/VSFXBufferLoader.d.ts +8 -0
- package/lib/Viewer/Loaders/VSFXModelLoader.d.ts +8 -0
- package/lib/Viewer/Loaders/VSFXPartialLoader.d.ts +10 -0
- package/lib/Viewer/Loaders/VSFXStreamingLoader.d.ts +8 -0
- package/lib/Viewer/Loaders/index.d.ts +67 -0
- package/lib/Viewer/Viewer.d.ts +7 -59
- package/lib/index.d.ts +3 -2
- package/package.json +5 -5
- package/src/Viewer/Loaders/{BaseLoader.ts → UpdateController.ts} +30 -10
- package/src/Viewer/Loaders/{LoaderFactory.ts → VSFBufferLoader.ts} +28 -19
- package/src/Viewer/Loaders/VSFModelLoader.ts +87 -0
- package/src/Viewer/Loaders/VSFXBufferLoader.ts +58 -0
- package/src/Viewer/Loaders/{VsfXLoader.ts → VSFXModelLoader.ts} +35 -35
- package/src/Viewer/Loaders/{VsfXPartialLoader.ts → VSFXPartialLoader.ts} +55 -45
- package/src/Viewer/Loaders/{VsfXStreamingLoader.ts → VSFXStreamingLoader.ts} +35 -25
- package/src/Viewer/Loaders/index.ts +108 -0
- package/src/Viewer/Viewer.ts +53 -103
- package/src/index.ts +6 -3
- package/lib/Viewer/Loaders/BaseLoader.d.ts +0 -10
- package/lib/Viewer/Loaders/LoaderFactory.d.ts +0 -10
- package/lib/Viewer/Loaders/TCSLoader.d.ts +0 -4
- package/lib/Viewer/Loaders/VsfXLoader.d.ts +0 -4
- package/lib/Viewer/Loaders/VsfXPartialLoader.d.ts +0 -4
- package/lib/Viewer/Loaders/VsfXStreamingLoader.d.ts +0 -4
- package/src/Viewer/Loaders/TCSLoader.ts +0 -83
- 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 {
|
|
25
|
-
import {
|
|
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
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
51
|
-
this.viewer
|
|
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
|
-
|
|
73
|
+
updateController.update(UpdateType.kDelay);
|
|
60
74
|
|
|
61
|
-
this.viewer.emitEvent({ type: "geometryprogress", data: progress,
|
|
75
|
+
this.viewer.emitEvent({ type: "geometryprogress", data: progress, file: model.file, model });
|
|
62
76
|
|
|
63
77
|
if (state) {
|
|
64
|
-
|
|
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,
|
|
85
|
+
this.viewer.emitEvent({ type: "databasechunk", data: chunk, file: model.file, model });
|
|
72
86
|
} else {
|
|
73
|
-
this.viewer.emitEvent({ type: "geometrychunk", data: chunk,
|
|
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
|
|
95
|
+
await model.downloadResourceRange(dataId, requestId, ranges, chunkLoadHandler, abortCtrl.signal);
|
|
82
96
|
} catch (error: any) {
|
|
83
|
-
this.viewer.emitEvent({ type: "geometryerror", data: error,
|
|
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
|
-
|
|
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(
|
|
129
|
+
downloadResourceRange(model.database, requestId, ranges);
|
|
116
130
|
},
|
|
117
131
|
|
|
118
132
|
onFullLoaded: () => {
|
|
119
|
-
|
|
133
|
+
updateController.update(UpdateType.kNormal);
|
|
120
134
|
},
|
|
121
135
|
|
|
122
136
|
onRequestResponseParsed: (requestId: number) => {
|
|
123
|
-
abortControllerForRequestMap.delete(requestId);
|
|
124
|
-
|
|
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 = `${
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
if (pendingRequestsTimerId) {
|
|
198
|
-
window.clearTimeout(pendingRequestsTimerId);
|
|
199
|
-
pendingRequestsTimerId = 0;
|
|
200
|
-
}
|
|
209
|
+
return this;
|
|
210
|
+
}
|
|
201
211
|
|
|
202
|
-
|
|
203
|
-
|
|
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 {
|
|
25
|
-
import {
|
|
24
|
+
import { Loader } from "@inweb/viewer-core";
|
|
25
|
+
import { Viewer } from "../Viewer";
|
|
26
|
+
import { UpdateController, UpdateType } from "./UpdateController";
|
|
26
27
|
|
|
27
|
-
export class
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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
|
-
|
|
62
|
+
updateController.update(UpdateType.kDelay);
|
|
47
63
|
|
|
48
|
-
this.viewer.emitEvent({ type: "geometryprogress", data: progress,
|
|
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
|
-
|
|
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,
|
|
83
|
+
this.viewer.emitEvent({ type: "databasechunk", data: chunk, file: model.file, model });
|
|
68
84
|
} else {
|
|
69
|
-
this.viewer.emitEvent({ type: "geometrychunk", data: chunk,
|
|
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
|
-
|
|
78
|
-
|
|
91
|
+
await model.downloadResource(model.database, chunkLoadHandler, this.abortController.signal);
|
|
92
|
+
updateController.update(UpdateType.kNormal);
|
|
79
93
|
|
|
80
|
-
|
|
94
|
+
console.timeEnd("File load time");
|
|
81
95
|
|
|
82
|
-
|
|
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));
|
package/src/Viewer/Viewer.ts
CHANGED
|
@@ -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
|
|
857
|
+
this.emitEvent({ type: "open", file });
|
|
892
858
|
|
|
893
|
-
let model:
|
|
894
|
-
if (
|
|
895
|
-
const models =
|
|
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
|
-
|
|
909
|
-
|
|
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
|
-
|
|
912
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
954
|
-
|
|
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
|
-
|
|
1007
|
-
|
|
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.
|
|
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();
|