@inweb/viewer-core 27.5.0 → 27.6.1

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.
@@ -21,16 +21,39 @@
21
21
  // acknowledge and accept the above terms.
22
22
  ///////////////////////////////////////////////////////////////////////////////
23
23
 
24
- import { EventEmitter2 } from "@inweb/eventemitter2";
24
+ import { IEventEmitter } from "@inweb/eventemitter2";
25
25
  import { CameraMode, defaultOptions, IOptions, RGB } from "./IOptions";
26
26
 
27
+ function isColorRGB(value: unknown): boolean {
28
+ return (
29
+ typeof value === "object" &&
30
+ value !== null &&
31
+ typeof (value as { r?: unknown }).r === "number" &&
32
+ typeof (value as { g?: unknown }).g === "number" &&
33
+ typeof (value as { b?: unknown }).b === "number"
34
+ );
35
+ }
36
+
37
+ function isLegacyRGB(value: unknown): boolean {
38
+ return (
39
+ typeof value === "object" &&
40
+ value !== null &&
41
+ typeof (value as { red?: unknown }).red === "number" &&
42
+ typeof (value as { green?: unknown }).green === "number" &&
43
+ typeof (value as { blue?: unknown }).blue === "number"
44
+ );
45
+ }
46
+
27
47
  export class Options implements IOptions {
28
- protected _emitter?: EventEmitter2;
48
+ protected _emitter?: IEventEmitter;
29
49
  protected _data: IOptions;
50
+ protected _defaults: IOptions;
51
+ protected _updateCount = 0;
30
52
 
31
- constructor(emitter?: EventEmitter2) {
53
+ constructor(emitter?: IEventEmitter) {
32
54
  this._emitter = emitter;
33
- this._data = defaultOptions();
55
+ this._data = Options.defaults();
56
+ this._defaults = Options.defaults();
34
57
  this.loadFromStorage();
35
58
  }
36
59
 
@@ -39,12 +62,21 @@ export class Options implements IOptions {
39
62
  }
40
63
 
41
64
  change(): void {
42
- if (this._emitter !== undefined) {
65
+ if (this._emitter !== undefined && this._updateCount === 0) {
43
66
  this.saveToStorage();
44
67
  this._emitter.emit({ type: "optionschange", data: this });
45
68
  }
46
69
  }
47
70
 
71
+ beginUpdate(): void {
72
+ this._updateCount++;
73
+ }
74
+
75
+ endUpdate(): void {
76
+ this._updateCount--;
77
+ this.change();
78
+ }
79
+
48
80
  saveToStorage(): void {
49
81
  if (typeof window !== "undefined")
50
82
  try {
@@ -58,10 +90,7 @@ export class Options implements IOptions {
58
90
  if (typeof window !== "undefined")
59
91
  try {
60
92
  const item = localStorage.getItem("od-client-settings");
61
- if (item) {
62
- const data = JSON.parse(item);
63
- this.data = { ...data };
64
- }
93
+ if (item) this.data = JSON.parse(item);
65
94
  } catch (error) {
66
95
  console.error("Cannot load client settings.", error);
67
96
  }
@@ -72,38 +101,59 @@ export class Options implements IOptions {
72
101
  *
73
102
  * @param fields - Name of fields to be reset. Specify `undefined` to reset all.
74
103
  */
75
- resetToDefaults(fields?: string[]): void {
76
- if (fields !== undefined) {
77
- const defaults = Options.defaults();
78
- const resetData = fields.reduce((acc, field) => {
79
- acc[field] = defaults[field];
80
- return acc;
81
- }, {});
82
- this.data = { ...this.data, ...resetData };
104
+ resetToDefaults(fields?: string[], defaults: IOptions = this._defaults): void {
105
+ if (Array.isArray(fields)) {
106
+ const resetData: Partial<IOptions> = {};
107
+ for (const field of fields) {
108
+ if (field in defaults) resetData[field] = defaults[field];
109
+ }
110
+ this.data = resetData;
83
111
  } else {
84
- this.data = { ...this.data, ...Options.defaults() };
112
+ this.data = defaults;
113
+ }
114
+ }
115
+
116
+ setProperty<K extends keyof IOptions>(
117
+ key: K,
118
+ value: IOptions[K] = this._defaults[key],
119
+ accept: boolean = this.isValidValue(key, value)
120
+ ): void {
121
+ if (!accept) {
122
+ console.warn(`Options.${key}: Invalid value`, value);
123
+ return;
124
+ }
125
+ if (this._data[key] !== value) {
126
+ this._data[key] = value;
127
+ this.change();
85
128
  }
86
129
  }
87
130
 
131
+ isValidValue(key: keyof IOptions, value: unknown): boolean {
132
+ return typeof value === typeof this._defaults[key];
133
+ }
134
+
88
135
  get data(): IOptions {
89
136
  return this._data;
90
137
  }
91
138
 
92
- set data(value: IOptions) {
93
- this._data = { ...Options.defaults(), ...this._data, ...value };
94
- // partial mode first
95
- if (this._data.enablePartialMode) {
96
- this._data.enableStreamingMode = true;
97
- this._data.sceneGraph = false;
139
+ set data(value: Partial<IOptions>) {
140
+ this.beginUpdate();
141
+ try {
142
+ for (const key of Object.keys(value)) {
143
+ if (key in this._defaults) this[key] = value[key];
144
+ else this._data[key] = value[key];
145
+ }
146
+ // partial streaming first
147
+ if ("enablePartialMode" in value) {
148
+ this.enablePartialMode = (value as IOptions).enablePartialMode;
149
+ }
150
+ // sectionFillColor since 27.5
151
+ if ("sectionFillColor" in value) {
152
+ this.sectionFillColor = (value as IOptions).sectionFillColor;
153
+ }
154
+ } finally {
155
+ this.endUpdate();
98
156
  }
99
- // sectionFillColor since 27.5
100
- if (!value.sectionFillColor && value.cuttingPlaneFillColor)
101
- this._data.sectionFillColor = {
102
- r: value.cuttingPlaneFillColor.red,
103
- g: value.cuttingPlaneFillColor.green,
104
- b: value.cuttingPlaneFillColor.blue,
105
- };
106
- this.change();
107
157
  }
108
158
 
109
159
  get showWCS(): boolean {
@@ -111,8 +161,7 @@ export class Options implements IOptions {
111
161
  }
112
162
 
113
163
  set showWCS(value: boolean) {
114
- this._data.showWCS = value;
115
- this.change();
164
+ this.setProperty("showWCS", value);
116
165
  }
117
166
 
118
167
  get cameraAnimation(): boolean {
@@ -120,8 +169,7 @@ export class Options implements IOptions {
120
169
  }
121
170
 
122
171
  set cameraAnimation(value: boolean) {
123
- this._data.cameraAnimation = value;
124
- this.change();
172
+ this.setProperty("cameraAnimation", value);
125
173
  }
126
174
 
127
175
  get antialiasing(): boolean | string {
@@ -129,8 +177,7 @@ export class Options implements IOptions {
129
177
  }
130
178
 
131
179
  set antialiasing(value: boolean | string) {
132
- this._data.antialiasing = value;
133
- this.change();
180
+ this.setProperty("antialiasing", value, typeof value === "boolean" || typeof value === "string");
134
181
  }
135
182
 
136
183
  get groundShadow(): boolean {
@@ -138,8 +185,7 @@ export class Options implements IOptions {
138
185
  }
139
186
 
140
187
  set groundShadow(value: boolean) {
141
- this._data.groundShadow = value;
142
- this.change();
188
+ this.setProperty("groundShadow", value);
143
189
  }
144
190
 
145
191
  get shadows(): boolean {
@@ -147,8 +193,7 @@ export class Options implements IOptions {
147
193
  }
148
194
 
149
195
  set shadows(value: boolean) {
150
- this._data.shadows = value;
151
- this.change();
196
+ this.setProperty("shadows", value);
152
197
  }
153
198
 
154
199
  get cameraAxisXSpeed(): number {
@@ -156,8 +201,7 @@ export class Options implements IOptions {
156
201
  }
157
202
 
158
203
  set cameraAxisXSpeed(value: number) {
159
- this._data.cameraAxisXSpeed = value;
160
- this.change();
204
+ this.setProperty("cameraAxisXSpeed", value);
161
205
  }
162
206
 
163
207
  get cameraAxisYSpeed(): number {
@@ -165,8 +209,7 @@ export class Options implements IOptions {
165
209
  }
166
210
 
167
211
  set cameraAxisYSpeed(value: number) {
168
- this.cameraAxisYSpeed = value;
169
- this.change();
212
+ this.setProperty("cameraAxisYSpeed", value);
170
213
  }
171
214
 
172
215
  get ambientOcclusion(): boolean {
@@ -174,8 +217,7 @@ export class Options implements IOptions {
174
217
  }
175
218
 
176
219
  set ambientOcclusion(value: boolean) {
177
- this._data.ambientOcclusion = value;
178
- this.change();
220
+ this.setProperty("ambientOcclusion", value);
179
221
  }
180
222
 
181
223
  get enableStreamingMode(): boolean {
@@ -183,9 +225,9 @@ export class Options implements IOptions {
183
225
  }
184
226
 
185
227
  set enableStreamingMode(value: boolean) {
186
- this._data.enableStreamingMode = value;
187
- if (!value) this._data.enablePartialMode = false;
188
- this.change();
228
+ this.setProperty("enableStreamingMode", value);
229
+ // partial streaming is only used if streaming is enabled
230
+ this.setProperty("enablePartialMode", this.enablePartialMode && this.enableStreamingMode);
189
231
  }
190
232
 
191
233
  get enablePartialMode(): boolean {
@@ -193,12 +235,10 @@ export class Options implements IOptions {
193
235
  }
194
236
 
195
237
  set enablePartialMode(value: boolean) {
196
- this._data.enablePartialMode = value;
197
- if (value) {
198
- this._data.enableStreamingMode = true;
199
- this._data.sceneGraph = false;
200
- }
201
- this.change();
238
+ this.setProperty("enablePartialMode", value);
239
+ // partial streaming requires streaming enabled and disables scene graph
240
+ this.setProperty("enableStreamingMode", this.enableStreamingMode || this.enablePartialMode);
241
+ this.setProperty("sceneGraph", this.sceneGraph && !this.enablePartialMode);
202
242
  }
203
243
 
204
244
  get memoryLimit(): number {
@@ -206,31 +246,30 @@ export class Options implements IOptions {
206
246
  }
207
247
 
208
248
  set memoryLimit(value: number) {
209
- this._data.memoryLimit = value;
210
- this.change();
249
+ this.setProperty("memoryLimit", value);
211
250
  }
212
251
 
213
252
  get cuttingPlaneFillColor(): RGB {
214
- console.warn(
215
- "Options.cuttingPlaneFillColor has been deprecated since 27.5 and will be removed in a future release, use sectionFillColor instead"
216
- );
217
- return {
218
- red: this._data.sectionFillColor.r,
219
- green: this._data.sectionFillColor.g,
220
- blue: this._data.sectionFillColor.b,
221
- };
253
+ return this._data.cuttingPlaneFillColor;
222
254
  }
223
255
 
224
256
  set cuttingPlaneFillColor(value: RGB) {
225
- console.warn(
226
- "Options.cuttingPlaneFillColor has been deprecated since 27.5 and will be removed in a future release, use sectionFillColor instead"
257
+ if (this._updateCount === 0) {
258
+ console.warn(
259
+ "Options.cuttingPlaneFillColor has been deprecated since 27.5 and will be removed in a future release, use sectionFillColor instead"
260
+ );
261
+ }
262
+ this.setProperty("cuttingPlaneFillColor", value, isLegacyRGB(value));
263
+ // kept in sync with sectionFillColor (deprecated since 27.5)
264
+ this.setProperty(
265
+ "sectionFillColor",
266
+ {
267
+ r: this._data.cuttingPlaneFillColor.red,
268
+ g: this._data.cuttingPlaneFillColor.green,
269
+ b: this._data.cuttingPlaneFillColor.blue,
270
+ },
271
+ true
227
272
  );
228
- this._data.sectionFillColor = {
229
- r: value.red,
230
- g: value.green,
231
- b: value.blue,
232
- };
233
- this.change();
234
273
  }
235
274
 
236
275
  get enableSectionFill(): boolean {
@@ -238,17 +277,25 @@ export class Options implements IOptions {
238
277
  }
239
278
 
240
279
  set enableSectionFill(value: boolean) {
241
- this._data.enableSectionFill = value;
242
- this.change();
280
+ this.setProperty("enableSectionFill", value);
243
281
  }
244
282
 
245
- get sectionFillColor() {
283
+ get sectionFillColor(): { r: number; g: number; b: number } {
246
284
  return this._data.sectionFillColor;
247
285
  }
248
286
 
249
- set sectionFillColor(value) {
250
- this._data.sectionFillColor = value;
251
- this.change();
287
+ set sectionFillColor(value: { r: number; g: number; b: number }) {
288
+ this.setProperty("sectionFillColor", value, isColorRGB(value));
289
+ // kept in sync with cuttingPlaneFillColor (deprecated since 27.5)
290
+ this.setProperty(
291
+ "cuttingPlaneFillColor",
292
+ {
293
+ red: this._data.sectionFillColor.r,
294
+ green: this._data.sectionFillColor.g,
295
+ blue: this._data.sectionFillColor.b,
296
+ },
297
+ true
298
+ );
252
299
  }
253
300
 
254
301
  get sectionUseObjectColor(): boolean {
@@ -256,8 +303,7 @@ export class Options implements IOptions {
256
303
  }
257
304
 
258
305
  set sectionUseObjectColor(value: boolean) {
259
- this._data.sectionUseObjectColor = value;
260
- this.change();
306
+ this.setProperty("sectionUseObjectColor", value);
261
307
  }
262
308
 
263
309
  get enableSectionHatch(): boolean {
@@ -265,17 +311,15 @@ export class Options implements IOptions {
265
311
  }
266
312
 
267
313
  set enableSectionHatch(value: boolean) {
268
- this._data.enableSectionHatch = value;
269
- this.change();
314
+ this.setProperty("enableSectionHatch", value);
270
315
  }
271
316
 
272
- get sectionHatchColor() {
317
+ get sectionHatchColor(): { r: number; g: number; b: number } {
273
318
  return this._data.sectionHatchColor;
274
319
  }
275
320
 
276
- set sectionHatchColor(value) {
277
- this._data.sectionHatchColor = value;
278
- this.change();
321
+ set sectionHatchColor(value: { r: number; g: number; b: number }) {
322
+ this.setProperty("sectionHatchColor", value, isColorRGB(value));
279
323
  }
280
324
 
281
325
  get sectionHatchScale(): number {
@@ -283,8 +327,7 @@ export class Options implements IOptions {
283
327
  }
284
328
 
285
329
  set sectionHatchScale(value: number) {
286
- this._data.sectionHatchScale = value;
287
- this.change();
330
+ this.setProperty("sectionHatchScale", value);
288
331
  }
289
332
 
290
333
  get enableSectionOutline(): boolean {
@@ -292,17 +335,15 @@ export class Options implements IOptions {
292
335
  }
293
336
 
294
337
  set enableSectionOutline(value: boolean) {
295
- this._data.enableSectionOutline = value;
296
- this.change();
338
+ this.setProperty("enableSectionOutline", value);
297
339
  }
298
340
 
299
- get sectionOutlineColor() {
341
+ get sectionOutlineColor(): { r: number; g: number; b: number } {
300
342
  return this._data.sectionOutlineColor;
301
343
  }
302
344
 
303
- set sectionOutlineColor(value) {
304
- this._data.sectionOutlineColor = value;
305
- this.change();
345
+ set sectionOutlineColor(value: { r: number; g: number; b: number }) {
346
+ this.setProperty("sectionOutlineColor", value, isColorRGB(value));
306
347
  }
307
348
 
308
349
  get sectionOutlineWidth(): number {
@@ -310,126 +351,113 @@ export class Options implements IOptions {
310
351
  }
311
352
 
312
353
  set sectionOutlineWidth(value: number) {
313
- this._data.sectionOutlineWidth = value;
314
- this.change();
354
+ this.setProperty("sectionOutlineWidth", value);
315
355
  }
316
356
 
317
- get edgesColor() {
357
+ get edgesColor(): { r: number; g: number; b: number } {
318
358
  return this._data.edgesColor;
319
359
  }
320
360
 
321
- set edgesColor(value) {
322
- this._data.edgesColor = value;
323
- this.change();
361
+ set edgesColor(value: { r: number; g: number; b: number }) {
362
+ this.setProperty("edgesColor", value, isColorRGB(value));
324
363
  }
325
364
 
326
- get facesColor() {
365
+ get facesColor(): { r: number; g: number; b: number } {
327
366
  return this._data.facesColor;
328
367
  }
329
368
 
330
- set facesColor(value) {
331
- this._data.facesColor = value;
332
- this.change();
369
+ set facesColor(value: { r: number; g: number; b: number }) {
370
+ this.setProperty("facesColor", value, isColorRGB(value));
333
371
  }
334
372
 
335
- get edgesVisibility() {
373
+ get edgesVisibility(): boolean {
336
374
  return this._data.edgesVisibility;
337
375
  }
338
376
 
339
- set edgesVisibility(value) {
340
- this._data.edgesVisibility = value;
341
- this.change();
377
+ set edgesVisibility(value: boolean) {
378
+ this.setProperty("edgesVisibility", value);
342
379
  }
343
380
 
344
- get edgesOverlap() {
381
+ get edgesOverlap(): boolean {
345
382
  return this._data.edgesOverlap;
346
383
  }
347
384
 
348
- set edgesOverlap(value) {
349
- this._data.edgesOverlap = value;
350
- this.change();
385
+ set edgesOverlap(value: boolean) {
386
+ this.setProperty("edgesOverlap", value);
351
387
  }
352
388
 
353
- get facesOverlap() {
389
+ get facesOverlap(): boolean {
354
390
  return this._data.facesOverlap;
355
391
  }
356
392
 
357
- set facesOverlap(value) {
358
- this._data.facesOverlap = value;
359
- this.change();
393
+ set facesOverlap(value: boolean) {
394
+ this.setProperty("facesOverlap", value);
360
395
  }
361
396
 
362
- get facesTransparancy() {
397
+ get facesTransparancy(): number {
363
398
  return this._data.facesTransparancy;
364
399
  }
365
400
 
366
- set facesTransparancy(value) {
367
- this._data.facesTransparancy = value;
368
- this.change();
401
+ set facesTransparancy(value: number) {
402
+ this.setProperty("facesTransparancy", value);
369
403
  }
370
404
 
371
- get enableCustomHighlight() {
405
+ get enableCustomHighlight(): boolean {
372
406
  return this._data.enableCustomHighlight;
373
407
  }
374
408
 
375
- set enableCustomHighlight(value) {
376
- this._data.enableCustomHighlight = value;
377
- this.change();
409
+ set enableCustomHighlight(value: boolean) {
410
+ this.setProperty("enableCustomHighlight", value);
378
411
  }
379
412
 
380
- get sceneGraph() {
413
+ get sceneGraph(): boolean {
381
414
  return this._data.sceneGraph;
382
415
  }
383
416
 
384
- set sceneGraph(value) {
385
- this._data.sceneGraph = value;
386
- if (value) this._data.enablePartialMode = false;
387
- this.change();
417
+ set sceneGraph(value: boolean) {
418
+ this.setProperty("sceneGraph", value);
419
+ // scene graph and partial streaming are mutually exclusive
420
+ this.setProperty("enablePartialMode", this.enablePartialMode && !this.sceneGraph);
388
421
  }
389
422
 
390
- get edgeModel() {
423
+ get edgeModel(): boolean {
391
424
  return Boolean(this._data.edgeModel);
392
425
  }
393
426
 
394
- set edgeModel(value) {
395
- this._data.edgeModel = Boolean(value);
396
- this.change();
427
+ set edgeModel(value: boolean) {
428
+ this.setProperty("edgeModel", value);
397
429
  }
398
430
 
399
- get reverseZoomWheel() {
431
+ get reverseZoomWheel(): boolean {
400
432
  return this._data.reverseZoomWheel;
401
433
  }
402
434
 
403
435
  set reverseZoomWheel(value: boolean) {
404
- this._data.reverseZoomWheel = !!value;
405
- this.change();
436
+ this.setProperty("reverseZoomWheel", value);
406
437
  }
407
438
 
408
- get enableZoomWheel() {
439
+ get enableZoomWheel(): boolean {
409
440
  return this._data.enableZoomWheel;
410
441
  }
411
442
 
412
443
  set enableZoomWheel(value: boolean) {
413
- this._data.enableZoomWheel = !!value;
414
- this.change();
444
+ this.setProperty("enableZoomWheel", value);
415
445
  }
416
446
 
417
- get enableGestures() {
447
+ get enableGestures(): boolean {
418
448
  return this._data.enableGestures;
419
449
  }
420
450
 
421
451
  set enableGestures(value: boolean) {
422
- this._data.enableGestures = !!value;
423
- this.change();
452
+ this.setProperty("enableGestures", value);
424
453
  }
425
454
 
426
- get geometryType() {
455
+ get geometryType(): string {
427
456
  return this._data.geometryType;
428
457
  }
429
458
 
430
459
  set geometryType(value: string) {
431
- this._data.geometryType = value;
432
- this.change();
460
+ this.setProperty("geometryType", value);
433
461
  }
434
462
 
435
463
  get rulerUnit(): string {
@@ -437,25 +465,38 @@ export class Options implements IOptions {
437
465
  }
438
466
 
439
467
  set rulerUnit(value: string) {
440
- this._data.rulerUnit = value;
441
- this.change();
468
+ this.setProperty("rulerUnit", value);
442
469
  }
443
470
 
444
- get rulerPrecision(): any {
471
+ get rulerPrecision(): "Default" | "Auto" | number {
445
472
  return this._data.rulerPrecision;
446
473
  }
447
474
 
448
- set rulerPrecision(value: any) {
449
- this._data.rulerPrecision = value;
450
- this.change();
475
+ set rulerPrecision(value: "Default" | "Auto" | number) {
476
+ this.setProperty("rulerPrecision", value, typeof value === "number" || value === "Default" || value === "Auto");
451
477
  }
452
478
 
453
479
  get cameraMode(): CameraMode {
454
- return this._data.cameraMode || "perspective";
480
+ return this._data.cameraMode;
455
481
  }
456
482
 
457
483
  set cameraMode(value: CameraMode) {
458
- this._data.cameraMode = value;
459
- this.change();
484
+ this.setProperty("cameraMode", value, value === "perspective" || value === "orthographic");
485
+ }
486
+
487
+ get snapshotMimeType(): string {
488
+ return this._data.snapshotMimeType;
489
+ }
490
+
491
+ set snapshotMimeType(value: string) {
492
+ this.setProperty("snapshotMimeType", value);
493
+ }
494
+
495
+ get snapshotQuality(): number {
496
+ return this._data.snapshotQuality;
497
+ }
498
+
499
+ set snapshotQuality(value: number) {
500
+ this.setProperty("snapshotQuality", value);
460
501
  }
461
502
  }