@inweb/viewer-core 27.6.0 → 27.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -24,13 +24,36 @@
24
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
48
  protected _emitter?: IEventEmitter;
29
49
  protected _data: IOptions;
50
+ protected _defaults: IOptions;
51
+ protected _updateCount = 0;
30
52
 
31
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,9 +101,8 @@ 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
- const defaults = Options.defaults();
77
- if (fields !== undefined) {
104
+ resetToDefaults(fields?: string[], defaults: IOptions = this._defaults): void {
105
+ if (Array.isArray(fields)) {
78
106
  const resetData: Partial<IOptions> = {};
79
107
  for (const field of fields) {
80
108
  if (field in defaults) resetData[field] = defaults[field];
@@ -85,39 +113,47 @@ export class Options implements IOptions {
85
113
  }
86
114
  }
87
115
 
88
- setProperty<K extends keyof IOptions>(key: K, value = Options.defaults()[key]): void {
89
- // for object-valued fields any new literal counts as a change
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
+ }
90
125
  if (this._data[key] !== value) {
91
126
  this._data[key] = value;
92
127
  this.change();
93
128
  }
94
129
  }
95
130
 
131
+ isValidValue(key: keyof IOptions, value: unknown): boolean {
132
+ return typeof value === typeof this._defaults[key];
133
+ }
134
+
96
135
  get data(): IOptions {
97
136
  return this._data;
98
137
  }
99
138
 
100
139
  set data(value: Partial<IOptions>) {
101
- const defaults = Options.defaults();
102
- const merged: IOptions = { ...defaults, ...this._data, ...value };
103
- // replace undefined to default value for known properties
104
- for (const key of Object.keys(defaults)) {
105
- if (merged[key] === undefined) merged[key] = defaults[key];
106
- }
107
- this._data = merged;
108
- // partial mode first
109
- if (this._data.enablePartialMode) {
110
- this._data.enableStreamingMode = true;
111
- this._data.sceneGraph = false;
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();
112
156
  }
113
- // sectionFillColor since 27.5
114
- if (!value.sectionFillColor && value.cuttingPlaneFillColor)
115
- this._data.sectionFillColor = {
116
- r: value.cuttingPlaneFillColor.red,
117
- g: value.cuttingPlaneFillColor.green,
118
- b: value.cuttingPlaneFillColor.blue,
119
- };
120
- this.change();
121
157
  }
122
158
 
123
159
  get showWCS(): boolean {
@@ -141,7 +177,7 @@ export class Options implements IOptions {
141
177
  }
142
178
 
143
179
  set antialiasing(value: boolean | string) {
144
- this.setProperty("antialiasing", value);
180
+ this.setProperty("antialiasing", value, typeof value === "boolean" || typeof value === "string");
145
181
  }
146
182
 
147
183
  get groundShadow(): boolean {
@@ -189,9 +225,12 @@ export class Options implements IOptions {
189
225
  }
190
226
 
191
227
  set enableStreamingMode(value: boolean) {
192
- this.setProperty("enableStreamingMode", value);
193
- if (!value) {
194
- this.setProperty("enablePartialMode", false);
228
+ this.beginUpdate();
229
+ try {
230
+ this.setProperty("enableStreamingMode", value);
231
+ this.setProperty("enablePartialMode", this.enablePartialMode && this.enableStreamingMode);
232
+ } finally {
233
+ this.endUpdate();
195
234
  }
196
235
  }
197
236
 
@@ -200,10 +239,13 @@ export class Options implements IOptions {
200
239
  }
201
240
 
202
241
  set enablePartialMode(value: boolean) {
203
- this.setProperty("enablePartialMode", value);
204
- if (value) {
205
- this.setProperty("enableStreamingMode", true);
206
- this.setProperty("sceneGraph", false);
242
+ this.beginUpdate();
243
+ try {
244
+ this.setProperty("enablePartialMode", value);
245
+ this.setProperty("enableStreamingMode", this.enableStreamingMode || this.enablePartialMode);
246
+ this.setProperty("sceneGraph", this.sceneGraph && !this.enablePartialMode);
247
+ } finally {
248
+ this.endUpdate();
207
249
  }
208
250
  }
209
251
 
@@ -216,25 +258,26 @@ export class Options implements IOptions {
216
258
  }
217
259
 
218
260
  get cuttingPlaneFillColor(): RGB {
219
- console.warn(
220
- "Options.cuttingPlaneFillColor has been deprecated since 27.5 and will be removed in a future release, use sectionFillColor instead"
221
- );
222
- return {
223
- red: this._data.sectionFillColor.r,
224
- green: this._data.sectionFillColor.g,
225
- blue: this._data.sectionFillColor.b,
226
- };
261
+ return this._data.cuttingPlaneFillColor;
227
262
  }
228
263
 
229
264
  set cuttingPlaneFillColor(value: RGB) {
230
- console.warn(
231
- "Options.cuttingPlaneFillColor has been deprecated since 27.5 and will be removed in a future release, use sectionFillColor instead"
232
- );
233
- this.setProperty("sectionFillColor", {
234
- r: value.red,
235
- g: value.green,
236
- b: value.blue,
237
- });
265
+ if (this._updateCount === 0) {
266
+ console.warn(
267
+ "Options.cuttingPlaneFillColor has been deprecated since 27.5 and will be removed in a future release, use sectionFillColor instead"
268
+ );
269
+ }
270
+ this.beginUpdate();
271
+ try {
272
+ this.setProperty("cuttingPlaneFillColor", value, isLegacyRGB(value));
273
+ this.setProperty("sectionFillColor", {
274
+ r: this._data.cuttingPlaneFillColor.red,
275
+ g: this._data.cuttingPlaneFillColor.green,
276
+ b: this._data.cuttingPlaneFillColor.blue,
277
+ });
278
+ } finally {
279
+ this.endUpdate();
280
+ }
238
281
  }
239
282
 
240
283
  get enableSectionFill(): boolean {
@@ -250,7 +293,17 @@ export class Options implements IOptions {
250
293
  }
251
294
 
252
295
  set sectionFillColor(value: { r: number; g: number; b: number }) {
253
- this.setProperty("sectionFillColor", value);
296
+ this.beginUpdate();
297
+ try {
298
+ this.setProperty("sectionFillColor", value, isColorRGB(value));
299
+ this.setProperty("cuttingPlaneFillColor", {
300
+ red: this._data.sectionFillColor.r,
301
+ green: this._data.sectionFillColor.g,
302
+ blue: this._data.sectionFillColor.b,
303
+ });
304
+ } finally {
305
+ this.endUpdate();
306
+ }
254
307
  }
255
308
 
256
309
  get sectionUseObjectColor(): boolean {
@@ -274,7 +327,7 @@ export class Options implements IOptions {
274
327
  }
275
328
 
276
329
  set sectionHatchColor(value: { r: number; g: number; b: number }) {
277
- this.setProperty("sectionHatchColor", value);
330
+ this.setProperty("sectionHatchColor", value, isColorRGB(value));
278
331
  }
279
332
 
280
333
  get sectionHatchScale(): number {
@@ -298,7 +351,7 @@ export class Options implements IOptions {
298
351
  }
299
352
 
300
353
  set sectionOutlineColor(value: { r: number; g: number; b: number }) {
301
- this.setProperty("sectionOutlineColor", value);
354
+ this.setProperty("sectionOutlineColor", value, isColorRGB(value));
302
355
  }
303
356
 
304
357
  get sectionOutlineWidth(): number {
@@ -314,7 +367,7 @@ export class Options implements IOptions {
314
367
  }
315
368
 
316
369
  set edgesColor(value: { r: number; g: number; b: number }) {
317
- this.setProperty("edgesColor", value);
370
+ this.setProperty("edgesColor", value, isColorRGB(value));
318
371
  }
319
372
 
320
373
  get facesColor(): { r: number; g: number; b: number } {
@@ -322,7 +375,7 @@ export class Options implements IOptions {
322
375
  }
323
376
 
324
377
  set facesColor(value: { r: number; g: number; b: number }) {
325
- this.setProperty("facesColor", value);
378
+ this.setProperty("facesColor", value, isColorRGB(value));
326
379
  }
327
380
 
328
381
  get edgesVisibility(): boolean {
@@ -370,9 +423,12 @@ export class Options implements IOptions {
370
423
  }
371
424
 
372
425
  set sceneGraph(value: boolean) {
373
- this.setProperty("sceneGraph", value);
374
- if (value) {
375
- this.setProperty("enablePartialMode", false);
426
+ this.beginUpdate();
427
+ try {
428
+ this.setProperty("sceneGraph", value);
429
+ this.setProperty("enablePartialMode", this.enablePartialMode && !this.sceneGraph);
430
+ } finally {
431
+ this.endUpdate();
376
432
  }
377
433
  }
378
434
 
@@ -429,15 +485,15 @@ export class Options implements IOptions {
429
485
  }
430
486
 
431
487
  set rulerPrecision(value: "Default" | "Auto" | number) {
432
- this.setProperty("rulerPrecision", value);
488
+ this.setProperty("rulerPrecision", value, typeof value === "number" || value === "Default" || value === "Auto");
433
489
  }
434
490
 
435
491
  get cameraMode(): CameraMode {
436
- return this._data.cameraMode || "perspective";
492
+ return this._data.cameraMode;
437
493
  }
438
494
 
439
495
  set cameraMode(value: CameraMode) {
440
- this.setProperty("cameraMode", value);
496
+ this.setProperty("cameraMode", value, value === "perspective" || value === "orthographic");
441
497
  }
442
498
 
443
499
  get snapshotMimeType(): string {