@shapediver/viewer.session-engine.session-engine 2.10.1-rc.0 → 2.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/implementation/OutputDelayException.js +5 -1
  2. package/dist/implementation/OutputDelayException.js.map +1 -1
  3. package/dist/implementation/OutputLoader.d.ts +4 -1
  4. package/dist/implementation/OutputLoader.d.ts.map +1 -1
  5. package/dist/implementation/OutputLoader.js +41 -30
  6. package/dist/implementation/OutputLoader.js.map +1 -1
  7. package/dist/implementation/SessionData.js +6 -2
  8. package/dist/implementation/SessionData.js.map +1 -1
  9. package/dist/implementation/SessionEngine.d.ts +22 -12
  10. package/dist/implementation/SessionEngine.d.ts.map +1 -1
  11. package/dist/implementation/SessionEngine.js +442 -301
  12. package/dist/implementation/SessionEngine.js.map +1 -1
  13. package/dist/implementation/SessionOutputData.js +6 -2
  14. package/dist/implementation/SessionOutputData.js.map +1 -1
  15. package/dist/implementation/SessionTreeNode.js +6 -2
  16. package/dist/implementation/SessionTreeNode.js.map +1 -1
  17. package/dist/implementation/dto/Export.js +17 -13
  18. package/dist/implementation/dto/Export.js.map +1 -1
  19. package/dist/implementation/dto/FileParameter.js +34 -7
  20. package/dist/implementation/dto/FileParameter.js.map +1 -1
  21. package/dist/implementation/dto/Output.d.ts +6 -6
  22. package/dist/implementation/dto/Output.d.ts.map +1 -1
  23. package/dist/implementation/dto/Output.js +21 -14
  24. package/dist/implementation/dto/Output.js.map +1 -1
  25. package/dist/implementation/dto/Parameter.js +60 -33
  26. package/dist/implementation/dto/Parameter.js.map +1 -1
  27. package/dist/index.js +20 -11
  28. package/dist/index.js.map +1 -1
  29. package/dist/interfaces/ISessionData.js +2 -1
  30. package/dist/interfaces/ISessionEngine.d.ts +12 -8
  31. package/dist/interfaces/ISessionEngine.d.ts.map +1 -1
  32. package/dist/interfaces/ISessionEngine.js +7 -5
  33. package/dist/interfaces/ISessionEngine.js.map +1 -1
  34. package/dist/interfaces/ISessionOutputData.js +2 -1
  35. package/dist/interfaces/ISessionTreeNode.js +2 -1
  36. package/dist/interfaces/dto/IExport.js +2 -1
  37. package/dist/interfaces/dto/IFileParameter.js +2 -1
  38. package/dist/interfaces/dto/IOutput.d.ts +1 -1
  39. package/dist/interfaces/dto/IOutput.d.ts.map +1 -1
  40. package/dist/interfaces/dto/IOutput.js +2 -1
  41. package/dist/interfaces/dto/IParameter.js +2 -1
  42. package/package.json +9 -9
  43. package/src/implementation/OutputLoader.ts +16 -5
  44. package/src/implementation/SessionEngine.ts +682 -500
  45. package/src/implementation/dto/Output.ts +16 -15
  46. package/src/interfaces/ISessionEngine.ts +51 -44
  47. package/src/interfaces/dto/IOutput.ts +1 -1
@@ -1,24 +1,65 @@
1
- import { HttpClient, HttpResponse, PerformanceEvaluator, UuidGenerator, SystemInfo, Logger, ShapeDiverViewerSessionError, ShapeDiverViewerError, Converter, SettingsEngine, EVENTTYPE, EventEngine, StateEngine, ShapeDiverViewerSettingsError, ShapeDiverGeometryBackendResponseError, ShapeDiverGeometryBackendRequestError } from '@shapediver/viewer.shared.services'
2
-
3
- import { OutputDelayException } from './OutputDelayException'
4
- import { OutputLoader, OutputLoaderTaskEventInfo } from './OutputLoader'
5
- import { SessionTreeNode } from './SessionTreeNode'
6
- import { ISessionEngine, ISettingsSections, PARAMETER_TYPE } from '../interfaces/ISessionEngine'
7
- import { SessionData } from './SessionData'
8
- import { create, ShapeDiverError as ShapeDiverBackendError, ShapeDiverResponseErrorType, ShapeDiverRequestGltfUploadQueryConversion, ShapeDiverResponseDto, ShapeDiverResponseError, ShapeDiverResponseExport, ShapeDiverResponseExportDefinitionType, ShapeDiverResponseOutput, ShapeDiverResponseParameter, ShapeDiverSdk, ShapeDiverSdkConfigType, ShapeDiverResponseModelComputationStatus, ShapeDiverRequestError, isGBResponseError } from '@shapediver/sdk.geometry-api-sdk-v2'
9
- import { ISessionTreeNode } from '../interfaces/ISessionTreeNode'
10
- import { ITree, ITreeNode, Tree, TreeNode } from '@shapediver/viewer.shared.node-tree'
11
- import { ITaskEvent, TASK_TYPE } from '@shapediver/viewer.shared.types'
12
- import { FileParameter } from './dto/FileParameter'
13
- import { IFileParameter } from '../interfaces/dto/IFileParameter'
14
- import { IExport } from '../interfaces/dto/IExport'
15
- import { IParameter } from '../interfaces/dto/IParameter'
16
- import { IOutput } from '../interfaces/dto/IOutput'
17
- import { Parameter } from './dto/Parameter'
18
- import { vec3 } from 'gl-matrix'
19
- import { Export } from './dto/Export'
20
- import { Output } from './dto/Output'
21
- import { convert, ISettings, latestVersion, validate, versions } from '@shapediver/viewer.settings'
1
+ import {
2
+ convert,
3
+ ISettings,
4
+ latestVersion,
5
+ validate,
6
+ versions
7
+ } from '@shapediver/viewer.settings';
8
+ import {
9
+ create,
10
+ isGBResponseError,
11
+ ShapeDiverError as ShapeDiverBackendError,
12
+ ShapeDiverRequestConfigure,
13
+ ShapeDiverRequestCustomization,
14
+ ShapeDiverRequestExport,
15
+ ShapeDiverRequestGltfUploadQueryConversion,
16
+ ShapeDiverResponseDto,
17
+ ShapeDiverResponseErrorType,
18
+ ShapeDiverResponseExport,
19
+ ShapeDiverResponseExportDefinitionType,
20
+ ShapeDiverResponseModelComputationStatus,
21
+ ShapeDiverResponseOutput,
22
+ ShapeDiverSdk,
23
+ ShapeDiverSdkConfigType
24
+ } from '@shapediver/sdk.geometry-api-sdk-v2';
25
+ import {
26
+ EventEngine,
27
+ EVENTTYPE,
28
+ HttpClient,
29
+ HttpResponse,
30
+ Logger,
31
+ PerformanceEvaluator,
32
+ SettingsEngine,
33
+ ShapeDiverViewerError,
34
+ ShapeDiverViewerSessionError,
35
+ ShapeDiverViewerSettingsError,
36
+ StateEngine,
37
+ SystemInfo,
38
+ UuidGenerator
39
+ } from '@shapediver/viewer.shared.services';
40
+ import { Export } from './dto/Export';
41
+ import { FileParameter } from './dto/FileParameter';
42
+ import { IExport } from '../interfaces/dto/IExport';
43
+ import { IFileParameter } from '../interfaces/dto/IFileParameter';
44
+ import { IOutput } from '../interfaces/dto/IOutput';
45
+ import { IParameter } from '../interfaces/dto/IParameter';
46
+ import { ISessionEngine, ISettingsSections, PARAMETER_TYPE } from '../interfaces/ISessionEngine';
47
+ import { ISessionTreeNode } from '../interfaces/ISessionTreeNode';
48
+ import { IOutputEvent, ITaskEvent, TASK_TYPE } from '@shapediver/viewer.shared.types';
49
+ import {
50
+ ITree,
51
+ ITreeNode,
52
+ Tree,
53
+ TreeNode
54
+ } from '@shapediver/viewer.shared.node-tree';
55
+ import { Output } from './dto/Output';
56
+ import { OutputDelayException } from './OutputDelayException';
57
+ import { OutputLoader, OutputLoaderTaskEventInfo } from './OutputLoader';
58
+ import { Parameter } from './dto/Parameter';
59
+ import { SessionData } from './SessionData';
60
+ import { SessionTreeNode } from './SessionTreeNode';
61
+ import { vec3 } from 'gl-matrix';
62
+ /* eslint-disable @typescript-eslint/no-empty-function */
22
63
 
