@inweb/viewer-core 27.6.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.
@@ -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 {
@@ -190,9 +226,8 @@ export class Options implements IOptions {
190
226
 
191
227
  set enableStreamingMode(value: boolean) {
192
228
  this.setProperty("enableStreamingMode", value);
193
- if (!value) {
194
- this.setProperty("enablePartialMode", false);
195
- }
229
+ // partial streaming is only used if streaming is enabled
230
+ this.setProperty("enablePartialMode", this.enablePartialMode && this.enableStreamingMode);
196
231
  }
197
232
 
198
233
  get enablePartialMode(): boolean {
@@ -201,10 +236,9 @@ export class Options implements IOptions {
201
236
 
202
237
  set enablePartialMode(value: boolean) {
203
238
  this.setProperty("enablePartialMode", value);
204
- if (value) {
205
- this.setProperty("enableStreamingMode", true);
206
- this.setProperty("sceneGraph", false);
207
- }
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);
208
242
  }
209
243
 
210
244
  get memoryLimit(): number {
@@ -216,25 +250,26 @@ export class Options implements IOptions {
216
250
  }
217
251
 
218
252
  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
- };
253
+ return this._data.cuttingPlaneFillColor;
227
254
  }
228
255
 
229
256
  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"
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
232
272
  );
233
- this.setProperty("sectionFillColor", {
234
- r: value.red,
235
- g: value.green,
236
- b: value.blue,
237
- });
238
273
  }
239
274
 
240
275
  get enableSectionFill(): boolean {
@@ -250,7 +285,17 @@ export class Options implements IOptions {
250
285
  }
251
286
 
252
287
  set sectionFillColor(value: { r: number; g: number; b: number }) {
253
- this.setProperty("sectionFillColor", value);
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
+ );
254
299
  }
255
300
 
256
301
  get sectionUseObjectColor(): boolean {
@@ -274,7 +319,7 @@ export class Options implements IOptions {
274
319
  }
275
320
 
276
321
  set sectionHatchColor(value: { r: number; g: number; b: number }) {
277
- this.setProperty("sectionHatchColor", value);
322
+ this.setProperty("sectionHatchColor", value, isColorRGB(value));
278
323
  }
279
324
 
280
325
  get sectionHatchScale(): number {
@@ -298,7 +343,7 @@ export class Options implements IOptions {
298
343
  }
299
344
 
300
345
  set sectionOutlineColor(value: { r: number; g: number; b: number }) {
301
- this.setProperty("sectionOutlineColor", value);
346
+ this.setProperty("sectionOutlineColor", value, isColorRGB(value));
302
347
  }
303
348
 
304
349
  get sectionOutlineWidth(): number {
@@ -314,7 +359,7 @@ export class Options implements IOptions {
314
359
  }
315
360
 
316
361
  set edgesColor(value: { r: number; g: number; b: number }) {
317
- this.setProperty("edgesColor", value);
362
+ this.setProperty("edgesColor", value, isColorRGB(value));
318
363
  }
319
364
 
320
365
  get facesColor(): { r: number; g: number; b: number } {
@@ -322,7 +367,7 @@ export class Options implements IOptions {
322
367
  }
323
368
 
324
369
  set facesColor(value: { r: number; g: number; b: number }) {
325
- this.setProperty("facesColor", value);
370
+ this.setProperty("facesColor", value, isColorRGB(value));
326
371
  }
327
372
 
328
373
  get edgesVisibility(): boolean {
@@ -371,9 +416,8 @@ export class Options implements IOptions {
371
416
 
372
417
  set sceneGraph(value: boolean) {
373
418
  this.setProperty("sceneGraph", value);
374
- if (value) {
375
- this.setProperty("enablePartialMode", false);
376
- }
419
+ // scene graph and partial streaming are mutually exclusive
420
+ this.setProperty("enablePartialMode", this.enablePartialMode && !this.sceneGraph);
377
421
  }
378
422
 
379
423
  get edgeModel(): boolean {
@@ -429,15 +473,15 @@ export class Options implements IOptions {
429
473
  }
430
474
 
431
475
  set rulerPrecision(value: "Default" | "Auto" | number) {
432
- this.setProperty("rulerPrecision", value);
476
+ this.setProperty("rulerPrecision", value, typeof value === "number" || value === "Default" || value === "Auto");
433
477
  }
434
478
 
435
479
  get cameraMode(): CameraMode {
436
- return this._data.cameraMode || "perspective";
480
+ return this._data.cameraMode;
437
481
  }
438
482
 
439
483
  set cameraMode(value: CameraMode) {
440
- this.setProperty("cameraMode", value);
484
+ this.setProperty("cameraMode", value, value === "perspective" || value === "orthographic");
441
485
  }
442
486
 
443
487
  get snapshotMimeType(): string {