@displayxr/inline3d 1.40.2 → 1.42.0

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/CHANGELOG.md CHANGED
@@ -5,6 +5,67 @@ entry points (`.`, `./three`) are frozen for 1.x, while the **scene subpaths** (
5
5
  `./splat`, `./model`) are a preview tier whose options may change in any release. Entries below say
6
6
  which tier they touch, because that is what tells you whether an upgrade can move your pixels.
7
7
 
8
+ ## 1.42.0 — 2026-10-11
9
+
10
+ Touches the **core** `./three` (`DepthCursor`) and the **preview tier** (`./model`, `./splat`,
11
+ `./call`, `./camera`: `cursor: 'depth'`), plus `./cursor-depth` (`CURSOR_DEFAULT_TUNING`). **One
12
+ deliberate default change.**
13
+
14
+ - **Depth-cursor margin 0.03 → 0.015 eye baselines (default change),** about 1 mm of crossed
15
+ on-screen disparity at a 63–65 mm IPD instead of about 2 mm. Runtime ADR-046 Amendment 2 A.2 was
16
+ halved after a Windows panel check, because 0.03 floated visibly off the content. The runtime
17
+ makes the same change (`U_CURSOR_DEPTH_DEFAULT_MARGIN`), so web and native cursors still agree.
18
+ Every depth cursor takes the default from `CURSOR_DEFAULT_TUNING`: `DepthCursor`,
19
+ `cursor: 'depth'` on both engines, and the stereo-tile pair cursor. `margin` still overrides it:
20
+ pass `margin: 0.03` for the 1.41.0 look.
21
+
22
+ ## 1.41.0 — 2026-10-11
23
+
24
+ Touches the **core** `./three` (`DepthCursor`'s look and two defaults) and the **preview tier**
25
+ (`./model`, `./splat`, `./call`, `./camera`: `cursor: 'depth'`), plus `./cursor-depth` (additive
26
+ exports, one deprecation). **Two deliberate default changes**, per runtime ADR-046 Amendment 2
27
+ ("one cursor policy everywhere"): pages that use a depth cursor will see it sit further forward and
28
+ stop going behind the glass.
29
+
30
+ ### Depth cursor: one policy everywhere, and the arrow
31
+
32
+ - **Never behind the glass (default change).** The target is now `d < 0 ? max(d − margin, −0.6) : 0`:
33
+ the cursor only comes forward, to stay ahead of content that comes out of the screen. Over content
34
+ on or behind the glass it stays on the glass. The clamp is `[−0.6, 0]` (was `[−0.6, +0.6]`), and
35
+ `cursorTarget` never returns more than 0 whatever a tuning's `maxDisparity` says.
36
+ - **Margin 0.005 → 0.03 eye baselines (default change),** about 2 mm of on-screen disparity at a
37
+ 65 mm IPD: the value native apps and the runtime use. `margin` still overrides it.
38
+ - **On the plane it is the OS cursor.** Nothing is drawn unless the filtered disparity is at or past
39
+ −0.005 baselines (about 0.3 mm) and at least 0.5 panel px. Off the subject the sprite is now
40
+ **hidden**, not drawn at zero disparity, and the CSS cursor the page set is left alone (a cursor
41
+ the page changes while the sprite hides it is no longer overwritten on restore).
42
+ - **The arrow replaces the crosshair.** Every depth cursor (three, PlayCanvas, and the stereo-tile
43
+ cursor on `./call` / `./camera`) draws the same classic arrow: 12 × 19 px proportions, white with a
44
+ dark outline, its **tip** on the hotspot. Its default size is the OS cursor's, 19 CSS px of the
45
+ canvas, recomputed as the canvas resizes; `height` still overrides. `DepthCursor`'s `color`
46
+ default is now white. Mirroring a page's CSS cursor (`grab`, `url(…)`) into the sprite is future
47
+ work (ADR-046 Amendment 2 §C).
48
+ - **The stereo-tile cursor runs on the shared core.** `./call` / `./camera` take the target, margin,
49
+ threshold, filter and arrow from `./cursor-depth` instead of their own copies. Tile pixels convert
50
+ to eye baselines through the tile's `displayMetrics()` (the panel's pixel pitch, × the eye
51
+ separation; the CSS reference px until it answers).
52
+ - This subsumes 1.40.2's change (#164), which already moved the stereo
53
+ tile's margin from ~1–2 buffer px to **2 mm** of on-screen disparity (0.3 mm hand-back),
54
+ converted through `displayMetrics()`. That conversion is kept. The margin and threshold are now
55
+ the core's 0.03 / 0.005 **eye baselines** (≈1.9 mm / 0.3 mm at the default 63 mm eye
56
+ separation) rather than fixed millimetres, so stereo tiles, lifted tiles (runtime #1908) and
57
+ scenes share one rule.
58
+ - The comfort cap is now the core's −0.6 baselines (it was 8 % of the eye width).
59
+ - The pair cursor's own constants (`PAIR_CURSOR_MARGIN_M`, `PAIR_CURSOR_SHOW_M`,
60
+ `PAIR_CURSOR_SHOW_MIN_PX`, `PAIR_CURSOR_CLAMP`; an internal module, not public API) are gone.
61
+ - **`./cursor-depth`, additive:** `cursorShouldDraw`, `CURSOR_DRAW_THRESHOLD`, `CURSOR_DRAW_MIN_PX`,
62
+ `cursorDefaultHeight`, `CURSOR_ARROW_CSS_PX`, `CURSOR_ARROW`, `CURSOR_ARROW_FILL`,
63
+ `cursorArrowInset`, `cursorArrowMesh`, `cursorArrowFootprint`, `cursorFootprint` (typed now),
64
+ `CURSOR_FILL_COLOR` / `CURSOR_OUTLINE_COLOR`. `CursorPlacement` gains `solved` and
65
+ `pxPerBaseline`, and `active` now means "drawn". `CursorDepthPlacer.update` takes an optional
66
+ `canvasPx` for the pixel floor. **Deprecated:** `cursorCrosshairMesh` (unused by the SDK; removed
67
+ in 2.0).
68
+
8
69
  ## 1.40.2 — 2026-10-11