23
64
  export class SessionEngine implements ISessionEngine {
24
65
  // #region Properties (43)
@@ -34,7 +75,7 @@ export class SessionEngine implements ISessionEngine {
34
75
  private readonly _outputs: { [key: string]: IOutput; } = {};
35
76
  private readonly _outputsFreeze: { [key: string]: boolean; } = {};
36
77
  private readonly _parameterValues: { [key: string]: string; } = {};
37
- private readonly _parameters: { [key: string]: IParameter<any>; } = {};
78
+ private readonly _parameters: { [key: string]: IParameter<unknown>; } = {};
38
79
  private readonly _performanceEvaluator = PerformanceEvaluator.instance;
39
80
  private readonly _sceneTree: ITree = Tree.instance;
40
81
  private readonly _sessionEngineId = (UuidGenerator.instance).create();
@@ -43,17 +84,17 @@ export class SessionEngine implements ISessionEngine {
43
84
  private readonly _ticket?: string;
44
85
  private readonly _uuidGenerator = UuidGenerator.instance;
45
86
 
46
- #customizationProcess!: string;
87
+ #customizationProcess?: string;
47
88
  #parameterHistory: {
48
89
  [key: string]: {
49
- value: any,
90
+ value: unknown,
50
91
  valueString: string
51
92
  }
52
93
  }[] = [];
53
94
  #parameterHistoryCall = false;
54
95
  #parameterHistoryForward: {
55
96
  [key: string]: {
56
- value: any,
97
+ value: unknown,
57
98
  valueString: string
58
99
  }
59
100
  }[] = [];
@@ -62,14 +103,14 @@ export class SessionEngine implements ISessionEngine {
62
103
  private _closed: boolean = false;
63
104
  private _customizeOnParameterChange: boolean = false;
64
105
  private _dataCache: {
65
- [key: string]: Promise<HttpResponse<any>>
106
+ [key: string]: Promise<HttpResponse<unknown>>
66
107
  } = {};
67
108
  private _excludeViewports: string[] = [];
68
109
  private _headers = {
69
- "X-ShapeDiver-Origin": (SystemInfo.instance).origin,
70
- "X-ShapeDiver-SessionEngineId": this._sessionEngineId,
71
- "X-ShapeDiver-BuildVersion": '',
72
- "X-ShapeDiver-BuildDate": ''
110
+ 'X-ShapeDiver-Origin': (SystemInfo.instance).origin,
111
+ 'X-ShapeDiver-SessionEngineId': this._sessionEngineId,
112
+ 'X-ShapeDiver-BuildVersion': '',
113
+ 'X-ShapeDiver-BuildDate': ''
73
114
  };
74
115
  private _initialized: boolean = false;
75
116
  private _jwtToken?: string;
@@ -115,7 +156,7 @@ export class SessionEngine implements ISessionEngine {
115
156
 
116
157
  // #endregion Constructors (1)
117
158
 
118
- // #region Public Accessors (24)
159
+ // #region Public Accessors (25)
119
160
 
120
161
  public get automaticSceneUpdate(): boolean {
121
162
  return this._automaticSceneUpdate;
@@ -192,7 +233,7 @@ export class SessionEngine implements ISessionEngine {
192
233
  return this._parameterValues;
193
234
  }
194
235
 
195
- public get parameters(): { [key: string]: IParameter<any>; } {
236
+ public get parameters(): { [key: string]: IParameter<unknown>; } {
196
237
  return this._parameters;
197
238
  }
198
239
 
@@ -213,149 +254,149 @@ export class SessionEngine implements ISessionEngine {
213
254
  }
214
255
 
215
256
  public get updateCallback(): ((newNode: ITreeNode, oldNode: ITreeNode) => void) | null {
216
- return this._updateCallback;
257
+ return this._updateCallback;
217
258
  }
218
259
 
219
260
  public set updateCallback(value: ((newNode: ITreeNode, oldNode: ITreeNode) => void) | null) {
220
- this._updateCallback = value;
261
+ this._updateCallback = value;
221
262
  }
222
263
 
223
264
  public get viewerSettings(): object | undefined {
224
265
  return this._viewerSettings;
225
266
  }
226
267
 
227
- // #endregion Public Accessors (24)
268
+ // #endregion Public Accessors (25)
228
269
 
229
- // #region Public Methods (24)
270
+ // #region Public Methods (27)
230
271
 
231
272
  public applySettings(response: ShapeDiverResponseDto, sections?: ISettingsSections) {
232
- sections = sections || {};
233
- if (sections.session === undefined) {
234
- sections.session = {
235
- parameter: { displayname: false, order: false, hidden: false },
236
- export: { displayname: false, order: false, hidden: false }
237
- };
238
- }
239
- if (sections.session.parameter === undefined)
240
- sections.session.parameter = { displayname: false, order: false, hidden: false, value: false };
241
- if (sections.session.export === undefined)
242
- sections.session.export = { displayname: false, order: false, hidden: false };
243
- if (sections.viewport === undefined)
244
- sections.viewport = { ar: false, scene: false, camera: false, light: false, environment: false, general: false, postprocessing: false };
245
-
246
- let config: object;
247
- if ((<ShapeDiverResponseDto>response).viewer !== undefined) {
248
- config = (<ShapeDiverResponseDto>response).viewer!.config;
249
- } else {
250
- throw new ShapeDiverViewerSettingsError('Session.applySettings: No config object available.');
251
- }
273
+ sections = sections || {};
274
+ if (sections.session === undefined) {
275
+ sections.session = {
276
+ parameter: { displayname: false, order: false, hidden: false },
277
+ export: { displayname: false, order: false, hidden: false }
278
+ };
279
+ }
280
+ if (sections.session.parameter === undefined)
281
+ sections.session.parameter = { displayname: false, order: false, hidden: false, value: false };
282
+ if (sections.session.export === undefined)
283
+ sections.session.export = { displayname: false, order: false, hidden: false };
284
+ if (sections.viewport === undefined)
285
+ sections.viewport = { ar: false, scene: false, camera: false, light: false, environment: false, general: false, postprocessing: false };
286
+
287
+ let config: object;
288
+ if ((<ShapeDiverResponseDto>response).viewer !== undefined) {
289
+ config = (<ShapeDiverResponseDto>response).viewer!.config;
290
+ } else {
291
+ throw new ShapeDiverViewerSettingsError('Session.applySettings: No config object available.');
292
+ }
252
293
 
253
- try {
254
- validate(config)
255
- } catch (e) {
256
- throw new ShapeDiverViewerSettingsError('Session.applySettings: Was not able to validate config object.');
257
- }
294
+ try {
295
+ validate(config);
296
+ } catch (e) {
297
+ throw new ShapeDiverViewerSettingsError('Session.applySettings: Was not able to validate config object.');
298
+ }
258
299
 
259
- const settings = <ISettings>convert(config, latestVersion);
300
+ const settings = <ISettings>convert(config, latestVersion);
260
301
 
261
- const exportMappingUid: { [key: string]: string | undefined } = {};
262
- if (sections.session.export.displayname || sections.session.export.order || sections.session.export.hidden)
263
- if (response.exports)
264
- for (let exportId in response.exports)
265
- if (response.exports[exportId].uid !== undefined)
266
- exportMappingUid[response.exports[exportId].uid!] = exportId;
302
+ const exportMappingUid: { [key: string]: string | undefined } = {};
303
+ if (sections.session.export.displayname || sections.session.export.order || sections.session.export.hidden)
304
+ if (response.exports)
305
+ for (const exportId in response.exports)
306
+ if (response.exports[exportId].uid !== undefined)
307
+ exportMappingUid[response.exports[exportId].uid!] = exportId;
267
308
 
268
- const currentSettings = this._settingsEngine.settings;
309
+ const currentSettings = this._settingsEngine.settings;
269
310
 
270
- // apply parameter settings
271
- if (sections.session.parameter.displayname || sections.session.parameter.order || sections.session.parameter.hidden || sections.session.parameter.value) {
272
- for (let p in this.parameters) {
273
- if (settings.session[p]) {
274
- if (sections.session.parameter.displayname) this.parameters[p].displayname = settings.session[p].displayname;
275
- if (sections.session.parameter.order) this.parameters[p].order = settings.session[p].order;
276
- if (sections.session.parameter.hidden) this.parameters[p].hidden = settings.session[p].hidden || false;
277
- }
311
+ // apply parameter settings
312
+ if (sections.session.parameter.displayname || sections.session.parameter.order || sections.session.parameter.hidden || sections.session.parameter.value) {
313
+ for (const p in this.parameters) {
314
+ if (settings.session[p]) {
315
+ if (sections.session.parameter.displayname) this.parameters[p].displayname = settings.session[p].displayname;
316
+ if (sections.session.parameter.order) this.parameters[p].order = settings.session[p].order;
317
+ if (sections.session.parameter.hidden) this.parameters[p].hidden = settings.session[p].hidden || false;
318
+ }
278
319
 
279
- if (response.parameters && response.parameters[p]) {
280
- if (sections.session.parameter.value) this.parameters[p].value = response.parameters[p].defval !== undefined ? response.parameters[p].defval : this.parameters[p].value;
281
- }
320
+ if (response.parameters && response.parameters[p] && !((this.parameters[p] instanceof FileParameter) || this.parameters[p].type.startsWith('s'))) {
321
+ if (sections.session.parameter.value) this.parameters[p].value = response.parameters[p].defval !== undefined ? response.parameters[p].defval : this.parameters[p].value;
282
322
  }
283
323
  }
324
+ }
284
325
 
285
- // apply export settings
286
- if (sections.session.export.displayname || sections.session.export.order || sections.session.export.hidden) {
287
- for (let p in this.exports) {
288
- let idForSettings = '';
289
- if (settings.session[p]) {
290
- idForSettings = p;
291
- } else {
292
- const uid = this.exports[p].uid;
293
- if (!uid) continue;
294
- if (!exportMappingUid[uid]) continue;
295
- idForSettings = exportMappingUid[uid]!;
296
- }
297
- if (settings.session[idForSettings]) {
298
- if (sections.session.export.displayname) this.exports[p].displayname = settings.session[idForSettings].displayname;
299
- if (sections.session.export.order) this.exports[p].order = settings.session[idForSettings].order;
300
- if (sections.session.export.hidden) this.exports[p].hidden = settings.session[idForSettings].hidden || false;
301
- }
326
+ // apply export settings
327
+ if (sections.session.export.displayname || sections.session.export.order || sections.session.export.hidden) {
328
+ for (const p in this.exports) {
329
+ let idForSettings = '';
330
+ if (settings.session[p]) {
331
+ idForSettings = p;
332
+ } else {
333
+ const uid = this.exports[p].uid;
334
+ if (!uid) continue;
335
+ if (!exportMappingUid[uid]) continue;
336
+ idForSettings = exportMappingUid[uid]!;
337
+ }
338
+ if (settings.session[idForSettings]) {
339
+ if (sections.session.export.displayname) this.exports[p].displayname = settings.session[idForSettings].displayname;
340
+ if (sections.session.export.order) this.exports[p].order = settings.session[idForSettings].order;
341
+ if (sections.session.export.hidden) this.exports[p].hidden = settings.session[idForSettings].hidden || false;
302
342
  }
303
343
  }
344
+ }
304
345
 
305
- // apply ar settings
306
- if (sections.viewport.ar) {
307
- currentSettings.ar = settings.ar;
308
- currentSettings.general.transformation = settings.general.transformation;
309
- }
346
+ // apply ar settings
347
+ if (sections.viewport.ar) {
348
+ currentSettings.ar = settings.ar;
349
+ currentSettings.general.transformation = settings.general.transformation;
350
+ }
310
351
 
311
- // apply camera settings
312
- if (sections.viewport.camera)
313
- currentSettings.camera = settings.camera;
314
-
315
- // apply light settings
316
- if (sections.viewport.light)
317
- currentSettings.light = settings.light;
318
-
319
- // apply scene settings
320
- if (sections.viewport.scene) {
321
- currentSettings.environmentGeometry.gridColor = settings.environmentGeometry.gridColor;
322
- currentSettings.environmentGeometry.gridVisibility = settings.environmentGeometry.gridVisibility;
323
- currentSettings.environmentGeometry.groundPlaneColor = settings.environmentGeometry.groundPlaneColor;
324
- currentSettings.environmentGeometry.groundPlaneVisibility = settings.environmentGeometry.groundPlaneVisibility;
325
- currentSettings.environmentGeometry.groundPlaneShadowColor = settings.environmentGeometry.groundPlaneShadowColor;
326
- currentSettings.environmentGeometry.groundPlaneShadowVisibility = settings.environmentGeometry.groundPlaneShadowVisibility;
327
-
328
- currentSettings.rendering.shadows = settings.rendering.shadows;
329
- currentSettings.rendering.softShadows = settings.rendering.softShadows;
330
-
331
- currentSettings.rendering.automaticColorAdjustment = settings.rendering.automaticColorAdjustment;
332
- currentSettings.rendering.textureEncoding = settings.rendering.textureEncoding;
333
- currentSettings.rendering.outputEncoding = settings.rendering.outputEncoding;
334
- currentSettings.rendering.physicallyCorrectLights = settings.rendering.physicallyCorrectLights;
335
- currentSettings.rendering.toneMapping = settings.rendering.toneMapping;
336
- currentSettings.rendering.toneMappingExposure = settings.rendering.toneMappingExposure;
337
- }
352
+ // apply camera settings
353
+ if (sections.viewport.camera)
354
+ currentSettings.camera = settings.camera;
355
+
356
+ // apply light settings
357
+ if (sections.viewport.light)
358
+ currentSettings.light = settings.light;
359
+
360
+ // apply scene settings
361
+ if (sections.viewport.scene) {
362
+ currentSettings.environmentGeometry.gridColor = settings.environmentGeometry.gridColor;
363
+ currentSettings.environmentGeometry.gridVisibility = settings.environmentGeometry.gridVisibility;
364
+ currentSettings.environmentGeometry.groundPlaneColor = settings.environmentGeometry.groundPlaneColor;
365
+ currentSettings.environmentGeometry.groundPlaneVisibility = settings.environmentGeometry.groundPlaneVisibility;
366
+ currentSettings.environmentGeometry.groundPlaneShadowColor = settings.environmentGeometry.groundPlaneShadowColor;
367
+ currentSettings.environmentGeometry.groundPlaneShadowVisibility = settings.environmentGeometry.groundPlaneShadowVisibility;
368
+
369
+ currentSettings.rendering.shadows = settings.rendering.shadows;
370
+ currentSettings.rendering.softShadows = settings.rendering.softShadows;
371
+
372
+ currentSettings.rendering.automaticColorAdjustment = settings.rendering.automaticColorAdjustment;
373
+ currentSettings.rendering.textureEncoding = settings.rendering.textureEncoding;
374
+ currentSettings.rendering.outputEncoding = settings.rendering.outputEncoding;
375
+ currentSettings.rendering.physicallyCorrectLights = settings.rendering.physicallyCorrectLights;
376
+ currentSettings.rendering.toneMapping = settings.rendering.toneMapping;
377
+ currentSettings.rendering.toneMappingExposure = settings.rendering.toneMappingExposure;
378
+ }
338
379
 
339
- if (sections.viewport.general) {
340
- currentSettings.general.defaultMaterialColor = settings.general.defaultMaterialColor;
341
- currentSettings.general.commitParameters = settings.general.commitParameters;
342
- currentSettings.general.pointSize = settings.general.pointSize;
343
- }
380
+ if (sections.viewport.general) {
381
+ currentSettings.general.defaultMaterialColor = settings.general.defaultMaterialColor;
382
+ currentSettings.general.commitParameters = settings.general.commitParameters;
383
+ currentSettings.general.pointSize = settings.general.pointSize;
384
+ }
344
385
 
345
- // apply postprocessing settings
346
- if (sections.viewport.postprocessing)
347
- currentSettings.postprocessing = settings.postprocessing;
348
-
349
- // apply environment settings
350
- if (sections.viewport.environment) {
351
- currentSettings.environment.clearAlpha = settings.environment.clearAlpha;
352
- currentSettings.environment.clearColor = settings.environment.clearColor;
353
- currentSettings.environment.map = settings.environment.map;
354
- currentSettings.environment.mapAsBackground = settings.environment.mapAsBackground;
355
- currentSettings.environment.rotation = settings.environment.rotation;
356
- currentSettings.environment.blurriness = settings.environment.blurriness;
357
- currentSettings.environment.intensity = settings.environment.intensity;
358
- }
386
+ // apply postprocessing settings
387
+ if (sections.viewport.postprocessing)
388
+ currentSettings.postprocessing = settings.postprocessing;
389
+
390
+ // apply environment settings
391
+ if (sections.viewport.environment) {
392
+ currentSettings.environment.clearAlpha = settings.environment.clearAlpha;
393
+ currentSettings.environment.clearColor = settings.environment.clearColor;
394
+ currentSettings.environment.map = settings.environment.map;
395
+ currentSettings.environment.mapAsBackground = settings.environment.mapAsBackground;
396
+ currentSettings.environment.rotation = settings.environment.rotation;
397
+ currentSettings.environment.blurriness = settings.environment.blurriness;
398
+ currentSettings.environment.intensity = settings.environment.intensity;
399
+ }
359
400
  }
360
401
 
361
402
  public canGoBack(): boolean {
@@ -368,11 +409,20 @@ export class SessionEngine implements ISessionEngine {
368
409
  return this.#parameterHistoryForward.length > 0;
369
410
  }
370
411
 
412
+ public cancelCustomization() {
413
+ if(this.#customizationProcess) {
414
+ for (const r in this._stateEngine.renderingEngines)
415
+ if (this._stateEngine.renderingEngines[r].busy.includes(this.#customizationProcess))
416
+ this._stateEngine.renderingEngines[r].busy.splice(this._stateEngine.renderingEngines[r].busy.indexOf(this.#customizationProcess), 1);
417
+ }
418
+ this.#customizationProcess = undefined;
419
+ }
420
+
371
421
  public async close(retry = false): Promise<void> {
372
422
  this.checkAvailability('close');
373
423
 
374
424
  try {
375
- this._httpClient.removeDataLoading(this._sessionId!)
425
+ this._httpClient.removeDataLoading(this._sessionId!);
376
426
  await this._sdk.session.close(this._sessionId!);
377
427
  if (this._automaticSceneUpdate) this.removeFromSceneTree(this._node);
378
428
 
@@ -389,7 +439,7 @@ export class SessionEngine implements ISessionEngine {
389
439
  * @param parameters the parameter set to update the session
390
440
  * @returns promise with a scene graph node
391
441
  */
392
- public async customize(force: boolean = false): Promise<ITreeNode> {
442
+ public async customize(force: boolean = false, waitForViewportUpdate: boolean = false): Promise<ITreeNode> {
393
443
  const eventId = this._uuidGenerator.create();
394
444
  const customizationId = this._uuidGenerator.create();
395
445
  try {
@@ -399,7 +449,7 @@ export class SessionEngine implements ISessionEngine {
399
449
  for (const parameterId in this.parameters)
400
450
  if (this.parameters[parameterId].sessionValue !== this.parameters[parameterId].value)
401
451
  changes = true;
402
- if(changes === false)
452
+ if (changes === false)
403
453
  return this.node;
404
454
  }
405
455
 
@@ -411,65 +461,28 @@ export class SessionEngine implements ISessionEngine {
411
461
 
412
462
  this._logger.debugLow(`Session(${this.id}).customize: Customizing session.`);
413
463
 
414
- for (let r in this._stateEngine.renderingEngines)
415
- if(!this.excludeViewports.includes(r))
464
+ for (const r in this._stateEngine.renderingEngines)
465
+ if (!this.excludeViewports.includes(r))
416
466
  this._stateEngine.renderingEngines[r].busy.push(customizationId);
417
467
 
418
468
  const eventFileUpload: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 0.1, data: { sessionId: this.id }, status: 'Uploading file parameters' };
419
469
  this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_PROCESS, eventFileUpload);
420
470
 
421
- const fileParameterIds: { [key: string]: string } = {}
471
+ const fileParameterIds: { [key: string]: string } = {};
422
472
  // load file parameter first
423
473
  for (const parameterId in this.parameters) {
424
474
  if (this.parameters[parameterId] instanceof FileParameter) {
425
475
  fileParameterIds[parameterId] = await (<IFileParameter>this.parameters[parameterId]).upload();
426
476
 
427
477
  // OPTION TO SKIP - PART 1a
428
- if (this.#customizationProcess !== customizationId) {
429
- for (let r in this._stateEngine.renderingEngines)
430
- if (this._stateEngine.renderingEngines[r].busy.includes(customizationId))
431
- this._stateEngine.renderingEngines[r].busy.splice(this._stateEngine.renderingEngines[r].busy.indexOf(customizationId), 1);
432
-
433
- this._logger.debug(`Session(${this.id}).customize: Session customization was exceeded by other customization request.`);
434
-
435
- const eventCancel1a: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 1, data: { sessionId: this.id }, status: 'Session customization was exceeded by other customization request' };
436
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_CANCEL, eventCancel1a);
437
- return new SessionTreeNode();
438
- } else if (this._closed === true) {
439
- for (let r in this._stateEngine.renderingEngines)
440
- if (this._stateEngine.renderingEngines[r].busy.includes(customizationId))
441
- this._stateEngine.renderingEngines[r].busy.splice(this._stateEngine.renderingEngines[r].busy.indexOf(customizationId), 1);
442
-
443
- this._logger.debug(`Session(${this.id}).customize: Session was closed during customization request.`);
444
-
445
- const eventCancel1a: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 1, data: { sessionId: this.id }, status: 'Session was closed during customization request' };
446
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_CANCEL, eventCancel1a);
447
- return new SessionTreeNode();
448
- }
478
+ const cancelResult = this.cancelProcess(customizationId, eventId, TASK_TYPE.SESSION_CUSTOMIZATION, 1, { sessionId: this.id });
479
+ if (cancelResult) return cancelResult;
449
480
  }
450
481
  }
451
482
 
452
483
  // OPTION TO SKIP - PART 1b
453
- if (this.#customizationProcess !== customizationId) {
454
- for (let r in this._stateEngine.renderingEngines)
455
- if (this._stateEngine.renderingEngines[r].busy.includes(customizationId))
456
- this._stateEngine.renderingEngines[r].busy.splice(this._stateEngine.renderingEngines[r].busy.indexOf(customizationId), 1);
457
-
458
- const eventCancel1b: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 1, data: { sessionId: this.id }, status: 'Session customization was exceeded by other customization request' };
459
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_CANCEL, eventCancel1b);
460
- this._logger.debug(`Session(${this.id}).customize: Session customization was exceeded by other customization request.`);
461
- return new SessionTreeNode();
462
- } else if (this._closed === true) {
463
- for (let r in this._stateEngine.renderingEngines)
464
- if (this._stateEngine.renderingEngines[r].busy.includes(customizationId))
465
- this._stateEngine.renderingEngines[r].busy.splice(this._stateEngine.renderingEngines[r].busy.indexOf(customizationId), 1);
466
-
467
- this._logger.debug(`Session(${this.id}).customize: Session was closed during customization request.`);
468
-
469
- const eventCancel1b: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 1, data: { sessionId: this.id }, status: 'Session was closed during customization request' };
470
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_CANCEL, eventCancel1b);
471
- return new SessionTreeNode();
472
- }
484
+ const cancelResult = this.cancelProcess(customizationId, eventId, TASK_TYPE.SESSION_CUSTOMIZATION, 1, { sessionId: this.id });
485
+ if (cancelResult) return cancelResult;
473
486
 
474
487
  // assign the uploaded parameters
475
488
  for (const parameterId in fileParameterIds)
@@ -477,7 +490,7 @@ export class SessionEngine implements ISessionEngine {
477
490
 
478
491
  const parameterSet: {
479
492
  [key: string]: {
480
- value: any,
493
+ value: unknown,
481
494
  valueString: string
482
495
  }
483
496
  } = {};
@@ -487,7 +500,7 @@ export class SessionEngine implements ISessionEngine {
487
500
  parameterSet[parameterId] = {
488
501
  value: this.parameters[parameterId].value,
489
502
  valueString: this.parameters[parameterId].stringify()
490
- }
503
+ };
491
504
  }
492
505
 
493
506
  // update the session engine parameter values if everything succeeded
@@ -498,6 +511,8 @@ export class SessionEngine implements ISessionEngine {
498
511
  const eventRequest: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 0.1, data: { sessionId: this.id }, status: 'Sending customization request' };
499
512
  this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_PROCESS, eventRequest);
500
513
 
514
+ const oldOutputVersions = this._outputLoader.getCurrentOutputVersions();
515
+
501
516
  const newNode = await this.customizeInternal(() => this.#customizationProcess !== customizationId, {
502
517
  eventId,
503
518
  type: TASK_TYPE.SESSION_CUSTOMIZATION,
@@ -508,29 +523,32 @@ export class SessionEngine implements ISessionEngine {
508
523
  data: { sessionId: this.id }
509
524
  });
510
525
 
526
+ // OPTION TO SKIP - PART 2
527
+ const cancelResult2 = this.cancelProcess(customizationId, eventId, TASK_TYPE.SESSION_CUSTOMIZATION, 1, { sessionId: this.id });
528
+ if (cancelResult2) return cancelResult2;
529
+
530
+ const newOutputVersions = this._outputLoader.getCurrentOutputVersions();
531
+
511
532
  const eventSceneUpdate: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 0.9, data: { sessionId: this.id }, status: 'Updating scene' };
512
533
  this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_PROCESS, eventSceneUpdate);
513
534
 
514
- // OPTION TO SKIP - PART 2
515
- if (this.#customizationProcess !== customizationId) {
516
- for (let r in this._stateEngine.renderingEngines)
517
- if (this._stateEngine.renderingEngines[r].busy.includes(customizationId))
518
- this._stateEngine.renderingEngines[r].busy.splice(this._stateEngine.renderingEngines[r].busy.indexOf(customizationId), 1);
519
-
520
- const eventCancel2: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 1, data: { sessionId: this.id }, status: 'Session customization was exceeded by other customization request' };
521
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_CANCEL, eventCancel2);
522
- this._logger.debug(`Session(${this.id}).customize: Session customization was exceeded by other customization request.`);
523
- return newNode;
524
- } else if ((this._closed as boolean) === true) { // I get a TS warning here that the type of _closed is "false", I think TS doesn't get that there is a promise inbetween
525
- for (let r in this._stateEngine.renderingEngines)
526
- if (this._stateEngine.renderingEngines[r].busy.includes(customizationId))
527
- this._stateEngine.renderingEngines[r].busy.splice(this._stateEngine.renderingEngines[r].busy.indexOf(customizationId), 1);
528
-
529
- this._logger.debug(`Session(${this.id}).customize: Session was closed during customization request.`);
530
-
531
- const eventCancel2: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 1, data: { sessionId: this.id }, status: 'Session was closed during customization request' };
532
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_CANCEL, eventCancel2);
533
- return new SessionTreeNode();
535
+ // call the update callbacks
536
+ if (waitForViewportUpdate === false) {
537
+ for (const outputId in this.outputs) {
538
+ if (oldOutputVersions[outputId] !== newOutputVersions[outputId]) {
539
+ this._eventEngine.emitEvent(EVENTTYPE.OUTPUT.OUTPUT_UPDATED, <IOutputEvent>{
540
+ outputId: outputId,
541
+ outputVersion: newOutputVersions[outputId],
542
+ newNode: newNode.children.find(c => c.name === outputId)!,
543
+ oldNode: oldNode.children.find(c => c.name === outputId)!
544
+ });
545
+ }
546
+ }
547
+
548
+ await this.waitForUpdateCallbacks(newOutputVersions, oldOutputVersions, newNode, oldNode);
549
+
550
+ const cancelResult = this.cancelProcess(customizationId, eventId, TASK_TYPE.SESSION_CUSTOMIZATION, 1, { sessionId: this.id });
551
+ if (cancelResult) return cancelResult;
534
552
  }
535
553
 
536
554
  // if this is not a call by the goBack or goForward functions, add the parameter values to the history and delete the forward history
@@ -547,8 +565,8 @@ export class SessionEngine implements ISessionEngine {
547
565
 
548
566
  // set the session values to the current ones in all parameters
549
567
  for (const parameterId in this.parameters)
550
- (<any>this.parameters[parameterId].sessionValue) = parameterSet[parameterId].value;
551
-
568
+ (<unknown>this.parameters[parameterId].sessionValue) = parameterSet[parameterId].value;
569
+
552
570
  // set the output content to what has been updated
553
571
  for (const outputId in this.outputs)
554
572
  this.outputs[outputId].updateOutput(
@@ -564,7 +582,7 @@ export class SessionEngine implements ISessionEngine {
564
582
 
565
583
  this.node.excludeViewports = JSON.parse(JSON.stringify(this._excludeViewports));
566
584
 
567
- for (let r in this._stateEngine.renderingEngines)
585
+ for (const r in this._stateEngine.renderingEngines)
568
586
  if (this._stateEngine.renderingEngines[r].busy.includes(customizationId))
569
587
  this._stateEngine.renderingEngines[r].busy.splice(this._stateEngine.renderingEngines[r].busy.indexOf(customizationId), 1);
570
588
 
@@ -576,26 +594,44 @@ export class SessionEngine implements ISessionEngine {
576
594
  this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_END, eventEnd);
577
595
 
578
596
  // update the viewports
579
- for (let r in this._stateEngine.renderingEngines)
580
- if(!this.excludeViewports.includes(this._stateEngine.renderingEngines[r].id))
581
- this._stateEngine.renderingEngines[r].update(`SessionEngine(${this.id}).customize`);
597
+ if (waitForViewportUpdate) {
598
+ for (const r in this._stateEngine.renderingEngines)
599
+ if (!this.excludeViewports.includes(this._stateEngine.renderingEngines[r].id))
600
+ this._stateEngine.renderingEngines[r].update(`SessionEngine(${this.id}).customize`);
601
+
602
+
603
+ for (const outputId in this.outputs) {
604
+ if (oldOutputVersions[outputId] !== newOutputVersions[outputId]) {
605
+ this._eventEngine.emitEvent(EVENTTYPE.OUTPUT.OUTPUT_UPDATED, <IOutputEvent>{
606
+ outputId: outputId,
607
+ outputVersion: newOutputVersions[outputId],
608
+ newNode: newNode.children.find(c => c.name === outputId)!,
609
+ oldNode: oldNode.children.find(c => c.name === outputId)!
610
+ });
611
+ }
612
+ }
582
613
 
583
- // call the update callback function on the session
584
- if (this._updateCallback) this._updateCallback(newNode, oldNode);
614
+ // call the update callbacks
615
+ await this.waitForUpdateCallbacks(newOutputVersions, oldOutputVersions, newNode, oldNode);
585
616
 
586
- // call the update callback functions on the outputs
587
- for (const outputId in this.outputs)
588
- this.outputs[outputId].triggerUpdateCallback(
589
- newNode.children.find(c => c.name === outputId)!,
590
- oldNode.children.find(c => c.name === outputId)!
591
- );
617
+ const cancelResult = this.cancelProcess(customizationId, eventId, TASK_TYPE.SESSION_CUSTOMIZATION, 1, { sessionId: this.id });
618
+ if (cancelResult) return cancelResult;
619
+ }
620
+
621
+ if (!waitForViewportUpdate) {
622
+ setTimeout(() => {
623
+ for (const r in this._stateEngine.renderingEngines)
624
+ if (!this.excludeViewports.includes(this._stateEngine.renderingEngines[r].id))
625
+ this._stateEngine.renderingEngines[r].update(`SessionEngine(${this.id}).customize`);
626
+ }, 0);
627
+ }
592
628
 
593
629
  return this.node;
594
630
  } catch (e) {
595
631
  const eventCancel: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 1, data: { sessionId: this.id }, status: 'Session customization failed' };
596
632
  this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_CANCEL, eventCancel);
597
633
 
598
- for (let r in this._stateEngine.renderingEngines)
634
+ for (const r in this._stateEngine.renderingEngines)
599
635
  if (this._stateEngine.renderingEngines[r].busy.includes(customizationId))
600
636
  this._stateEngine.renderingEngines[r].busy.splice(this._stateEngine.renderingEngines[r].busy.indexOf(customizationId), 1);
601
637
 
@@ -603,34 +639,36 @@ export class SessionEngine implements ISessionEngine {
603
639
  }
604
640
  }
605
641
 
606
- public async customizeParallel(parameterValues: { [key: string]: string }): Promise<ITreeNode> {
607
- const eventId = this._uuidGenerator.create();
642
+ public async customizeParallel(parameterValues: { [key: string]: string }, loadOutputs = true): Promise<ISessionTreeNode | ShapeDiverResponseDto> {
643
+ const eventId = this._uuidGenerator.create();
608
644
 
609
- const eventStart: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 0, data: { sessionId: this.id }, status: 'Customizing session' };
610
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_START, eventStart);
645
+ const eventStart: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 0, data: { sessionId: this.id }, status: 'Customizing session' };
646
+ this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_START, eventStart);
611
647
 
612
- const parameterSet: {
613
- [key: string]: string
614
- } = {};
648
+ const parameterSet: {
649
+ [key: string]: string
650
+ } = {};
615
651
 
616
- // create a set of the current validated parameter values
617
- for (const parameterId in this.parameters)
618
- parameterSet[parameterId] = parameterValues[parameterId] !== undefined ? (' ' + parameterValues[parameterId]).slice(1) : this.parameters[parameterId].stringify()
652
+ // create a set of the current validated parameter values
653
+ for (const parameterId in this.parameters)
654
+ parameterSet[parameterId] = parameterValues[parameterId] !== undefined ? (' ' + parameterValues[parameterId]).slice(1) : this.parameters[parameterId].stringify();
619
655
 
620
- const newNode = await this.customizeSession(parameterSet, () => false, {
621
- eventId,
622
- type: TASK_TYPE.SESSION_CUSTOMIZATION,
623
- progressRange: {
624
- min: 0.0,
625
- max: 1
626
- },
627
- data: { sessionId: this.id }
628
- }, true);
629
- newNode.excludeViewports = JSON.parse(JSON.stringify(this._excludeViewports));
630
-
631
- const eventEnd: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 1, data: { sessionId: this.id }, status: 'Session customized' };
632
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_END, eventEnd);
633
- return newNode;
656
+ const result = await this.customizeSession(parameterSet, () => false, {
657
+ eventId,
658
+ type: TASK_TYPE.SESSION_CUSTOMIZATION,
659
+ progressRange: {
660
+ min: 0.0,
661
+ max: 1
662
+ },
663
+ data: { sessionId: this.id }
664
+ }, true, loadOutputs);
665
+
666
+ if (result instanceof SessionTreeNode)
667
+ result.excludeViewports = JSON.parse(JSON.stringify(this._excludeViewports));
668
+
669
+ const eventEnd: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 1, data: { sessionId: this.id }, status: 'Session customized' };
670
+ this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_END, eventEnd);
671
+ return result;
634
672
  }
