@shapediver/viewer.session-engine.session-engine 3.3.4 → 3.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/implementation/SessionEngine.d.ts.map +1 -1
  2. package/dist/implementation/SessionEngine.js +18 -3
  3. package/dist/implementation/SessionEngine.js.map +1 -1
  4. package/dist/implementation/dto/DrawingParameter.d.ts +13 -0
  5. package/dist/implementation/dto/DrawingParameter.d.ts.map +1 -0
  6. package/dist/implementation/dto/DrawingParameter.js +43 -0
  7. package/dist/implementation/dto/DrawingParameter.js.map +1 -0
  8. package/dist/index.d.ts +3 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +3 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/interfaces/dto/IDrawingParameter.d.ts +5 -0
  13. package/dist/interfaces/dto/IDrawingParameter.d.ts.map +1 -0
  14. package/dist/interfaces/dto/IDrawingParameter.js +3 -0
  15. package/dist/interfaces/dto/IDrawingParameter.js.map +1 -0
  16. package/dist/interfaces/dto/IDrawingToolsParameter.d.ts +5 -0
  17. package/dist/interfaces/dto/IDrawingToolsParameter.d.ts.map +1 -0
  18. package/dist/interfaces/dto/IDrawingToolsParameter.js +3 -0
  19. package/dist/interfaces/dto/IDrawingToolsParameter.js.map +1 -0
  20. package/package.json +10 -11
  21. package/src/implementation/OutputDelayException.ts +0 -22
  22. package/src/implementation/OutputLoader.ts +0 -378
  23. package/src/implementation/SessionData.ts +0 -44
  24. package/src/implementation/SessionEngine.ts +0 -2006
  25. package/src/implementation/SessionOutputData.ts +0 -44
  26. package/src/implementation/SessionTreeNode.ts +0 -44
  27. package/src/implementation/dto/Export.ts +0 -231
  28. package/src/implementation/dto/FileParameter.ts +0 -117
  29. package/src/implementation/dto/Output.ts +0 -223
  30. package/src/implementation/dto/Parameter.ts +0 -370
  31. package/src/implementation/dto/interaction/GumballParameter.ts +0 -78
  32. package/src/implementation/dto/interaction/SelectionParameter.ts +0 -66
  33. package/src/index.ts +0 -27
  34. package/src/interfaces/ISessionData.ts +0 -16
  35. package/src/interfaces/ISessionEngine.ts +0 -69
  36. package/src/interfaces/ISessionOutputData.ts +0 -16
  37. package/src/interfaces/ISessionTreeNode.ts +0 -9
  38. package/src/interfaces/dto/IExport.ts +0 -17
  39. package/src/interfaces/dto/IFileParameter.ts +0 -10
  40. package/src/interfaces/dto/IOutput.ts +0 -58
  41. package/src/interfaces/dto/IParameter.ts +0 -19
  42. package/src/interfaces/dto/interaction/IGumballParameter.ts +0 -5
  43. package/src/interfaces/dto/interaction/IInteractionParameter.ts +0 -10
  44. package/src/interfaces/dto/interaction/ISelectionParameter.ts +0 -5
  45. package/tsconfig.json +0 -17