9
70
 
10
71
  Touches the **preview tier** (`./call`, `./camera`) only; no API change.
package/cursor-depth.d.ts CHANGED
@@ -1,6 +1,11 @@
1
1
  // Type definitions for @displayxr/inline3d/cursor-depth — the depth-aware cursor's placement
2
2
  // maths, dependency-free. A port of the runtime's u_cursor_depth (ADR-046, XR_DXR_cursor_depth):
3
3
  // same geometry, same defaults, same numbers. For three.js, ./three's `DepthCursor` wires it up.
4
+ //
5
+ // The policy (ADR-046 Amendment 2), the same for every DisplayXR depth cursor: never behind the
6
+ // glass (`cursorTarget`), margin 0.015 eye baselines, drawn only past the draw threshold
7
+ // (`cursorShouldDraw`; otherwise the OS cursor is the cursor), the classic arrow at the OS cursor's
8
+ // size (`CURSOR_ARROW`, `cursorArrowMesh`, `cursorDefaultHeight`).
4
9
 
5
10
  /** A view as an XRView carries it: column-major projection + transform matrices. */
6
11
  export interface CursorView {
@@ -12,16 +17,27 @@ export interface CursorView {
12
17
  export interface CursorTuning {
13
18
  margin: number;
14
19
  minDisparity: number;
20
+ /** Never above 0 in effect: `cursorTarget` never puts the cursor behind the glass, whatever this says. */
15
21
  maxDisparity: number;
16
22
  riseTau: number;
17
23
  sinkTau: number;
18
24
  stale: number;
19
25
  }
20
26
 
21
- /** The runtime's defaults: margin 0.005 (~1.5 mm at 60 cm), clamp ±0.6, rise 30 ms, sink 250 ms, stale 0.5 s. */
27
+ /** The defaults: margin 0.015 (~1 mm at a 63–65 mm IPD), clamp [−0.6, 0], rise 30 ms, sink 250 ms, stale 0.5 s. */
22
28
  export const CURSOR_DEFAULT_TUNING: Readonly<CursorTuning>;
23
- /** Default sprite height, as a fraction of the canvas height (0.03). */
29
+ /** Fallback sprite height, as a fraction of the canvas height (0.03), when the canvas's on-page size is unknown. */
24
30
  export const CURSOR_DEFAULT_HEIGHT: number;
31
+ /** The OS arrow's height in CSS px (19): the drawn arrow's default size. */
32
+ export const CURSOR_ARROW_CSS_PX: number;
33
+ /** The default sprite height for a canvas `cssHeight` CSS px tall: 19 CSS px of it (capped at 0.5); the fallback when unknown. */
34
+ export function cursorDefaultHeight(cssHeight: number): number;
35
+ /** The draw threshold, eye-baseline units (−0.005): drawn only at or below it. */
36
+ export const CURSOR_DRAW_THRESHOLD: number;
37
+ /** …and only at ≥ this many panel pixels of on-screen disparity (0.5), where pixels are knowable. */
38
+ export const CURSOR_DRAW_MIN_PX: number;
39
+ /** Draw at this filtered disparity? `pxPerBaseline`: panel px of on-screen disparity per baseline unit, when known. */
40
+ export function cursorShouldDraw(disparity: number, pxPerBaseline?: number | null): boolean;
25
41
 
26
42
  /** The cursor's line of sight for one frame. */
27
43
  export interface CursorGeometry {
@@ -40,7 +56,7 @@ export function cursorViewRay(view: CursorView, u: number, v: number): { origin:
40
56
  export function solveCursorGeometry(viewA: CursorView, viewB: CursorView, u: number, v: number): CursorGeometry | null;
41
57
  /** A point's disparity; null when it is not in front of the eye. */
42
58
  export function cursorPointDisparity(g: CursorGeometry, point: ArrayLike<number>): number | null;
43
- /** Content minus the margin, clamped; 0 when nothing is under the cursor. */
59
+ /** The policy: `d < 0 ? max(d − margin, minDisparity) : 0` — never behind the glass; 0 with nothing under the cursor. */
44
60
  export function cursorTarget(tuning: CursorTuning, hasContent: boolean, contentDisparity: number): number;
45
61
  /** Advance a filter (`{}` to start; the first step snaps) toward `target` at `nowSec`. */
46
62
  export function cursorFilterStep(filter: object, tuning: CursorTuning, target: number, nowSec: number): number;
@@ -61,9 +77,9 @@ export const CURSOR_ANCHOR_MODES: readonly CursorAnchorMode[];
61
77
 
62
78
  /** `cursor: { … }` on ./model and ./splat (an object also means `'depth'`). */
63
79
  export interface CursorOptions {
64
- /** Sprite height as a fraction of the canvas height (default 0.03). */
80
+ /** Sprite height as a fraction of the canvas height (default: the OS arrow's 19 CSS px of the canvas). */
65
81
  height?: number;
66
- /** How far in front of the content it floats, in eye-baseline units (default 0.005). */
82
+ /** How far in front of out-of-screen content it floats, in eye-baseline units (default 0.015). */
67
83
  margin?: number;
68
84
  /** Default `'hybrid'`. */
69
85
  anchor?: CursorAnchorMode;
@@ -74,17 +90,40 @@ export const CURSOR_OPTION_KEYS: readonly (keyof CursorOptions)[];
74
90
  /** `cursor` as ./model and ./splat accept it: null = off; throws on a bad value. */
75
91
  export function resolveCursorOption(opt: unknown, who?: string): CursorOptions | null;
76
92
 
77
- /** The crosshair as filled, outlined strokes in its own XY plane (unit = sprite height), non-indexed triangles. */
93
+ /** A ring of 9 canvas-normalised points: (u, v) and 8 around it at 0.75 × height. */
94
+ export function cursorFootprint(u: number, v: number, height: number, aspect: number): Array<[number, number]>;
95
+ /** What the arrow covers: its tip (the hotspot) first, then a ring around its body (down-right of the tip). */
96
+ export function cursorArrowFootprint(u: number, v: number, height: number, aspect: number): Array<[number, number]>;
97
+ /** The classic arrow's outline polygon (12 × 19 px normalised): unit = its height, x right, y DOWN, the tip = the hotspot at (0, 0). */
98
+ export const CURSOR_ARROW: ReadonlyArray<readonly [number, number]>;
99
+ /** Its white fill: the outline inset by one px of 19. */
100
+ export const CURSOR_ARROW_FILL: ReadonlyArray<readonly [number, number]>;
101
+ /** CURSOR_ARROW (or `poly`) inset by `d`, in units of its height. */
102
+ export function cursorArrowInset(d: number, poly?: ReadonlyArray<readonly [number, number]>): Array<[number, number]>;
103
+ /** White, and the dark outline (RGBA, 0..1). */
104
+ export const CURSOR_FILL_COLOR: readonly number[];
105
+ export const CURSOR_OUTLINE_COLOR: readonly number[];
106
+ /** The arrow as a 3D mesh in its XY plane (y UP, tip at the origin, unit = sprite height): outline then fill, non-indexed triangles. */
107
+ export function cursorArrowMesh(
108
+ fill?: readonly number[],
109
+ outline?: readonly number[],
110
+ ): { positions: Float32Array; colors: Float32Array; count: number };
111
+ /** @deprecated The crosshair is retired (ADR-046 Amendment 2); use `cursorArrowMesh`. Removed in 2.0. */
78
112
  export function cursorCrosshairMesh(
79
113
  fill?: readonly number[],
80
114
  outline?: readonly number[],
81
115
  ): { positions: Float32Array; colors: Float32Array; count: number };
82
- /** Column-major model matrix placing the unit crosshair for a placement. */
116
+ /** Column-major model matrix placing the unit sprite (tip at its origin) for a placement. */
83
117
  export function cursorModelMatrix(placement: CursorPlacement, out?: number[]): number[];
84
118
 
85
- /** One frame's placement. `position`/`basis` are in the views' space; null when inactive. */
119
+ /** One frame's placement. `position`/`basis` are in the views' space; null when not solved. */
86
120
  export interface CursorPlacement {
121
+ /** Draw the sprite this frame (the policy's draw threshold). False on the plane: the OS cursor is the cursor. */
87
122
  active: boolean;
123
+ /** The geometry solved (two views, pointer on the canvas): position/basis/height are filled even when not drawn. */
124
+ solved?: boolean;
125
+ /** Panel px of on-screen disparity per baseline unit, when `canvasPx` was given. */
126
+ pxPerBaseline?: number | null;
88
127
  position: number[] | null;
89
128
  basis: { x: number[]; y: number[]; z: number[] } | null;
90
129
  height: number;
@@ -105,8 +144,10 @@ export class CursorDepthPlacer {
105
144
  v: number;
106
145
  /** Nearest content point under the cursor FOOTPRINT, in the views' space; null = nothing. */
107
146
  nearestPoint: ArrayLike<number> | null;
108
- /** Sprite height as a fraction of the canvas height (default 0.03). */
147
+ /** Sprite height as a fraction of the canvas height (default 0.03; see `cursorDefaultHeight`). */
109
148
  cursorHeight?: number;
149
+ /** The canvas's height in panel pixels, for the draw threshold's pixel floor. */
150
+ canvasPx?: number;
110
151
  },
111
152
  nowSec: number,
112
153
  ): CursorPlacement;