635
673
 
636
674
  public async goBack(): Promise<ITreeNode> {
@@ -684,7 +722,7 @@ export class SessionEngine implements ISessionEngine {
684
722
  public async init(parameterValues?: {
685
723
  [key: string]: string;
686
724
  }, retry = false): Promise<void> {
687
- if (this._initialized === true)
725
+ if (this._initialized === true)
688
726
  throw new ShapeDiverViewerSessionError('Session.init: Session already initialized.');
689
727
 
690
728
  try {
@@ -695,13 +733,13 @@ export class SessionEngine implements ISessionEngine {
695
733
  for (const parameterNameOrId in parameterValues)
696
734
  parameterSet[parameterNameOrId] = (' ' + parameterValues[parameterNameOrId]).slice(1);
697
735
 
698
- if(this._ticket) {
736
+ if (this._ticket) {
699
737
  this._responseDto = await this._sdk.session.init(this._ticket, parameterSet);
700
- } else if(this._guid) {
738
+ } else if (this._guid) {
701
739
  this._responseDto = await this._sdk.session.initForModel(this._guid, parameterSet);
702
740
  } else {
703
741
  // we should never get here
704
- throw new ShapeDiverViewerSessionError(`Session.init: Initialization of session failed. Neither a ticket nor a guid are available.`)
742
+ throw new ShapeDiverViewerSessionError('Session.init: Initialization of session failed. Neither a ticket nor a guid are available.');
705
743
  }
706
744
  this._performanceEvaluator.endSection('sessionResponse');
707
745
 
@@ -709,18 +747,18 @@ export class SessionEngine implements ISessionEngine {
709
747
  this._viewerSettingsVersionBackend = this._responseDto.viewerSettingsVersion || latestVersion;
710
748
  this._sessionId = this._responseDto.sessionId;
711
749
  this._modelId = this._responseDto.model?.id;
712
-
750
+
713
751
  this._httpClient.addDataLoading(this._sessionId!, {
714
752
  getAsset: this._sdk.asset.getAsset.bind(this._sdk.asset),
715
753
  downloadTexture: this._sdk.asset.downloadImage.bind(this._sdk.asset),
716
- })
754
+ });
717
755
 
718
756
  this._settingsEngine.loadSettings(this._viewerSettings);
719
-
757
+
720
758
  if (!this._sessionId)
721
- throw new ShapeDiverViewerSessionError(`Session.init: Initialization of session failed. ResponseDto did not have a sessionId.`)
759
+ throw new ShapeDiverViewerSessionError('Session.init: Initialization of session failed. ResponseDto did not have a sessionId.');
722
760
  if (!this._modelId)
723
- throw new ShapeDiverViewerSessionError(`Session.init: Initialization of session failed. ResponseDto did not have a model.id.`)
761
+ throw new ShapeDiverViewerSessionError('Session.init: Initialization of session failed. ResponseDto did not have a model.id.');
724
762
 
725
763
  this.updateResponseDto(this._responseDto, parameterSet);
726
764
  this._initialized = true;
@@ -730,7 +768,63 @@ export class SessionEngine implements ISessionEngine {
730
768
  }
731
769
  }
732
770
 
733
-
771
+ public async loadCachedOutputsParallel(outputMapping: { [key: string]: string }, taskEventInfo?: OutputLoaderTaskEventInfo, retry = false): Promise<{ [key: string]: ITreeNode | undefined }> {
772
+ this.checkAvailability();
773
+ // if there is already task event info, use it
774
+ // this happens after a retry
775
+ const eventId = taskEventInfo ? taskEventInfo.eventId : this._uuidGenerator.create();
776
+ const eventType = taskEventInfo ? taskEventInfo.type : TASK_TYPE.SESSION_OUTPUTS_LOADING;
777
+ const eventData = taskEventInfo ? taskEventInfo.data : { sessionId: this.id };
778
+
779
+ taskEventInfo = taskEventInfo ? taskEventInfo : {
780
+ eventId,
781
+ type: eventType,
782
+ progressRange: {
783
+ min: 0,
784
+ max: 1
785
+ },
786
+ data: eventData
787
+ };
788
+
789
+ try {
790
+ // send start event if this function was called initially
791
+ if (!taskEventInfo) {
792
+ const eventStart: ITaskEvent = { type: eventType, id: eventId, progress: 0, data: eventData, status: 'Loading cached outputs' };
793
+ this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_START, eventStart);
794
+ }
795
+
796
+ // get the cached outputs
797
+ const responseDto = await this._sdk.output.getCache(this._sessionId!, outputMapping);
798
+
799
+ // create atomic output api objects for them
800
+ const outputs: {
801
+ [key: string]: IOutput;
802
+ } = {};
803
+ for (const outputId in responseDto.outputs) {
804
+ responseDto.outputs[outputId].id = outputId;
805
+ outputs[outputId] = new Output(<ShapeDiverResponseOutput>responseDto.outputs[outputId], this);
806
+ }
807
+
808
+ // process the output data
809
+ const node = await this._outputLoader.loadOutputs(this._responseDto!.model?.name || 'model', outputs, {}, taskEventInfo, false);
810
+
811
+ // send the end event once done
812
+ const eventEnd: ITaskEvent = { type: eventType, id: eventId, progress: 1, data: eventData, status: 'Loaded cached outputs' };
813
+ this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_END, eventEnd);
814
+
815
+ // create a mapping with a dictionary for the id of the outputs
816
+ const outputNodeMapping: { [key: string]: ITreeNode | undefined } = {};
817
+ for (const outputId in outputMapping)
818
+ outputNodeMapping[outputId] = node.children.find(n => n.name === outputId);
819
+
820
+ return outputNodeMapping;
821
+ }
822
+ catch (e) {
823
+ await this.handleError(e, retry);
824
+ return await this.loadCachedOutputsParallel(outputMapping, taskEventInfo, true);
825
+ }
826
+ }
827
+
734
828
  /**
735
829
  * Load the outputs and return the scene graph node of the result.
736
830
  * In case the outputs have a delay property, another customization request with the parameter set is sent.
@@ -739,25 +833,16 @@ export class SessionEngine implements ISessionEngine {
739
833
  * @param outputs the outputs to load
740
834
  * @returns promise with a scene graph node
741
835
  */
742
- public async loadOutputsParallel(responseDto: ShapeDiverResponseDto, cancelRequest: () => boolean = () => false, taskEventInfo: OutputLoaderTaskEventInfo, retry = false): Promise<ISessionTreeNode> {
836
+ public async loadOutputs(cancelRequest: () => boolean = () => false, taskEventInfo: OutputLoaderTaskEventInfo, retry = false): Promise<ISessionTreeNode> {
743
837
  this.checkAvailability();
744
838
 
745
- let outputs: {
746
- [key: string]: IOutput;
747
- } = {}
748
- let outputsFreeze: {
749
- [key: string]: boolean;
750
- } = {}
751
-
752
- for (let outputId in responseDto.outputs) {
753
- responseDto.outputs[outputId].id = outputId;
754
- if (this.outputsFreeze[outputId] === undefined) outputsFreeze[outputId] = false;
755
- outputs[outputId] = new Output(<ShapeDiverResponseOutput>responseDto.outputs[outputId], this);
756
- }
757
-
839
+ const o = Object.assign({}, this._outputs);
840
+ const of = Object.assign({}, this._outputsFreeze);
758
841
  try {
759
- const node = await this._outputLoader.loadOutputs(this._responseDto!.model?.name || 'model', outputs, outputsFreeze, taskEventInfo);
760
- node.data.push(new SessionData(responseDto));
842
+ const node = await this._outputLoader.loadOutputs(this._responseDto!.model?.name || 'model', o, of, taskEventInfo);
843
+ node.data.push(new SessionData(this._responseDto!));
844
+ if (cancelRequest()) return node;
845
+ node.excludeViewports = JSON.parse(JSON.stringify(this._excludeViewports));
761
846
  return node;
762
847
  }
763
848
  catch (e) {
@@ -766,23 +851,23 @@ export class SessionEngine implements ISessionEngine {
766
851
  } else {
767
852
  await this.handleError(e, retry);
768
853
  if (cancelRequest()) return new SessionTreeNode();
769
- return await this.loadOutputsParallel(responseDto, cancelRequest, taskEventInfo, true);
854
+ return await this.loadOutputs(cancelRequest, taskEventInfo, true);
770
855
  }
771
856
 
772
857
  if (cancelRequest()) return new SessionTreeNode();
773
- let outputMapping: { [key: string]: string } = {};
774
- for (let output in outputs)
775
- outputMapping[output] = outputs[output].version;
858
+ const outputMapping: { [key: string]: string } = {};
859
+ for (const output in o)
860
+ outputMapping[output] = o[output].version;
776
861
 
777
862
  try {
778
863
  const responseDto = await this._sdk.output.getCache(this._sessionId!, outputMapping);
779
864
  if (cancelRequest()) return new SessionTreeNode();
780
865
  this.updateResponseDto(responseDto);
781
- return await this.loadOutputsParallel(responseDto, cancelRequest, taskEventInfo);
866
+ return await this.loadOutputs(cancelRequest, taskEventInfo);
782
867
  } catch (e) {
783
868
  await this.handleError(e, retry);
784
869
  if (cancelRequest()) return new SessionTreeNode();
785
- return await this.loadOutputsParallel(responseDto, cancelRequest, taskEventInfo, true);
870
+ return await this.loadOutputs(cancelRequest, taskEventInfo, true);
786
871
  }
787
872
  }
788
873
  }
@@ -795,23 +880,25 @@ export class SessionEngine implements ISessionEngine {
795
880
  * @param outputs the outputs to load
796
881
  * @returns promise with a scene graph node
797
882
  */
798
- public async loadOutputs(cancelRequest: () => boolean = () => false, taskEventInfo: OutputLoaderTaskEventInfo, retry = false): Promise<ISessionTreeNode> {
883
+ public async loadOutputsParallel(responseDto: ShapeDiverResponseDto, cancelRequest: () => boolean = () => false, taskEventInfo: OutputLoaderTaskEventInfo, retry = false): Promise<ISessionTreeNode> {
799
884
  this.checkAvailability();
800
885
 
801
- const o = Object.assign({}, this._outputs);
802
- const of = Object.assign({}, this._outputsFreeze);
803
- try {
804
- const node = await this._outputLoader.loadOutputs(this._responseDto!.model?.name || 'model', o, of, taskEventInfo);
805
- node.data.push(new SessionData(this._responseDto!));
806
-
807
- if (cancelRequest()) return node;
808
-
809
- if (this._automaticSceneUpdate) this.removeFromSceneTree(this._node);
810
- this._node = node;
811
- if (this._automaticSceneUpdate && this._closed === false) this.addToSceneTree(this._node);
886
+ const outputs: {
887
+ [key: string]: IOutput;
888
+ } = {};
889
+ const outputsFreeze: {
890
+ [key: string]: boolean;
891
+ } = {};
812
892
 
813
- this.node.excludeViewports = JSON.parse(JSON.stringify(this._excludeViewports));
893
+ for (const outputId in responseDto.outputs) {
894
+ responseDto.outputs[outputId].id = outputId;
895
+ if (this.outputsFreeze[outputId] === undefined) outputsFreeze[outputId] = false;
896
+ outputs[outputId] = new Output(<ShapeDiverResponseOutput>responseDto.outputs[outputId], this);
897
+ }
814
898
 
899
+ try {
900
+ const node = await this._outputLoader.loadOutputs(this._responseDto!.model?.name || 'model', outputs, outputsFreeze, taskEventInfo);
901
+ node.data.push(new SessionData(responseDto));
815
902
  return node;
816
903
  }
817
904
  catch (e) {
@@ -820,23 +907,23 @@ export class SessionEngine implements ISessionEngine {
820
907
  } else {
821
908
  await this.handleError(e, retry);
822
909
  if (cancelRequest()) return new SessionTreeNode();
823
- return await this.loadOutputs(cancelRequest, taskEventInfo, true);
910
+ return await this.loadOutputsParallel(responseDto, cancelRequest, taskEventInfo, true);
824
911
  }
825
912
 
826
913
  if (cancelRequest()) return new SessionTreeNode();
827
- let outputMapping: { [key: string]: string } = {};
828
- for (let output in o)
829
- outputMapping[output] = o[output].version;
914
+ const outputMapping: { [key: string]: string } = {};
915
+ for (const output in outputs)
916
+ outputMapping[output] = outputs[output].version;
830
917
 
831
918
  try {
832
919
  const responseDto = await this._sdk.output.getCache(this._sessionId!, outputMapping);
833
920
  if (cancelRequest()) return new SessionTreeNode();
834
921
  this.updateResponseDto(responseDto);
835
- return await this.loadOutputs(cancelRequest, taskEventInfo);
922
+ return await this.loadOutputsParallel(responseDto, cancelRequest, taskEventInfo);
836
923
  } catch (e) {
837
924
  await this.handleError(e, retry);
838
925
  if (cancelRequest()) return new SessionTreeNode();
839
- return await this.loadOutputs(cancelRequest, taskEventInfo, true);
926
+ return await this.loadOutputsParallel(responseDto, cancelRequest, taskEventInfo, true);
840
927
  }
841
928
  }
842
929
  }
@@ -844,32 +931,8 @@ export class SessionEngine implements ISessionEngine {
844
931
  public async requestExport(exportId: string, parameters: { [key: string]: string }, maxWaitTime: number, retry = false): Promise<ShapeDiverResponseExport> {
845
932
  this.checkAvailability('export');
846
933
  try {
847
- const requestParameterSet: { [key: string]: string } = {};
848
-
849
- // first step, we convert all our names and displaynames to ids
850
- for (const parameterIdOrName in parameters) {
851
- // we prioritize id, then name and then displayname
852
- // 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)
853
- const parameterObject = Object.values(this._parameters).find(p => p.id === parameterIdOrName || p.name === parameterIdOrName || p.displayname === parameterIdOrName);
854
-
855
- // in case the key of the key value pair was neither the id, name or displayname, skip
856
- if(!parameterObject) continue;
857
-
858
- // deep copy into new dictionary
859
- requestParameterSet[parameterObject.id] = (' ' + parameters[parameterIdOrName]).slice(1);
860
- }
861
-
862
- // seconds step, fill all other parameter values that are currently not set
863
- const currentParameters = this.parameterValues;
864
- for (const parameterId in currentParameters) {
865
- // if already set by input values, skip
866
- if(requestParameterSet[parameterId] !== undefined) continue;
867
-
868
- // deep copy into new dictionary
869
- requestParameterSet[parameterId] = (' ' + currentParameters[parameterId]).slice(1);
870
- }
871
-
872
- const responseDto = await this._sdk.utils.submitAndWaitForExport(this._sdk, this._sessionId!, { exports: { id: exportId }, parameters: requestParameterSet }, maxWaitTime)
934
+ const requestParameterSet = this.cleanExportParameters(parameters);
935
+ const responseDto = await this._sdk.utils.submitAndWaitForExport(this._sdk, this._sessionId!, { exports: { id: exportId }, parameters: requestParameterSet }, maxWaitTime);
873
936
  this.updateResponseDto(responseDto);
874
937
  return this.exports[exportId];
875
938
  } catch (e) {
@@ -878,42 +941,55 @@ export class SessionEngine implements ISessionEngine {
878
941
  }
879
942
  }
880
943
 
944
+ public async requestExports(body: ShapeDiverRequestExport, maxWaitMsec?: number, retry = false): Promise<ShapeDiverResponseDto> {
945
+ this.checkAvailability('export');
946
+ try {
947
+ const requestParameterSet = this.cleanExportParameters(body.parameters);
948
+ 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);
949
+ this.updateResponseDto(responseDto);
950
+ return responseDto;
951
+ } catch (e) {
952
+ await this.handleError(e, retry);
953
+ return await this.requestExports(body, maxWaitMsec, true);
954
+ }
955
+ }
956
+
881
957
  public resetSettings(sections?: ISettingsSections): void {
882
958
  if (!this._responseDto)
883
- throw new ShapeDiverViewerSessionError(`Session.resetSettings: responseDto not available.`);
884
-
885
- sections = sections || {};
886
- if (sections.session === undefined) {
887
- sections.session = {
888
- parameter: { displayname: true, order: true, hidden: true },
889
- export: { displayname: true, order: true, hidden: true }
890
- };
891
- }
892
- if (sections.session.parameter === undefined)
893
- sections.session.parameter = { displayname: true, order: true, hidden: true, value: true };
894
- if (sections.session.export === undefined)
895
- sections.session.export = { displayname: true, order: true, hidden: true };
896
- if (sections.viewport === undefined)
897
- sections.viewport = { ar: true, scene: true, camera: true, light: true, environment: true, general: true, postprocessing: true };
898
-
899
- return this.applySettings(this._responseDto, sections);
959
+ throw new ShapeDiverViewerSessionError('Session.resetSettings: responseDto not available.');
960
+
961
+ sections = sections || {};
962
+ if (sections.session === undefined) {
963
+ sections.session = {
964
+ parameter: { displayname: true, order: true, hidden: true },
965
+ export: { displayname: true, order: true, hidden: true }
966
+ };
967
+ }
968
+ if (sections.session.parameter === undefined)
969
+ sections.session.parameter = { displayname: true, order: true, hidden: true, value: true };
970
+ if (sections.session.export === undefined)
971
+ sections.session.export = { displayname: true, order: true, hidden: true };
972
+ if (sections.viewport === undefined)
973
+ sections.viewport = { ar: true, scene: true, camera: true, light: true, environment: true, general: true, postprocessing: true };
974
+
975
+ return this.applySettings(this._responseDto, sections);
900
976
  }
901
977
 
902
978
  public async saveDefaultParameterValues(): Promise<boolean> {
903
- this._logger.debugLow(`Session(${this.id}).saveDefaultParameters: Saving default parameters.`);
904
- const response = await this.saveDefaultParameters();
905
- if (response) {
906
- this._logger.debug(`Session(${this.id}).saveDefaultParameters: Saved default parameters.`);
907
- } else {
908
- throw new ShapeDiverViewerSessionError(`Session(${this.id}).saveDefaultParameters: Could not save default parameters.`);
909
- }
910
- return response;
979
+ this._logger.debugLow(`Session(${this.id}).saveDefaultParameters: Saving default parameters.`);
980
+ const response = await this.saveDefaultParameters();
981
+ if (response) {
982
+ this._logger.debug(`Session(${this.id}).saveDefaultParameters: Saved default parameters.`);
983
+ } else {
984
+ throw new ShapeDiverViewerSessionError(`Session(${this.id}).saveDefaultParameters: Could not save default parameters.`);
985
+ }
986
+ return response;
911
987
  }
912
988
 
913
989
  public async saveDefaultParameters(retry = false): Promise<boolean> {
914
990
  this.checkAvailability('defaultparam', true);
915
991
  try {
916
- await this._sdk.model.setDefaultParams(this._modelId!, this._parameterValues)
992
+ await this._sdk.model.setDefaultParams(this._modelId!, this._parameterValues);
917
993
  return true;
918
994
  } catch (e) {
919
995
  await this.handleError(e, retry);
@@ -993,22 +1069,22 @@ export class SessionEngine implements ISessionEngine {
993
1069
  }
994
1070
  }
995
1071
 
996
- public async saveSettings(json: any, retry = false): Promise<boolean> {
1072
+ public async saveSettings(json: unknown, retry = false): Promise<boolean> {
997
1073
  this.checkAvailability('configure', true);
998
-
1074
+
999
1075
  try {
1000
- validate(json, <versions>this._viewerSettingsVersion)
1001
-
1076
+ validate(json, <versions>this._viewerSettingsVersion);
1077
+
1002
1078
  // if viewer settings version is higher than backend settings version
1003
1079
  // convert to backend settings version
1004
- if(+this._viewerSettingsVersion > +this._viewerSettingsVersionBackend)
1005
- json = convert(json, <versions>this._viewerSettingsVersionBackend)
1080
+ if (+this._viewerSettingsVersion > +this._viewerSettingsVersionBackend)
1081
+ json = convert(json, <versions>this._viewerSettingsVersionBackend);
1006
1082
  } catch (e) {
1007
1083
  throw new ShapeDiverViewerSettingsError('Session.saveSettings: Settings could not be validated. ' + (<Error>e).message, <Error>e);
1008
- }
1009
-
1010
- try {
1011
- await this._sdk.model.updateConfig(this._modelId!, json);
1084
+ }
1085
+
1086
+ try {
1087
+ await this._sdk.model.updateConfig(this._modelId!, json as ShapeDiverRequestConfigure);
1012
1088
  return true;
1013
1089
  } catch (e) {
1014
1090
  await this.handleError(e, retry);
@@ -1017,60 +1093,60 @@ export class SessionEngine implements ISessionEngine {
1017
1093
  }
1018
1094
 
1019
1095
  public async saveUiProperties(saveInSettings: boolean = true): Promise<boolean> {
1020
- this._logger.debugLow(`Session(${this.id}).saveSessionProperties: Saving session properties.`);
1096
+ this._logger.debugLow(`Session(${this.id}).saveSessionProperties: Saving session properties.`);
1021
1097
 
1022
- // settings saving
1023
- this._saveSessionSettings();
1098
+ // settings saving
1099
+ this._saveSessionSettings();
1024
1100
 
1025
- let properties: {
1026
- [key: string]: {
1027
- displayname: string,
1028
- hidden: boolean,
1029
- order: number,
1030
- tooltip: string
1031
- }
1032
- } = {};
1033
- for (let p in this.parameters) {
1034
- properties[p] = {
1035
- displayname: this.parameters[p].displayname !== undefined ? this.parameters[p].displayname! : '',
1036
- hidden: this.parameters[p].hidden !== undefined ? this.parameters[p].hidden : false,
1037
- order: this.parameters[p].order !== undefined ? this.parameters[p].order! : 0,
1038
- tooltip: this.parameters[p].tooltip !== undefined ? this.parameters[p].tooltip! : '',
1039
- };
1040
- }
1041
- const responseP = Object.values(properties).length !== 0 ? await this.saveParameterProperties(properties) : true;
1042
-
1043
- properties = {};
1044
- for (let e in this.exports) {
1045
- properties[e] = {
1046
- displayname: this.exports[e].displayname !== undefined ? this.exports[e].displayname! : '',
1047
- hidden: this.exports[e].hidden !== undefined ? this.exports[e].hidden : false,
1048
- order: this.exports[e].order !== undefined ? this.exports[e].order! : 0,
1049
- tooltip: this.exports[e].tooltip !== undefined ? this.exports[e].tooltip! : '',
1050
- };
1051
- }
1052
- const responseE = Object.values(properties).length !== 0 ? await this.saveExportProperties(properties) : true;
1053
-
1054
- properties = {};
1055
- for (let o in this.outputs) {
1056
- properties[o] = {
1057
- displayname: this.outputs[o].displayname !== undefined ? this.outputs[o].displayname! : '',
1058
- hidden: this.outputs[o].hidden !== undefined ? this.outputs[o].hidden : false,
1059
- order: this.outputs[o].order !== undefined ? this.outputs[o].order! : 0,
1060
- tooltip: this.outputs[o].tooltip !== undefined ? this.outputs[o].tooltip! : '',
1061
- };
1101
+ let properties: {
1102
+ [key: string]: {
1103
+ displayname: string,
1104
+ hidden: boolean,
1105
+ order: number,
1106
+ tooltip: string
1062
1107
  }
1063
- const responseO = Object.values(properties).length !== 0 ? await this.saveOutputProperties(properties) : true;
1108
+ } = {};
1109
+ for (const p in this.parameters) {
1110
+ properties[p] = {
1111
+ displayname: this.parameters[p].displayname !== undefined ? this.parameters[p].displayname! : '',
1112
+ hidden: this.parameters[p].hidden !== undefined ? this.parameters[p].hidden : false,
1113
+ order: this.parameters[p].order !== undefined ? this.parameters[p].order! : 0,
1114
+ tooltip: this.parameters[p].tooltip !== undefined ? this.parameters[p].tooltip! : '',
1115
+ };
1116
+ }
1117
+ const responseP = Object.values(properties).length !== 0 ? await this.saveParameterProperties(properties) : true;
1118
+
1119
+ properties = {};
1120
+ for (const e in this.exports) {
1121
+ properties[e] = {
1122
+ displayname: this.exports[e].displayname !== undefined ? this.exports[e].displayname! : '',
1123
+ hidden: this.exports[e].hidden !== undefined ? this.exports[e].hidden : false,
1124
+ order: this.exports[e].order !== undefined ? this.exports[e].order! : 0,
1125
+ tooltip: this.exports[e].tooltip !== undefined ? this.exports[e].tooltip! : '',
1126
+ };
1127
+ }
1128
+ const responseE = Object.values(properties).length !== 0 ? await this.saveExportProperties(properties) : true;
1129
+
1130
+ properties = {};
1131
+ for (const o in this.outputs) {
1132
+ properties[o] = {
1133
+ displayname: this.outputs[o].displayname !== undefined ? this.outputs[o].displayname! : '',
1134
+ hidden: this.outputs[o].hidden !== undefined ? this.outputs[o].hidden : false,
1135
+ order: this.outputs[o].order !== undefined ? this.outputs[o].order! : 0,
1136
+ tooltip: this.outputs[o].tooltip !== undefined ? this.outputs[o].tooltip! : '',
1137
+ };
1138
+ }
1139
+ const responseO = Object.values(properties).length !== 0 ? await this.saveOutputProperties(properties) : true;
1064
1140
 
1065
- // save partial settings
1066
- const response = saveInSettings ? await this.saveSettings(this._settingsEngine.settings) : true;
1141
+ // save partial settings
1142
+ const response = saveInSettings ? await this.saveSettings(this._settingsEngine.settings) : true;
1067
1143
 
1068
- if (response && responseP && responseO && responseE) {
1069
- this._logger.debug(`Session(${this.id}).saveSessionProperties: Saved session properties.`);
1070
- } else {
1071
- this._logger.warn(`Session(${this.id}).saveSessionProperties: Could not save session properties.`);
1072
- }
1073
- return response && responseP && responseO && responseE;
1144
+ if (response && responseP && responseO && responseE) {
1145
+ this._logger.debug(`Session(${this.id}).saveSessionProperties: Saved session properties.`);
1146
+ } else {
1147
+ this._logger.warn(`Session(${this.id}).saveSessionProperties: Could not save session properties.`);
1148
+ }
1149
+ return response && responseP && responseO && responseE;
1074
1150
  }
1075
1151
 
1076
1152
  public async setJwtToken(value: string) {
@@ -1080,18 +1156,18 @@ export class SessionEngine implements ISessionEngine {
1080
1156
  try {
1081
1157
  this._sdk.setConfigurationValue(ShapeDiverSdkConfigType.JWT_TOKEN, value);
1082
1158
  const responseDto = await this._sdk.session.default(this._sessionId!);
1083
- if(this._responseDto) this._responseDto.actions = responseDto.actions;
1159
+ if (this._responseDto) this._responseDto.actions = responseDto.actions;
1084
1160
  } catch (e) {
1085
1161
  throw this._httpClient.convertError(e);
1086
1162
  }
1087
1163
  }
1088
1164
 
1089
- public async updateOutputs(taskEventInfo?: OutputLoaderTaskEventInfo): Promise<ITreeNode> {
1165
+ public async updateOutputs(taskEventInfo?: OutputLoaderTaskEventInfo, waitForViewportUpdate: boolean = false): Promise<ITreeNode> {
1090
1166
  const eventId = taskEventInfo ? taskEventInfo.eventId : this._uuidGenerator.create();
1091
1167
  const eventType = taskEventInfo ? taskEventInfo.type : TASK_TYPE.SESSION_OUTPUTS_UPDATE;
1092
1168
  const eventData = taskEventInfo ? taskEventInfo.data : { sessionId: this.id };
1093
1169
 
1094
- if(!taskEventInfo) {
1170
+ if (!taskEventInfo) {
1095
1171
  const eventStart: ITaskEvent = { type: eventType, id: eventId, progress: 0, data: eventData, status: 'Updating outputs' };
1096
1172
  this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_START, eventStart);
1097
1173
  }
@@ -1102,13 +1178,15 @@ export class SessionEngine implements ISessionEngine {
1102
1178
 
1103
1179
  this._logger.debugLow(`Session(${this.id}).updateOutputs: Updating Outputs.`);
1104
1180
 
1105
- for (let r in this._stateEngine.renderingEngines)
1106
- if(!this.excludeViewports.includes(r))
1181
+ for (const r in this._stateEngine.renderingEngines)
1182
+ if (!this.excludeViewports.includes(r))
1107
1183
  this._stateEngine.renderingEngines[r].busy.push(customizationId);
1108
1184
 
1109
1185
  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' };
1110
1186
  this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_PROCESS, eventRequest);
1111
1187
 
1188
+ const oldOutputVersions = this._outputLoader.getCurrentOutputVersions();
1189
+
1112
1190
  const newNode = await this.loadOutputs(() => this.#customizationProcess !== customizationId, {
1113
1191
  eventId,
1114
1192
  type: eventType,
@@ -1119,29 +1197,33 @@ export class SessionEngine implements ISessionEngine {
1119
1197
  data: eventData
1120
1198
  });
1121
1199
 
1200
+ const newOutputVersions = this._outputLoader.getCurrentOutputVersions();
1201
+
1122
1202
  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' };
1123
1203
  this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_PROCESS, eventSceneUpdate);
1124
1204
 
1125
1205
  // OPTION TO SKIP - PART 1
1126
- if (this.#customizationProcess !== customizationId) {
1127
- for (let r in this._stateEngine.renderingEngines)
1128
- if (this._stateEngine.renderingEngines[r].busy.includes(customizationId))
1129
- this._stateEngine.renderingEngines[r].busy.splice(this._stateEngine.renderingEngines[r].busy.indexOf(customizationId), 1);
1130
-
1131
- const eventCancel1: ITaskEvent = { type: eventType, id: eventId, progress: taskEventInfo ? (taskEventInfo.progressRange.max - taskEventInfo.progressRange.min) * 1 + taskEventInfo.progressRange.min : 1, data: eventData, status: 'Output updating was exceeded by other customization request' };
1132
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_CANCEL, eventCancel1);
1133
- this._logger.debug(`Session(${this.id}).updateOutputs: Output updating was exceeded by other request.`);
1134
- return newNode;
1135
- } else if (this._closed === true) {
1136
- for (let r in this._stateEngine.renderingEngines)
1137
- if (this._stateEngine.renderingEngines[r].busy.includes(customizationId))
1138
- this._stateEngine.renderingEngines[r].busy.splice(this._stateEngine.renderingEngines[r].busy.indexOf(customizationId), 1);
1139
-
1140
- this._logger.debug(`Session(${this.id}).customize: Session was closed during customization request.`);
1206
+ const cancelResult = this.cancelProcess(customizationId, eventId, eventType, taskEventInfo ? (taskEventInfo.progressRange.max - taskEventInfo.progressRange.min) * 1 + taskEventInfo.progressRange.min : 1, eventData, newNode);
1207
+ if (cancelResult) return cancelResult;
1208
+
1209
+ // call the update callbacks
1210
+ if (waitForViewportUpdate === false) {
1211
+ for (const outputId in this.outputs) {
1212
+ if (oldOutputVersions[outputId] !== newOutputVersions[outputId]) {
1213
+ this._eventEngine.emitEvent(EVENTTYPE.OUTPUT.OUTPUT_UPDATED, {
1214
+ outputId: outputId,
1215
+ outputVersion: newOutputVersions[outputId],
1216
+ newNode: newNode.children.find(c => c.name === outputId)!,
1217
+ oldNode: oldNode.children.find(c => c.name === outputId)!
1218
+ });
1219
+ }
1220
+ }
1221
+
1222
+ await this.waitForUpdateCallbacks(newOutputVersions, oldOutputVersions, newNode, oldNode);
1141
1223
 
1142
- const eventCancel1a: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 1, data: { sessionId: this.id }, status: 'Session was closed during customization request' };
1143
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_CANCEL, eventCancel1a);
1144
- return new SessionTreeNode();
1224
+ // OPTION TO SKIP - PART 2
1225
+ const cancelResult = this.cancelProcess(customizationId, eventId, eventType, taskEventInfo ? (taskEventInfo.progressRange.max - taskEventInfo.progressRange.min) * 1 + taskEventInfo.progressRange.min : 1, eventData, newNode);
1226
+ if (cancelResult) return cancelResult;
1145
1227
  }
1146
1228
 
1147
1229
  if (this.automaticSceneUpdate) this.removeFromSceneTree(this.node);
@@ -1149,7 +1231,7 @@ export class SessionEngine implements ISessionEngine {
1149
1231
  if (this.automaticSceneUpdate && this._closed === false) this.addToSceneTree(this.node);
1150
1232
 
1151
1233
  this._logger.debug(`Session(${this.id}).updateOutputs: Updating outputs finished, updating geometry.`);
1152
-
1234
+
1153
1235
  // set the output content to what has been updated
1154
1236
  for (const outputId in this.outputs) {
1155
1237
  this.outputs[outputId].updateOutput(
@@ -1165,31 +1247,40 @@ export class SessionEngine implements ISessionEngine {
1165
1247
  this._warningCreator();
1166
1248
  this.node.excludeViewports = JSON.parse(JSON.stringify(this._excludeViewports));
1167
1249
 
1168
- for (let r in this._stateEngine.renderingEngines)
1250
+ for (const r in this._stateEngine.renderingEngines)
1169
1251
  if (this._stateEngine.renderingEngines[r].busy.includes(customizationId))
1170
1252
  this._stateEngine.renderingEngines[r].busy.splice(this._stateEngine.renderingEngines[r].busy.indexOf(customizationId), 1);
1171
1253
 
1172
1254
  this._logger.debug(`Session(${this.id}).updateOutputs: Updated outputs.`);
1173
-
1174
- if(!taskEventInfo) {
1255
+
1256
+ if (!taskEventInfo) {
1175
1257
  const eventEnd: ITaskEvent = { type: eventType, id: eventId, progress: 1, data: eventData, status: 'Outputs updated' };
1176
1258
  this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_END, eventEnd);
1177
1259
  }
1178
1260
 
1179
1261
  // update the viewports
1180
- for (let r in this._stateEngine.renderingEngines)
1181
- if (!this.excludeViewports.includes(this._stateEngine.renderingEngines[r].id))
1182
- this._stateEngine.renderingEngines[r].update(`SessionEngine(${this.id}).customize`);
1262
+ if (waitForViewportUpdate) {
1263
+ for (const r in this._stateEngine.renderingEngines)
1264
+ if (!this.excludeViewports.includes(this._stateEngine.renderingEngines[r].id))
1265
+ this._stateEngine.renderingEngines[r].update(`SessionEngine(${this.id}).updateOutputs`);
1266
+
1267
+ for (const outputId in this.outputs) {
1268
+ if (oldOutputVersions[outputId] !== newOutputVersions[outputId]) {
1269
+ this._eventEngine.emitEvent(EVENTTYPE.OUTPUT.OUTPUT_UPDATED, {
1270
+ outputId: outputId,
1271
+ outputVersion: newOutputVersions[outputId],
1272
+ newNode: newNode.children.find(c => c.name === outputId)!,
1273
+ oldNode: oldNode.children.find(c => c.name === outputId)!
1274
+ });
1275
+ }
1276
+ }
1183
1277
 
1184
- // call the update callback function on the session
1185
- if (this._updateCallback) this._updateCallback(newNode, oldNode);
1278
+ await this.waitForUpdateCallbacks(newOutputVersions, oldOutputVersions, newNode, oldNode);
1186
1279
 
1187
- // call the update callback functions on the outputs
1188
- for (const outputId in this.outputs)
1189
- this.outputs[outputId].triggerUpdateCallback(
1190
- newNode.children.find(c => c.name === outputId)!,
1191
- oldNode.children.find(c => c.name === outputId)!
1192
- );
1280
+ // OPTION TO SKIP - PART 3
1281
+ const cancelResult = this.cancelProcess(customizationId, eventId, eventType, taskEventInfo ? (taskEventInfo.progressRange.max - taskEventInfo.progressRange.min) * 1 + taskEventInfo.progressRange.min : 1, eventData, newNode);
1282
+ if (cancelResult) return cancelResult;
1283
+ }
1193
1284
 
1194
1285
  return this.node;
1195
1286
  }
@@ -1199,14 +1290,14 @@ export class SessionEngine implements ISessionEngine {
1199
1290
  try {
1200
1291
  const responseDto = await this._sdk.file.requestUpload(this._sessionId!, {
1201
1292
  [parameterId]: { size: data.size, format: type }
1202
- })
1293
+ });
1203
1294
 
1204
1295
  if (responseDto && responseDto.asset && responseDto.asset.file && responseDto.asset.file[parameterId]) {
1205
1296
  const fileAsset = responseDto.asset.file[parameterId];
1206
1297
  await this._sdk.utils.upload(fileAsset.href, await data.arrayBuffer(), type);
1207
1298
  return fileAsset.id;
1208
1299
  } else {
1209
- throw new ShapeDiverViewerSessionError(`Session.uploadFile: Upload reply has not the required format.`);
1300
+ throw new ShapeDiverViewerSessionError('Session.uploadFile: Upload reply has not the required format.');
1210
1301
  }
1211
1302
  } catch (e) {
1212
1303
  await this.handleError(e, retry);
@@ -1218,8 +1309,8 @@ export class SessionEngine implements ISessionEngine {
1218
1309
  this.checkAvailability('gltf-upload');
1219
1310
  try {
1220
1311
  const responseDto = await this._sdk.gltf.upload(this._sessionId!, await blob.arrayBuffer(), 'model/gltf-binary', conversion);
1221
- if (!responseDto || !responseDto.gltf || !responseDto.gltf.href)
1222
- throw new ShapeDiverViewerSessionError(`Session.uploadGLTF: Upload reply has not the required format.`);
1312
+ if (!responseDto || !responseDto.gltf || !responseDto.gltf.href)
1313
+ throw new ShapeDiverViewerSessionError('Session.uploadGLTF: Upload reply has not the required format.');
1223
1314
  return responseDto;
1224
1315
  } catch (e) {
1225
1316
  await this.handleError(e, retry);
@@ -1227,9 +1318,9 @@ export class SessionEngine implements ISessionEngine {
1227
1318
  }
1228
1319
  }
1229
1320
 
1230
- // #endregion Public Methods (24)
1321
+ // #endregion Public Methods (27)
1231
1322
 
1232
- // #region Private Methods (10)
1323
+ // #region Private Methods (13)
1233
1324
 
1234
1325
  private _saveSessionSettings() {
1235
1326
  const parameters = this.parameters;
@@ -1242,26 +1333,26 @@ export class SessionEngine implements ISessionEngine {
1242
1333
  hidden: boolean;
1243
1334
  }
1244
1335
  } = {};
1245
- for (let p in parameters) {
1336
+ for (const p in parameters) {
1246
1337
  sessionProperties[p] = {
1247
1338
  order: parameters[p].order || 0,
1248
1339
  displayname: parameters[p].displayname || '',
1249
1340
  hidden: parameters[p].hidden
1250
- }
1341
+ };
1251
1342
  }
1252
- for (let e in exports) {
1343
+ for (const e in exports) {
1253
1344
  sessionProperties[e] = {
1254
1345
  order: exports[e].order || 0,
1255
1346
  displayname: exports[e].displayname || '',
1256
1347
  hidden: exports[e].hidden
1257
- }
1348
+ };
1258
1349
  }
1259
1350
  this._settingsEngine.session = sessionProperties;
1260
1351
 
1261
1352
  let orderedOutputs: IOutput[] = [];
1262
- for (let o in this.outputs) orderedOutputs.push(this.outputs[o]);
1353
+ for (const o in this.outputs) orderedOutputs.push(this.outputs[o]);
1263
1354
  orderedOutputs.sort((a, b) => ((a.order || Infinity) - (b.order || Infinity)));
1264
- let zerosOutputs = orderedOutputs.filter(x => x.order === 0);
1355
+ const zerosOutputs = orderedOutputs.filter(x => x.order === 0);
1265
1356
  orderedOutputs = orderedOutputs.filter((el) => { return !zerosOutputs.includes(el); });
1266
1357
  orderedOutputs = zerosOutputs.concat(orderedOutputs);
1267
1358
 
@@ -1306,41 +1397,110 @@ export class SessionEngine implements ISessionEngine {
1306
1397
  this._sceneTree.root.updateVersion();
1307
1398
  }
1308
1399
 
1400
+ private cancelProcess(customizationId: string, eventId: string, eventType: TASK_TYPE, eventProgress: number, eventData: unknown, newNode: ITreeNode = new SessionTreeNode()): ITreeNode | undefined {
1401
+ if (this.#customizationProcess !== customizationId) {
1402
+ for (const r in this._stateEngine.renderingEngines)
1403
+ if (this._stateEngine.renderingEngines[r].busy.includes(customizationId))
1404
+ this._stateEngine.renderingEngines[r].busy.splice(this._stateEngine.renderingEngines[r].busy.indexOf(customizationId), 1);
1405
+
1406
+ const eventCancel: ITaskEvent = {
1407
+ type: eventType,
1408
+ id: eventId,
1409
+ progress: eventProgress,
1410
+ data: eventData,
1411
+ status: 'The request was exceeded by another customization request'
1412
+ };
1413
+ this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_CANCEL, eventCancel);
1414
+ this._logger.debug(`Session(${this.id}).cancelProcess: The request was was exceeded by another request.`);
1415
+ return newNode;
1416
+ } else if ((this._closed as boolean) === true) {
1417
+ for (const r in this._stateEngine.renderingEngines)
1418
+ if (this._stateEngine.renderingEngines[r].busy.includes(customizationId))
1419
+ this._stateEngine.renderingEngines[r].busy.splice(this._stateEngine.renderingEngines[r].busy.indexOf(customizationId), 1);
1420
+
1421
+ this._logger.debug(`Session(${this.id}).cancelProcess: The session was closed during the request.`);
1422
+
1423
+ const eventCancel: ITaskEvent = { type: TASK_TYPE.SESSION_CUSTOMIZATION, id: eventId, progress: 1, data: { sessionId: this.id }, status: 'The session was closed during the request.' };
1424
+ this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_CANCEL, eventCancel);
1425
+ return new SessionTreeNode();
1426
+ }
1427
+ }
1428
+
1309
1429
  private checkAvailability(action?: string, checkForModelId = false) {
1310
1430
  if (!this._responseDto)
1311
- throw new ShapeDiverViewerSessionError(`Session.checkAvailability: responseDto not available.`);
1431
+ throw new ShapeDiverViewerSessionError('Session.checkAvailability: responseDto not available.');
1312
1432
 
1313
1433
  if (!this._sessionId)
1314
- throw new ShapeDiverViewerSessionError(`Session.checkAvailability: sessionId not available.`);
1434
+ throw new ShapeDiverViewerSessionError('Session.checkAvailability: sessionId not available.');
1315
1435
 
1316
1436
  if (checkForModelId && !this._modelId)
1317
- throw new ShapeDiverViewerSessionError(`Session.checkAvailability: modelId not available.`);
1437
+ throw new ShapeDiverViewerSessionError('Session.checkAvailability: modelId not available.');
1318
1438
 
1319
1439
  if (action && !this._responseDto.actions)
1320
- throw new ShapeDiverViewerSessionError(`Session.checkAvailability: actions not available.`);
1440
+ throw new ShapeDiverViewerSessionError('Session.checkAvailability: actions not available.');
1321
1441
 
1322
1442
  const responseDtoAction = this._responseDto.actions?.find(a => a.name === action);
1323
1443
  if (action && !responseDtoAction)
1324
1444
  throw new ShapeDiverViewerSessionError(`Session.checkAvailability: action ${action} not available.`);
1325
1445
  }
1326
1446
 
1447
+ private cleanExportParameters(parameters: ShapeDiverRequestCustomization): ShapeDiverRequestCustomization {
1448
+ const requestParameterSet: ShapeDiverRequestCustomization = {};
1449
+
1450
+ // first step, we convert all our names and displaynames to ids
1451
+ for (const parameterIdOrName in parameters) {
1452
+ // we prioritize id, then name and then displayname
1453
+ // 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)
1454
+ const parameterObject = Object.values(this._parameters).find(p => p.id === parameterIdOrName || p.name === parameterIdOrName || p.displayname === parameterIdOrName);
1455
+
1456
+ // in case the key of the key value pair was neither the id, name or displayname, skip
1457
+ if (!parameterObject) continue;
1458
+
1459
+ // copy into new dictionary
1460
+ requestParameterSet[parameterObject.id] = parameters[parameterIdOrName];
1461
+ }
1462
+
1463
+ // seconds step, fill all other parameter values that are currently not set
1464
+ const currentParameters = this.parameterValues;
1465
+ for (const parameterId in currentParameters) {
1466
+ // if already set by input values, skip
1467
+ if (requestParameterSet[parameterId] !== undefined) continue;
1468
+
1469
+ // deep copy into new dictionary
1470
+ requestParameterSet[parameterId] = (' ' + currentParameters[parameterId]).slice(1);
1471
+ }
1472
+
1473
+ return requestParameterSet;
1474
+ }
1475
+
1327
1476
  private async customizeInternal(cancelRequest: () => boolean, taskEventInfo: OutputLoaderTaskEventInfo): Promise<ISessionTreeNode> {
1328
- return this.customizeSession(this._parameterValues, cancelRequest, taskEventInfo);
1477
+ return this.customizeSession(this._parameterValues, cancelRequest, taskEventInfo) as Promise<ISessionTreeNode>;
1329
1478
  }
1330
1479
 
1331
- private async customizeSession(parameters: { [key: string]: string }, cancelRequest: () => boolean, taskEventInfo: OutputLoaderTaskEventInfo, parallel = false, retry = false): Promise<ISessionTreeNode> {
1480
+ private async customizeSession(parameters: { [key: string]: string }, cancelRequest: () => boolean, taskEventInfo: OutputLoaderTaskEventInfo, parallel = false, loadOutputs = true, retry = false): Promise<ISessionTreeNode | ShapeDiverResponseDto> {
1332
1481
  this.checkAvailability('customize');
1333
1482
  try {
1334
1483
  this._performanceEvaluator.startSection('sessionResponse');
1335
1484
  const responseDto = await this._sdk.utils.submitAndWaitForCustomization(this._sdk, this._sessionId!, parameters);
1336
1485
  this._performanceEvaluator.endSection('sessionResponse');
1337
- if (cancelRequest()) return new SessionTreeNode();
1338
- if (parallel === false) this.updateResponseDto(responseDto);
1339
- return parallel === false ? this.loadOutputs(cancelRequest, taskEventInfo) : this.loadOutputsParallel(responseDto, cancelRequest, taskEventInfo);
1486
+ if (loadOutputs === true) {
1487
+ if (cancelRequest()) return new SessionTreeNode();
1488
+ if (parallel === true) {
1489
+ // special case, we load the outputs put don't add them to the scene
1490
+ return this.loadOutputsParallel(responseDto, cancelRequest, taskEventInfo);
1491
+ } else {
1492
+ // default case, we load the outputs and return the nodes
1493
+ this.updateResponseDto(responseDto);
1494
+ return this.loadOutputs(cancelRequest, taskEventInfo);
1495
+ }
1496
+ } else {
1497
+ // special case, we don't load the outputs and only return the responseDto
1498
+ return responseDto;
1499
+ }
1340
1500
  } catch (e) {
1341
1501
  await this.handleError(e, retry);
1342
1502
  if (cancelRequest()) return new SessionTreeNode();
1343
- return await this.customizeSession(parameters, cancelRequest, taskEventInfo, parallel, true);
1503
+ return await this.customizeSession(parameters, cancelRequest, taskEventInfo, parallel, loadOutputs, true);
1344
1504
  }
1345
1505
  }
1346
1506
 
@@ -1350,8 +1510,8 @@ export class SessionEngine implements ISessionEngine {
1350
1510
  // case 1: the session is no longer available
1351
1511
  // we try to re-initialize the session 3 times, if that does not work, we close it
1352
1512
 
1353
- this._logger.warn(`The session has been closed, trying to re-initialize.`);
1354
- if(this._sessionId) this._httpClient.removeDataLoading(this._sessionId)
1513
+ this._logger.warn('The session has been closed, trying to re-initialize.');
1514
+ if (this._sessionId) this._httpClient.removeDataLoading(this._sessionId);
1355
1515
 
1356
1516
  if (this._retryCounter < 3) {
1357
1517
  // we retry this 3 times, the `retry` option in the init function is set to true and passed on
@@ -1361,6 +1521,7 @@ export class SessionEngine implements ISessionEngine {
1361
1521
  } else {
1362
1522
  // the retries were exceeded, we close the session
1363
1523
  this._logger.warn('Tried to retry the connect multiple times, bearer token still not valid. Closing Session.');
1524
+ // eslint-disable-next-line no-empty
1364
1525
  try { await this._closeOnFailure(); } catch (e) { }
1365
1526
  throw this._httpClient.convertError(e);
1366
1527
  }
@@ -1375,12 +1536,14 @@ export class SessionEngine implements ISessionEngine {
1375
1536
  } else {
1376
1537
  // no bearer tokens are supplied, we close the session
1377
1538
  this._logger.warn('No retry possible, no new bearer token was supplied. Closing Session.');
1539
+ // eslint-disable-next-line no-empty
1378
1540
  try { await this._closeOnFailure(); } catch (e) { }
1379
1541
  throw this._httpClient.convertError(e);
1380
1542
  }
1381
1543
  } else {
1382
1544
  // the retries were exceeded, we close the session
1383
1545
  this._logger.warn('Tried to retry the connect multiple times, bearer token still not valid. Closing Session.');
1546
+ // eslint-disable-next-line no-empty
1384
1547
  try { await this._closeOnFailure(); } catch (e) { }
1385
1548
  throw this._httpClient.convertError(e);
1386
1549
  }
@@ -1403,7 +1566,7 @@ export class SessionEngine implements ISessionEngine {
1403
1566
  * @param ms the milliseconds
1404
1567
  * @returns promise that resolve after specified milliseconds
1405
1568
  */
1406
- private async timeout(ms: number): Promise<any> {
1569
+ private async timeout(ms: number): Promise<unknown> {
1407
1570
  return new Promise(resolve => setTimeout(resolve, ms));
1408
1571
  }
1409
1572
 
@@ -1417,7 +1580,7 @@ export class SessionEngine implements ISessionEngine {
1417
1580
 
1418
1581
  // convert parameters
1419
1582
  if (responseDto.parameters) {
1420
- for (let parameterId in responseDto.parameters) {
1583
+ for (const parameterId in responseDto.parameters) {
1421
1584
  this._responseDto.parameters = this._responseDto.parameters || {};
1422
1585
  this._responseDto.parameters[parameterId] = this._responseDto.parameters[parameterId] || responseDto.parameters[parameterId];
1423
1586
  }
@@ -1425,7 +1588,7 @@ export class SessionEngine implements ISessionEngine {
1425
1588
 
1426
1589
  // convert outputs
1427
1590
  if (responseDto.outputs) {
1428
- for (let outputId in responseDto.outputs) {
1591
+ for (const outputId in responseDto.outputs) {
1429
1592
  this._responseDto.outputs = this._responseDto.outputs || {};
1430
1593
  if ('version' in responseDto.outputs[outputId] || !(this._responseDto.outputs[outputId] && 'version' in this._responseDto.outputs[outputId]))
1431
1594
  this._responseDto.outputs[outputId] = responseDto.outputs[outputId];
@@ -1434,7 +1597,7 @@ export class SessionEngine implements ISessionEngine {
1434
1597
 
1435
1598
  // convert exports
1436
1599
  if (responseDto.exports) {
1437
- for (let exportId in responseDto.exports) {
1600
+ for (const exportId in responseDto.exports) {
1438
1601
  this._responseDto.exports = this._responseDto.exports || {};
1439
1602
  if ('version' in responseDto.exports[exportId] || !(this._responseDto.exports[exportId] && 'version' in this._responseDto.exports[exportId]))
1440
1603
  this._responseDto.exports[exportId] = responseDto.exports[exportId];
@@ -1443,12 +1606,12 @@ export class SessionEngine implements ISessionEngine {
1443
1606
 
1444
1607
  const parameterSet: {
1445
1608
  [key: string]: {
1446
- value: any,
1609
+ value: unknown,
1447
1610
  valueString: string
1448
1611
  }
1449
1612
  } = {};
1450
1613
 
1451
- for (let parameterId in this._responseDto.parameters) {
1614
+ for (const parameterId in this._responseDto.parameters) {
1452
1615
  if (this.parameters[parameterId]) continue;
1453
1616
  this._responseDto.parameters[parameterId].id = parameterId;
1454
1617
 
@@ -1471,15 +1634,15 @@ export class SessionEngine implements ISessionEngine {
1471
1634
  }
1472
1635
 
1473
1636
  // 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
1474
- if(initialParameters) {
1637
+ if (initialParameters) {
1475
1638
  // check if the id is within the initial parameters
1476
- if(initialParameters[parameterId] !== undefined) {
1477
- this.parameters[parameterId].value = initialParameters[parameterId]
1478
- }
1639
+ if (initialParameters[parameterId] !== undefined) {
1640
+ this.parameters[parameterId].value = initialParameters[parameterId];
1641
+ }
1479
1642
  // check if the name is within the initial parameters
1480
- else if(initialParameters[this.parameters[parameterId].name] !== undefined) {
1643
+ else if (initialParameters[this.parameters[parameterId].name] !== undefined) {
1481
1644
  this.parameters[parameterId].value = initialParameters[this.parameters[parameterId].name];
1482
- }
1645
+ }
1483
1646
  // NOTE: At some point the checking may also be done with the displayname, this is the code for it
1484
1647
  // // check if the displayname is within the initial parameters
1485
1648
  // else if(this.parameters[parameterId].displayname && initialParameters[this.parameters[parameterId].displayname!] !== undefined) {
@@ -1490,7 +1653,7 @@ export class SessionEngine implements ISessionEngine {
1490
1653
  parameterSet[parameterId] = {
1491
1654
  value: this.parameters[parameterId].value,
1492
1655
  valueString: this.parameters[parameterId].stringify()
1493
- }
1656
+ };
1494
1657
 
1495
1658
  if (!this.initialized)
1496
1659
  this.parameterValues[parameterId] = parameterSet[parameterId].valueString;
@@ -1500,27 +1663,46 @@ export class SessionEngine implements ISessionEngine {
1500
1663
  if (!this.initialized)
1501
1664
  this.#parameterHistory.push(parameterSet);
1502
1665
 
1503
- for (let exportId in this._responseDto.exports) {
1666
+ for (const exportId in this._responseDto.exports) {
1504
1667
  if (this._responseDto.exports[exportId].type === ShapeDiverResponseExportDefinitionType.EMAIL || this._responseDto.exports[exportId].type === ShapeDiverResponseExportDefinitionType.DOWNLOAD) {
1505
- if(!this.exports[exportId]) {
1668
+ if (!this.exports[exportId]) {
1506
1669
  this._responseDto.exports[exportId].id = exportId;
1507
1670
  this.exports[exportId] = new Export(this._responseDto.exports[exportId], this);
1508
1671
  } else {
1509
- this.exports[exportId].updateExportDefinition(this._responseDto.exports[exportId])
1672
+ this.exports[exportId].updateExportDefinition(this._responseDto.exports[exportId]);
1510
1673
  }
1511
1674
  }
1512
1675
  }
1513
1676
 
1514
- for (let outputId in this._responseDto.outputs) {
1515
- if(!this.outputs[outputId]) {
1677
+ for (const outputId in this._responseDto.outputs) {
1678
+ if (!this.outputs[outputId]) {
1516
1679
  this._responseDto.outputs[outputId].id = outputId;
1517
1680
  if (this.outputsFreeze[outputId] === undefined) this.outputsFreeze[outputId] = false;
1518
1681
  this.outputs[outputId] = new Output(<ShapeDiverResponseOutput>this._responseDto.outputs[outputId], this);
1519
1682
  } else {
1520
- this.outputs[outputId].updateOutputDefinition(<ShapeDiverResponseOutput>this._responseDto.outputs[outputId])
1683
+ this.outputs[outputId].updateOutputDefinition(<ShapeDiverResponseOutput>this._responseDto.outputs[outputId]);
1684
+ }
1685
+ }
1686
+ }
1687
+
1688
+ private async waitForUpdateCallbacks(newOutputVersions: { [key: string]: string }, oldOutputVersions: { [key: string]: string }, newNode: ITreeNode, oldNode: ITreeNode) {
1689
+ // call the update callback function on the session
1690
+ if (this._updateCallback) await Promise.resolve(this._updateCallback(newNode, oldNode));
1691
+
1692
+ const promises = [];
1693
+ // call the update callback functions on the outputs
1694
+ for (const outputId in this.outputs) {
1695
+ if (oldOutputVersions[outputId] !== newOutputVersions[outputId]) {
1696
+ promises.push(
1697
+ this.outputs[outputId].triggerUpdateCallback(
1698
+ newNode.children.find(c => c.name === outputId)!,
1699
+ oldNode.children.find(c => c.name === outputId)!
1700
+ )
1701
+ );
1521
1702
  }
1522
1703
  }
1704
+ await Promise.all(promises);
1523
1705
  }
1524
1706
 
1525
- // #endregion Private Methods (10)
1707
+ // #endregion Private Methods (13)
1526
1708
  }