@preference-sl/pref-viewer 2.17.0-beta.0 → 2.17.0-beta.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.
- package/README.md +48 -16
- package/index.d.ts +2 -1
- package/package.json +1 -1
- package/src/babylonjs-animation-controller.js +12 -163
- package/src/babylonjs-animation-opening-menu.js +8 -1
- package/src/babylonjs-animation-opening.js +21 -1
- package/src/babylonjs-animation-overlay-controller.js +145 -0
- package/src/babylonjs-controller.js +332 -177
- package/src/babylonjs-selection-controller.js +215 -0
- package/src/babylonjs-view-cube-controller.js +169 -77
- package/src/file-storage.js +13 -0
- package/src/gltf-storage.js +97 -0
- package/src/localization/i18n.js +147 -74
- package/src/material-utils.js +67 -60
- package/src/panzoom-controller.js +14 -1
- package/src/pref-viewer-2d.js +16 -4
- package/src/pref-viewer-3d-data.js +166 -0
- package/src/pref-viewer-3d.js +11 -0
- package/src/pref-viewer-dialog.js +7 -1
- package/src/pref-viewer-menu-3d.js +55 -35
- package/src/pref-viewer.js +56 -20
- package/src/render-math.js +130 -117
- package/src/svg-resolver.js +5 -4
|
@@ -15,6 +15,11 @@
|
|
|
15
15
|
* - Inspect flags via `isPending`, `isSuccess`, `isVisible`, and `mustBeShown`.
|
|
16
16
|
*/
|
|
17
17
|
export class ContainerData {
|
|
18
|
+
/**
|
|
19
|
+
* Creates container state with no loaded assets and resets its staged update.
|
|
20
|
+
* @public
|
|
21
|
+
* @param {string} [name=""] - Container identity, such as model or environment.
|
|
22
|
+
*/
|
|
18
23
|
constructor(name = "") {
|
|
19
24
|
this.metadata = {};
|
|
20
25
|
this.name = name;
|
|
@@ -26,6 +31,12 @@ export class ContainerData {
|
|
|
26
31
|
// Set initial information about ongoing update
|
|
27
32
|
this.reset();
|
|
28
33
|
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Clears staged cache and load flags while preserving committed container fields.
|
|
37
|
+
* @public
|
|
38
|
+
* @returns {void}
|
|
39
|
+
*/
|
|
29
40
|
reset() {
|
|
30
41
|
this.update = {
|
|
31
42
|
metadata: {},
|
|
@@ -37,6 +48,13 @@ export class ContainerData {
|
|
|
37
48
|
timeStamp: null,
|
|
38
49
|
};
|
|
39
50
|
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Marks the staged result; success commits visibility intent, cache metadata and a supplied source.
|
|
54
|
+
* @public
|
|
55
|
+
* @param {boolean} [success=false] - Whether the staged load succeeded.
|
|
56
|
+
* @returns {void}
|
|
57
|
+
*/
|
|
40
58
|
setSuccess(success = false) {
|
|
41
59
|
if (success) {
|
|
42
60
|
this.update.success = true;
|
|
@@ -51,6 +69,14 @@ export class ContainerData {
|
|
|
51
69
|
this.update.success = false;
|
|
52
70
|
}
|
|
53
71
|
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Resets staged state and queues a source and visibility intent for the next load.
|
|
75
|
+
* @public
|
|
76
|
+
* @param {object|null} [storage=null] - URL or IndexedDB storage descriptor.
|
|
77
|
+
* @param {boolean} [show] - Requested visibility; omission retains the current intent.
|
|
78
|
+
* @returns {void}
|
|
79
|
+
*/
|
|
54
80
|
setPending(storage = null, show) {
|
|
55
81
|
this.reset();
|
|
56
82
|
this.update.pending = true;
|
|
@@ -61,11 +87,26 @@ export class ContainerData {
|
|
|
61
87
|
this.storage = storage;
|
|
62
88
|
}
|
|
63
89
|
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Stages resolved source size, timestamp and a copied metadata snapshot.
|
|
93
|
+
* @public
|
|
94
|
+
* @param {number} [size=0] - Resolved source size.
|
|
95
|
+
* @param {string|null} [timeStamp=null] - Source modification marker.
|
|
96
|
+
* @param {object} [metadata={}] - Source metadata to copy.
|
|
97
|
+
* @returns {void}
|
|
98
|
+
*/
|
|
64
99
|
setPendingCacheData(size = 0, timeStamp = null, metadata = {}) {
|
|
65
100
|
this.update.size = size;
|
|
66
101
|
this.update.timeStamp = timeStamp;
|
|
67
102
|
this.update.metadata = { ...(metadata ?? {}) };
|
|
68
103
|
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Queues the committed source and cache metadata again when a source is available.
|
|
107
|
+
* @public
|
|
108
|
+
* @returns {boolean}
|
|
109
|
+
*/
|
|
69
110
|
setPendingWithCurrentStorage() {
|
|
70
111
|
const storedSource = this.storage ?? null;
|
|
71
112
|
if (!storedSource) {
|
|
@@ -76,15 +117,39 @@ export class ContainerData {
|
|
|
76
117
|
this.setPendingCacheData(this.size, this.timeStamp, this.metadata);
|
|
77
118
|
return true;
|
|
78
119
|
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Reports whether a container load is staged.
|
|
123
|
+
* @public
|
|
124
|
+
* @returns {boolean}
|
|
125
|
+
*/
|
|
79
126
|
get isPending() {
|
|
80
127
|
return this.update.pending === true;
|
|
81
128
|
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Reports whether the staged load succeeded.
|
|
132
|
+
* @public
|
|
133
|
+
* @returns {boolean}
|
|
134
|
+
*/
|
|
82
135
|
get isSuccess() {
|
|
83
136
|
return this.update.success === true;
|
|
84
137
|
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Reports whether the container is currently attached as visible.
|
|
141
|
+
* @public
|
|
142
|
+
* @returns {boolean}
|
|
143
|
+
*/
|
|
85
144
|
get isVisible() {
|
|
86
145
|
return this.visible === true;
|
|
87
146
|
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Resolves visibility intent from the staged value or committed show flag.
|
|
150
|
+
* @public
|
|
151
|
+
* @returns {boolean}
|
|
152
|
+
*/
|
|
88
153
|
get mustBeShown() {
|
|
89
154
|
return this.update.show !== null ? this.update.show === true : this.show === true;
|
|
90
155
|
}
|
|
@@ -105,6 +170,14 @@ export class ContainerData {
|
|
|
105
170
|
* - Inspect flags via `isPending` and `isSuccess`.
|
|
106
171
|
*/
|
|
107
172
|
export class MaterialData {
|
|
173
|
+
/**
|
|
174
|
+
* Creates material option state and stores exact-name and prefix targets.
|
|
175
|
+
* @public
|
|
176
|
+
* @param {string} [name=""] - Material option identity.
|
|
177
|
+
* @param {string|null} [value=null] - Selected material name.
|
|
178
|
+
* @param {string[]} [nodeNames=[]] - Exact mesh names to target.
|
|
179
|
+
* @param {string[]} [nodePrefixes=[]] - Mesh-name prefixes to target.
|
|
180
|
+
*/
|
|
108
181
|
constructor(name = "", value = null, nodeNames = [], nodePrefixes = []) {
|
|
109
182
|
this.name = name;
|
|
110
183
|
this.nodeNames = nodeNames;
|
|
@@ -113,6 +186,12 @@ export class MaterialData {
|
|
|
113
186
|
// Set initial information about ongoing update
|
|
114
187
|
this.reset();
|
|
115
188
|
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Clears staged material flags without changing the committed selection.
|
|
192
|
+
* @public
|
|
193
|
+
* @returns {void}
|
|
194
|
+
*/
|
|
116
195
|
reset() {
|
|
117
196
|
this.update = {
|
|
118
197
|
pending: false,
|
|
@@ -120,6 +199,13 @@ export class MaterialData {
|
|
|
120
199
|
value: null,
|
|
121
200
|
};
|
|
122
201
|
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Marks the result and commits the staged material value only on success.
|
|
205
|
+
* @public
|
|
206
|
+
* @param {boolean} [success=false] - Whether material application succeeded.
|
|
207
|
+
* @returns {void}
|
|
208
|
+
*/
|
|
123
209
|
setSuccess(success = false) {
|
|
124
210
|
if (success) {
|
|
125
211
|
this.update.success = true;
|
|
@@ -128,12 +214,25 @@ export class MaterialData {
|
|
|
128
214
|
this.update.success = false;
|
|
129
215
|
}
|
|
130
216
|
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Resets staged state and queues a material value for application.
|
|
220
|
+
* @public
|
|
221
|
+
* @param {string|null} [value=null] - Material name to apply.
|
|
222
|
+
* @returns {void}
|
|
223
|
+
*/
|
|
131
224
|
setPending(value = null) {
|
|
132
225
|
this.reset();
|
|
133
226
|
this.update.pending = true;
|
|
134
227
|
this.update.success = false;
|
|
135
228
|
this.update.value = value;
|
|
136
229
|
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Queues the committed material value unless it is undefined.
|
|
233
|
+
* @public
|
|
234
|
+
* @returns {boolean}
|
|
235
|
+
*/
|
|
137
236
|
setPendingWithCurrent() {
|
|
138
237
|
if (this.value === undefined) {
|
|
139
238
|
return false;
|
|
@@ -141,9 +240,21 @@ export class MaterialData {
|
|
|
141
240
|
this.setPending(this.value);
|
|
142
241
|
return true;
|
|
143
242
|
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Reports whether a material change is staged.
|
|
246
|
+
* @public
|
|
247
|
+
* @returns {boolean}
|
|
248
|
+
*/
|
|
144
249
|
get isPending() {
|
|
145
250
|
return this.update.pending === true;
|
|
146
251
|
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Reports whether the staged material change succeeded.
|
|
255
|
+
* @public
|
|
256
|
+
* @returns {boolean}
|
|
257
|
+
*/
|
|
147
258
|
get isSuccess() {
|
|
148
259
|
return this.update.success === true;
|
|
149
260
|
}
|
|
@@ -165,6 +276,13 @@ export class MaterialData {
|
|
|
165
276
|
*/
|
|
166
277
|
export class CameraData {
|
|
167
278
|
defaultLocked = true;
|
|
279
|
+
/**
|
|
280
|
+
* Creates camera selection and lock state and resets its staged update.
|
|
281
|
+
* @public
|
|
282
|
+
* @param {string} [name=""] - Camera option identity.
|
|
283
|
+
* @param {string|null} [value=null] - Authored camera name or null for the default camera.
|
|
284
|
+
* @param {boolean} [locked=true] - Initial navigation lock.
|
|
285
|
+
*/
|
|
168
286
|
constructor(name = "", value = null, locked = this.defaultLocked) {
|
|
169
287
|
this.name = name;
|
|
170
288
|
this.value = value;
|
|
@@ -172,6 +290,12 @@ export class CameraData {
|
|
|
172
290
|
// Set initial information about ongoing update
|
|
173
291
|
this.reset();
|
|
174
292
|
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Clears staged camera flags while preserving the committed camera selection.
|
|
296
|
+
* @public
|
|
297
|
+
* @returns {void}
|
|
298
|
+
*/
|
|
175
299
|
reset() {
|
|
176
300
|
this.update = {
|
|
177
301
|
pending: false,
|
|
@@ -179,6 +303,13 @@ export class CameraData {
|
|
|
179
303
|
value: null,
|
|
180
304
|
};
|
|
181
305
|
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Marks the result and commits the staged camera value and lock only on success.
|
|
309
|
+
* @public
|
|
310
|
+
* @param {boolean} [success=false] - Whether camera application succeeded.
|
|
311
|
+
* @returns {void}
|
|
312
|
+
*/
|
|
182
313
|
setSuccess(success = false) {
|
|
183
314
|
if (success) {
|
|
184
315
|
this.update.success = true;
|
|
@@ -188,6 +319,14 @@ export class CameraData {
|
|
|
188
319
|
this.update.success = false;
|
|
189
320
|
}
|
|
190
321
|
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Queues a camera change; undefined clears staging and null requests an unlocked default camera.
|
|
325
|
+
* @public
|
|
326
|
+
* @param {string|null|undefined} [value] - Camera selection to stage.
|
|
327
|
+
* @param {boolean} [locked=true] - Navigation lock for an authored camera.
|
|
328
|
+
* @returns {void}
|
|
329
|
+
*/
|
|
191
330
|
setPending(value = undefined, locked = this.defaultLocked) {
|
|
192
331
|
this.reset();
|
|
193
332
|
if (value === undefined) {
|
|
@@ -201,14 +340,31 @@ export class CameraData {
|
|
|
201
340
|
this.update.value = value;
|
|
202
341
|
this.update.locked = locked;
|
|
203
342
|
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Queues the committed camera selection and lock again.
|
|
346
|
+
* @public
|
|
347
|
+
* @returns {boolean}
|
|
348
|
+
*/
|
|
204
349
|
setPendingWithCurrent() {
|
|
205
350
|
const currentValue = this.value !== undefined ? this.value : null;
|
|
206
351
|
this.setPending(currentValue, this.locked);
|
|
207
352
|
return true;
|
|
208
353
|
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Reports whether a camera change is staged.
|
|
357
|
+
* @public
|
|
358
|
+
* @returns {boolean}
|
|
359
|
+
*/
|
|
209
360
|
get isPending() {
|
|
210
361
|
return this.update.pending === true;
|
|
211
362
|
}
|
|
363
|
+
/**
|
|
364
|
+
* Reports whether the staged camera change succeeded.
|
|
365
|
+
* @public
|
|
366
|
+
* @returns {boolean}
|
|
367
|
+
*/
|
|
212
368
|
get isSuccess() {
|
|
213
369
|
return this.update.success === true;
|
|
214
370
|
}
|
|
@@ -234,6 +390,16 @@ export class CameraData {
|
|
|
234
390
|
export class IBLData {
|
|
235
391
|
defaultIntensity = 1.0;
|
|
236
392
|
defaultShadows = false;
|
|
393
|
+
/**
|
|
394
|
+
* Creates HDR source/cache references and effective image-based lighting settings.
|
|
395
|
+
* @public
|
|
396
|
+
* @param {string|null} [url=null] - HDR source identifier.
|
|
397
|
+
* @param {string|null} [cachedUrl=null] - Resolved temporary HDR URL.
|
|
398
|
+
* @param {number} [intensity=1] - Environment intensity.
|
|
399
|
+
* @param {boolean} [shadows=false] - IBL shadow toggle.
|
|
400
|
+
* @param {string|null} [timeStamp=null] - Source modification marker.
|
|
401
|
+
* @param {boolean} [valid=false] - Whether a reusable HDR texture is available.
|
|
402
|
+
*/
|
|
237
403
|
constructor(url = null, cachedUrl = null, intensity = this.defaultIntensity, shadows = this.defaultShadows, timeStamp = null, valid = false) {
|
|
238
404
|
this.url = url;
|
|
239
405
|
this.cachedUrl = cachedUrl;
|
package/src/pref-viewer-3d.js
CHANGED
|
@@ -77,6 +77,7 @@ import FileStorage from "./file-storage.js";
|
|
|
77
77
|
* - "pref-viewer-3d-options-applying": Dispatched when viewer options are being set. Legacy alias: "scene-setting-options".
|
|
78
78
|
* - "pref-viewer-3d-options-applied": Dispatched when viewer options have been set. Legacy alias: "scene-set-options".
|
|
79
79
|
* - "pref-viewer-3d-animation-changed": Dispatched by OpeningAnimation instances via the pref-viewer-3d host when an animation state, progress, or loop value changes.
|
|
80
|
+
* - "pref-viewer-3d-element-click": Internal event carrying distance-ordered semantic model mesh ids.
|
|
80
81
|
*
|
|
81
82
|
* Notes:
|
|
82
83
|
* - Internal state and heavy initialization are handled in connectedCallback.
|
|
@@ -794,6 +795,16 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
794
795
|
this.#babylonJSController.setContainerVisibility("environment", false);
|
|
795
796
|
}
|
|
796
797
|
|
|
798
|
+
/**
|
|
799
|
+
* Applies the host-owned semantic selection snapshot to the 3D model.
|
|
800
|
+
* @public
|
|
801
|
+
* @param {object} state - Canonical PrefViewer selection state.
|
|
802
|
+
* @returns {void}
|
|
803
|
+
*/
|
|
804
|
+
applySelectionState(state = {}) {
|
|
805
|
+
this.#babylonJSController?.applySelectionState(state);
|
|
806
|
+
}
|
|
807
|
+
|
|
797
808
|
/**
|
|
798
809
|
* Downloads the current 3D model as a GLB file.
|
|
799
810
|
* @public
|
|
@@ -44,7 +44,7 @@ export default class PrefViewerDialog extends HTMLElement {
|
|
|
44
44
|
* Called when the element is inserted into the DOM.
|
|
45
45
|
* Sets up the dialog's DOM structure and styles.
|
|
46
46
|
* @returns {void}
|
|
47
|
-
|
|
47
|
+
*/
|
|
48
48
|
connectedCallback() {
|
|
49
49
|
this.#handles.onBackdropClick = this.#onBackdropClick.bind(this);
|
|
50
50
|
this.#createDOMElements();
|
|
@@ -85,6 +85,12 @@ export default class PrefViewerDialog extends HTMLElement {
|
|
|
85
85
|
this.#footer = this.#wrapper.querySelector(".dialog-footer");
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Closes the dialog only when its own backdrop is clicked, preserving content interactions.
|
|
90
|
+
* @private
|
|
91
|
+
* @param {MouseEvent} event - Dialog click event.
|
|
92
|
+
* @returns {void}
|
|
93
|
+
*/
|
|
88
94
|
#onBackdropClick(event) {
|
|
89
95
|
if (event.target === this) {
|
|
90
96
|
this.close();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PrefViewerMenu3DStyles } from "./styles.js";
|
|
2
2
|
import BabylonJSController from "./babylonjs-controller.js";
|
|
3
|
-
import
|
|
3
|
+
import I18n from "./localization/i18n.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* PrefViewerMenu3D - Localized floating control panel for Babylon.js render settings.
|
|
@@ -55,6 +55,7 @@ export default class PrefViewerMenu3D extends HTMLElement {
|
|
|
55
55
|
onColorSliderInput: null,
|
|
56
56
|
onLightingSliderInput: null,
|
|
57
57
|
onLightingPlayClick: null,
|
|
58
|
+
onLightingTick: null,
|
|
58
59
|
onHostLeave: null,
|
|
59
60
|
onPanelEnter: null,
|
|
60
61
|
onSwitchChange: null,
|
|
@@ -63,7 +64,7 @@ export default class PrefViewerMenu3D extends HTMLElement {
|
|
|
63
64
|
|
|
64
65
|
#DEFAULT_RENDER_SETTINGS = { ...BabylonJSController.config.render.defaultSettings };
|
|
65
66
|
// Only include boolean settings as switches; scalar settings are handled by sliders.
|
|
66
|
-
#MENU_SWITCHES = Object.keys(this.#DEFAULT_RENDER_SETTINGS).filter(key =>
|
|
67
|
+
#MENU_SWITCHES = Object.keys(this.#DEFAULT_RENDER_SETTINGS).filter(key =>
|
|
67
68
|
typeof this.#DEFAULT_RENDER_SETTINGS[key] === "boolean"
|
|
68
69
|
);
|
|
69
70
|
#settings = { ...this.#DEFAULT_RENDER_SETTINGS };
|
|
@@ -77,9 +78,9 @@ export default class PrefViewerMenu3D extends HTMLElement {
|
|
|
77
78
|
#lightingPlaybackBase = 0.5;
|
|
78
79
|
#lightingPlaybackDurationMs = 15000;
|
|
79
80
|
|
|
80
|
-
#culture = DEFAULT_LOCALE;
|
|
81
|
+
#culture = I18n.DEFAULT_LOCALE;
|
|
81
82
|
#texts = {};
|
|
82
|
-
|
|
83
|
+
|
|
83
84
|
/**
|
|
84
85
|
* Creates the menu custom element.
|
|
85
86
|
* Rendering is deferred until connectedCallback() so attributes can be set before the UI is built.
|
|
@@ -87,8 +88,9 @@ export default class PrefViewerMenu3D extends HTMLElement {
|
|
|
87
88
|
*/
|
|
88
89
|
constructor() {
|
|
89
90
|
super();
|
|
91
|
+
this.#handles.onLightingTick = this.#tickLightingPlayback.bind(this);
|
|
90
92
|
}
|
|
91
|
-
|
|
93
|
+
|
|
92
94
|
/**
|
|
93
95
|
* Returns the list of attributes to observe for changes.
|
|
94
96
|
* @public
|
|
@@ -97,7 +99,7 @@ export default class PrefViewerMenu3D extends HTMLElement {
|
|
|
97
99
|
static get observedAttributes() {
|
|
98
100
|
return ["culture"];
|
|
99
101
|
}
|
|
100
|
-
|
|
102
|
+
|
|
101
103
|
/**
|
|
102
104
|
* Reacts to observed attribute mutations and mirrors them into the internal settings/UI state.
|
|
103
105
|
* @public
|
|
@@ -109,7 +111,7 @@ export default class PrefViewerMenu3D extends HTMLElement {
|
|
|
109
111
|
attributeChangedCallback(name, _old, value) {
|
|
110
112
|
switch (name) {
|
|
111
113
|
case "culture":
|
|
112
|
-
this.#setCultureInternal(value || DEFAULT_LOCALE);
|
|
114
|
+
this.#setCultureInternal(value || I18n.DEFAULT_LOCALE);
|
|
113
115
|
break;
|
|
114
116
|
}
|
|
115
117
|
}
|
|
@@ -222,7 +224,7 @@ export default class PrefViewerMenu3D extends HTMLElement {
|
|
|
222
224
|
* @returns {object} Resolved `prefViewer.menu3d` translation object or an empty fallback.
|
|
223
225
|
*/
|
|
224
226
|
#getMenuNamespace() {
|
|
225
|
-
const namespace = translate("prefViewer.menu3d", { locale: this.#culture });
|
|
227
|
+
const namespace = I18n.translate("prefViewer.menu3d", { locale: this.#culture });
|
|
226
228
|
return namespace && typeof namespace === "object" ? namespace : {};
|
|
227
229
|
}
|
|
228
230
|
|
|
@@ -291,11 +293,11 @@ export default class PrefViewerMenu3D extends HTMLElement {
|
|
|
291
293
|
/**
|
|
292
294
|
* Resolves and applies a locale internally, updating texts when it changes.
|
|
293
295
|
* @private
|
|
294
|
-
* @param {string} [cultureId=DEFAULT_LOCALE] - Desired locale identifier.
|
|
296
|
+
* @param {string} [cultureId=I18n.DEFAULT_LOCALE] - Desired locale identifier.
|
|
295
297
|
* @returns {string} The applied locale.
|
|
296
298
|
*/
|
|
297
|
-
#setCultureInternal(cultureId = DEFAULT_LOCALE) {
|
|
298
|
-
const resolved = resolveLocale(cultureId);
|
|
299
|
+
#setCultureInternal(cultureId = I18n.DEFAULT_LOCALE) {
|
|
300
|
+
const resolved = I18n.resolveLocale(cultureId);
|
|
299
301
|
if (resolved === this.#culture) {
|
|
300
302
|
return this.#culture;
|
|
301
303
|
}
|
|
@@ -333,7 +335,7 @@ export default class PrefViewerMenu3D extends HTMLElement {
|
|
|
333
335
|
this.#elements.sliderWrappers["highlightColor"] = this.querySelector(".menu-slider[data-setting='highlightColor']");
|
|
334
336
|
this.#elements.sliders["lightingTimeOfDay"] = this.querySelector("input[data-setting='lightingTimeOfDay']");
|
|
335
337
|
this.#elements.sliderWrappers["lightingTimeOfDay"] = this.querySelector(".menu-slider[data-setting='lightingTimeOfDay']");
|
|
336
|
-
|
|
338
|
+
|
|
337
339
|
this.#MENU_SWITCHES.forEach((key) => {
|
|
338
340
|
this.#elements.switches[key] = this.querySelector(`input[data-setting="${key}"]`);
|
|
339
341
|
const wrapper = this.querySelector(`.menu-switch[data-setting="${key}"]`);
|
|
@@ -499,19 +501,26 @@ export default class PrefViewerMenu3D extends HTMLElement {
|
|
|
499
501
|
this.#lightingPlaybackBase = this.#settings.lightingTimeOfDay ?? 0.5;
|
|
500
502
|
this.#lightingPlaybackStart = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
501
503
|
this.#updateLightingPlayButtonUI();
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
504
|
+
|
|
505
|
+
this.#lightingAnimationFrame = requestAnimationFrame(this.#handles.onLightingTick);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Advances the time-of-day playback cycle and emits transient settings while playback remains active.
|
|
510
|
+
* @private
|
|
511
|
+
* @returns {void}
|
|
512
|
+
*/
|
|
513
|
+
#tickLightingPlayback() {
|
|
514
|
+
if (!this.#isLightingPlaying) {
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
const now = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
518
|
+
const elapsed = now - this.#lightingPlaybackStart;
|
|
519
|
+
const cycle = (this.#lightingPlaybackBase + (elapsed / this.#lightingPlaybackDurationMs)) % 1;
|
|
520
|
+
this.#settings.lightingTimeOfDay = cycle;
|
|
521
|
+
this.#updateLightingSliderUI(cycle);
|
|
522
|
+
this.#emitApply({ lightingTimeOfDay: cycle, persist: false }, { partial: true });
|
|
523
|
+
this.#lightingAnimationFrame = requestAnimationFrame(this.#handles.onLightingTick);
|
|
515
524
|
}
|
|
516
525
|
|
|
517
526
|
/**
|
|
@@ -555,11 +564,22 @@ export default class PrefViewerMenu3D extends HTMLElement {
|
|
|
555
564
|
static #hueToHex(hue) {
|
|
556
565
|
const h = ((hue % 360) + 360) % 360;
|
|
557
566
|
const a = Math.min(0.5, 1 - 0.5);
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
567
|
+
|
|
568
|
+
return '#' + [0, 8, 4].map((phase) => PrefViewerMenu3D.#hueChannel(phase, h, a)).map((v) => Math.round(v * 255).toString(16).padStart(2, '0')).join('');
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Computes a normalized RGB channel from the current hue.
|
|
573
|
+
* @private
|
|
574
|
+
* @static
|
|
575
|
+
* @param {number} phase - Channel phase offset.
|
|
576
|
+
* @param {number} hue - Normalized hue angle in degrees.
|
|
577
|
+
* @param {number} amplitude - RGB channel amplitude.
|
|
578
|
+
* @returns {number}
|
|
579
|
+
*/
|
|
580
|
+
static #hueChannel(phase, hue, amplitude) {
|
|
581
|
+
const k = (phase + hue / 30) % 12;
|
|
582
|
+
return 0.5 - amplitude * Math.max(Math.min(k - 3, 9 - k, 1), -1);
|
|
563
583
|
}
|
|
564
584
|
|
|
565
585
|
/**
|
|
@@ -724,21 +744,21 @@ export default class PrefViewerMenu3D extends HTMLElement {
|
|
|
724
744
|
this.#isApplying = isApplying;
|
|
725
745
|
this.toggleAttribute("data-applying", isApplying);
|
|
726
746
|
}
|
|
727
|
-
|
|
747
|
+
|
|
728
748
|
/**
|
|
729
749
|
* Sets the locale used for menu labels and helper text.
|
|
730
750
|
* @public
|
|
731
751
|
* @param {string} cultureId - Locale identifier, e.g., "en-EN".
|
|
732
752
|
* @returns {void}
|
|
733
753
|
*/
|
|
734
|
-
setCulture(cultureId = DEFAULT_LOCALE) {
|
|
735
|
-
const resolved = resolveLocale(cultureId);
|
|
754
|
+
setCulture(cultureId = I18n.DEFAULT_LOCALE) {
|
|
755
|
+
const resolved = I18n.resolveLocale(cultureId);
|
|
736
756
|
const applied = this.#setCultureInternal(resolved);
|
|
737
757
|
if (this.getAttribute("culture") !== applied) {
|
|
738
758
|
this.setAttribute("culture", applied);
|
|
739
759
|
}
|
|
740
760
|
}
|
|
741
|
-
|
|
761
|
+
|
|
742
762
|
/**
|
|
743
763
|
* Enables or disables the menu entirely, hiding it when 3D mode is unavailable.
|
|
744
764
|
* @public
|
|
@@ -774,7 +794,7 @@ export default class PrefViewerMenu3D extends HTMLElement {
|
|
|
774
794
|
this.#updateLightingPlayButtonUI();
|
|
775
795
|
this.setApplying(false);
|
|
776
796
|
}
|
|
777
|
-
|
|
797
|
+
|
|
778
798
|
/**
|
|
779
799
|
* Receives viewer hover state so the menu only opens when the user interacts with the 3D viewport.
|
|
780
800
|
* @public
|