@preference-sl/pref-viewer 2.17.0-beta.4 → 2.17.0-beta.6
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 +40 -34
- package/package.json +1 -1
- package/src/babylonjs-view-cube-controller.js +570 -213
- package/src/babylonjs-view-cube-renderer.js +546 -0
- package/src/styles.js +0 -88
- package/src/view-cube-orbit-math.js +199 -0
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
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
|
|
9
|
+
- Default-enabled, draggable 3D ViewCube with 26 selectable orientations: click a face, edge or corner for a smooth, model-fitted view, or drag the cube to orbit the model. It follows the camera in real time, including its projection: the cube is orthographic for an orthographic viewer camera and perspective, with the same vertical FOV, for a perspective one.
|
|
10
10
|
- Model-only, geometry-dependent orbital zoom protection with near-plane sampling, independent of ViewCube visibility.
|
|
11
11
|
- Viewer dialogs above the render menu and ViewCube, with background pointer-input protection.
|
|
12
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.
|
|
@@ -239,13 +239,13 @@ Storage descriptors are passed through `model.storage`, `scene.storage`, `materi
|
|
|
239
239
|
|
|
240
240
|
## ViewCube
|
|
241
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.
|
|
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, or drag the cube to orbit the model (see [Dragging the cube](#dragging-the-cube)). 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
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.
|
|
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: the cube keeps following the camera, but hover, click and drag do nothing. Selection does nothing when usable model bounds, a supported camera or positive viewport dimensions are unavailable.
|
|
245
245
|
|
|
246
246
|
### Orientation and feedback
|
|
247
247
|
|
|
248
|
-
The inner view is the principal model view. English ViewCube labels
|
|
248
|
+
The inner view is the principal model view. English ViewCube face labels read `Inner` and `Outer`; Spanish labels are `Interior` and `Exterior`. Labels are drawn on the cube faces exactly as translated and follow culture changes. Face names use this world-axis convention:
|
|
249
249
|
|
|
250
250
|
| Direction | World axis | Reference |
|
|
251
251
|
| --- | --- | --- |
|
|
@@ -256,17 +256,37 @@ The inner view is the principal model view. English ViewCube labels use `Inner`
|
|
|
256
256
|
| Top / Superior | +Y | Looks down toward the model. |
|
|
257
257
|
| Bottom / Inferior | -Y | Looks up toward the model. |
|
|
258
258
|
|
|
259
|
-
The cube
|
|
259
|
+
The cube is drawn on its own small canvas, with a separate Babylon.js engine and virtual scene, in a PrefSketch-style look: six square labelled faces, thin bars on the twelve edges and small spheres on the eight corners; there are no chamfers. Every part of an edge or corner selects the same orientation. The cube follows the projection and FOV of the viewer camera: it is drawn with an orthographic camera when the viewer camera is orthographic, and with a perspective camera using the same vertical FOV when it is perspective (the cube keeps the same on-screen size either way, and an FOV change redraws it). Apart from that it follows only the direction of the camera, independent of model zoom. It lives in its own scene, so it adds nothing to the model scene, to model picking or to exports.
|
|
260
260
|
|
|
261
|
-
Hover
|
|
261
|
+
Hover paints the region under the pointer with `styles.colorPrimary` (`--pref-viewer-color-primary`). Any CSS colour syntax is accepted: hex, `rgb()`, `hsl()` or a named colour. The angularly nearest of the 26 views is shown as the current region, painted with the primary colour mixed 25% into the face fill; hover takes precedence until the pointer leaves. A primary-colour change is picked up within 250 ms, or immediately after a resize, a culture change or re-enabling the cube. The rest of the palette (face fill, borders, label text, edge bars and corner spheres) is a fixed light palette and is not configurable. There are no region tooltips.
|
|
262
262
|
|
|
263
|
-
|
|
263
|
+
Edges and corners are thin, so selection has a tolerance: a front-facing edge or corner whose centre is within 8 CSS pixels of the pointer is picked ahead of the face under it, corners before edges. On cubes smaller than 96 px the tolerance shrinks to a twelfth of the cube side so face centres stay selectable. Hover and click use the same pick, so the highlighted region is the one a click selects.
|
|
264
|
+
|
|
265
|
+
### Dragging the cube
|
|
266
|
+
|
|
267
|
+
Press on the cube and move the pointer more than 3 CSS pixels to drag it. Dragging orbits the viewer camera around its target at 0.012 radians per pixel, in the same direction as dragging the model with the mouse on the main canvas: the model follows the pointer, and the cube follows the camera. Elevation stops just short of the poles and the camera keeps world +Y as up. Dragging works with ArcRotateCamera, FreeCamera and UniversalCamera; authored alpha/beta limits of an ArcRotateCamera keep being enforced by Babylon on the next frame.
|
|
268
|
+
|
|
269
|
+
The whole cube canvas is the drag surface, including its transparent corners, and a press on it never starts a gesture on the main camera. A press that moves 3 pixels or less is a click. A drag never selects a region, even if it ends where it started, and it cancels a running click transition. Locked cameras, active XR tracked cameras and unsupported camera classes keep the cube display-only. The cursor is `grab` over the cube, `pointer` over a selectable region and `grabbing` while dragging.
|
|
270
|
+
|
|
271
|
+
### ViewCube accessibility
|
|
272
|
+
|
|
273
|
+
The cube canvas has `role="img"` and one localized accessible name (`View orientation` in English, `Orientación de vista` in Spanish). Individual regions have no accessible names. The cube is pointer-driven: the canvas is not a tab stop, does not take keyboard focus after pointer use and draws no focus ring. Keyboard operation of the cube is not provided.
|
|
264
274
|
|
|
265
275
|
### ViewCube layout and lifecycle
|
|
266
276
|
|
|
267
|
-
The cube
|
|
277
|
+
The cube is a square at the top right of the 3D view: **top 0px**, **right 0px** unless the host sets the inset below. Its side, in CSS pixels, is `min(clamp(22% of min(viewer width, viewer height), 96, 180), 40% of min(viewer width, viewer height))`, rounded to an integer. For example, a viewer whose smaller side is 300 px gets a 96 px cube, 600 px gets 132 px, 900 px or more gets the 180 px maximum, and a 200 px viewer gets 80 px (the 40% cap). A viewer without a usable size shows no cube.
|
|
278
|
+
|
|
279
|
+
Hosts can inset the cube from the right edge with the CSS custom property `--pref-viewer-view-cube-inset-right` (default `0px`). Set it on `<pref-viewer>` or on any ancestor; it inherits into the viewer:
|
|
268
280
|
|
|
269
|
-
|
|
281
|
+
```css
|
|
282
|
+
pref-viewer {
|
|
283
|
+
--pref-viewer-view-cube-inset-right: 24px;
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
When both overlays are enabled, the render-menu top equals the **measured side of the cube canvas** and updates when that size changes; the menu offset is measured, not a fixed 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. The internal `view-cube-layout` event carries `{ offset, height }` in CSS pixels: `offset` is the measured cube side (0 when there is no cube) and `height` is the scene viewport height.
|
|
288
|
+
|
|
289
|
+
Cube resources are created only while enabled in a visible 3D view. Disabling it or switching to 2D releases its overlay, listeners, observers, pending transition, engine and WebGL context. An active cube uses a second WebGL context next to the viewer's; it is repainted after a context restore and after a device pixel ratio change. Reactivation uses the current camera without reloading assets. Layout subscriptions connect before activation and are removed when inactive or disconnected.
|
|
270
290
|
|
|
271
291
|
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
292
|
|
|
@@ -449,7 +469,7 @@ The component emits custom events so host applications can integrate loading ind
|
|
|
449
469
|
|
|
450
470
|
### Internal Listeners
|
|
451
471
|
|
|
452
|
-
`<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
|
|
472
|
+
`<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 `{ offset, height }` in CSS pixels, where `offset` is the measured side of the square cube and `height` is the scene viewport height, so the render menu can sit below the cube; it is not a new public root event.
|
|
453
473
|
|
|
454
474
|
Example:
|
|
455
475
|
|
|
@@ -532,7 +552,7 @@ viewer.setStyles({
|
|
|
532
552
|
});
|
|
533
553
|
```
|
|
534
554
|
|
|
535
|
-
The ViewCube uses this same primary color for its hover and
|
|
555
|
+
The ViewCube uses this same primary color for its hover feedback and, mixed into the face fill, for its current-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.
|
|
536
556
|
|
|
537
557
|
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.
|
|
538
558
|
|
|
@@ -587,38 +607,24 @@ Focused regressions for this work live in `tests/babylonjs-selection-controller.
|
|
|
587
607
|
|
|
588
608
|
### Camera implementation and maintenance
|
|
589
609
|
|
|
590
|
-
- `src/babylonjs-view-cube-controller.js`: dedicated `BabylonJSViewCubeController` for
|
|
610
|
+
- `src/babylonjs-view-cube-controller.js`: dedicated `BabylonJSViewCubeController` for pointer interaction (hover, click, drag), camera reading and writing, click transitions, size, colours and cleanup. It delegates model bounds and destination-fit calculations to BabylonJSCameraFraming and the drawing to the renderer. Cube-only helpers and constants remain private; region generation remains a public static utility. Its constructor JSDoc documents the host object and callbacks.
|
|
611
|
+
- `src/babylonjs-view-cube-renderer.js`: `BabylonJSViewCubeRenderer`, the cube's own Babylon.js engine and virtual scene with its 26 region meshes, picking and painting. It owns no render loop and no interaction logic; the controller renders it from the host loop.
|
|
612
|
+
- `src/view-cube-orbit-math.js`: pure helpers for orbit angles, the 26 view directions, drag deltas and the cube size rule, without Babylon or DOM dependencies.
|
|
591
613
|
- `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.
|
|
592
614
|
- `src/babylonjs-camera-zoom-controller.js`: independent `BabylonJSCameraZoomController` for model-only dynamic radius protection and authored-floor restoration. It works without cube resources.
|
|
593
615
|
- `src/babylonjs-camera-wheel-motion.js`: internal boundary for inspecting and cancelling the coupled native wheel inertia during rendering, geometry protection and camera lifecycle changes. Babylon computes the zoom; this boundary isolates version-sensitive native input state.
|
|
594
616
|
- `src/babylonjs-controller.js`: owns the scene, active camera, containers, native wheel settings and render scheduler; coordinates both controllers.
|
|
595
|
-
- `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.
|
|
596
617
|
- `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.
|
|
597
618
|
|
|
598
|
-
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
|
|
619
|
+
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-view-cube-renderer.test.js`, `tests/view-cube-orbit-math.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 cube rendering, hover regions, dragging, focus and cursor behavior, and modal stacking, because the tests run on a null engine.
|
|
599
620
|
|
|
600
|
-
### ViewCube
|
|
621
|
+
### ViewCube rendering maintenance
|
|
601
622
|
|
|
602
|
-
The
|
|
623
|
+
The cube's visual constants (the light palette, face label font and size, edge and corner sizes, lighting) are internal to `src/babylonjs-view-cube-renderer.js`. They are not properties supported by `setStyles` or `config.styles`; only `colorPrimary` reaches the cube, through the hover and current colours. The renderer module also contains a dark palette constant; it is not exported from the package and is not part of the public API.
|
|
603
624
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
| `--cube-border-color` | `#78838f` |
|
|
608
|
-
| `--cube-border-width` | `1.0px` in the SVG coordinate system; also used by the hover outline |
|
|
609
|
-
| `--cube-hover-color` | Shared primary color; used by the fill and opaque outline |
|
|
610
|
-
| `--cube-hover-opacity` | `0.50` fill opacity |
|
|
611
|
-
| `--cube-active-color` | Shared primary color |
|
|
612
|
-
| `--cube-active-opacity` | `0.15` fill opacity |
|
|
613
|
-
| `--cube-text-font-family` | `Arial, sans-serif` |
|
|
614
|
-
| `--cube-text-font-size` | `9px` |
|
|
615
|
-
| `--cube-text-color` | `#29323b` |
|
|
616
|
-
| `--cube-text-font-weight` | `600` |
|
|
617
|
-
| `--cube-text-letter-spacing` | `1.0px` |
|
|
618
|
-
|
|
619
|
-
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.
|
|
620
|
-
|
|
621
|
-
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.
|
|
625
|
+
Keep these rules when changing the renderer: the cube scene stays virtual and its engine stays out of `EngineStore.Instances` while it is alive (otherwise the viewer can create resources on the cube's WebGL context); Babylon input stays detached from the cube canvas (otherwise the cursor and keyboard focus are disturbed); and the engine keeps `loseContextOnDispose` so its WebGL context is released.
|
|
626
|
+
|
|
627
|
+
ViewCube listener registration, drag handling and hover updates are private instance methods. Region construction, colour parsing and extent interpolation are private static helpers; interpolation progress is 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.
|
|
622
628
|
|
|
623
629
|
## Package scripts
|
|
624
630
|
|
package/package.json
CHANGED