@@ -1,2006 +0,0 @@
1
- import {
2
- convert,
3
- ISettings,
4
- latestVersion,
5
- validate,
6
- versions
7
- } from '@shapediver/viewer.settings';
8
- import {
9
- Converter,
10
- EventEngine,
11
- EVENTTYPE,
12
- HttpClient,
13
- HttpResponse,
14
- Logger,
15
- PerformanceEvaluator,
16
- SettingsEngine,
17
- ShapeDiverViewerError,
18
- ShapeDiverViewerSessionError,
19
- ShapeDiverViewerSettingsError,
20
- StateEngine,
21
- SystemInfo,
22
- UuidGenerator
23
- } from '@shapediver/viewer.shared.services';
24
- import {
25
- create,
26
- isGBResponseError,
27
- ShapeDiverError as ShapeDiverBackendError,
28
- ShapeDiverRequestConfigure,
29
- ShapeDiverRequestCustomization,
30
- ShapeDiverRequestExport,
31
- ShapeDiverRequestFileUploadPart,
32
- ShapeDiverRequestGltfUploadQueryConversion,
33
- ShapeDiverRequestModelState,
34
- ShapeDiverResponseDto,
35
- ShapeDiverResponseErrorType,
36
- ShapeDiverResponseExport,
37
- ShapeDiverResponseExportDefinitionType,
38
- ShapeDiverResponseFileInfo,
39
- ShapeDiverResponseModelComputationStatus,
40
- ShapeDiverResponseModelState,
41
- ShapeDiverResponseOutput,
42
- ShapeDiverResponseParameter,
43
- ShapeDiverSdk,
44
- ShapeDiverSdkConfigType
45
- } from '@shapediver/sdk.geometry-api-sdk-v2';
46
- import { Export } from './dto/Export';
47
- import { FileParameter } from './dto/FileParameter';
48
- import { GumballParameter } from './dto/interaction/GumballParameter';
49
- import { IExport } from '../interfaces/dto/IExport';
50
- import { IFileParameter } from '../interfaces/dto/IFileParameter';
51
- import {
52
- IInteractionParameterSettings,
53
- IOutputEvent,
54
- ISettingsSections,
55
- ITaskEvent,
56
- PARAMETER_TYPE,
57
- TASK_TYPE,
58
- validateInteractionParameterSettings
59
- } from '@shapediver/viewer.shared.types';
60
- import { IOutput } from '../interfaces/dto/IOutput';
61
- import { IParameter } from '../interfaces/dto/IParameter';
62
- import { ISessionEngine } from '../interfaces/ISessionEngine';
63
- import { ISessionTreeNode } from '../interfaces/ISessionTreeNode';
64
- import {
65
- ITree,
66
- ITreeNode,
67
- Tree,
68
- TreeNode
69
- } from '@shapediver/viewer.shared.node-tree';
70
- import { Output } from './dto/Output';
71
- import { OutputDelayException } from './OutputDelayException';
72
- import { OutputLoader, OutputLoaderTaskEventInfo } from './OutputLoader';
73
- import { Parameter } from './dto/Parameter';
74
- import { SelectionParameter } from './dto/interaction/SelectionParameter';
75
- import { SessionData } from './SessionData';
76
- import { SessionTreeNode } from './SessionTreeNode';
77
- import { vec3 } from 'gl-matrix';
78
- /* eslint-disable @typescript-eslint/no-empty-function */
79
-
80
- export class SessionEngine implements ISessionEngine {
81
- // #region Properties (50)
82
-
83
- private readonly _converter = Converter.instance;
84
- private readonly _eventEngine = EventEngine.instance;
85
- private readonly _exports: { [key: string]: IExport; } = {};
86
- private readonly _guid?: string;
87
- private readonly _httpClient: HttpClient = HttpClient.instance;
88
- private readonly _id: string;
89
- private readonly _logger: Logger = Logger.instance;
90
- private readonly _modelViewUrl: string;
91
- private readonly _outputLoader: OutputLoader;
92
- private readonly _outputs: { [key: string]: IOutput; } = {};
93
- private readonly _outputsFreeze: { [key: string]: boolean; } = {};
94
- private readonly _parameterValues: { [key: string]: string; } = {};
95
- private readonly _parameters: { [key: string]: IParameter<unknown>; } = {};
96
- private readonly _performanceEvaluator = PerformanceEvaluator.instance;
97
- private readonly _sceneTree: ITree = Tree.instance;
98
- private readonly _sessionEngineId = (UuidGenerator.instance).create();
99
- private readonly _settingsEngine: SettingsEngine = new SettingsEngine();
100
- private readonly _stateEngine: StateEngine = StateEngine.instance;
101
- private readonly _ticket?: string;
102
- private readonly _uuidGenerator = UuidGenerator.instance;
103
-
104
- #customizationBusyModes: string[] = [];
105
- #customizationProcess?: string;
106
- #parameterHistory: {
107
- [key: string]: {
108
- value: unknown,
109
- valueString: string
110
- }
111
- }[] = [];
112
- #parameterHistoryCall = false;
113
- #parameterHistoryForward: {
114
- [key: string]: {
115
- value: unknown,
116
- valueString: string
117
- }
118
- }[] = [];
119
- private _allowOutputLoading: boolean = true;
120
- private _automaticSceneUpdate: boolean = true;
121
- private _closeOnFailure: () => Promise<void> = async () => { };
122
- private _closed: boolean = false;
123
- private _customizeOnParameterChange: boolean = false;
124
- private _dataCache: {
125
- [key: string]: Promise<HttpResponse<unknown>>
126
- } = {};
127
- private _excludeViewports: string[] = [];
128
- private _headers = {
129
- 'X-ShapeDiver-Origin': (SystemInfo.instance).origin,
130
- 'X-ShapeDiver-SessionEngineId': this._sessionEngineId,
131
- 'X-ShapeDiver-BuildVersion': '',
132
- 'X-ShapeDiver-BuildDate': ''
133
- };
134
- private _initialized: boolean = false;
135
- private _jwtToken?: string;
136
- private _loadSdtf: boolean = false;
137
- private _modelId?: string;
138
- private _modelState?: ShapeDiverResponseModelState;
139
- private _modelStateId?: string;
140
- private _modelStateValidationMode?: boolean;
141
- private _node: ITreeNode;
142
- private _refreshJwtToken?: () => Promise<string>;
143
- private _responseDto?: ShapeDiverResponseDto;
144
- private _retryCounter = 0;
145
- private _sdk!: ShapeDiverSdk;
146
- private _sessionId?: string;
147
- private _updateCallback: ((newNode: ITreeNode, oldNode: ITreeNode) => void) | null = null;
148
- private _viewerSettings?: object;
149
- private _viewerSettingsVersion: string = latestVersion;
150
- private _viewerSettingsVersionBackend: string = latestVersion;
151
-
152
- // #endregion Properties (50)
153
-
154
- // #region Constructors (1)
155
-
156
- /**
157
- * Can be use to initialize a session with the ticket/guid and modelViewUrl and returns a scene graph node with the result.
158
- * Can be use to customize the session with updated parameters to get the updated scene graph node.
159
- */
160
- constructor(properties: { id: string, ticket?: string, guid?: string, modelViewUrl: string, buildVersion: string, buildDate: string, jwtToken?: string, excludeViewports?: string[], allowOutputLoading: boolean, loadSdtf: boolean, modelStateId?: string, modelStateValidationMode?: boolean }) {
161
- this._id = properties.id;
162
- this._node = new TreeNode(properties.id);
163
- this._guid = properties.guid;
164
- this._ticket = properties.ticket;
165
- this._modelViewUrl = properties.modelViewUrl;
166
- this._excludeViewports = properties.excludeViewports || [];
167
- this._jwtToken = properties.jwtToken;
168
- this._allowOutputLoading = properties.allowOutputLoading;
169
- this._loadSdtf = properties.loadSdtf;
170
- this._modelStateId = properties.modelStateId;
171
- this._modelStateValidationMode = properties.modelStateValidationMode;
172
-
173
- this._headers['X-ShapeDiver-BuildDate'] = properties.buildDate;
174
- this._headers['X-ShapeDiver-BuildVersion'] = properties.buildVersion;
175
-
176
- this._outputLoader = new OutputLoader(this);
177
-
178
- try {
179
- this._sdk = create(this._modelViewUrl, this._jwtToken);
180
- this._sdk.setConfigurationValue(ShapeDiverSdkConfigType.REQUEST_HEADERS, this._headers);
181
- } catch (e) {
182
- throw this._httpClient.convertError(e);
183
- }
184
- }
185
-
186
- // #endregion Constructors (1)
187
-
188
- // #region Public Getters And Setters (28)
189
-
190
- public get automaticSceneUpdate(): boolean {
191
- return this._automaticSceneUpdate;
192
- }
193
-
194
- public set automaticSceneUpdate(value: boolean) {
195
- this._automaticSceneUpdate = value;
196
- value && this._closed === false ? this.addToSceneTree(this._node) : this.removeFromSceneTree(this._node);
197
- }
198
-
199
- public get canUploadGLTF(): boolean {
200
- try {
201
- this.checkAvailability('gltf-upload');
202
- return true;
203
- } catch (e) {
204
- return false;
205
- }
206
- }
207
-
208
- public get customizeOnParameterChange(): boolean {
209
- return this._customizeOnParameterChange;
210
- }
211
-
212
- public set customizeOnParameterChange(value: boolean) {
213
- this._customizeOnParameterChange = value;
214
- }
215
-
216
- public get excludeViewports(): string[] {
217
- return this._excludeViewports;
218
- }
219
-
220
- public set excludeViewports(value: string[]) {
221
- this._excludeViewports = JSON.parse(JSON.stringify(value));
222
- this._node.excludeViewports = JSON.parse(JSON.stringify(value));
223
- }
224
-
225
- public get exports(): { [key: string]: IExport; } {
226
- return this._exports;
227
- }
228
-
229
- public get guid(): string | undefined {
230
- return this._guid;
231
- }
232
-
233
- public get id(): string {
234
- return this._id;
235
- }
236
-
237
- public get initialized(): boolean {
238
- return this._initialized;
239
- }
240
-
241
- public get jwtToken(): string | undefined {
242
- return this._jwtToken;
243
- }
244
-
245
- public get loadSdtf(): boolean {
246
- return this._loadSdtf;
247
- }
248
-
249
- public set loadSdtf(value: boolean) {
250
- this._loadSdtf = value;
251
- if (this._initialized === true && this._loadSdtf === true) {
252
- (async () => {
253
- this._outputLoader.reloadSdtf = true;
254
- await this.updateOutputs();
255
- this._outputLoader.reloadSdtf = false;
256
- this._eventEngine.emitEvent(EVENTTYPE.SESSION.SESSION_SDTF_DELAYED_LOADED, { sessionId: this.id });
257
- })();
258
- }
259
- }
260
-
261
- public get modelState(): ShapeDiverResponseModelState | undefined {
262
- return this._modelState;
263
- }
264
-
265
- public get modelViewUrl(): string {
266
- return this._modelViewUrl;
267
- }
268
-
269
- public get node(): ITreeNode {
270
- return this._node;
271
- }
272
-
273
- public get outputs(): { [key: string]: IOutput; } {
274
- return this._outputs;
275
- }
276
-
277
- public get outputsFreeze(): { [key: string]: boolean; } {
278
- return this._outputsFreeze;
279
- }
280
-
281
- public get parameterValues(): { [key: string]: string; } {
282
- return this._parameterValues;
283
- }
284
-
285
- public get parameters(): { [key: string]: IParameter<unknown>; } {
286
- return this._parameters;
287
- }
288
-
289
- public get refreshJwtToken(): (() => Promise<string>) | undefined {
290
- return this._refreshJwtToken;
291
- }
292
-
293
- public set refreshJwtToken(value: (() => Promise<string>) | undefined) {
294
- this._refreshJwtToken = value;
295
- }
296
-
297
- public get settingsEngine(): SettingsEngine {
298
- return this._settingsEngine;
299
- }
300
-
301
- public get ticket(): string | undefined {
302
- return this._ticket;
303
- }
304
-
305
- public get updateCallback(): ((newNode: ITreeNode, oldNode: ITreeNode) => void) | null {
306
- return this._updateCallback;
307
- }
308
-
309
- public set updateCallback(value: ((newNode: ITreeNode, oldNode: ITreeNode) => void) | null) {
310
- this._updateCallback = value;
311
- }
312
-
313
- public get viewerSettings(): object | undefined {
314
- return this._viewerSettings;
315
- }
316
-
317
- // #endregion Public Getters And Setters (28)
318
-
319
- // #region Public Methods (30)
320
-
321
- public applySettings(response: ShapeDiverResponseDto, sections?: ISettingsSections) {
322
- sections = sections || {};
323
- if (sections.session === undefined) {
324
- sections.session = {
325
- parameter: { displayname: false, order: false, hidden: false },
326
- export: { displayname: false, order: false, hidden: false }
327
- };
328
- }
329
- if (sections.session.parameter === undefined)
330
- sections.session.parameter = { displayname: false, order: false, hidden: false, value: false };
331
- if (sections.session.export === undefined)
332
- sections.session.export = { displayname: false, order: false, hidden: false };
333
- if (sections.viewport === undefined)
334
- sections.viewport = { ar: false, scene: false, camera: false, light: false, environment: false, general: false, postprocessing: false };
335
-
336
- let config: object;
337
- if ((<ShapeDiverResponseDto>response).viewer !== undefined) {
338
- config = (<ShapeDiverResponseDto>response).viewer!.config;
339
- } else {
340
- throw new ShapeDiverViewerSettingsError('Session.applySettings: No config object available.');
341
- }
342
-
343
- try {
344
- validate(config);
345
- } catch (e) {
346
- throw new ShapeDiverViewerSettingsError('Session.applySettings: Was not able to validate config object.');
347
- }
348
-
349
- const settings = <ISettings>convert(config, latestVersion);
350
-
351
- const exportMappingUid: { [key: string]: string | undefined } = {};
352
- if (sections.session.export.displayname || sections.session.export.order || sections.session.export.hidden)
353
- if (response.exports)
354
- for (const exportId in response.exports)
355
- if (response.exports[exportId].uid !== undefined)
356
- exportMappingUid[response.exports[exportId].uid!] = exportId;
357
-
358
- const currentSettings = this._settingsEngine.settings;
359
-
360
- // apply parameter settings
361
- if (sections.session.parameter.displayname || sections.session.parameter.order || sections.session.parameter.hidden || sections.session.parameter.value) {
362
- for (const p in this.parameters) {
363
- if (settings.session[p]) {
364
- if (sections.session.parameter.displayname) this.parameters[p].displayname = settings.session[p].displayname;
365
- if (sections.session.parameter.order) this.parameters[p].order = settings.session[p].order;
366
- if (sections.session.parameter.hidden) this.parameters[p].hidden = settings.session[p].hidden || false;
367
- }
368
-
369
- if (response.parameters && response.parameters[p] && !((this.parameters[p] instanceof FileParameter) || this.parameters[p].type.startsWith('s'))) {
370
- if (sections.session.parameter.value) this.parameters[p].value = response.parameters[p].defval !== undefined ? response.parameters[p].defval : this.parameters[p].value;
371
- }
372
- }
373
- }
374
-
375
- // apply export settings
376
- if (sections.session.export.displayname || sections.session.export.order || sections.session.export.hidden) {
377
- for (const p in this.exports) {
378
- let idForSettings = '';
379
- if (settings.session[p]) {
380
- idForSettings = p;
381
- } else {
382
- const uid = this.exports[p].uid;
383
- if (!uid) continue;
384
- if (!exportMappingUid[uid]) continue;
385
- idForSettings = exportMappingUid[uid]!;
386
- }
387
- if (settings.session[idForSettings]) {
388
- if (sections.session.export.displayname) this.exports[p].displayname = settings.session[idForSettings].displayname;
389
- if (sections.session.export.order) this.exports[p].order = settings.session[idForSettings].order;
390
- if (sections.session.export.hidden) this.exports[p].hidden = settings.session[idForSettings].hidden || false;
391
- }
392
- }
393
- }
394
-
395
- // apply ar settings
396
- if (sections.viewport.ar) {
397
- currentSettings.ar = settings.ar;
398
- currentSettings.general.transformation = settings.general.transformation;
399
- }
400
-
401
- // apply camera settings
402
- if (sections.viewport.camera)
403
- currentSettings.camera = settings.camera;
404
-
405
- // apply light settings
406
- if (sections.viewport.light)
407
- currentSettings.light = settings.light;
408
-
409
- // apply scene settings
410
- if (sections.viewport.scene) {
411
- currentSettings.environmentGeometry.gridColor = settings.environmentGeometry.gridColor;
412
- currentSettings.environmentGeometry.gridVisibility = settings.environmentGeometry.gridVisibility;
413
- currentSettings.environmentGeometry.groundPlaneColor = settings.environmentGeometry.groundPlaneColor;
414
- currentSettings.environmentGeometry.groundPlaneVisibility = settings.environmentGeometry.groundPlaneVisibility;
415
- currentSettings.environmentGeometry.groundPlaneShadowColor = settings.environmentGeometry.groundPlaneShadowColor;
416
- currentSettings.environmentGeometry.groundPlaneShadowVisibility = settings.environmentGeometry.groundPlaneShadowVisibility;
417
-
418
- currentSettings.rendering.shadows = settings.rendering.shadows;
419
- currentSettings.rendering.softShadows = settings.rendering.softShadows;
420
-
421
- currentSettings.rendering.automaticColorAdjustment = settings.rendering.automaticColorAdjustment;
422
- currentSettings.rendering.textureEncoding = settings.rendering.textureEncoding;
423
- currentSettings.rendering.outputEncoding = settings.rendering.outputEncoding;
424
- currentSettings.rendering.physicallyCorrectLights = settings.rendering.physicallyCorrectLights;
425
- currentSettings.rendering.toneMapping = settings.rendering.toneMapping;
426
- currentSettings.rendering.toneMappingExposure = settings.rendering.toneMappingExposure;
427
- }
428
-
429
- if (sections.viewport.general) {
430
- currentSettings.general.defaultMaterialColor = settings.general.defaultMaterialColor;
431
- currentSettings.general.commitParameters = settings.general.commitParameters;
432
- currentSettings.general.pointSize = settings.general.pointSize;
433
- }
434
-
435
- // apply postprocessing settings
436
- if (sections.viewport.postprocessing)
437
- currentSettings.postprocessing = settings.postprocessing;
438
-
439
- // apply environment settings
440
- if (sections.viewport.environment) {
441
- currentSettings.environment.clearAlpha = settings.environment.clearAlpha;
442
- currentSettings.environment.clearColor = settings.environment.clearColor;
443
- currentSettings.environment.map = settings.environment.map;
444
- currentSettings.environment.mapAsBackground = settings.environment.mapAsBackground;
445
- currentSettings.environment.rotation = settings.environment.rotation;
446
- currentSettings.environment.blurriness = settings.environment.blurriness;
447
- currentSettings.environment.intensity = settings.environment.intensity;
448
- }
449
- }
450
-
451
- public canGoBack(): boolean {
452
- // the first entry is always the one from the init call
453
- // all additional entries can be undone
454
- return this.#parameterHistory.length > 1;
455
- }
456
-
457
- public canGoForward(): boolean {
458
- return this.#parameterHistoryForward.length > 0;
459
- }
460
-
461
- public cancelCustomization() {
462
- if (this.#customizationProcess)
463
- this.removeBusyMode(this.#customizationProcess);
464
-
465
- for (const busyId of this.#customizationBusyModes) {
466
- for (const r in this._stateEngine.viewportEngines) {
467
- if (this._stateEngine.viewportEngines[r] && this._stateEngine.viewportEngines[r]!.busy.includes(busyId))
468
- this._stateEngine.viewportEngines[r]!.busy.splice(this._stateEngine.viewportEngines[r]!.busy.indexOf(busyId), 1);
469
- }
470
- }
471
-
472
- this.#customizationBusyModes = [];
473
- this.#customizationProcess = undefined;
474
- }
475
-
476
- public async close(retry = false): Promise<void> {
477
- this.checkAvailability('close');
478
-
479
- try {
480
- this._httpClient.removeDataLoading(this._sessionId!);
481
- await this._sdk.session.close(this._sessionId!);
482
- if (this._automaticSceneUpdate) this.removeFromSceneTree(this._node);
483
-
484
- this._closed = true;
485
- } catch (e) {
486
- await this.handleError(e, retry);
487
- return await this.close(true);
488
- }
489
- }
490
-
491
- public async createModelState(
492
- parameterValues: { [key: string]: unknown; } = {},
493
- omitSessionParameterValues: boolean = false,
494
- image?: (() => string) | string | Blob | File,
495
- data?: Record<string, any>,
496
- arScene?: (() => Promise<ArrayBuffer>) | ArrayBuffer | (() => Promise<Blob>) | Blob | File
497
- ): Promise < string > {
498
- this.checkAvailability();
499
-
500
- try {
501
- const promises = [];
502
-
503
- // process the parameters
504
- const parameterSet: {
505
- [key: string]: string
506
- } = {};
507
- promises.push(
508
- this.uploadFileParameters(parameterValues).then(() => {
509
- // create a set of the current validated parameter values
510
- for (const parameterId in this.parameters) {
511
- // if the parameter has not been set, we do not include it in the parameter set if the omitSessionParameterValues flag is set
512
- if (!(omitSessionParameterValues === true && parameterValues[parameterId] === undefined)) {
513
- parameterSet[parameterId] = (' ' + this.parameters[parameterId].stringify(parameterValues[parameterId])).slice(1);
514
- }
515
- }
516
- })
517
- );
518
-
519
- // process the image input
520
- let imageData: ShapeDiverRequestFileUploadPart | undefined;
521
- let imageArrayBuffer: ArrayBuffer | undefined;
522
- if (image) {
523
- promises.push(
524
- this.processImageInput(image)
525
- .then(result => {
526
- imageData = result?.imageData;
527
- imageArrayBuffer = result?.arrayBuffer;
528
- })
529
- );
530
- }
531
-
532
- // process the arScene input
533
- let arSceneId: string | undefined;
534
- if (arScene) {
535
- promises.push(
536
- this._converter.convertToArrayBuffer(arScene)
537
- .then(arSceneArrayBuffer => this._sdk.gltf.upload(this._sessionId!, arSceneArrayBuffer, 'model/gltf-binary', ShapeDiverRequestGltfUploadQueryConversion.SCENE))
538
- .then(arSceneResponseDto => {
539
- arSceneId = arSceneResponseDto.gltf?.sceneId;
540
- })
541
- );
542
- }
543
-
544
- // wait for all promises to resolve
545
- await Promise.all(promises);
546
-
547
- // create the model state
548
- const response = await this._sdk.modelState.create(this._sessionId!, {
549
- parameters: parameterSet,
550
- data: data,
551
- image: imageData,
552
- arSceneId: arSceneId
553
- } as ShapeDiverRequestModelState);
554
-
555
- if (imageData && imageArrayBuffer)
556
- await this._sdk.utils.uploadAsset(response.asset!.modelState!.href, imageArrayBuffer, response.asset!.modelState!.headers);
557
-
558
- return response.modelState!.id!;
559
- } catch (e) {
560
- throw this._httpClient.convertError(e);
561
- }
562
- }
563
-
564
- /**
565
- * Customizes the session with updated parameters to get the updated scene graph node.
566
- *
567
- * @param parameters the parameter set to update the session
568
- * @returns promise with a scene graph node
569
- */
570
- public async customize(force: boolean = false, waitForViewportUpdate: boolean = false): Promise<ITreeNode> {
571
- const eventId = this._uuidGenerator.create();
572
- const customizationId = this._uuidGenerator.create();
573
- try {
574
- // we check if something changed
575
- if (force === false) {
576
- let changes = false;
577
- for (const parameterId in this.parameters)
578
- if (this.parameters[parameterId].sessionValue !== this.parameters[parameterId].value)
579
- changes = true;
580
- if (changes === false)
581
- return this.node;
582
- }
583
-
584
- const eventStart: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 0, data: { sessionId: this.id }, status: 'Customizing session' };
585
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_START, eventStart);
586
-
587
- const oldNode = this.node;
588
- this.#customizationProcess = customizationId;
589
-
590
- this._logger.debugLow(`Session(${this.id}).customize: Customizing session.`);
591
-
592
- this.addBusyMode(customizationId);
593
-
594
- const eventFileUpload: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 0.1, data: { sessionId: this.id }, status: 'Uploading file parameters' };
595
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_PROCESS, eventFileUpload);
596
-
597
- // upload file parameters
598
- await this.uploadFileParameters();
599
-
600
- // OPTION TO SKIP - PART 1b
601
- const cancelResult = this.cancelProcess(customizationId, eventId, TASK_TYPE.SESSION_CUSTOMIZATION, 1, { sessionId: this.id });
602
- if (cancelResult) return cancelResult;
603
-
604
- const parameterSet: {
605
- [key: string]: {
606
- value: unknown,
607
- valueString: string
608
- }
609
- } = {};
610
-
611
- // create a set of the current validated parameter values
612
- for (const parameterId in this.parameters) {
613
- parameterSet[parameterId] = {
614
- value: this.parameters[parameterId].value,
615
- valueString: this.parameters[parameterId].stringify()
616
- };
617
- }
618
-
619
- // update the session engine parameter values if everything succeeded
620
- for (const parameterId in this.parameters)
621
- this.parameterValues[parameterId] = parameterSet[parameterId].valueString;
622
- this._logger.info(`Session(${this.id}).customize: Customizing session with parameters ${JSON.stringify(this.parameterValues)}.`);
623
-
624
- const eventRequest: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 0.1, data: { sessionId: this.id }, status: 'Sending customization request' };
625
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_PROCESS, eventRequest);
626
-
627
- const oldOutputVersions = this._outputLoader.getCurrentOutputVersions();
628
-
629
- const newNode = await this.customizeInternal(() => this.#customizationProcess !== customizationId, {
630
- eventId,
631
- type: TASK_TYPE.SESSION_CUSTOMIZATION,
632
- progressRange: {
633
- min: 0.1,
634
- max: 0.9
635
- },
636
- data: { sessionId: this.id }
637
- });
638
-
639
- // OPTION TO SKIP - PART 2
640
- const cancelResult2 = this.cancelProcess(customizationId, eventId, TASK_TYPE.SESSION_CUSTOMIZATION, 1, { sessionId: this.id });
641
- if (cancelResult2) return cancelResult2;
642
-
643
- const newOutputVersions = this._outputLoader.getCurrentOutputVersions();
644
-
645
- const eventSceneUpdate: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 0.9, data: { sessionId: this.id }, status: 'Updating scene' };
646
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_PROCESS, eventSceneUpdate);
647
-
648
- // call the update callbacks
649
- if (waitForViewportUpdate === false) {
650
- for (const outputId in this.outputs) {
651
- if (oldOutputVersions[outputId] !== newOutputVersions[outputId]) {
652
- this._eventEngine.emitEvent(EVENTTYPE.OUTPUT.OUTPUT_UPDATED, <IOutputEvent>{
653
- outputId: outputId,
654
- outputVersion: newOutputVersions[outputId],
655
- newNode: newNode.children.find(c => c.name === outputId)!,
656
- oldNode: oldNode.children.find(c => c.name === outputId)!
657
- });
658
- }
659
- }
660
-
661
- await this.waitForUpdateCallbacks(newOutputVersions, oldOutputVersions, newNode, oldNode);
662
-
663
- const cancelResult = this.cancelProcess(customizationId, eventId, TASK_TYPE.SESSION_CUSTOMIZATION, 1, { sessionId: this.id });
664
- if (cancelResult) return cancelResult;
665
- }
666
-
667
- // if this is not a call by the goBack or goForward functions, add the parameter values to the history and delete the forward history
668
- if (!this.#parameterHistoryCall) {
669
- this.#parameterHistory.push(parameterSet);
670
- this.#parameterHistoryForward = [];
671
- }
672
-
673
- if (this.automaticSceneUpdate) this.removeFromSceneTree(this.node);
674
- this._node = newNode;
675
- if (this.automaticSceneUpdate && this._closed === false) this.addToSceneTree(this.node);
676
-
677
- this._logger.debug(`Session(${this.id}).customize: Customization request finished, updating geometry.`);
678
-
679
- // set the session values to the current ones in all parameters
680
- for (const parameterId in this.parameters)
681
- (<unknown>this.parameters[parameterId].sessionValue) = parameterSet[parameterId].value;
682
-
683
- // set the output content to what has been updated
684
- for (const outputId in this.outputs)
685
- this.outputs[outputId].updateOutput(
686
- newNode.children.find(c => c.name === outputId)!,
687
- oldNode.children.find(c => c.name === outputId)!
688
- );
689
-
690
- // set the export definitions
691
- for (const exportId in this.exports)
692
- this.exports[exportId].updateExport();
693
-
694
- this._warningCreator();
695
-
696
- this.node.excludeViewports = JSON.parse(JSON.stringify(this._excludeViewports));
697
-
698
- this.removeBusyMode(customizationId);
699
-
700
- this._logger.debug(`Session(${this.id}).customize: Session customized.`);
701
-
702
- this._eventEngine.emitEvent(EVENTTYPE.SESSION.SESSION_CUSTOMIZED, { sessionId: this.id });
703
-
704
- const eventEnd: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 1, data: { sessionId: this.id }, status: 'Session customized' };
705
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_END, eventEnd);
706
-
707
- // update the viewports
708
- if (waitForViewportUpdate) {
709
- for (const r in this._stateEngine.viewportEngines)
710
- if (this._stateEngine.viewportEngines[r] && !this.excludeViewports.includes(this._stateEngine.viewportEngines[r]!.id))
711
- this._stateEngine.viewportEngines[r]!.update(`SessionEngine(${this.id}).customize`);
712
-
713
- for (const outputId in this.outputs) {
714
- if (oldOutputVersions[outputId] !== newOutputVersions[outputId]) {
715
- this._eventEngine.emitEvent(EVENTTYPE.OUTPUT.OUTPUT_UPDATED, <IOutputEvent>{
716
- outputId: outputId,
717
- outputVersion: newOutputVersions[outputId],
718
- newNode: newNode.children.find(c => c.name === outputId)!,
719
- oldNode: oldNode.children.find(c => c.name === outputId)!
720
- });
721
- }
722
- }
723
-
724
- // call the update callbacks
725
- await this.waitForUpdateCallbacks(newOutputVersions, oldOutputVersions, newNode, oldNode);
726
-
727
- const cancelResult = this.cancelProcess(customizationId, eventId, TASK_TYPE.SESSION_CUSTOMIZATION, 1, { sessionId: this.id });
728
- if (cancelResult) return cancelResult;
729
- }
730
-
731
- if (!waitForViewportUpdate) {
732
- setTimeout(() => {
733
- for (const r in this._stateEngine.viewportEngines)
734
- if (this._stateEngine.viewportEngines[r] && !this.excludeViewports.includes(this._stateEngine.viewportEngines[r]!.id))
735
- this._stateEngine.viewportEngines[r]!.update(`SessionEngine(${this.id}).customize`);
736
- }, 0);
737
- }
738
-
739
- return this.node;
740
- } catch (e) {
741
- const eventCancel: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 1, data: { sessionId: this.id }, status: 'Session customization failed' };
742
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_CANCEL, eventCancel);
743
-
744
- this.removeBusyMode(customizationId);
745
-
746
- throw this._httpClient.convertError(e);
747
- }
748
- }
749
-
750
- public async customizeParallel(parameterValues: { [key: string]: unknown }, loadOutputs = true): Promise<ISessionTreeNode | ShapeDiverResponseDto> {
751
- const eventId = this._uuidGenerator.create();
752
-
753
- const eventStart: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 0, data: { sessionId: this.id }, status: 'Customizing session' };
754
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_START, eventStart);
755
-
756
- // upload file parameters
757
- await this.uploadFileParameters(parameterValues);
758
-
759
- const parameterSet: {
760
- [key: string]: string
761
- } = {};
762
-
763
- // create a set of the current validated parameter values
764
- for (const parameterId in this.parameters)
765
- parameterSet[parameterId] = (' ' + this.parameters[parameterId].stringify(parameterValues[parameterId])).slice(1);
766
-
767
- const result = await this.customizeSession(parameterSet, () => false, {
768
- eventId,
769
- type: TASK_TYPE.SESSION_CUSTOMIZATION,
770
- progressRange: {
771
- min: 0.0,
772
- max: 1
773
- },
774
- data: { sessionId: this.id }
775
- }, true, loadOutputs);
776
-
777
- if (result instanceof SessionTreeNode)
778
- result.excludeViewports = JSON.parse(JSON.stringify(this._excludeViewports));
779
-
780
- const eventEnd: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 1, data: { sessionId: this.id }, status: 'Session customized' };
781
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_END, eventEnd);
782
- return result;
783
- }
784
-
785
- public async getFileInfo(parameterId: string, fileId: string): Promise<ShapeDiverResponseFileInfo> {
786
- this.checkAvailability();
787
- try {
788
- return await this._sdk.file.info(this._sessionId!, parameterId, fileId);
789
- } catch (e) {
790
- throw this._httpClient.convertError(e);
791
- }
792
- }
793
-
794
- public async goBack(): Promise<ITreeNode> {
795
- if (!this.canGoBack()) {
796
- this._logger.debug(`Session(${this.id}).goBack: Cannot go further back.`);
797
- return new TreeNode();
798
- }
799
- // get the current parameter set and store it in the forward history later on
800
- const currentParameterSet = this.#parameterHistory.pop()!;
801
-
802
- // adjust the parameters according to the last parameter set
803
- const lastParameterSet = this.#parameterHistory[this.#parameterHistory.length - 1];
804
- for (const parameterId in lastParameterSet)
805
- this.parameters[parameterId].value = lastParameterSet[parameterId].value;
806
-
807
- // call the customization function with the parameterHistoryCall value set to true
808
- this.#parameterHistoryCall = true;
809
- const node = await this.customize();
810
- this.#parameterHistoryCall = false;
811
-
812
- // add the current (not anymore current) parameter set to the forward history
813
- this.#parameterHistoryForward.push(currentParameterSet);
814
- return node;
815
- }
816
-
817
- public async goForward(): Promise<ITreeNode> {
818
- if (!this.canGoForward()) {
819
- this._logger.debug(`Session(${this.id}).goForward: Cannot go further forward.`);
820
- return new TreeNode();
821
- }
822
- // get the last undone parameter set and apply the values to the parameters
823
- const lastParameterSet = this.#parameterHistoryForward.pop()!;
824
- for (const parameterId in lastParameterSet)
825
- this.parameters[parameterId].value = lastParameterSet[parameterId].value;
826
-
827
- // call the customization function with the parameterHistoryCall value set to true
828
- this.#parameterHistoryCall = true;
829
- const node = await this.customize();
830
- this.#parameterHistoryCall = false;
831
-
832
- // add the current parameter set to the history
833
- this.#parameterHistory.push(lastParameterSet);
834
- return node;
835
- }
836
-
837
- /**
838
- * Initializes the session with the ticket and modelViewUrl.
839
- *
840
- * @returns promise with a scene graph node
841
- */
842
- public async init(parameterValues?: {
843
- [key: string]: string;
844
- }, retry = false): Promise<void> {
845
- if (this._initialized === true)
846
- throw new ShapeDiverViewerSessionError('Session.init: Session already initialized.');
847
-
848
- try {
849
- this._performanceEvaluator.startSection('sessionResponse');
850
-
851
- const parameterSet: { [key: string]: string } = {};
852
- // the slice here is done as a way for deep copying the string values
853
- for (const parameterNameOrId in parameterValues)
854
- parameterSet[parameterNameOrId] = (' ' + parameterValues[parameterNameOrId]).slice(1);
855
-
856
- if (this._ticket) {
857
- this._responseDto = await this._sdk.session.init(this._ticket, parameterSet, this._modelStateId, this._modelStateValidationMode);
858
- } else if (this._guid) {
859
- this._responseDto = await this._sdk.session.initForModel(this._guid, parameterSet, this._modelStateId, this._modelStateValidationMode);
860
- } else {
861
- // we should never get here
862
- throw new ShapeDiverViewerSessionError('Session.init: Initialization of session failed. Neither a ticket nor a guid are available.');
863
- }
864
- this._performanceEvaluator.endSection('sessionResponse');
865
-
866
- this._viewerSettings = this._responseDto.viewer?.config;
867
- this._viewerSettingsVersionBackend = this._responseDto.viewerSettingsVersion || latestVersion;
868
- this._sessionId = this._responseDto.sessionId;
869
- this._modelId = this._responseDto.model?.id;
870
- this._modelState = this._responseDto.modelState;
871
-
872
- this._httpClient.addDataLoading(this._sessionId!, {
873
- getAsset: this._sdk.asset.getAsset.bind(this._sdk.asset),
874
- downloadTexture: this._sdk.asset.downloadImage.bind(this._sdk.asset),
875
- });
876
-
877
- this._settingsEngine.loadSettings(this._viewerSettings);
878
-
879
- if (!this._sessionId)
880
- throw new ShapeDiverViewerSessionError('Session.init: Initialization of session failed. ResponseDto did not have a sessionId.');
881
- if (!this._modelId)
882
- throw new ShapeDiverViewerSessionError('Session.init: Initialization of session failed. ResponseDto did not have a model.id.');
883
-
884
- this.updateResponseDto(this._responseDto, parameterSet);
885
- this._initialized = true;
886
- } catch (e) {
887
- await this.handleError(e, retry);
888
- return await this.init(parameterValues, true);
889
- }
890
- }
891
-
892
- public async loadCachedOutputsParallel(outputMapping: { [key: string]: string }, taskEventInfo?: OutputLoaderTaskEventInfo, retry = false): Promise<{ [key: string]: ITreeNode | undefined }> {
893
- this.checkAvailability();
894
- // if there is already task event info, use it
895
- // this happens after a retry
896
- const eventId = taskEventInfo ? taskEventInfo.eventId : this._uuidGenerator.create();
897
- const eventType = taskEventInfo ? taskEventInfo.type : TASK_TYPE.SESSION_OUTPUTS_LOADING;
898
- const eventData = taskEventInfo ? taskEventInfo.data : { sessionId: this.id };
899
-
900
- taskEventInfo = taskEventInfo ? taskEventInfo : {
901
- eventId,
902
- type: eventType,
903
- progressRange: {
904
- min: 0,
905
- max: 1
906
- },
907
- data: eventData
908
- };
909
-
910
- try {
911
- // send start event if this function was called initially
912
- if (!taskEventInfo) {
913
- const eventStart: ITaskEvent = { type: eventType, id: eventId, progress: 0, data: eventData, status: 'Loading cached outputs' };
914
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_START, eventStart);
915
- }
916
-
917
- // get the cached outputs
918
- const responseDto = await this._sdk.output.getCache(this._sessionId!, outputMapping);
919
-
920
- // create atomic output api objects for them
921
- const outputs: {
922
- [key: string]: IOutput;
923
- } = {};
924
- for (const outputId in responseDto.outputs) {
925
- responseDto.outputs[outputId].id = outputId;
926
- outputs[outputId] = new Output(<ShapeDiverResponseOutput>responseDto.outputs[outputId], this);
927
- }
928
-
929
- // process the output data
930
- const node = await this._outputLoader.loadOutputs(this._responseDto!.model?.name || 'model', outputs, {}, taskEventInfo, false);
931
-
932
- // send the end event once done
933
- const eventEnd: ITaskEvent = { type: eventType, id: eventId, progress: 1, data: eventData, status: 'Loaded cached outputs' };
934
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_END, eventEnd);
935
-
936
- // create a mapping with a dictionary for the id of the outputs
937
- const outputNodeMapping: { [key: string]: ITreeNode | undefined } = {};
938
- for (const outputId in outputMapping)
939
- outputNodeMapping[outputId] = node.children.find(n => n.name === outputId);
940
-
941
- return outputNodeMapping;
942
- }
943
- catch (e) {
944
- await this.handleError(e, retry);
945
- return await this.loadCachedOutputsParallel(outputMapping, taskEventInfo, true);
946
- }
947
- }
948
-
949
- /**
950
- * Load the outputs and return the scene graph node of the result.
951
- * In case the outputs have a delay property, another customization request with the parameter set is sent.
952
- *
953
- * @param parameters the parameter set to update the session
954
- * @param outputs the outputs to load
955
- * @returns promise with a scene graph node
956
- */
957
- public async loadOutputs(cancelRequest: () => boolean = () => false, taskEventInfo: OutputLoaderTaskEventInfo, retry = false): Promise<ISessionTreeNode> {
958
- this.checkAvailability();
959
-
960
- const o = Object.assign({}, this._outputs);
961
- const of = Object.assign({}, this._outputsFreeze);
962
- try {
963
- const node = await this._outputLoader.loadOutputs(this._responseDto!.model?.name || 'model', o, of, taskEventInfo);
964
- node.data.push(new SessionData(this._responseDto!));
965
- if (cancelRequest()) return node;
966
- node.excludeViewports = JSON.parse(JSON.stringify(this._excludeViewports));
967
- return node;
968
- }
969
- catch (e) {
970
- if (e instanceof OutputDelayException) {
971
- await this.timeout(e.delay);
972
- } else {
973
- await this.handleError(e, retry);
974
- if (cancelRequest()) return new SessionTreeNode();
975
- return await this.loadOutputs(cancelRequest, taskEventInfo, true);
976
- }
977
-
978
- if (cancelRequest()) return new SessionTreeNode();
979
- const outputMapping: { [key: string]: string } = {};
980
- for (const output in o)
981
- outputMapping[output] = o[output].version;
982
-
983
- try {
984
- const responseDto = await this._sdk.output.getCache(this._sessionId!, outputMapping);
985
- if (cancelRequest()) return new SessionTreeNode();
986
- this.updateResponseDto(responseDto);
987
- return await this.loadOutputs(cancelRequest, taskEventInfo);
988
- } catch (e) {
989
- await this.handleError(e, retry);
990
- if (cancelRequest()) return new SessionTreeNode();
991
- return await this.loadOutputs(cancelRequest, taskEventInfo, true);
992
- }
993
- }
994
- }
995
-
996
- /**
997
- * Load the outputs and return the scene graph node of the result.
998
- * In case the outputs have a delay property, another customization request with the parameter set is sent.
999
- *
1000
- * @param parameters the parameter set to update the session
1001
- * @param outputs the outputs to load
1002
- * @returns promise with a scene graph node
1003
- */
1004
- public async loadOutputsParallel(responseDto: ShapeDiverResponseDto, cancelRequest: () => boolean = () => false, taskEventInfo: OutputLoaderTaskEventInfo, retry = false): Promise<ISessionTreeNode> {
1005
- this.checkAvailability();
1006
-
1007
- const outputs: {
1008
- [key: string]: IOutput;
1009
- } = {};
1010
- const outputsFreeze: {
1011
- [key: string]: boolean;
1012
- } = {};
1013
-
1014
- for (const outputId in responseDto.outputs) {
1015
- responseDto.outputs[outputId].id = outputId;
1016
- if (this.outputsFreeze[outputId] === undefined) outputsFreeze[outputId] = false;
1017
- outputs[outputId] = new Output(<ShapeDiverResponseOutput>responseDto.outputs[outputId], this);
1018
- }
1019
-
1020
- try {
1021
- const node = await this._outputLoader.loadOutputs(this._responseDto!.model?.name || 'model', outputs, outputsFreeze, taskEventInfo);
1022
- node.data.push(new SessionData(responseDto));
1023
- return node;
1024
- }
1025
- catch (e) {
1026
- if (e instanceof OutputDelayException) {
1027
- await this.timeout(e.delay);
1028
- } else {
1029
- await this.handleError(e, retry);
1030
- if (cancelRequest()) return new SessionTreeNode();
1031
- return await this.loadOutputsParallel(responseDto, cancelRequest, taskEventInfo, true);
1032
- }
1033
-
1034
- if (cancelRequest()) return new SessionTreeNode();
1035
- const outputMapping: { [key: string]: string } = {};
1036
- for (const output in outputs)
1037
- outputMapping[output] = outputs[output].version;
1038
-
1039
- try {
1040
- const responseDto = await this._sdk.output.getCache(this._sessionId!, outputMapping);
1041
- if (cancelRequest()) return new SessionTreeNode();
1042
- this.updateResponseDto(responseDto);
1043
- return await this.loadOutputsParallel(responseDto, cancelRequest, taskEventInfo);
1044
- } catch (e) {
1045
- await this.handleError(e, retry);
1046
- if (cancelRequest()) return new SessionTreeNode();
1047
- return await this.loadOutputsParallel(responseDto, cancelRequest, taskEventInfo, true);
1048
- }
1049
- }
1050
- }
1051
-
1052
- public async requestExport(exportId: string, parameters: { [key: string]: unknown }, maxWaitTime: number, retry = false): Promise<ShapeDiverResponseExport> {
1053
- this.checkAvailability('export');
1054
- try {
1055
- await this.uploadFileParameters(parameters);
1056
- const requestParameterSet = this.cleanExportParameters(parameters);
1057
- const responseDto = await this._sdk.utils.submitAndWaitForExport(this._sdk, this._sessionId!, { exports: { id: exportId }, parameters: requestParameterSet }, maxWaitTime);
1058
- this.updateResponseDto(responseDto);
1059
- return this.exports[exportId];
1060
- } catch (e) {
1061
- await this.handleError(e, retry);
1062
- return await this.requestExport(exportId, parameters, maxWaitTime, true);
1063
- }
1064
- }
1065
-
1066
- public async requestExports(body: ShapeDiverRequestExport, loadOutputs: boolean = false, maxWaitMsec?: number, retry = false): Promise<ShapeDiverResponseDto> {
1067
- let processId;
1068
- this.checkAvailability('export');
1069
- try {
1070
- // activate the busy mode if outputs are loaded
1071
- if (loadOutputs === true && this._allowOutputLoading === true &&
1072
- body.outputs && Object.keys(body.outputs).length > 0) {
1073
- processId = this._uuidGenerator.create();
1074
- this.addBusyMode(processId);
1075
- }
1076
-
1077
- await this.uploadFileParameters(body.parameters as { [key: string]: string | File | Blob });
1078
- const requestParameterSet = this.cleanExportParameters(body.parameters);
1079
-
1080
- const responseDto = await this._sdk.utils.submitAndWaitForExport(this._sdk, this._sessionId!, { exports: body.exports, parameters: requestParameterSet, outputs: body.outputs, max_wait_time: body.max_wait_time }, maxWaitMsec);
1081
- this.updateResponseDto(responseDto);
1082
- if (loadOutputs === true && this._allowOutputLoading === true) this.updateOutputs();
1083
-
1084
- if (processId) this.removeBusyMode(processId);
1085
- return responseDto;
1086
- } catch (e) {
1087
- if (processId) this.removeBusyMode(processId);
1088
- await this.handleError(e, retry);
1089
- return await this.requestExports(body, loadOutputs, maxWaitMsec, true);
1090
- }
1091
- }
1092
-
1093
- public resetSettings(sections?: ISettingsSections): void {
1094
- if (!this._responseDto)
1095
- throw new ShapeDiverViewerSessionError('Session.resetSettings: responseDto not available.');
1096
-
1097
- sections = sections || {};
1098
- if (sections.session === undefined) {
1099
- sections.session = {
1100
- parameter: { displayname: true, order: true, hidden: true },
1101
- export: { displayname: true, order: true, hidden: true }
1102
- };
1103
- }
1104
- if (sections.session.parameter === undefined)
1105
- sections.session.parameter = { displayname: true, order: true, hidden: true, value: true };
1106
- if (sections.session.export === undefined)
1107
- sections.session.export = { displayname: true, order: true, hidden: true };
1108
- if (sections.viewport === undefined)
1109
- sections.viewport = { ar: true, scene: true, camera: true, light: true, environment: true, general: true, postprocessing: true };
1110
-
1111
- return this.applySettings(this._responseDto, sections);
1112
- }
1113
-
1114
- public async saveDefaultParameterValues(): Promise<boolean> {
1115
- this._logger.debugLow(`Session(${this.id}).saveDefaultParameters: Saving default parameters.`);
1116
- const response = await this.saveDefaultParameters();
1117
- if (response) {
1118
- this._logger.debug(`Session(${this.id}).saveDefaultParameters: Saved default parameters.`);
1119
- } else {
1120
- throw new ShapeDiverViewerSessionError(`Session(${this.id}).saveDefaultParameters: Could not save default parameters.`);
1121
- }
1122
- return response;
1123
- }
1124
-
1125
- public async saveDefaultParameters(retry = false): Promise<boolean> {
1126
- this.checkAvailability('defaultparam', true);
1127
- try {
1128
- await this._sdk.model.setDefaultParams(this._modelId!, this._parameterValues);
1129
- return true;
1130
- } catch (e) {
1131
- await this.handleError(e, retry);
1132
- return await this.saveDefaultParameters(true);
1133
- }
1134
- }
1135
-
1136
- /**
1137
- * Save the export properties for displayname, order, tooltip and hidden
1138
- *
1139
- * @param exports
1140
- * @returns
1141
- */
1142
- public async saveExportProperties(exports: {
1143
- [key: string]: {
1144
- displayname: string,
1145
- hidden: boolean,
1146
- order: number,
1147
- tooltip: string
1148
- }
1149
- }, retry = false): Promise<boolean> {
1150
- this.checkAvailability('export-definition', true);
1151
- try {
1152
- await this._sdk.export.updateDefinitions(this._modelId!, exports);
1153
- return true;
1154
- } catch (e) {
1155
- await this.handleError(e, retry);
1156
- return await this.saveExportProperties(exports, true);
1157
- }
1158
- }
1159
-
1160
- /**
1161
- * Save the output properties for displayname, order, tooltip and hidden
1162
- *
1163
- * @param outputs
1164
- * @returns
1165
- */
1166
- public async saveOutputProperties(outputs: {
1167
- [key: string]: {
1168
- displayname: string,
1169
- hidden: boolean,
1170
- order: number,
1171
- tooltip: string
1172
- }
1173
- }, retry = false): Promise<boolean> {
1174
- this.checkAvailability('output-definition', true);
1175
- try {
1176
- await this._sdk.output.updateDefinitions(this._modelId!, outputs);
1177
- return true;
1178
- } catch (e) {
1179
- await this.handleError(e, retry);
1180
- return await this.saveOutputProperties(outputs, true);
1181
- }
1182
- }
1183
-
1184
- /**
1185
- * Save the parameter properties for displayname, order, tooltip and hidden
1186
- *
1187
- * @param parameters
1188
- * @returns
1189
- */
1190
- public async saveParameterProperties(parameters: {
1191
- [key: string]: {
1192
- displayname: string,
1193
- hidden: boolean,
1194
- order: number,
1195
- tooltip: string
1196
- }
1197
- }, retry = false): Promise<boolean> {
1198
- this.checkAvailability('parameter-definition', true);
1199
- try {
1200
- await this._sdk.model.updateParameterDefinitions(this._modelId!, parameters);
1201
- return true;
1202
- } catch (e) {
1203
- await this.handleError(e, retry);
1204
- return await this.saveParameterProperties(parameters, true);
1205
- }
1206
- }
1207
-
1208
- public async saveSettings(json: unknown, retry = false): Promise<boolean> {
1209
- this.checkAvailability('configure', true);
1210
-
1211
- try {
1212
- validate(json, <versions>this._viewerSettingsVersion);
1213
-
1214
- // if viewer settings version is higher than backend settings version
1215
- // convert to backend settings version
1216
- if (+this._viewerSettingsVersion > +this._viewerSettingsVersionBackend)
1217
- json = convert(json, <versions>this._viewerSettingsVersionBackend);
1218
- } catch (e) {
1219
- throw new ShapeDiverViewerSettingsError('Session.saveSettings: Settings could not be validated. ' + (<Error>e).message, <Error>e);
1220
- }
1221
-
1222
- try {
1223
- await this._sdk.model.updateConfig(this._modelId!, json as ShapeDiverRequestConfigure);
1224
- return true;
1225
- } catch (e) {
1226
- await this.handleError(e, retry);
1227
- return await this.saveSettings(json, true);
1228
- }
1229
- }
1230
-
1231
- public async saveUiProperties(saveInSettings: boolean = true): Promise<boolean> {
1232
- this._logger.debugLow(`Session(${this.id}).saveSessionProperties: Saving session properties.`);
1233
-
1234
- // settings saving
1235
- this._saveSessionSettings();
1236
-
1237
- let properties: {
1238
- [key: string]: {
1239
- displayname: string,
1240
- hidden: boolean,
1241
- order: number,
1242
- tooltip: string
1243
- }
1244
- } = {};
1245
- for (const p in this.parameters) {
1246
- properties[p] = {
1247
- displayname: this.parameters[p].displayname !== undefined ? this.parameters[p].displayname! : '',
1248
- hidden: this.parameters[p].hidden !== undefined ? this.parameters[p].hidden : false,
1249
- order: this.parameters[p].order !== undefined ? this.parameters[p].order! : 0,
1250
- tooltip: this.parameters[p].tooltip !== undefined ? this.parameters[p].tooltip! : '',
1251
- };
1252
- }
1253
- const responseP = Object.values(properties).length !== 0 ? await this.saveParameterProperties(properties) : true;
1254
-
1255
- properties = {};
1256
- for (const e in this.exports) {
1257
- properties[e] = {
1258
- displayname: this.exports[e].displayname !== undefined ? this.exports[e].displayname! : '',
1259
- hidden: this.exports[e].hidden !== undefined ? this.exports[e].hidden : false,
1260
- order: this.exports[e].order !== undefined ? this.exports[e].order! : 0,
1261
- tooltip: this.exports[e].tooltip !== undefined ? this.exports[e].tooltip! : '',
1262
- };
1263
- }
1264
- const responseE = Object.values(properties).length !== 0 ? await this.saveExportProperties(properties) : true;
1265
-
1266
- properties = {};
1267
- for (const o in this.outputs) {
1268
- properties[o] = {
1269
- displayname: this.outputs[o].displayname !== undefined ? this.outputs[o].displayname! : '',
1270
- hidden: this.outputs[o].hidden !== undefined ? this.outputs[o].hidden : false,
1271
- order: this.outputs[o].order !== undefined ? this.outputs[o].order! : 0,
1272
- tooltip: this.outputs[o].tooltip !== undefined ? this.outputs[o].tooltip! : '',
1273
- };
1274
- }
1275
- const responseO = Object.values(properties).length !== 0 ? await this.saveOutputProperties(properties) : true;
1276
-
1277
- // save partial settings
1278
- const response = saveInSettings ? await this.saveSettings(this._settingsEngine.settings) : true;
1279
-
1280
- if (response && responseP && responseO && responseE) {
1281
- this._logger.debug(`Session(${this.id}).saveSessionProperties: Saved session properties.`);
1282
- } else {
1283
- this._logger.warn(`Session(${this.id}).saveSessionProperties: Could not save session properties.`);
1284
- }
1285
- return response && responseP && responseO && responseE;
1286
- }
1287
-
1288
- public async setJwtToken(value: string) {
1289
- this.checkAvailability();
1290
-
1291
- this._jwtToken = value;
1292
- try {
1293
- this._sdk.setConfigurationValue(ShapeDiverSdkConfigType.JWT_TOKEN, value);
1294
- const responseDto = await this._sdk.session.default(this._sessionId!);
1295
- if (this._responseDto) this._responseDto.actions = responseDto.actions;
1296
- } catch (e) {
1297
- throw this._httpClient.convertError(e);
1298
- }
1299
- }
1300
-
1301
- public async updateOutputs(taskEventInfo?: OutputLoaderTaskEventInfo, waitForViewportUpdate: boolean = false): Promise<ITreeNode> {
1302
- const eventId = taskEventInfo ? taskEventInfo.eventId : this._uuidGenerator.create();
1303
- const eventType = taskEventInfo ? taskEventInfo.type : TASK_TYPE.SESSION_OUTPUTS_UPDATE;
1304
- const eventData = taskEventInfo ? taskEventInfo.data : { sessionId: this.id };
1305
-
1306
- if (!taskEventInfo) {
1307
- const eventStart: ITaskEvent = { type: eventType, id: eventId, progress: 0, data: eventData, status: 'Updating outputs' };
1308
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_START, eventStart);
1309
- }
1310
-
1311
- const customizationId = this._uuidGenerator.create();
1312
- const oldNode = this.node;
1313
- this.#customizationProcess = customizationId;
1314
-
1315
- this._logger.debugLow(`Session(${this.id}).updateOutputs: Updating Outputs.`);
1316
-
1317
- this.addBusyMode(customizationId);
1318
-
1319
- const eventRequest: ITaskEvent = { type: eventType, id: eventId, progress: taskEventInfo ? (taskEventInfo.progressRange.max - taskEventInfo.progressRange.min) * 0.1 + taskEventInfo.progressRange.min : 0.1, data: eventData, status: 'Loading outputs' };
1320
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_PROCESS, eventRequest);
1321
-
1322
- const oldOutputVersions = this._outputLoader.getCurrentOutputVersions();
1323
-
1324
- const newNode = await this.loadOutputs(() => this.#customizationProcess !== customizationId, {
1325
- eventId,
1326
- type: eventType,
1327
- progressRange: {
1328
- min: taskEventInfo ? (taskEventInfo.progressRange.max - taskEventInfo.progressRange.min) * 0.1 + taskEventInfo.progressRange.min : 0.1,
1329
- max: taskEventInfo ? (taskEventInfo.progressRange.max - taskEventInfo.progressRange.min) * 0.9 + taskEventInfo.progressRange.min : 0.9
1330
- },
1331
- data: eventData
1332
- });
1333
-
1334
- const newOutputVersions = this._outputLoader.getCurrentOutputVersions();
1335
-
1336
- const eventSceneUpdate: ITaskEvent = { type: eventType, id: eventId, progress: taskEventInfo ? (taskEventInfo.progressRange.max - taskEventInfo.progressRange.min) * 0.9 + taskEventInfo.progressRange.min : 0.9, data: eventData, status: 'Updating scene' };
1337
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_PROCESS, eventSceneUpdate);
1338
-
1339
- // OPTION TO SKIP - PART 1
1340
- const cancelResult = this.cancelProcess(customizationId, eventId, eventType, taskEventInfo ? (taskEventInfo.progressRange.max - taskEventInfo.progressRange.min) * 1 + taskEventInfo.progressRange.min : 1, eventData, newNode);
1341
- if (cancelResult) return cancelResult;
1342
-
1343
- // call the update callbacks
1344
- if (waitForViewportUpdate === false) {
1345
- for (const outputId in this.outputs) {
1346
- if (oldOutputVersions[outputId] !== newOutputVersions[outputId]) {
1347
- this._eventEngine.emitEvent(EVENTTYPE.OUTPUT.OUTPUT_UPDATED, {
1348
- outputId: outputId,
1349
- outputVersion: newOutputVersions[outputId],
1350
- newNode: newNode.children.find(c => c.name === outputId)!,
1351
- oldNode: oldNode.children.find(c => c.name === outputId)!
1352
- });
1353
- }
1354
- }
1355
-
1356
- await this.waitForUpdateCallbacks(newOutputVersions, oldOutputVersions, newNode, oldNode);
1357
-
1358
- // OPTION TO SKIP - PART 2
1359
- const cancelResult = this.cancelProcess(customizationId, eventId, eventType, taskEventInfo ? (taskEventInfo.progressRange.max - taskEventInfo.progressRange.min) * 1 + taskEventInfo.progressRange.min : 1, eventData, newNode);
1360
- if (cancelResult) return cancelResult;
1361
- }
1362
-
1363
- if (this.automaticSceneUpdate) this.removeFromSceneTree(this.node);
1364
- this._node = newNode;
1365
- if (this.automaticSceneUpdate && this._closed === false) this.addToSceneTree(this.node);
1366
-
1367
- this._logger.debug(`Session(${this.id}).updateOutputs: Updating outputs finished, updating geometry.`);
1368
-
1369
- // set the output content to what has been updated
1370
- for (const outputId in this.outputs) {
1371
- this.outputs[outputId].updateOutput(
1372
- newNode.children.find(c => c.name === outputId)!,
1373
- oldNode.children.find(c => c.name === outputId)!
1374
- );
1375
- }
1376
-
1377
- // set the export definitions
1378
- for (const exportId in this.exports)
1379
- this.exports[exportId].updateExport();
1380
-
1381
- this._warningCreator();
1382
- this.node.excludeViewports = JSON.parse(JSON.stringify(this._excludeViewports));
1383
-
1384
- this.removeBusyMode(customizationId);
1385
-
1386
- this._logger.debug(`Session(${this.id}).updateOutputs: Updated outputs.`);
1387
-
1388
- if (!taskEventInfo) {
1389
- const eventEnd: ITaskEvent = { type: eventType, id: eventId, progress: 1, data: eventData, status: 'Outputs updated' };
1390
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_END, eventEnd);
1391
- }
1392
-
1393
- // update the viewports
1394
- if (waitForViewportUpdate) {
1395
- for (const r in this._stateEngine.viewportEngines)
1396
- if (this._stateEngine.viewportEngines[r] && !this.excludeViewports.includes(this._stateEngine.viewportEngines[r]!.id))
1397
- this._stateEngine.viewportEngines[r]!.update(`SessionEngine(${this.id}).updateOutputs`);
1398
-
1399
- for (const outputId in this.outputs) {
1400
- if (oldOutputVersions[outputId] !== newOutputVersions[outputId]) {
1401
- this._eventEngine.emitEvent(EVENTTYPE.OUTPUT.OUTPUT_UPDATED, {
1402
- outputId: outputId,
1403
- outputVersion: newOutputVersions[outputId],
1404
- newNode: newNode.children.find(c => c.name === outputId)!,
1405
- oldNode: oldNode.children.find(c => c.name === outputId)!
1406
- });
1407
- }
1408
- }
1409
-
1410
- await this.waitForUpdateCallbacks(newOutputVersions, oldOutputVersions, newNode, oldNode);
1411
-
1412
- // OPTION TO SKIP - PART 3
1413
- const cancelResult = this.cancelProcess(customizationId, eventId, eventType, taskEventInfo ? (taskEventInfo.progressRange.max - taskEventInfo.progressRange.min) * 1 + taskEventInfo.progressRange.min : 1, eventData, newNode);
1414
- if (cancelResult) return cancelResult;
1415
- }
1416
-
1417
- return this.node;
1418
- }
1419
-
1420
- public async uploadFile(parameterId: string, data: File, type: string, retry = false): Promise<string> {
1421
- this.checkAvailability('file-upload');
1422
- try {
1423
- const result = await this._sdk.file.requestUpload(this._sessionId!, {
1424
- [parameterId]: {
1425
- size: data.size,
1426
- format: type,
1427
- filename: data.name === '' ? undefined : data.name
1428
- }
1429
- });
1430
-
1431
- if (result && result.asset && result.asset.file && result.asset.file[parameterId]) {
1432
- const fileAsset = result.asset.file[parameterId];
1433
- await this._sdk.utils.uploadAsset(
1434
- fileAsset.href,
1435
- await data.arrayBuffer(),
1436
- fileAsset.headers
1437
- );
1438
- return fileAsset.id;
1439
- } else {
1440
- throw new ShapeDiverViewerSessionError('Session.uploadFile: Upload reply has not the required format.');
1441
- }
1442
- } catch (e) {
1443
- await this.handleError(e, retry);
1444
- return await this.uploadFile(parameterId, data, type, true);
1445
- }
1446
- }
1447
-
1448
- /**
1449
- * Uploads all file parameters and returns the file parameter values.
1450
- * If parameterValues is provided, the file parameter values are added to it.
1451
- *
1452
- * @param parameterValues
1453
- * @returns
1454
- */
1455
- public async uploadFileParameters(parameterValues?: { [key: string]: unknown }): Promise<{ [key: string]: string }> {
1456
- const parameterValueSet = parameterValues !== undefined ? this.getFileParameterSet(parameterValues) : undefined;
1457
-
1458
- const fileParameterValues: { [key: string]: string } = {};
1459
- // load file parameter first
1460
- for (const parameterId in this.parameters) {
1461
- if (this.parameters[parameterId] instanceof FileParameter) {
1462
- fileParameterValues[parameterId] = await (<IFileParameter>this.parameters[parameterId]).upload(parameterValueSet ? parameterValueSet[parameterId] : undefined);
1463
- if (parameterValues) {
1464
- parameterValues[parameterId] = fileParameterValues[parameterId];
1465
-
1466
- // if the parameter value of the file parameter was used, set the value to the parameter
1467
- if (parameterValues[parameterId] === undefined && this.parameters[parameterId].value !== fileParameterValues[parameterId])
1468
- this.parameters[parameterId].value = fileParameterValues[parameterId];
1469
- } else if (this.parameters[parameterId].value !== fileParameterValues[parameterId]) {
1470
- this.parameters[parameterId].value = fileParameterValues[parameterId];
1471
- }
1472
- }
1473
- }
1474
-
1475
- return fileParameterValues;
1476
- }
1477
-
1478
- public async uploadGLTF(blob: Blob, conversion: ShapeDiverRequestGltfUploadQueryConversion = ShapeDiverRequestGltfUploadQueryConversion.NONE, retry = false): Promise<ShapeDiverResponseDto> {
1479
- this.checkAvailability('gltf-upload');
1480
- try {
1481
- const responseDto = await this._sdk.gltf.upload(this._sessionId!, await blob.arrayBuffer(), 'model/gltf-binary', conversion);
1482
- if (!responseDto || !responseDto.gltf || !responseDto.gltf.href)
1483
- throw new ShapeDiverViewerSessionError('Session.uploadGLTF: Upload reply has not the required format.');
1484
- return responseDto;
1485
- } catch (e) {
1486
- await this.handleError(e, retry);
1487
- return await this.uploadGLTF(blob, conversion, true);
1488
- }
1489
- }
1490
-
1491
- // #endregion Public Methods (30)
1492
-
1493
- // #region Private Methods (18)
1494
-
1495
- private _saveSessionSettings() {
1496
- const parameters = this.parameters;
1497
- const exports = this.exports;
1498
-
1499
- const sessionProperties: {
1500
- [key: string]: {
1501
- order: number;
1502
- displayname: string;
1503
- hidden: boolean;
1504
- }
1505
- } = {};
1506
- for (const p in parameters) {
1507
- sessionProperties[p] = {
1508
- order: parameters[p].order || 0,
1509
- displayname: parameters[p].displayname || '',
1510
- hidden: parameters[p].hidden
1511
- };
1512
- }
1513
- for (const e in exports) {
1514
- sessionProperties[e] = {
1515
- order: exports[e].order || 0,
1516
- displayname: exports[e].displayname || '',
1517
- hidden: exports[e].hidden
1518
- };
1519
- }
1520
- this._settingsEngine.session = sessionProperties;
1521
- }
1522
-
1523
- private _warningCreator() {
1524
- // set the output content to what has been updated
1525
- for (const outputId in this.outputs) {
1526
- let warning: string = '';
1527
- if (this.outputs[outputId].msg)
1528
- warning += `\n\t- ${this.outputs[outputId].msg}`;
1529
- if (this.outputs[outputId].status_collect && this.outputs[outputId].status_collect !== ShapeDiverResponseModelComputationStatus.SUCCESS)
1530
- warning += `\n\t- status_collect is ${this.outputs[outputId].status_collect}`;
1531
- if (this.outputs[outputId].status_computation && this.outputs[outputId].status_computation !== ShapeDiverResponseModelComputationStatus.SUCCESS)
1532
- warning += `\n\t- status_computation is ${this.outputs[outputId].status_computation}`;
1533
- if (warning)
1534
- this._logger.warn(`\nOutput(${outputId}):${warning}`);
1535
- }
1536
-
1537
- // set the export definitions
1538
- for (const exportId in this.exports) {
1539
- let warning: string = '';
1540
- if (this.exports[exportId].msg)
1541
- warning += `\n\t- ${this.exports[exportId].msg}`;
1542
- if (this.exports[exportId].status_collect && this.exports[exportId].status_collect !== ShapeDiverResponseModelComputationStatus.SUCCESS)
1543
- warning += `\n\t- status_collect is ${this.exports[exportId].status_collect}`;
1544
- if (this.exports[exportId].status_computation && this.exports[exportId].status_computation !== ShapeDiverResponseModelComputationStatus.SUCCESS)
1545
- warning += `\n\t- status_computation is ${this.exports[exportId].status_computation}`;
1546
- if (warning)
1547
- this._logger.warn(`\nExport(${exportId}):${warning}`);
1548
- }
1549
- }
1550
-
1551
- private addBusyMode(busyId: string) {
1552
- for (const r in this._stateEngine.viewportEngines) {
1553
- if (this._stateEngine.viewportEngines[r] && !this.excludeViewports.includes(r)) {
1554
- this._stateEngine.viewportEngines[r]!.busy.push(busyId);
1555
- this.#customizationBusyModes.push(busyId);
1556
- }
1557
- }
1558
- }
1559
-
1560
- private addToSceneTree(node: ITreeNode) {
1561
- this._sceneTree.addNode(node);
1562
- this._sceneTree.root.updateVersion();
1563
- }
1564
-
1565
- private cancelProcess(customizationId: string, eventId: string, eventType: TASK_TYPE, eventProgress: number, eventData: unknown, newNode: ITreeNode = new SessionTreeNode()): ITreeNode | undefined {
1566
- if (this.#customizationProcess !== customizationId) {
1567
- this.removeBusyMode(customizationId);
1568
-
1569
- const eventCancel: ITaskEvent = {
1570
- type: eventType,
1571
- id: eventId,
1572
- progress: eventProgress,
1573
- data: eventData,
1574
- status: 'The request was exceeded by another customization request'
1575
- };
1576
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_CANCEL, eventCancel);
1577
- this._logger.debug(`Session(${this.id}).cancelProcess: The request was was exceeded by another request.`);
1578
- return newNode;
1579
- } else if ((this._closed as boolean) === true) {
1580
- this.removeBusyMode(customizationId);
1581
-
1582
- this._logger.debug(`Session(${this.id}).cancelProcess: The session was closed during the request.`);
1583
-
1584
- const eventCancel: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 1, data: { sessionId: this.id }, status: 'The session was closed during the request.' };
1585
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_CANCEL, eventCancel);
1586
- return new SessionTreeNode();
1587
- }
1588
- }
1589
-
1590
- private checkAvailability(action?: string, checkForModelId = false) {
1591
- if (!this._responseDto)
1592
- throw new ShapeDiverViewerSessionError('Session.checkAvailability: responseDto not available.');
1593
-
1594
- if (!this._sessionId)
1595
- throw new ShapeDiverViewerSessionError('Session.checkAvailability: sessionId not available.');
1596
-
1597
- if (checkForModelId && !this._modelId)
1598
- throw new ShapeDiverViewerSessionError('Session.checkAvailability: modelId not available.');
1599
-
1600
- if (action && !this._responseDto.actions)
1601
- throw new ShapeDiverViewerSessionError('Session.checkAvailability: actions not available.');
1602
-
1603
- const responseDtoAction = this._responseDto.actions?.find(a => a.name === action);
1604
- if (action && !responseDtoAction)
1605
- throw new ShapeDiverViewerSessionError(`Session.checkAvailability: action ${action} not available.`);
1606
- }
1607
-
1608
- private cleanExportParameters(parameters: { [key: string]: unknown }): ShapeDiverRequestCustomization {
1609
- const requestParameterSet: ShapeDiverRequestCustomization = {};
1610
-
1611
- // first step, we convert all our names and displaynames to ids
1612
- for (const parameterIdOrName in parameters) {
1613
- // we prioritize id, then name and then displayname
1614
- // if there are two parameters with the same name or displayname, we take the one that is found first (no way for us to evaluate which one the user meant)
1615
- const parameterObject = Object.values(this._parameters).find(p => p.id === parameterIdOrName || p.name === parameterIdOrName || p.displayname === parameterIdOrName);
1616
-
1617
- // in case the key of the key value pair was neither the id, name or displayname, skip
1618
- if (!parameterObject) continue;
1619
-
1620
- // copy into new dictionary
1621
- requestParameterSet[parameterObject.id] = (' ' + parameterObject.stringify(parameters[parameterIdOrName])).slice(1);
1622
- }
1623
-
1624
- // seconds step, fill all other parameter values that are currently not set
1625
- const currentParameters = this.parameterValues;
1626
- for (const parameterId in currentParameters) {
1627
- // if already set by input values, skip
1628
- if (requestParameterSet[parameterId] !== undefined) continue;
1629
-
1630
- // deep copy into new dictionary
1631
- requestParameterSet[parameterId] = (' ' + currentParameters[parameterId]).slice(1);
1632
- }
1633
-
1634
- return requestParameterSet;
1635
- }
1636
-
1637
- /**
1638
- * Create an interaction parameter based on the parameter definition.
1639
- *
1640
- * @param parameter
1641
- * @returns
1642
- */
1643
- private createInteractionParameter(parameter: ShapeDiverResponseParameter): IParameter<unknown> {
1644
- const result = validateInteractionParameterSettings(parameter.settings);
1645
- if (result.success) {
1646
- switch ((parameter.settings as IInteractionParameterSettings).type) {
1647
- case 'selection':
1648
- return new SelectionParameter(parameter, this);
1649
- case 'gumball':
1650
- return new GumballParameter(parameter, this);
1651
- }
1652
- } else {
1653
- this._logger.warn(`SessionEngine.createInteractionParameter: The value ${parameter.settings} is not a valid InteractionParameter: ${result.error.message}`);
1654
- }
1655
- return new Parameter<string>(parameter, this);
1656
- }
1657
-
1658
- private async customizeInternal(cancelRequest: () => boolean, taskEventInfo: OutputLoaderTaskEventInfo): Promise<ISessionTreeNode> {
1659
- return this.customizeSession(this._parameterValues, cancelRequest, taskEventInfo);
1660
- }
1661
-
1662
- private async customizeSession(parameters: { [key: string]: string }, cancelRequest: () => boolean, taskEventInfo: OutputLoaderTaskEventInfo, parallel = false, loadOutputs = true, retry = false): Promise<ISessionTreeNode> {
1663
- this.checkAvailability('customize');
1664
- try {
1665
- this._performanceEvaluator.startSection('sessionResponse');
1666
- const responseDto = await this._sdk.utils.submitAndWaitForCustomization(this._sdk, this._sessionId!, parameters);
1667
- this._performanceEvaluator.endSection('sessionResponse');
1668
- if (loadOutputs === true && this._allowOutputLoading === true) {
1669
- if (cancelRequest()) return new SessionTreeNode();
1670
- if (parallel === true) {
1671
- // special case, we load the outputs put don't add them to the scene
1672
- return this.loadOutputsParallel(responseDto, cancelRequest, taskEventInfo);
1673
- } else {
1674
- // default case, we load the outputs and return the nodes
1675
- this.updateResponseDto(responseDto);
1676
- return this.loadOutputs(cancelRequest, taskEventInfo);
1677
- }
1678
- } else {
1679
- // special case, we don't load the outputs and only return the responseDto
1680
- const node = new SessionTreeNode();
1681
- node.data.push(new SessionData(responseDto));
1682
- return node;
1683
- }
1684
- } catch (e) {
1685
- await this.handleError(e, retry);
1686
- if (cancelRequest()) return new SessionTreeNode();
1687
- return await this.customizeSession(parameters, cancelRequest, taskEventInfo, parallel, loadOutputs, true);
1688
- }
1689
- }
1690
-
1691
- /**
1692
- * Get all file parameters from the parameter set.
1693
- * If the parameter is not set in the parameter set, the value from the parameter object is used.
1694
- *
1695
- * @param parameters
1696
- * @returns
1697
- */
1698
- private getFileParameterSet(parameters: { [key: string]: unknown }): { [key: string]: string | File | Blob } {
1699
- const fileParameterSet: { [key: string]: string | File | Blob } = {};
1700
- for (const parameterId in this.parameters) {
1701
- if (this.parameters[parameterId] instanceof FileParameter) {
1702
- fileParameterSet[parameterId] = parameters[parameterId] !== undefined ? parameters[parameterId] as string | File | Blob : (this.parameters[parameterId] as FileParameter).value;
1703
- }
1704
- }
1705
- return fileParameterSet;
1706
- }
1707
-
1708
- private async handleError(e: ShapeDiverBackendError | ShapeDiverViewerError | Error | unknown, retry = false) {
1709
- if (isGBResponseError(e)) {
1710
- if (e.error === ShapeDiverResponseErrorType.SESSION_GONE_ERROR) {
1711
- // case 1: the session is no longer available
1712
- // we try to re-initialize the session 3 times, if that does not work, we close it
1713
-
1714
- this._logger.warn('The session has been closed, trying to re-initialize.');
1715
- if (this._sessionId) this._httpClient.removeDataLoading(this._sessionId);
1716
-
1717
- if (this._retryCounter < 3) {
1718
- // we retry this 3 times, the `retry` option in the init function is set to true and passed on
1719
- this._retryCounter = retry ? this._retryCounter + 1 : 1;
1720
- this._initialized = false;
1721
- await this.init(this.parameterValues, true);
1722
- } else {
1723
- // the retries were exceeded, we close the session
1724
- this._logger.warn('Tried to retry the connect multiple times, bearer token still not valid. Closing Session.');
1725
- // eslint-disable-next-line no-empty
1726
- try { await this._closeOnFailure(); } catch (e) { }
1727
- throw this._httpClient.convertError(e);
1728
- }
1729
- } else if (e.error === ShapeDiverResponseErrorType.JWT_VALIDATION_ERROR) {
1730
- // if any of the above errors occur, we try to get a new bearer token
1731
- // if we get a new one, we retry 3 times (by requiring new bearer tokens every time)
1732
- if (this._retryCounter < 3) {
1733
- if (this._refreshJwtToken) {
1734
- await this.setJwtToken(await this._refreshJwtToken());
1735
- this._retryCounter = retry ? this._retryCounter + 1 : 1;
1736
- this._logger.warn('Re-trying with new bearer token.');
1737
- } else {
1738
- // no bearer tokens are supplied, we close the session
1739
- this._logger.warn('No retry possible, no new bearer token was supplied. Closing Session.');
1740
- // eslint-disable-next-line no-empty
1741
- try { await this._closeOnFailure(); } catch (e) { }
1742
- throw this._httpClient.convertError(e);
1743
- }
1744
- } else {
1745
- // the retries were exceeded, we close the session
1746
- this._logger.warn('Tried to retry the connect multiple times, bearer token still not valid. Closing Session.');
1747
- // eslint-disable-next-line no-empty
1748
- try { await this._closeOnFailure(); } catch (e) { }
1749
- throw this._httpClient.convertError(e);
1750
- }
1751
- } else {
1752
- throw this._httpClient.convertError(e);
1753
- }
1754
- } else {
1755
- throw this._httpClient.convertError(e);
1756
- }
1757
- }
1758
-
1759
- /**
1760
- * Process the image input and return the image data and array buffer.
1761
- *
1762
- * In the case of the image being a Blob or File, the image data is constructed from the Blob or File.
1763
- * In the case of the image being a string, we check if it is a data URL or a URL.
1764
- * If it is a data URL, we convert it to a Blob and construct the image data from the Blob.
1765
- * If it is a URL, we download the image and return the image data and array buffer.
1766
- *
1767
- * @param image
1768
- * @returns
1769
- */
1770
- private async processImageInput(image: (() => string) | string | Blob | File): Promise<{
1771
- imageData: ShapeDiverRequestFileUploadPart,
1772
- arrayBuffer: ArrayBuffer
1773
- }> {
1774
- if (image instanceof File || image instanceof Blob)
1775
- return this._converter.constructImageData(image);
1776
-
1777
- let imageString: string;
1778
- if (typeof image === 'function') {
1779
- imageString = image();
1780
- } else {
1781
- imageString = image;
1782
- }
1783
-
1784
- if (imageString.startsWith('data:')) {
1785
- // case where the image is a data URL
1786
- const { blob, arrayBuffer } = this._converter.dataURLtoBlob(imageString);
1787
- return {
1788
- imageData: {
1789
- format: blob.type,
1790
- size: blob.size
1791
- },
1792
- arrayBuffer
1793
- };
1794
- } else {
1795
- // case where the image is a URL
1796
- const [arrayBuffer, type] = await this._sdk.asset.downloadImage(this._sessionId!, imageString);
1797
- return {
1798
- imageData: {
1799
- format: type,
1800
- size: arrayBuffer.byteLength
1801
- },
1802
- arrayBuffer
1803
- };
1804
- }
1805
- }
1806
-
1807
- private removeBusyMode(busyId: string) {
1808
- for (const r in this._stateEngine.viewportEngines) {
1809
- if (this._stateEngine.viewportEngines[r] && this._stateEngine.viewportEngines[r]!.busy.includes(busyId))
1810
- this._stateEngine.viewportEngines[r]!.busy.splice(this._stateEngine.viewportEngines[r]!.busy.indexOf(busyId), 1);
1811
-
1812
- if (this.#customizationBusyModes.includes(busyId))
1813
- this.#customizationBusyModes.splice(this.#customizationBusyModes.indexOf(busyId), 1);
1814
- }
1815
- }
1816
-
1817
- private removeFromSceneTree(node: ITreeNode) {
1818
- this._sceneTree.removeNode(node);
1819
- this._sceneTree.root.updateVersion();
1820
- }
1821
-
1822
- /**
1823
- * Returns a promise that resolves after the amount of milliseconds provided.
1824
- *
1825
- * @param ms the milliseconds
1826
- * @returns promise that resolve after specified milliseconds
1827
- */
1828
- private async timeout(ms: number): Promise<unknown> {
1829
- return new Promise(resolve => setTimeout(resolve, ms));
1830
- }
1831
-
1832
- private updateResponseDto(responseDto: ShapeDiverResponseDto, initialParameters?: {
1833
- [key: string]: string;
1834
- }) {
1835
- if (!this._responseDto) {
1836
- this._responseDto = responseDto;
1837
- return;
1838
- }
1839
-
1840
- // convert parameters
1841
- if (responseDto.parameters) {
1842
- for (const parameterId in responseDto.parameters) {
1843
- this._responseDto.parameters = this._responseDto.parameters || {};
1844
- this._responseDto.parameters[parameterId] = this._responseDto.parameters[parameterId] || responseDto.parameters[parameterId];
1845
- }
1846
- }
1847
-
1848
- // convert outputs
1849
- if (responseDto.outputs) {
1850
- for (const outputId in responseDto.outputs) {
1851
- this._responseDto.outputs = this._responseDto.outputs || {};
1852
- if ('version' in responseDto.outputs[outputId] || !(this._responseDto.outputs[outputId] && 'version' in this._responseDto.outputs[outputId]))
1853
- this._responseDto.outputs[outputId] = responseDto.outputs[outputId];
1854
- }
1855
- }
1856
-
1857
- // convert exports
1858
- if (responseDto.exports) {
1859
- for (const exportId in responseDto.exports) {
1860
- this._responseDto.exports = this._responseDto.exports || {};
1861
- if ('version' in responseDto.exports[exportId] || !(this._responseDto.exports[exportId] && 'version' in this._responseDto.exports[exportId]))
1862
- this._responseDto.exports[exportId] = responseDto.exports[exportId];
1863
- }
1864
- }
1865
-
1866
- const parameterSet: {
1867
- [key: string]: {
1868
- value: unknown,
1869
- valueString: string
1870
- }
1871
- } = {};
1872
-
1873
- for (const parameterId in this._responseDto.parameters) {
1874
- if (this.parameters[parameterId]) continue;
1875
- this._responseDto.parameters[parameterId].id = parameterId;
1876
-
1877
- /**
1878
- *
1879
- * REMOVE THIS LOGIC - START
1880
- *
1881
- */
1882
- const fakeSelectionParameterName = 'FAKE_SELECTION_PARAMETER';
1883
- const nameStartsWithFakeSelectionParameter = this._responseDto.parameters[parameterId].name.startsWith(fakeSelectionParameterName);
1884
- const displaynameStartsWithFakeSelectionParameter = this._responseDto.parameters[parameterId].displayname?.startsWith(fakeSelectionParameterName);
1885
-
1886
- if (nameStartsWithFakeSelectionParameter || displaynameStartsWithFakeSelectionParameter) {
1887
- this._responseDto.parameters[parameterId].type = PARAMETER_TYPE.INTERACTION;
1888
- const name = nameStartsWithFakeSelectionParameter ? this._responseDto.parameters[parameterId].name : this._responseDto.parameters[parameterId].displayname!;
1889
- const urlParams = new URLSearchParams(name.replace(fakeSelectionParameterName + '?', ''));
1890
- const jsonString = urlParams.get('settings');
1891
- if (jsonString)
1892
- this._responseDto.parameters[parameterId].settings = JSON.parse(jsonString);
1893
- }
1894
-
1895
- const fakeGumballParameterName = 'FAKE_GUMBALL_PARAMETER';
1896
- const nameStartsWithFakeGumballParameter = this._responseDto.parameters[parameterId].name.startsWith(fakeGumballParameterName);
1897
- const displaynameStartsWithFakeGumballParameter = this._responseDto.parameters[parameterId].displayname?.startsWith(fakeGumballParameterName);
1898
-
1899
- if (nameStartsWithFakeGumballParameter || displaynameStartsWithFakeGumballParameter) {
1900
- this._responseDto.parameters[parameterId].type = PARAMETER_TYPE.INTERACTION;
1901
- const name = nameStartsWithFakeGumballParameter ? this._responseDto.parameters[parameterId].name : this._responseDto.parameters[parameterId].displayname!;
1902
- const urlParams = new URLSearchParams(name.replace(fakeGumballParameterName + '?', ''));
1903
- const jsonString = urlParams.get('settings');
1904
- if (jsonString)
1905
- this._responseDto.parameters[parameterId].settings = JSON.parse(jsonString);
1906
- }
1907
- /**
1908
- *
1909
- * REMOVE THIS LOGIC - END
1910
- *
1911
- */
1912
-
1913
- switch (true) {
1914
- case this._responseDto.parameters[parameterId].type === PARAMETER_TYPE.BOOL:
1915
- this.parameters[parameterId] = new Parameter<boolean>(this._responseDto.parameters[parameterId], this);
1916
- break;
1917
- case this._responseDto.parameters[parameterId].type === PARAMETER_TYPE.COLOR:
1918
- this.parameters[parameterId] = new Parameter<number | vec3>(this._responseDto.parameters[parameterId], this);
1919
- break;
1920
- case this._responseDto.parameters[parameterId].type === PARAMETER_TYPE.FILE:
1921
- this.parameters[parameterId] = new FileParameter(this._responseDto.parameters[parameterId], this);
1922
- break;
1923
- case this._responseDto.parameters[parameterId].type === PARAMETER_TYPE.EVEN || this._responseDto.parameters[parameterId].type === PARAMETER_TYPE.FLOAT || this._responseDto.parameters[parameterId].type === PARAMETER_TYPE.INT || this._responseDto.parameters[parameterId].type === PARAMETER_TYPE.ODD:
1924
- this.parameters[parameterId] = new Parameter<number>(this._responseDto.parameters[parameterId], this);
1925
- break;
1926
- case this._responseDto.parameters[parameterId].type === PARAMETER_TYPE.INTERACTION:
1927
- this.parameters[parameterId] = this.createInteractionParameter(this._responseDto.parameters[parameterId]);
1928
- break;
1929
- default:
1930
- this.parameters[parameterId] = new Parameter<string>(this._responseDto.parameters[parameterId], this);
1931
- break;
1932
- }
1933
-
1934
- // we don't have to do larger restrictions for this as the backend would have already thrown an error if the values were not correct
1935
- if (initialParameters) {
1936
- // check if the id is within the initial parameters
1937
- if (initialParameters[parameterId] !== undefined) {
1938
- this.parameters[parameterId].value = initialParameters[parameterId];
1939
- }
1940
- // check if the name is within the initial parameters
1941
- else if (initialParameters[this.parameters[parameterId].name] !== undefined) {
1942
- this.parameters[parameterId].value = initialParameters[this.parameters[parameterId].name];
1943
- }
1944
- // NOTE: At some point the checking may also be done with the displayname, this is the code for it
1945
- // // check if the displayname is within the initial parameters
1946
- // else if(this.parameters[parameterId].displayname && initialParameters[this.parameters[parameterId].displayname!] !== undefined) {
1947
- // this.parameters[parameterId].value = initialParameters[this.parameters[parameterId].displayname!];
1948
- // }
1949
- }
1950
-
1951
- parameterSet[parameterId] = {
1952
- value: this.parameters[parameterId].value,
1953
- valueString: this.parameters[parameterId].stringify()
1954
- };
1955
-
1956
- if (!this.initialized)
1957
- this.parameterValues[parameterId] = parameterSet[parameterId].valueString;
1958
- }
1959
-
1960
- // store the initialization as the first parameter set in the history
1961
- if (!this.initialized)
1962
- this.#parameterHistory.push(parameterSet);
1963
-
1964
- for (const exportId in this._responseDto.exports) {
1965
- if (this._responseDto.exports[exportId].type === ShapeDiverResponseExportDefinitionType.EMAIL || this._responseDto.exports[exportId].type === ShapeDiverResponseExportDefinitionType.DOWNLOAD) {
1966
- if (!this.exports[exportId]) {
1967
- this._responseDto.exports[exportId].id = exportId;
1968
- this.exports[exportId] = new Export(this._responseDto.exports[exportId], this);
1969
- } else {
1970
- this.exports[exportId].updateExportDefinition(this._responseDto.exports[exportId]);
1971
- }
1972
- }
1973
- }
1974
-
1975
- for (const outputId in this._responseDto.outputs) {
1976
- if (!this.outputs[outputId]) {
1977
- this._responseDto.outputs[outputId].id = outputId;
1978
- if (this.outputsFreeze[outputId] === undefined) this.outputsFreeze[outputId] = false;
1979
- this.outputs[outputId] = new Output(<ShapeDiverResponseOutput>this._responseDto.outputs[outputId], this);
1980
- } else {
1981
- this.outputs[outputId].updateOutputDefinition(<ShapeDiverResponseOutput>this._responseDto.outputs[outputId]);
1982
- }
1983
- }
1984
- }
1985
-
1986
- private async waitForUpdateCallbacks(newOutputVersions: { [key: string]: string }, oldOutputVersions: { [key: string]: string }, newNode: ITreeNode, oldNode: ITreeNode) {
1987
- // call the update callback function on the session
1988
- if (this._updateCallback) await Promise.resolve(this._updateCallback(newNode, oldNode));
1989
-
1990
- const promises = [];
1991
- // call the update callback functions on the outputs
1992
- for (const outputId in this.outputs) {
1993
- if (oldOutputVersions[outputId] !== newOutputVersions[outputId]) {
1994
- promises.push(
1995
- this.outputs[outputId].triggerUpdateCallback(
1996
- newNode.children.find(c => c.name === outputId)!,
1997
- oldNode.children.find(c => c.name === outputId)!
1998
- )
1999
- );
2000
- }
2001
- }
2002
- await Promise.all(promises);
2003
- }
2004
-
2005
- // #endregion Private Methods (18)
2006
- }