@preference-sl/pref-viewer 2.16.2 → 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 +184 -22
- package/index.d.ts +11 -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-camera-framing.js +171 -0
- package/src/babylonjs-camera-zoom-controller.js +272 -0
- package/src/babylonjs-controller.js +640 -378
- package/src/babylonjs-selection-controller.js +215 -0
- package/src/babylonjs-view-cube-controller.js +810 -0
- package/src/file-storage.js +13 -0
- package/src/gltf-storage.js +97 -0
- package/src/localization/i18n.js +147 -74
- package/src/localization/translations.js +18 -0
- 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 +40 -5
- package/src/pref-viewer-dialog.js +7 -1
- package/src/pref-viewer-menu-3d.js +57 -37
- package/src/pref-viewer.js +155 -44
- package/src/render-math.js +130 -117
- package/src/styles.js +94 -3
- package/src/svg-resolver.js +5 -4
package/README.md
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
- Native custom elements: `<pref-viewer>`, `<pref-viewer-2d>`, `<pref-viewer-3d>`, `<pref-viewer-dialog>`, and `<pref-viewer-menu-3d>`.
|
|
8
8
|
- Babylon.js-based 3D visualization for glTF and GLB assets.
|
|
9
|
+
- Default-enabled ViewCube with 26 selectable orientations, synchronized camera feedback, smooth model framing and perspective/orthographic support.
|
|
10
|
+
- Model-only, geometry-dependent orbital zoom protection with near-plane sampling, independent of ViewCube visibility.
|
|
11
|
+
- Viewer dialogs above the render menu and ViewCube, with background pointer-input protection.
|
|
12
|
+
- Proportional mouse-wheel zoom for unlocked orbital cameras: increments decrease as the camera approaches its target, using Babylon's native input without an additional viewer zoom increment. Wheel sensitivity is currently an internal setting, not a public attribute or option.
|
|
9
13
|
- 2D SVG drawing support with pan and zoom controls.
|
|
10
14
|
- Unified `<pref-viewer>` wrapper that coordinates 2D, 3D, menu, dialog, localization, and loading lifecycle.
|
|
11
15
|
- Sequential task queue for deterministic config, model, scene, materials, drawing, options, and culture updates.
|
|
@@ -92,7 +96,9 @@ viewer.setOptions({
|
|
|
92
96
|
|
|
93
97
|
## Configuration model
|
|
94
98
|
|
|
95
|
-
The main `config` payload can include model, scene, materials, drawing, options, and
|
|
99
|
+
The main `config` payload can include model, scene, materials, drawing, options, culture, styles, and boolean `showRenderMenu` / `showViewCube` flags.
|
|
100
|
+
|
|
101
|
+
When a queued configuration is processed, explicit boolean flags update the corresponding `show-render-menu` and `show-view-cube` attributes before its assets load. Omitted or non-boolean flags preserve the current settings. Explicit processed flags override the corresponding current attributes; subsequent direct attribute changes remain effective. Reapplying an equal attribute value does not recreate the render menu. The same rules apply to `loadConfig(config)`, the `config` property setter and the JSON `config` attribute; flags follow existing task ordering, including during pending asset loads. These fields belong to the root config, alongside `culture` and `styles`.
|
|
96
102
|
|
|
97
103
|
```js
|
|
98
104
|
viewer.loadConfig({
|
|
@@ -141,10 +147,39 @@ viewer.loadConfig({
|
|
|
141
147
|
colorPrimary: "#ff6700",
|
|
142
148
|
},
|
|
143
149
|
culture: "en-EN",
|
|
150
|
+
showRenderMenu: true,
|
|
151
|
+
showViewCube: true,
|
|
144
152
|
forceReload: false,
|
|
145
153
|
});
|
|
146
154
|
```
|
|
147
155
|
|
|
156
|
+
### Overlay visibility
|
|
157
|
+
|
|
158
|
+
Use boolean values in config and string values in attributes:
|
|
159
|
+
|
|
160
|
+
```js
|
|
161
|
+
viewer.config = {
|
|
162
|
+
showRenderMenu: true,
|
|
163
|
+
showViewCube: false,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// Change only the cube flag; the menu keeps its current setting.
|
|
167
|
+
viewer.loadConfig({ showViewCube: true });
|
|
168
|
+
|
|
169
|
+
// Direct attribute control remains available after processing config.
|
|
170
|
+
viewer.setAttribute("show-view-cube", "false");
|
|
171
|
+
viewer.setAttribute("show-render-menu", "true");
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
```html
|
|
175
|
+
<pref-viewer
|
|
176
|
+
mode="3d"
|
|
177
|
+
config='{"showRenderMenu":true,"showViewCube":false}'
|
|
178
|
+
></pref-viewer>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Both overlays are enabled by default in 3D when their attributes are absent. Use explicit `"true"`/`"false"` strings for attributes: the menu accepts an absent attribute or literal `"true"` as enabled, whereas only literal `"false"` disables the cube. A config value such as `"false"` is a string and does not change visibility.
|
|
182
|
+
|
|
148
183
|
## Asset storage descriptors
|
|
149
184
|
|
|
150
185
|
Storage descriptors are passed through `model.storage`, `scene.storage`, `materials.storage`, or `drawing.storage`.
|
|
@@ -198,16 +233,77 @@ Storage descriptors are passed through `model.storage`, `scene.storage`, `materi
|
|
|
198
233
|
| `mode` | Viewer mode: `3d` or `2d`. Defaults to `3d`. |
|
|
199
234
|
| `show-model` | Shows or hides the current 3D model. Use `true` or `false`. |
|
|
200
235
|
| `show-scene` | Shows or hides the current 3D scene/environment. Use `true` or `false`. |
|
|
201
|
-
| `show-render-menu` | Shows
|
|
236
|
+
| `show-render-menu` | Shows the floating 3D render settings menu when absent or `"true"`; `"false"` hides it. Also configurable through boolean `config.showRenderMenu`. See [ViewCube layout](#viewcube-layout-and-lifecycle). |
|
|
237
|
+
| `show-view-cube` | Shows the orientation cube in 3D. Enabled by default; only literal `"false"` disables it. Also configurable through boolean `config.showViewCube`. Runtime toggles preserve loaded assets and camera state. See [ViewCube](#viewcube). |
|
|
202
238
|
| `styles` | JSON string with supported style overrides. Currently supports `colorPrimary`, applies it as `--pref-viewer-color-primary`, updates `PrefViewerStyleVariables.colorPrimary`, and persists it in `localStorage`. |
|
|
203
239
|
|
|
240
|
+
## ViewCube
|
|
241
|
+
|
|
242
|
+
The ViewCube reflects the effective active camera orientation and offers **26 views: six faces, twelve edges and eight corners**. Click any region to change orientation smoothly and refit the model assetContainer. Every accepted click refits, even when selecting the current orientation: previous pan, framing and zoom are discarded. Environment geometry is excluded from this fit; only visible, enabled model meshes with valid geometry contribute to bounds. Hidden or disabled model geometry, including auxiliary walls/floors hidden by viewer options, does not enlarge the fit.
|
|
243
|
+
|
|
244
|
+
The active camera retains its identity and perspective or orthographic projection. Navigation supports Babylon ArcRotateCamera, FreeCamera and UniversalCamera, including supported parent transforms. Other camera classes, locked cameras and active XR tracked cameras are display-only. Selection does nothing when usable model bounds, a supported camera or positive viewport dimensions are unavailable.
|
|
245
|
+
|
|
246
|
+
### Orientation and feedback
|
|
247
|
+
|
|
248
|
+
The inner view is the principal model view. English ViewCube labels use `Inner` and `Outer` (displayed as `INNER` and `OUTER`); Spanish labels remain `Interior` and `Exterior`. Accessible names and combined edge/corner labels use the same localized terms. Face names use this world-axis convention:
|
|
249
|
+
|
|
250
|
+
| Direction | World axis | Reference |
|
|
251
|
+
| --- | --- | --- |
|
|
252
|
+
| Inner / Front | +Z | Looks toward the model from +Z. |
|
|
253
|
+
| Outer / Back | -Z | Looks toward the model from -Z. |
|
|
254
|
+
| Left | +X | Left relative to the inner view. |
|
|
255
|
+
| Right | -X | Right relative to the inner view. |
|
|
256
|
+
| Top / Superior | +Y | Looks down toward the model. |
|
|
257
|
+
| Bottom / Inferior | -Y | Looks up toward the model. |
|
|
258
|
+
|
|
259
|
+
The cube has sharp edges without chamfers. Edge regions appear as prism strips on both adjacent faces, with boundaries parallel to the edge; corner regions appear as small cubes. All visible surfaces belonging to an edge or corner select the same orientation.
|
|
260
|
+
|
|
261
|
+
Hover uses `styles.colorPrimary`, backed by `PrefViewerStyleVariables.colorPrimary`, with **50% fill opacity** and fully opaque borders around the complete clickable region. Face hover fills only its central clickable area, excluding edge/corner regions. The angularly nearest of the 26 views has **15% primary-color fill** and no highlighted border; hover takes precedence until the pointer leaves. Primary-color and culture changes update the existing overlay. There are no region tooltips; visible face labels use uppercase lettering and increased letter spacing for readability; accessible region names remain localized.
|
|
262
|
+
|
|
263
|
+
Cube perspective follows the active camera's projection and FOV. Mouse movement changes feedback without rebuilding SVG geometry; camera pose/projection changes update the projected geometry. The cube remains independent of model zoom and does not add Babylon scene meshes or appear in exports.
|
|
264
|
+
|
|
265
|
+
### ViewCube layout and lifecycle
|
|
266
|
+
|
|
267
|
+
The cube stays at **top 0px / right 0px**. When both overlays are enabled, the render-menu top equals the **measured cube SVG viewport height** and updates when that size changes. The maximum cube viewport is 100 × 100 CSS pixels and shrinks to fit smaller scene viewports; the menu offset is measured, not fixed to that value. Without the cube, the menu returns to its style-defined default top. The cube remains below the menu's layer, and viewer modals cover both.
|
|
268
|
+
|
|
269
|
+
Cube resources are created only while enabled in a visible 3D view. Disabling it or switching to 2D releases its overlay, listeners, observers and pending transition. Reactivation uses the current camera without reloading assets. Layout subscriptions connect before activation and are removed when inactive or disconnected.
|
|
270
|
+
|
|
271
|
+
Rapid selections retarget the transition from the displayed pose. Canvas pointer/wheel/key interaction, resize, projection change, camera/model replacement, locking, disabling or disposal cancels pending navigation; unlocking/reactivation does not replay it. Transitions use the existing demand-rendering scheduler and release their activity when finished without stopping unrelated model animations.
|
|
272
|
+
|
|
273
|
+
## 3D camera and wheel zoom
|
|
274
|
+
|
|
275
|
+
The default orbital camera starts on the **Inner (+Z)** side before the ViewCube is created, so the loading indicator does not first show Outer and then switch to Inner when model framing completes. The initial fit uses the same model bounds and projection-aware calculation as the ViewCube, with the shared framing factor (`BabylonJSController.config.camera.fitMarginFactor`) of 0.15. The resulting fit matches a cube destination for the same orientation, projection and canvas dimensions; the initial orientation is exact Inner, with no elevation, so selecting Inner immediately after loading preserves the view. The initial fit preserves its orbit angles; later model reloads preserve the user's current orientation. Explicitly selected cameras from the model or environment retain their authored view, which may differ from Inner.
|
|
276
|
+
|
|
277
|
+
Camera selection through `options.camera` uses `null` for the interactive default camera. A named camera such as `"Inner"` is resolved from the model first, then the environment, and is locked by default. If that name is absent from both assets, the viewer activates its default camera with `locked = false` and reconnects canvas controls, even after a previously locked imported camera. A missing name does not retain the previous imported view. Omitting `options.camera` preserves the current selection.
|
|
278
|
+
|
|
279
|
+
The shared internal setting `BabylonJSController.config.camera.fitMarginFactor` accepts finite values from **0 to 0.5, inclusive**. It reserves a total fraction of the viewport along the limiting axis; the model's projected bounding envelope can occupy `1 - fitMarginFactor` of that axis. It is applied when automatic or ViewCube framing runs, rather than continuously changing the current zoom.
|
|
280
|
+
|
|
281
|
+
| fitMarginFactor | Space available for the model | Margin at each opposing edge of the limiting axis |
|
|
282
|
+
| --- | --- | --- |
|
|
283
|
+
| 0 | 100% | 0% |
|
|
284
|
+
| 0.1 | 90% | 5% |
|
|
285
|
+
| 0.15 (default) | 85% | 7.5% |
|
|
286
|
+
| 0.5 | 50% | 25% |
|
|
287
|
+
|
|
288
|
+
Increasing the factor makes the fitted model smaller. The other axis can have more free space because framing preserves aspect ratio. The calculation uses visible model world bounds; the actual silhouette may occupy less space, and near-plane safety can require extra clearance. Invalid values leave the existing camera framing unchanged. This setting replaces the internal `fitFactor` name and its previous semantics; it is not a property of the public viewer `config` object.
|
|
289
|
+
|
|
290
|
+
The default orbital camera starts without `lowerBetaLimit` or `upperBetaLimit` and disables upside-down orbiting, whether or not the cube is enabled. ViewCube navigation keeps the camera up direction on world **+Y**, including top/bottom views and subsequent orbiting. Automatic loading preserves authored camera composition and navigation limits; an explicit unlocked cube action may relax incompatible orbit limits and adjust clipping/radius ranges to reach and fit the selected view. Locked cameras retain their view and limits.
|
|
291
|
+
|
|
292
|
+
Use the mouse wheel over the 3D canvas to zoom an unlocked orbital camera. Babylon's native input applies a radius-proportional increment once, so the same wheel input moves less at a smaller radius. Input wakes idle rendering. This applies to the default and selected unlocked orbital cameras independently of the cube; existing Free/Universal wheel navigation remains unchanged. The internal orbital wheel sensitivity (`wheelDeltaPercentage`) is 0.015. Wheel sensitivity has no public attribute or config option.
|
|
293
|
+
|
|
294
|
+
The orbital minimum radius adapts to eligible triangle surfaces in the **current visible model assetContainer only**. Hidden, disabled, non-pickable, disposed or zero-visibility meshes do not contribute. Environment meshes are excluded from both hits and raycast traversal, and model replacement/removal cannot retain an obsolete geometry floor. Clear checked paths allow close inspection without a minimum based on overall model bounds.
|
|
295
|
+
|
|
296
|
+
For the default camera with a visible model, the near clipping plane moves closer to the eye as the radius decreases: `minZ` is capped at `max(0.00001, abs(radius) * 0.001)`, retaining any smaller existing positive value. The plane stays in front of the eye; `maxZ`, FOV and projection mode remain unchanged. Imported cameras retain their clipping configuration, and locks/XR/explicit cube transitions preserve their existing policy. Protection samples the updated projection and accounts for near-plane depth and a small safety margin. Perspective samples the center, edge midpoints and corners of the near-plane footprint; orthographic uses the center probe with near-depth clearance. Large inward steps and inertia are constrained at detected surfaces, while outward retreat remains possible. If a checked near-plane point is already inside a closed model mesh, recovery finds its outward triangle-surface exit even when the previous radius is equally short. Empty gaps within mesh bounds are not treated as solids; environment meshes are never queried during recovery. The geometry guard changes radius rather than moving or constraining the target and preserves projection mode; adaptive default-camera near depth is handled separately. Authored minimum-radius constraints remain respected and are restored when releasing a camera. Explicit cube transitions temporarily suspend the geometry floor so the requested model fit can complete; protection resumes afterward.
|
|
297
|
+
|
|
298
|
+
These are sampled ray checks, not continuous volume collision. Small surfaces between probes, open/non-manifold geometry and custom/deformed rigs are not guaranteed to be protected. Interior-point recovery assumes closed meshes. In extreme close-up or grazing-angle views, small parts between the samples may still show minor edge clipping; this is a known limitation of the sampled protection. It does not guarantee clipping-free inspection of every surface. Orthographic protection does not sample the complete near-plane area.
|
|
299
|
+
|
|
204
300
|
## Public methods
|
|
205
301
|
|
|
206
302
|
### Loading and configuration
|
|
207
303
|
|
|
208
304
|
| Method | Description |
|
|
209
305
|
| --- | --- |
|
|
210
|
-
| `loadConfig(config)` | Queues a full configuration update. Accepts an object or JSON string with model, scene, materials, drawing, options, culture, styles, and visibility descriptors. |
|
|
306
|
+
| `loadConfig(config)` | Queues a full configuration update. Accepts an object or JSON string with model, scene, materials, drawing, options, culture, styles, boolean showRenderMenu/showViewCube flags, and visibility descriptors. |
|
|
211
307
|
| `loadModel(model)` | Queues a model update. |
|
|
212
308
|
| `loadScene(scene)` | Queues a scene/environment update. |
|
|
213
309
|
| `loadMaterials(materials)` | Queues a materials update. |
|
|
@@ -225,19 +321,19 @@ Storage descriptors are passed through `model.storage`, `scene.storage`, `materi
|
|
|
225
321
|
| --- | --- |
|
|
226
322
|
| `showModel()` / `hideModel()` | Shows or hides the loaded model. |
|
|
227
323
|
| `showScene()` / `hideScene()` | Shows or hides the loaded scene/environment. |
|
|
228
|
-
| `syncVisibility(name, isVisible)` | Synchronizes `model` or `scene` visibility with the corresponding reflected attribute. |
|
|
229
324
|
| `zoomCenter()` | Centers the 2D drawing view. |
|
|
230
325
|
| `zoomExtentsAll()` | Fits all 2D drawing content in view. |
|
|
231
326
|
| `zoomIn()` / `zoomOut()` | Controls 2D drawing zoom. |
|
|
232
|
-
|
|
327
|
+
|
|
328
|
+
These zoom methods operate only in 2D mode. In 3D, use canvas navigation and the ViewCube to orient and refit the model. Switching modes retains the semantic selection snapshot.
|
|
233
329
|
|
|
234
330
|
### Semantic selection and turnable state
|
|
235
331
|
|
|
236
332
|
| Method | Description |
|
|
237
333
|
| --- | --- |
|
|
238
|
-
| `setSelectableElements(elements)` | Defines and renders selectable
|
|
239
|
-
| `setSelectedElements(elements)` | Defines and renders selected highlights in red (`#FF0000`, opacity `0.5`). Accepts strings or objects with `id`/`Id` and optional `highlights`/`Highlights`. |
|
|
240
|
-
| `applySelectionState(state)` | Applies selectable and selected state to the semantic snapshot and
|
|
334
|
+
| `setSelectableElements(elements)` | Defines and renders selectable 2D/3D ids in yellow (`#FFFF00`, opacity `0.5`). Accepts strings or objects with `id`/`Id`. |
|
|
335
|
+
| `setSelectedElements(elements)` | Defines and renders selected 2D/3D highlights in red (`#FF0000`, opacity `0.5`). Accepts strings or objects with `id`/`Id` and optional `highlights`/`Highlights`. |
|
|
336
|
+
| `applySelectionState(state)` | Applies selectable and selected state to the semantic snapshot and both renderers in one call. Accepts `selectableIds`/`selectables` and `selectedItems`/`selectedIds`/`selected`; state supplied before drawing or model load is reapplied after that renderer loads. |
|
|
241
337
|
| `clearSelectedElements()` | Clears selected elements while preserving the selectable set. |
|
|
242
338
|
| `clearSelectableElements()` | Clears selectable elements while preserving the selected set. |
|
|
243
339
|
| `clearSelectionState()` | Clears all semantic selection state. |
|
|
@@ -250,27 +346,39 @@ Storage descriptors are passed through `model.storage`, `scene.storage`, `materi
|
|
|
250
346
|
| `toggleTurnable(options)` | Toggles semantic turnable state. |
|
|
251
347
|
| `getTurnableState()` | Returns `{ running, speed, direction }`. |
|
|
252
348
|
|
|
253
|
-
|
|
349
|
+
In 2D, selection colors are rendered through disposable SVG clones; the loaded base SVG and shared definitions are not modified. In 3D, model meshes use Babylon overlays without mutating shared materials; every mesh instance with the semantic id is updated independently. Selected ids and their highlights take red priority over yellow selectable state. Reapplying state is idempotent, and replacing a drawing or model reapplies the current semantic snapshot.
|
|
350
|
+
|
|
351
|
+
3D model loading disables Babylon's automatic glTF mesh instancing so repeated geometry can have independent overlays while materials remain shared. Environment loading retains instancing. This can increase draw calls for models with many repeated parts. Clearing selection restores the original overlay properties.
|
|
352
|
+
|
|
353
|
+
3D clicks report eligible crossed ids independently of the selectable set; the host decides which ids to select. Only a final `_primitiveN` suffix is removed, and hits are sorted by distance before duplicate ids are removed.
|
|
354
|
+
|
|
355
|
+
The 3D click threshold defaults to 6 CSS pixels and is defined by the internal `BabylonJSController.config.pointerPicking.clickMaxDistancePx` setting. Movement beyond the threshold at any point cancels the click, including drags returning to their starting point. Cancelled pointers and multi-pointer gestures emit no click. Hidden, disabled, non-pickable meshes, groups and environment geometry are excluded.
|
|
356
|
+
|
|
357
|
+
The turnable methods store and publish semantic state for host integration. They do not start a camera rotation or a render-loop animation. Hosts can read the snapshot through `getTurnableState()` or consume it in `pref-viewer-3d-turnable-changed`.
|
|
254
358
|
|
|
255
359
|
### Animation controls
|
|
256
360
|
|
|
257
361
|
| Method | Description |
|
|
258
362
|
| --- | --- |
|
|
259
363
|
| `getAnimations()` | Returns available model animations with state, progress, loop, and affected node ids. |
|
|
260
|
-
| `playAnimation(name, action)` | Controls a named animation
|
|
261
|
-
| `setAnimationProgress(name, progress)` |
|
|
262
|
-
| `setAnimationLoop(name, loop)` | Enables or disables looping for a named animation. |
|
|
364
|
+
| `playAnimation(name, action)` | Controls a named opening animation with `open`, `close`, `pause`, `goToOpened` or `goToClosed`. Returns whether the animation and action were accepted. |
|
|
365
|
+
| `setAnimationProgress(name, progress)` | Seeks to normalized progress from `0` to `1` and pauses playback. Returns `false` when the animation is absent. |
|
|
366
|
+
| `setAnimationLoop(name, loop)` | Enables or disables ping-pong looping for a named animation. Returns `false` when the animation is absent. |
|
|
367
|
+
|
|
368
|
+
Opening animations are discovered from paired glTF animation groups named `animation_open_<name>` and `animation_close_<name>`. State values are `0` (paused), `1` (closed), `2` (opened), `3` (opening) and `4` (closing). Enable `options.render.showAnimationMenu` to create the animation controller. Disabling that setting releases the controller: `getAnimations()` returns an empty array and imperative animation actions return `false`. A controller that is enabled can still receive imperative actions while its controls are closed.
|
|
263
369
|
|
|
264
370
|
### Dialogs and downloads
|
|
265
371
|
|
|
266
372
|
| Method | Description |
|
|
267
373
|
| --- | --- |
|
|
268
|
-
| `openDialog(title, content, footer)` | Opens a viewer dialog. |
|
|
374
|
+
| `openDialog(title, content, footer)` | Opens a modal above viewer content, including the render menu and ViewCube. Its backdrop intercepts background pointer input. Returns the dialog element on success or `null` if opening fails. |
|
|
269
375
|
| `closeDialog()` | Closes the active viewer dialog. |
|
|
270
376
|
| `downloadModelGLB()` / `downloadModelGLTF()` / `downloadModelUSDZ()` | Exports the current model. |
|
|
271
377
|
| `downloadSceneGLB()` / `downloadSceneGLTF()` / `downloadSceneUSDZ()` | Exports the current scene/environment. |
|
|
272
378
|
| `downloadModelAndSceneGLB()` / `downloadModelAndSceneGLTF()` / `downloadModelAndSceneUSDZ()` | Exports the combined model and scene. |
|
|
273
379
|
|
|
380
|
+
All viewer dialogs share this priority, including the model-download dialog opened with **Ctrl+Alt+D** while the 3D canvas receives keyboard input. Existing backdrop dismissal remains available: a click over a covered control may close the dialog, but does not also activate that control. Closing the dialog restores access to the underlying controls.
|
|
381
|
+
|
|
274
382
|
## Public properties
|
|
275
383
|
|
|
276
384
|
### Setters
|
|
@@ -279,7 +387,7 @@ These setters are convenience bridges for framework bindings and property-based
|
|
|
279
387
|
|
|
280
388
|
| Property | Description |
|
|
281
389
|
| --- | --- |
|
|
282
|
-
| `config` | Forwards to `loadConfig(value)
|
|
390
|
+
| `config` | Forwards an object or JSON string to `loadConfig(value)`, including boolean overlay flags with the same queue ordering and omission rules. |
|
|
283
391
|
| `model` | Forwards to `loadModel(value)`. |
|
|
284
392
|
| `scene` | Forwards to `loadScene(value)`. |
|
|
285
393
|
| `materials` | Forwards to `loadMaterials(value)`. |
|
|
@@ -302,18 +410,18 @@ These setters are convenience bridges for framework bindings and property-based
|
|
|
302
410
|
|
|
303
411
|
## Events
|
|
304
412
|
|
|
305
|
-
The component emits custom events so host applications can integrate loading indicators, error states, and viewer controls.
|
|
413
|
+
The component emits custom events so host applications can integrate loading indicators, error states, and viewer controls. Root-dispatched lifecycle, selection-state and viewport events include `detail.timestamp` in Unix milliseconds. Generic root events also include `detail.mode`, which is always the current `<pref-viewer>` mode (`"2d"` or `"3d"`); scoped 2D/3D and legacy `drawing-*`/`scene-*` events omit it. `pref-viewer-element-click` is an exception: its detail remains exactly `{ ids: string[] }`, without mode or timestamp. Renderer events received directly from child components can have different metadata from their root forwarding.
|
|
306
414
|
|
|
307
415
|
### General Events
|
|
308
416
|
|
|
309
417
|
| Event | Emitted by | Legacy alias | Description |
|
|
310
418
|
| --- | --- | --- | --- |
|
|
311
|
-
| `pref-viewer-initialized` | `<pref-viewer>` | None | The root component
|
|
419
|
+
| `pref-viewer-initialized` | `<pref-viewer>` | None | The root component is connected and ready to process queued work. Detail includes `isInitialized`, `mode` and `timestamp`; it does not mean assets have finished loading. |
|
|
312
420
|
| `pref-viewer-loading` | `<pref-viewer>` | None | A 2D or 3D load operation has started. Includes `detail.mode` with the current root mode. |
|
|
313
421
|
| `pref-viewer-loaded` | `<pref-viewer>` | None | A 2D or 3D load operation completed. Includes `detail.mode` with the current root mode. |
|
|
314
422
|
| `pref-viewer-error` | `<pref-viewer>` | None | A 2D or 3D operation failed. Includes `detail.mode` with the current root mode. |
|
|
315
|
-
| `pref-viewer-selection-state-changed` | `<pref-viewer>` | None | The semantic selection state changed. |
|
|
316
|
-
| `pref-viewer-element-click` | `<pref-viewer>` | None | A semantic
|
|
423
|
+
| `pref-viewer-selection-state-changed` | `<pref-viewer>` | None | The semantic selection state changed. Detail includes `selectableIds`, `selectedIds`, `selectedItems`, `mode` and `timestamp`. |
|
|
424
|
+
| `pref-viewer-element-click` | `<pref-viewer>` | None | A semantic 2D or 3D element was clicked or forwarded by the host. In 2D, geometry without its own id resolves the nearest id-bearing ancestor. In 3D, a primary click that moves no more than 6 CSS pixels reports every crossed visible, enabled, pickable model mesh, nearest first, from `mesh.id`; only a final `_primitiveN` suffix is removed and duplicate ids are omitted. Empty/background clicks do not emit. `detail` is exactly `{ ids: string[] }`, independently of the current selectable set. |
|
|
317
425
|
|
|
318
426
|
### 3D Events
|
|
319
427
|
|
|
@@ -325,8 +433,8 @@ The component emits custom events so host applications can integrate loading ind
|
|
|
325
433
|
| `pref-viewer-3d-options-applying` | `<pref-viewer-3d>` | `scene-setting-options` | 3D viewer options are being applied. |
|
|
326
434
|
| `pref-viewer-3d-options-applied` | `<pref-viewer-3d>` | `scene-set-options` | 3D viewer options were applied. |
|
|
327
435
|
| `pref-viewer-3d-render-settings-change-requested` | `<pref-viewer-menu-3d>` | None | The 3D settings menu requested render-setting changes. |
|
|
328
|
-
| `pref-viewer-3d-animation-changed` |
|
|
329
|
-
| `pref-viewer-3d-turnable-changed` | `<pref-viewer>` | None | The semantic turnable state changed. |
|
|
436
|
+
| `pref-viewer-3d-animation-changed` | `<pref-viewer-3d>` via its opening animation controller | None | An opening animation state, progress or loop changed. Detail is `{ name, state, progress, loop }`. The event bubbles across the shadow boundary. |
|
|
437
|
+
| `pref-viewer-3d-turnable-changed` | `<pref-viewer>` | None | The semantic turnable state changed. Detail includes `state: { running, speed, direction }` and `timestamp`. |
|
|
330
438
|
|
|
331
439
|
### 2D Events
|
|
332
440
|
|
|
@@ -339,7 +447,7 @@ The component emits custom events so host applications can integrate loading ind
|
|
|
339
447
|
|
|
340
448
|
### Internal Listeners
|
|
341
449
|
|
|
342
|
-
`<pref-viewer>` listens to `pref-viewer-2d-viewport-changed` from `<pref-viewer-2d>`, `pref-viewer-3d-loaded` and `pref-viewer-3d-error` from `<pref-viewer-3d>`, and `pref-viewer-3d-render-settings-change-requested` from `<pref-viewer-menu-3d>`. `BabylonJSController` listens to `pref-viewer-3d-animation-changed` from the nearest `<pref-viewer-3d>` host.
|
|
450
|
+
`<pref-viewer>` listens to `pref-viewer-2d-viewport-changed` from `<pref-viewer-2d>`, `pref-viewer-3d-loaded` and `pref-viewer-3d-error` from `<pref-viewer-3d>`, and `pref-viewer-3d-render-settings-change-requested` from `<pref-viewer-menu-3d>`. Internal `pref-viewer-2d-element-click` and `pref-viewer-3d-element-click` events are intercepted and forwarded through the single public `pref-viewer-element-click` contract. `BabylonJSController` listens to `pref-viewer-3d-animation-changed` from the nearest `<pref-viewer-3d>` host. The root also conditionally subscribes to the internal, non-bubbling `view-cube-layout` event while the cube is active in 3D. It carries measured layout dimensions for the render menu and is not a new public root event.
|
|
343
451
|
|
|
344
452
|
Example:
|
|
345
453
|
|
|
@@ -389,6 +497,8 @@ viewer.setOptions({
|
|
|
389
497
|
|
|
390
498
|
Render settings are persisted in `localStorage` under the `pref-viewer/render-settings` key when changed through the runtime settings flow. The menu is always synchronized from the effective controller settings after changes from the menu, `setOptions`, or `loadConfig`.
|
|
391
499
|
|
|
500
|
+
The showroom daylight background uses a shared linear HDR color calibrated with exposure and ACES tone mapping for the current light studio gray target, `#d9d9d9`, in a scene-less view. Both the static look and time-of-day cycle use this same daylight value; the cycle retains its dark night endpoint. Scene geometry can cover the background. The internal `BabylonJSController.config.look.showroom.backgroundDay` value is not a public background-color option and must be recalibrated if tone mapping or exposure changes.
|
|
501
|
+
|
|
392
502
|
## Runtime styles
|
|
393
503
|
|
|
394
504
|
Runtime styles can be passed through the `styles` attribute, the `styles` property setter, `setStyles(styles)`, or `loadConfig({ styles })`.
|
|
@@ -403,6 +513,8 @@ viewer.setStyles({
|
|
|
403
513
|
});
|
|
404
514
|
```
|
|
405
515
|
|
|
516
|
+
The ViewCube uses this same primary color for its hover and active-orientation feedback. Changing `colorPrimary` through `styles` or `config` attributes, `setStyles`, or `loadConfig({ styles })` also updates animation-opening highlight color, including an opening currently highlighted, without reloading the model or changing whether highlighting is enabled. The render menu color control stays synchronized. Persisted primary styles are applied to highlights when the viewer connects. `options.render.highlightColor` can still set an explicit highlight color; a later primary-style update replaces it.
|
|
517
|
+
|
|
406
518
|
Only `colorPrimary` is currently supported. The value updates `PrefViewerStyleVariables.colorPrimary`, is exposed on the host as `--pref-viewer-color-primary`, and is persisted in `localStorage` under `pref-viewer/styles`. Persisted styles are restored when a new `<pref-viewer>` instance is created and connected.
|
|
407
519
|
|
|
408
520
|
## Localization
|
|
@@ -440,7 +552,53 @@ Start the local playground:
|
|
|
440
552
|
npm start
|
|
441
553
|
```
|
|
442
554
|
|
|
443
|
-
The playground is available from `playground/index.html` and serves the project through `http-server` after bundling the local source.
|
|
555
|
+
The playground is available from `playground/index.html` at `http://localhost:8080/` and serves the project through `http-server` after bundling the local source. Use it for manual ViewCube, wheel-zoom and modal checks with representative model/environment assets. The separate ViewCube test HTML/script and its generated fixture bundle have been removed; automated tests remain.
|
|
556
|
+
|
|
557
|
+
### 3D selection implementation and maintenance
|
|
558
|
+
|
|
559
|
+
`src/babylonjs-selection-controller.js` encapsulates semantic picking helpers as public static methods of `BabylonJSSelectionController`: `normalizeMeshId`, `isPrimaryClickGesture`, `isPickableModelMesh`, `getModelMeshSet` and `collectPickedMeshIds`. The Babylon controller and tests call these methods through the class. Instance methods retain selection state and manage reversible overlays. These internal implementation helpers do not change the `<pref-viewer>` public API.
|
|
560
|
+
|
|
561
|
+
JavaScript classes, methods and named helper functions outside tests are documented with JSDoc, including parameters and return values where applicable. This also covers existing helpers and webpack configuration callbacks. Keep those comments and this README aligned with the current implementation. Edited code files use Windows CRLF line endings.
|
|
562
|
+
|
|
563
|
+
Shared calculations live in the static `RenderMath` and `MaterialUtils` classes. `I18n` owns the shared locale and translation subscribers, with private lookup helpers and public static methods. These modules export their classes only; viewer components and tests call their static methods directly. Former named function/constant exports and utility reexports from `BabylonJSController` have been removed. These helpers are internal modules, not additional package-root API exports. `BabylonJSController` encapsulates resize application, metadata path parsing and debug serialization as private methods, passing scene mesh snapshots explicitly to material diagnostics.
|
|
564
|
+
|
|
565
|
+
`BabylonJSAnimationOverlayController` owns one opening-highlight group: its utility scene, cached overlay bases, transformed presentation meshes and observers. It creates clones directly in the utility scene, hides geometry bases used only by instances, and releases only its own resources through an idempotent `dispose()`. These animation overlays remain separate from semantic 3D selection overlays. The temporary legacy storage module and opening-control callbacks retain their existing structure.
|
|
566
|
+
|
|
567
|
+
Focused regressions for this work live in `tests/babylonjs-selection-controller.test.js`, `tests/babylonjs-animation-overlay-controller.test.js`, `tests/encapsulated-utilities.test.js`, `tests/pref-viewer-menu-3d.test.js`, `tests/babylonjs-controller-wheel.test.js` and `tests/pref-viewer.test.js`. They cover picking and repeated model parts, reversible overlays, root state synchronization, animation resource ownership, localization, menu playback and diagnostics. Use `npm test -- --run --maxWorkers=2` for a bounded full-suite run; validate both builds and actual rendering when changing overlays.
|
|
568
|
+
|
|
569
|
+
### Camera implementation and maintenance
|
|
570
|
+
|
|
571
|
+
- `src/babylonjs-view-cube-controller.js`: dedicated `BabylonJSViewCubeController` for SVG regions, orientation/projection feedback, transitions and cleanup. It delegates model bounds and destination-fit calculations to BabylonJSCameraFraming. Cube-only helpers and constants remain private; region generation remains a public static utility. Its constructor JSDoc documents the host object and callbacks.
|
|
572
|
+
- `src/babylonjs-camera-framing.js`: stateless `BabylonJSCameraFraming` with static `getModelBounds`, `calculateCameraFit` and `isCameraSupported` utilities. Bounds use only visible, enabled geometry in the supplied model container; fit calculations preserve camera state and projection, use the shared camera margin and return a destination for the caller to apply. Default-camera automatic fitting uses these same utilities, preserving the current orientation and applying the destination immediately.
|
|
573
|
+
- `src/babylonjs-camera-zoom-controller.js`: independent `BabylonJSCameraZoomController` for model-only dynamic radius protection and authored-floor restoration. It works without cube resources.
|
|
574
|
+
- `src/babylonjs-controller.js`: owns the scene, active camera, containers, native wheel settings and render scheduler; coordinates both controllers.
|
|
575
|
+
- `src/styles.js`: exports `PrefViewerViewCubeStyles`, imported by the cube controller. The SVG stylesheet uses multiline CSS and scoped variables for the main visual properties; no JSDoc is required for this styles constant.
|
|
576
|
+
- `src/pref-viewer-3d.js` and `src/pref-viewer.js`: bridge lazy visibility, queued config, localization and measured menu layout. The root stores the layout callback in `#handlers` and removes its listener during teardown. Shared styles place dialogs above viewer overlays.
|
|
577
|
+
|
|
578
|
+
ViewCube class members are ordered as properties, constructor, static methods, private methods and public methods/getters. New classes and new/modified methods carry JSDoc describing their parameters, return values and current lifecycle/side effects. JavaScript `if` and `for` bodies always use braces, including single-statement bodies. Focused tests live in `tests/babylonjs-camera-framing.test.js`, `tests/babylonjs-view-cube-controller.test.js`, `tests/babylonjs-camera-zoom-controller.test.js`, `tests/babylonjs-controller-wheel.test.js` and `tests/pref-viewer.test.js`. Browser checks in the existing playground remain necessary for visual projection, hover regions and modal stacking.
|
|
579
|
+
|
|
580
|
+
### ViewCube SVG style maintenance
|
|
581
|
+
|
|
582
|
+
The following variables are defined in the root rule of `PrefViewerViewCubeStyles` in `src/styles.js`. They centralize defaults for future stylesheet changes; they are not additional properties supported by `setStyles` or `config.styles`.
|
|
583
|
+
|
|
584
|
+
| CSS variable | Default |
|
|
585
|
+
| --- | --- |
|
|
586
|
+
| `--cube-fill-color` | `#eef0f3` |
|
|
587
|
+
| `--cube-border-color` | `#78838f` |
|
|
588
|
+
| `--cube-border-width` | `1.0px` in the SVG coordinate system; also used by the hover outline |
|
|
589
|
+
| `--cube-hover-color` | Shared primary color; used by the fill and opaque outline |
|
|
590
|
+
| `--cube-hover-opacity` | `0.50` fill opacity |
|
|
591
|
+
| `--cube-active-color` | Shared primary color |
|
|
592
|
+
| `--cube-active-opacity` | `0.15` fill opacity |
|
|
593
|
+
| `--cube-text-font-family` | `Arial, sans-serif` |
|
|
594
|
+
| `--cube-text-font-size` | `9px` |
|
|
595
|
+
| `--cube-text-color` | `#29323b` |
|
|
596
|
+
| `--cube-text-font-weight` | `600` |
|
|
597
|
+
| `--cube-text-letter-spacing` | `1.0px` |
|
|
598
|
+
|
|
599
|
+
Both feedback colors default to `var(--pref-viewer-color-primary, ...)`, preserving supported runtime primary-color updates. Text size and spacing apply in the SVG coordinate system and scale with its viewport. Active-view borders remain transparent; hover borders remain fully opaque regardless of fill opacity. Geometry and input behavior remain owned by the controller.
|
|
600
|
+
|
|
601
|
+
ViewCube listener registration and hover updates are private instance methods. Surface creation, region construction, hit-region lookup, extent interpolation and SVG projection are private static helpers; per-update projection values and interpolation progress are passed explicitly. The render menu also encapsulates hue-channel calculation and lighting playback as private methods. Playback uses one callback bound to the menu instance and cancels its scheduled frame on stop or disconnection.
|
|
444
602
|
|
|
445
603
|
## Package scripts
|
|
446
604
|
|
|
@@ -477,11 +635,15 @@ export function ProductViewer() {
|
|
|
477
635
|
- The component is a Web Component, so it can be used from plain HTML, React, Vue, Angular, or any framework that supports custom elements.
|
|
478
636
|
- Give the element an explicit width and height. Babylon.js needs a measurable canvas container.
|
|
479
637
|
- Use object-based JavaScript APIs for complex payloads. Attributes must be valid JSON strings.
|
|
638
|
+
- Root load/configuration methods enqueue work and return `void` on accepted input, rather than a completion promise. Use the loading/loaded/error events for completion. Malformed JSON strings can throw synchronously.
|
|
639
|
+
- `loadConfig({ forceReload: true })` bypasses 3D asset cache metadata checks. A supplied `timestamp` that differs from the last supplied timestamp also forces the 3D reload; repeating the same value does not. These controls apply to the 3D configuration flow, not SVG cache invalidation.
|
|
480
640
|
- Remote assets must be reachable by the browser and must satisfy the expected CORS policy.
|
|
481
641
|
- glTF files with external resources are resolved and cached by the viewer when possible.
|
|
482
642
|
- Avoid repeatedly replacing large assets if only visibility, camera, lighting, render, or material options changed.
|
|
483
643
|
- Use `pref-viewer-initialized` only as a component-readiness signal. If a host generates model data asynchronously, wait for the host data-ready signal before calling `loadConfig()`.
|
|
484
644
|
- For PrefWeb integration, consume the webpack UMD build (`bundle-umd.js`) and prefer development UMD builds while debugging source maps.
|
|
645
|
+
- Babylon's initial PBR BRDF preparation keeps demand rendering awake and waits at most two seconds, so a missing readiness signal does not indefinitely block a queued model load. `getDebugInfo()` provides camera, rendering and asset-readiness diagnostics.
|
|
646
|
+
- Release versioning and npm publication run in Bitbucket Pipelines: `develop` publishes the `beta` tag and `main` publishes `latest`. Do not run the release scripts locally.
|
|
485
647
|
|
|
486
648
|
## License
|
|
487
649
|
|
package/index.d.ts
CHANGED
|
@@ -29,6 +29,12 @@ export interface PrefViewerAsset {
|
|
|
29
29
|
|
|
30
30
|
/** Payload for `loadConfig`. Pass the object; a JSON string is also accepted. */
|
|
31
31
|
export interface PrefViewerConfig {
|
|
32
|
+
/** Sets show-render-menu when supplied; omission preserves the current attribute. */
|
|
33
|
+
showRenderMenu?: boolean;
|
|
34
|
+
/** Sets show-view-cube when supplied; omission preserves the current attribute. */
|
|
35
|
+
showViewCube?: boolean;
|
|
36
|
+
culture?: string;
|
|
37
|
+
styles?: Record<string, unknown> | string;
|
|
32
38
|
model?: PrefViewerAsset;
|
|
33
39
|
scene?: PrefViewerAsset;
|
|
34
40
|
materials?: PrefViewerAsset;
|
|
@@ -88,6 +94,7 @@ export interface PrefViewerSelectionInput {
|
|
|
88
94
|
}
|
|
89
95
|
|
|
90
96
|
export interface PrefViewerElementClickDetail {
|
|
97
|
+
/** Semantic 2D ids, or distance-ordered, deduplicated 3D mesh ids with final _primitiveN removed. */
|
|
91
98
|
ids: string[];
|
|
92
99
|
}
|
|
93
100
|
|
|
@@ -250,6 +257,7 @@ export declare class PrefViewer extends HTMLElement {
|
|
|
250
257
|
/** Convenience wrapper for `setOptions({ ibl })`. False when the payload is unusable. */
|
|
251
258
|
setIBL(ibl: Record<string, unknown> | string): boolean | void;
|
|
252
259
|
/** Runtime style overrides; currently `{ colorPrimary }`. False when unusable. */
|
|
260
|
+
/** Updates primary UI and animation-opening highlight color, including an active highlight. */
|
|
253
261
|
setStyles(styles: Record<string, unknown> | string): boolean | void;
|
|
254
262
|
/** 3D runtime diagnostics, or null before the 3D component exists. */
|
|
255
263
|
getDebugInfo(): Record<string, unknown> | null;
|
|
@@ -264,7 +272,7 @@ export declare class PrefViewer extends HTMLElement {
|
|
|
264
272
|
zoomIn(): void;
|
|
265
273
|
zoomOut(): void;
|
|
266
274
|
|
|
267
|
-
// ── Semantic selection ──
|
|
275
|
+
// ── Semantic selection synchronized to both 2D and 3D renderers ──
|
|
268
276
|
setSelectableElements(elements?: PrefViewerElementDescriptor[]): PrefViewerSelectionState;
|
|
269
277
|
setSelectedElements(elements?: Array<string | PrefViewerSelectionDescriptor>): PrefViewerSelectionState;
|
|
270
278
|
applySelectionState(state?: PrefViewerSelectionInput): PrefViewerSelectionState;
|
|
@@ -350,6 +358,8 @@ export interface PrefViewerAttributes {
|
|
|
350
358
|
"show-model"?: boolean | "true" | "false";
|
|
351
359
|
"show-scene"?: boolean | "true" | "false";
|
|
352
360
|
"show-render-menu"?: boolean | "true" | "false";
|
|
361
|
+
/** Enable the orientation cube in 3D (default: true); false releases its overlay resources. */
|
|
362
|
+
"show-view-cube"?: boolean | "true" | "false";
|
|
353
363
|
}
|
|
354
364
|
|
|
355
365
|
/** <pref-viewer mode="3d" style="display:block; width:100%; height:600px" /> */
|
package/package.json
CHANGED