@realitycollective/webxr-interactions 0.1.1-preview.2 → 0.1.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/CHANGELOG.md +29 -2
- package/README.md +17 -2
- package/dist/behaviours/behaviour.d.ts +17 -1
- package/dist/behaviours/behaviour.js +12 -2
- package/dist/behaviours/behaviour.js.map +1 -1
- package/dist/behaviours/grab.d.ts +1 -0
- package/dist/behaviours/grab.js +10 -4
- package/dist/behaviours/grab.js.map +1 -1
- package/dist/contract-cases.js +84 -0
- package/dist/contract-cases.js.map +1 -1
- package/dist/descriptor.d.ts +7 -1
- package/dist/descriptor.js.map +1 -1
- package/dist/gaze.d.ts +50 -0
- package/dist/gaze.js +162 -0
- package/dist/gaze.js.map +1 -1
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/math.d.ts +8 -0
- package/dist/math.js +17 -0
- package/dist/math.js.map +1 -1
- package/dist/memory-physics.d.ts +41 -0
- package/dist/memory-physics.js +168 -0
- package/dist/memory-physics.js.map +1 -0
- package/dist/memory-ports.d.ts +55 -0
- package/dist/memory-ports.js +128 -0
- package/dist/memory-ports.js.map +1 -0
- package/dist/near-pointer-contract-cases.d.ts +16 -0
- package/dist/near-pointer-contract-cases.js +283 -0
- package/dist/near-pointer-contract-cases.js.map +1 -0
- package/dist/near-pointer.d.ts +88 -0
- package/dist/near-pointer.js +114 -0
- package/dist/near-pointer.js.map +1 -0
- package/dist/physics-contract-cases.d.ts +11 -0
- package/dist/physics-contract-cases.js +229 -0
- package/dist/physics-contract-cases.js.map +1 -0
- package/dist/physics.d.ts +148 -0
- package/dist/physics.js +35 -0
- package/dist/physics.js.map +1 -0
- package/dist/ports.d.ts +15 -0
- package/dist/ports.js.map +1 -1
- package/dist/release-velocity.d.ts +58 -0
- package/dist/release-velocity.js +89 -0
- package/dist/release-velocity.js.map +1 -0
- package/dist/runtime.d.ts +129 -4
- package/dist/runtime.js +362 -40
- package/dist/runtime.js.map +1 -1
- package/dist/trace-synthetic.d.ts +12 -0
- package/dist/trace-synthetic.js +194 -0
- package/dist/trace-synthetic.js.map +1 -0
- package/dist/trace.d.ts +215 -0
- package/dist/trace.js +350 -0
- package/dist/trace.js.map +1 -0
- package/package.json +8 -4
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The release velocity of a throw - the core rule, restated from what Meta
|
|
3
|
+
* IWSDK 1.0.0 does with a held object so every platform's throw carries the
|
|
4
|
+
* same speed on the same input.
|
|
5
|
+
*
|
|
6
|
+
* On the web a thrown object is carried by IWSDK's grab system and Havok.
|
|
7
|
+
* While the object is `Grabbed`, `physics-system.js` sends the body a
|
|
8
|
+
* `SetTargetTransform` command every frame with the object's pose (the pose
|
|
9
|
+
* the grab pointer moved it to), so Havok drives the body kinematically to
|
|
10
|
+
* that target at each 1/60 s step (`PHYSICS_DEFAULTS.stepsPerSecond`). When
|
|
11
|
+
* the grab ends the tag is removed and nothing resets the body: it keeps the
|
|
12
|
+
* velocity the engine gave it to reach the last target, the displacement of
|
|
13
|
+
* the OBJECT over the last physics step divided by the step. So IWSDK's
|
|
14
|
+
* throw is:
|
|
15
|
+
*
|
|
16
|
+
* - the velocity of the held OBJECT, not of the grip: for a rigid carry the
|
|
17
|
+
* two agree, but the rule is stated on the object, which every platform
|
|
18
|
+
* has, rather than on a runtime-reported hand velocity, which most do not;
|
|
19
|
+
* - measured over the last physics step, `RELEASE_WINDOW_SECONDS` (1/60 s):
|
|
20
|
+
* at 72 Hz that is one frame, at 90 Hz two, at 120 Hz two;
|
|
21
|
+
* - angular velocity from the orientation delta over the same window.
|
|
22
|
+
*
|
|
23
|
+
* `ReleaseVelocityTracker` keeps the poses the carry wrote and their frame
|
|
24
|
+
* times; `release()` differentiates over the most recent frames that cover
|
|
25
|
+
* the window, or over the last frame when only one has been seen. A hold
|
|
26
|
+
* that never moved (or was released the frame it started) throws nothing:
|
|
27
|
+
* zeros, so the object rests where it was let go. A synthesised release
|
|
28
|
+
* (source lost, target unregistered, runtime disposed) never reads this: the
|
|
29
|
+
* grab behaviour hands the port zeros, as `grab.ts` says.
|
|
30
|
+
*/
|
|
31
|
+
import { velocityBetween } from "@realitycollective/webxr-input";
|
|
32
|
+
/** IWSDK's physics step, 1/60 s: the window a throw's velocity is measured over. */
|
|
33
|
+
export const RELEASE_WINDOW_SECONDS = 1 / 60;
|
|
34
|
+
/** How many past frames the tracker keeps: enough to cover the window at any refresh rate down to 30 Hz. */
|
|
35
|
+
const MAX_SAMPLES = 8;
|
|
36
|
+
export class ReleaseVelocityTracker {
|
|
37
|
+
window;
|
|
38
|
+
samples = [];
|
|
39
|
+
constructor(options = {}) {
|
|
40
|
+
const window = options.windowSeconds ?? RELEASE_WINDOW_SECONDS;
|
|
41
|
+
if (!Number.isFinite(window) || window <= 0) {
|
|
42
|
+
throw new Error(`[webxr-interactions] release window must be a positive number of seconds, got ${String(window)}`);
|
|
43
|
+
}
|
|
44
|
+
this.window = window;
|
|
45
|
+
}
|
|
46
|
+
/** Forget every pose: the hold starts afresh. */
|
|
47
|
+
reset() {
|
|
48
|
+
this.samples.length = 0;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Record the pose the carry wrote this frame, `dt` seconds after the last
|
|
52
|
+
* one. The tuples are copied, so the caller may reuse them.
|
|
53
|
+
*/
|
|
54
|
+
record(pose, dt) {
|
|
55
|
+
const copy = {
|
|
56
|
+
position: [pose.position[0], pose.position[1], pose.position[2]],
|
|
57
|
+
quaternion: [pose.quaternion[0], pose.quaternion[1], pose.quaternion[2], pose.quaternion[3]],
|
|
58
|
+
};
|
|
59
|
+
const step = this.samples.length === 0 || !Number.isFinite(dt) || dt < 0 ? 0 : dt;
|
|
60
|
+
this.samples.push({ pose: copy, dt: step });
|
|
61
|
+
if (this.samples.length > MAX_SAMPLES)
|
|
62
|
+
this.samples.shift();
|
|
63
|
+
}
|
|
64
|
+
/** How many poses are recorded. */
|
|
65
|
+
get size() {
|
|
66
|
+
return this.samples.length;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The velocity the object carries at release: linear m/s and angular
|
|
70
|
+
* rad/s (axis scaled), world space, over the most recent frames that span
|
|
71
|
+
* at least the window. Zeros with fewer than two poses, or no elapsed time.
|
|
72
|
+
*/
|
|
73
|
+
release() {
|
|
74
|
+
const n = this.samples.length;
|
|
75
|
+
if (n < 2)
|
|
76
|
+
return { linearVelocity: [0, 0, 0], angularVelocity: [0, 0, 0] };
|
|
77
|
+
let elapsed = 0;
|
|
78
|
+
let start = n - 1;
|
|
79
|
+
while (start > 0 && elapsed < this.window) {
|
|
80
|
+
elapsed += this.samples[start].dt;
|
|
81
|
+
start -= 1;
|
|
82
|
+
}
|
|
83
|
+
if (elapsed <= 0)
|
|
84
|
+
return { linearVelocity: [0, 0, 0], angularVelocity: [0, 0, 0] };
|
|
85
|
+
const velocity = velocityBetween(this.samples[start].pose, this.samples[n - 1].pose, elapsed);
|
|
86
|
+
return { linearVelocity: velocity.linear, angularVelocity: velocity.angular };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=release-velocity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"release-velocity.js","sourceRoot":"","sources":["../src/release-velocity.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The release velocity of a throw - the core rule, restated from what Meta\n * IWSDK 1.0.0 does with a held object so every platform's throw carries the\n * same speed on the same input.\n *\n * On the web a thrown object is carried by IWSDK's grab system and Havok.\n * While the object is `Grabbed`, `physics-system.js` sends the body a\n * `SetTargetTransform` command every frame with the object's pose (the pose\n * the grab pointer moved it to), so Havok drives the body kinematically to\n * that target at each 1/60 s step (`PHYSICS_DEFAULTS.stepsPerSecond`). When\n * the grab ends the tag is removed and nothing resets the body: it keeps the\n * velocity the engine gave it to reach the last target, the displacement of\n * the OBJECT over the last physics step divided by the step. So IWSDK's\n * throw is:\n *\n * - the velocity of the held OBJECT, not of the grip: for a rigid carry the\n * two agree, but the rule is stated on the object, which every platform\n * has, rather than on a runtime-reported hand velocity, which most do not;\n * - measured over the last physics step, `RELEASE_WINDOW_SECONDS` (1/60 s):\n * at 72 Hz that is one frame, at 90 Hz two, at 120 Hz two;\n * - angular velocity from the orientation delta over the same window.\n *\n * `ReleaseVelocityTracker` keeps the poses the carry wrote and their frame\n * times; `release()` differentiates over the most recent frames that cover\n * the window, or over the last frame when only one has been seen. A hold\n * that never moved (or was released the frame it started) throws nothing:\n * zeros, so the object rests where it was let go. A synthesised release\n * (source lost, target unregistered, runtime disposed) never reads this: the\n * grab behaviour hands the port zeros, as `grab.ts` says.\n */\nimport { velocityBetween, type PoseTuple } from \"@realitycollective/webxr-input\";\nimport type { HoldRelease } from \"./ports.js\";\n\n/** IWSDK's physics step, 1/60 s: the window a throw's velocity is measured over. */\nexport const RELEASE_WINDOW_SECONDS = 1 / 60;\n\n/** How many past frames the tracker keeps: enough to cover the window at any refresh rate down to 30 Hz. */\nconst MAX_SAMPLES = 8;\n\ninterface Sample {\n pose: PoseTuple;\n /** Seconds between this pose and the one before it (0 for the first). */\n dt: number;\n}\n\nexport interface ReleaseVelocityTrackerOptions {\n /** Seconds the release velocity is measured over. Default `RELEASE_WINDOW_SECONDS`. */\n windowSeconds?: number;\n}\n\nexport class ReleaseVelocityTracker {\n private readonly window: number;\n private readonly samples: Sample[] = [];\n\n constructor(options: ReleaseVelocityTrackerOptions = {}) {\n const window = options.windowSeconds ?? RELEASE_WINDOW_SECONDS;\n if (!Number.isFinite(window) || window <= 0) {\n throw new Error(`[webxr-interactions] release window must be a positive number of seconds, got ${String(window)}`);\n }\n this.window = window;\n }\n\n /** Forget every pose: the hold starts afresh. */\n reset(): void {\n this.samples.length = 0;\n }\n\n /**\n * Record the pose the carry wrote this frame, `dt` seconds after the last\n * one. The tuples are copied, so the caller may reuse them.\n */\n record(pose: PoseTuple, dt: number): void {\n const copy: PoseTuple = {\n position: [pose.position[0], pose.position[1], pose.position[2]],\n quaternion: [pose.quaternion[0], pose.quaternion[1], pose.quaternion[2], pose.quaternion[3]],\n };\n const step = this.samples.length === 0 || !Number.isFinite(dt) || dt < 0 ? 0 : dt;\n this.samples.push({ pose: copy, dt: step });\n if (this.samples.length > MAX_SAMPLES) this.samples.shift();\n }\n\n /** How many poses are recorded. */\n get size(): number {\n return this.samples.length;\n }\n\n /**\n * The velocity the object carries at release: linear m/s and angular\n * rad/s (axis scaled), world space, over the most recent frames that span\n * at least the window. Zeros with fewer than two poses, or no elapsed time.\n */\n release(): HoldRelease {\n const n = this.samples.length;\n if (n < 2) return { linearVelocity: [0, 0, 0], angularVelocity: [0, 0, 0] };\n let elapsed = 0;\n let start = n - 1;\n while (start > 0 && elapsed < this.window) {\n elapsed += this.samples[start]!.dt;\n start -= 1;\n }\n if (elapsed <= 0) return { linearVelocity: [0, 0, 0], angularVelocity: [0, 0, 0] };\n const velocity = velocityBetween(this.samples[start]!.pose, this.samples[n - 1]!.pose, elapsed);\n return { linearVelocity: velocity.linear, angularVelocity: velocity.angular };\n }\n}\n"]}
|
package/dist/runtime.d.ts
CHANGED
|
@@ -13,13 +13,14 @@
|
|
|
13
13
|
* unregister, enable and disable; behaviours disabled by capability
|
|
14
14
|
* negotiation announce themselves with a `behaviourDisabled` event.
|
|
15
15
|
*/
|
|
16
|
-
import { type InputCapabilities, type InputProvider, type InputSourceSnapshot, type Unsubscribe } from "@realitycollective/webxr-input";
|
|
16
|
+
import { PointerArbiter, PointerDisplay, type ActivePointerKind, type EyeGazeOptions, type InputCapabilities, type InputProvider, type InputSourceSnapshot, type PointerDisplayConfig, type PointerDrawing, type PointerVisuals, type Unsubscribe } from "@realitycollective/webxr-input";
|
|
17
17
|
import type { Behaviour } from "./behaviours/behaviour.js";
|
|
18
18
|
import type { InteractableDescriptor } from "./descriptor.js";
|
|
19
19
|
import { type InteractionEventListener } from "./events.js";
|
|
20
20
|
import type { FeedbackListener } from "./feedback.js";
|
|
21
21
|
import { type DwellConfig } from "./gaze.js";
|
|
22
22
|
import type { HitTester, TransformPort } from "./ports.js";
|
|
23
|
+
import { type NearPointerOptions } from "./near-pointer.js";
|
|
23
24
|
import { type VelocityTrackerOptions } from "./velocity-tracker.js";
|
|
24
25
|
/** Interactor id used when gaze is synthesized from the head pose. */
|
|
25
26
|
export declare const HEAD_GAZE_INTERACTOR_ID = "head-gaze";
|
|
@@ -39,13 +40,43 @@ export interface InteractionRuntimeOptions {
|
|
|
39
40
|
provider: InputProvider;
|
|
40
41
|
hitTester?: HitTester;
|
|
41
42
|
dwellDefaults?: DwellConfig;
|
|
43
|
+
/**
|
|
44
|
+
* Eye-gaze targeting, when the provider reports `capabilities.eyeGaze`:
|
|
45
|
+
* the selection cone, dwell window, near-pointer suppression and whether a
|
|
46
|
+
* gaze-started drag follows the pinching hand. Defaults are IWSDK 1.0.0's
|
|
47
|
+
* (`EYE_GAZE_DEFAULTS`).
|
|
48
|
+
*/
|
|
49
|
+
eyeGaze?: EyeGazeOptions;
|
|
42
50
|
/**
|
|
43
51
|
* Per-source velocity tracking. On by default with no smoothing; pass
|
|
44
52
|
* `false` to skip it, or options to smooth. The tracker only fills in
|
|
45
53
|
* what the provider did not supply.
|
|
46
54
|
*/
|
|
47
55
|
velocity?: VelocityTrackerOptions | false;
|
|
56
|
+
/**
|
|
57
|
+
* The near-pointer rule's distances: touch hover enter and exit, touch
|
|
58
|
+
* press, grip grab radius. Defaults are IWSDK 1.0.0's
|
|
59
|
+
* (`NEAR_POINTER_DEFAULTS`). An interactable's own `pokeRadius` overrides
|
|
60
|
+
* the touch hover enter distance for that target alone.
|
|
61
|
+
*/
|
|
62
|
+
nearPointer?: Partial<NearPointerOptions>;
|
|
63
|
+
/**
|
|
64
|
+
* The pointer arbiter shared with everything else a pointer can reach (the
|
|
65
|
+
* UI Extensions window hosts), so one decision per source covers panels
|
|
66
|
+
* and interactables alike, as IWSDK's `MultiPointer` does. Omit for a
|
|
67
|
+
* runtime used on its own: it makes one, and decides over its own targets.
|
|
68
|
+
*/
|
|
69
|
+
pointers?: PointerArbiter;
|
|
70
|
+
/**
|
|
71
|
+
* The app's pointer display settings (ray never, always or while hitting;
|
|
72
|
+
* cursors on objects and on panels; the ray and cursor look), or a
|
|
73
|
+
* `PointerDisplay` to share. Defaults are IWSDK 1.0.0's. Settable at run
|
|
74
|
+
* time through `getPointerDisplay().set(...)`.
|
|
75
|
+
*/
|
|
76
|
+
pointerDisplay?: PointerDisplay | Partial<PointerDisplayConfig>;
|
|
48
77
|
}
|
|
78
|
+
/** The target set this runtime registers with its arbiter. */
|
|
79
|
+
export declare const INTERACTIONS_POINTER_SET = "interactions";
|
|
49
80
|
export interface RegisterPorts {
|
|
50
81
|
transform?: TransformPort;
|
|
51
82
|
}
|
|
@@ -58,11 +89,26 @@ export declare class InteractionRuntime {
|
|
|
58
89
|
private readonly events;
|
|
59
90
|
private readonly feedbackEmitter;
|
|
60
91
|
private readonly afterSample;
|
|
92
|
+
private readonly visualsEmitter;
|
|
93
|
+
private readonly drawingEmitter;
|
|
94
|
+
private readonly nearPointer;
|
|
95
|
+
private readonly pointers;
|
|
96
|
+
private readonly targetSet;
|
|
97
|
+
private readonly pointerDisplay;
|
|
98
|
+
private lastVisuals;
|
|
99
|
+
private lastDrawings;
|
|
61
100
|
private readonly velocityTracker;
|
|
62
101
|
private lastSources;
|
|
63
102
|
private capabilities;
|
|
64
103
|
private readonly unsubscribeCaps;
|
|
65
104
|
private disposed;
|
|
105
|
+
private readonly eyeGaze;
|
|
106
|
+
private readonly gazeConsensus;
|
|
107
|
+
/** The eye-gaze source's target this frame: cone, consensus and suppression applied. */
|
|
108
|
+
private eyeGazeTarget;
|
|
109
|
+
/** Where the eye-gaze ray last aimed at each target, for the pinching hand to aim at. */
|
|
110
|
+
private readonly gazePoints;
|
|
111
|
+
private gazeHold;
|
|
66
112
|
constructor(options: InteractionRuntimeOptions);
|
|
67
113
|
registerInteractable(def: InteractableDescriptor, ports?: RegisterPorts): void;
|
|
68
114
|
unregisterInteractable(id: string): void;
|
|
@@ -104,15 +150,94 @@ export declare class InteractionRuntime {
|
|
|
104
150
|
getSource(id: string): InputSourceSnapshot | undefined;
|
|
105
151
|
getCapabilities(): InputCapabilities;
|
|
106
152
|
getProvider(): InputProvider;
|
|
153
|
+
/** The near-pointer distances in force (the defaults, or the options given). */
|
|
154
|
+
getNearPointerOptions(): Readonly<NearPointerOptions>;
|
|
155
|
+
/**
|
|
156
|
+
* The pointer that owns a source this frame: `"touch"`, `"grab"`, `"ray"`
|
|
157
|
+
* or `"gaze"`, or null when no pointer has a candidate (or the source is
|
|
158
|
+
* unknown). Decided by the core rule in `near-pointer.ts`, on every
|
|
159
|
+
* platform; a provider hint names the target but never the pointer.
|
|
160
|
+
*/
|
|
161
|
+
getActivePointer(sourceId: string): ActivePointerKind | null;
|
|
162
|
+
/**
|
|
163
|
+
* What to draw for every source this frame: the ray only while the ray is
|
|
164
|
+
* the active pointer or nothing is, the cursor at the active pointer's
|
|
165
|
+
* candidate. Published once per `update`, after the sources; a binding
|
|
166
|
+
* applies it to its ray and cursor visuals, and a native binding hands it
|
|
167
|
+
* to the host as `applyPointerVisuals`. The list and its entries are fresh
|
|
168
|
+
* each frame and never written to again.
|
|
169
|
+
*/
|
|
170
|
+
onPointerVisuals(listener: (visuals: readonly PointerVisuals[]) => void): Unsubscribe;
|
|
171
|
+
/** The visuals last published for a source, or undefined before its first frame. */
|
|
172
|
+
getPointerVisuals(sourceId: string): PointerVisuals | undefined;
|
|
173
|
+
/**
|
|
174
|
+
* What to draw for every source this frame under the app's pointer display
|
|
175
|
+
* settings: the visuals above resolved through `pointerDrawing`. Published
|
|
176
|
+
* right after `onPointerVisuals`. A binding that draws its own ray and
|
|
177
|
+
* cursor applies exactly this; a native binding hands it to the host.
|
|
178
|
+
*/
|
|
179
|
+
onPointerDrawing(listener: (drawings: readonly PointerDrawing[]) => void): Unsubscribe;
|
|
180
|
+
/** The drawing last published for a source, or undefined before its first frame. */
|
|
181
|
+
getPointerDrawing(sourceId: string): PointerDrawing | undefined;
|
|
182
|
+
/** The arbiter deciding which pointer owns each source, shared with the UI Extensions hosts when the app passed one. */
|
|
183
|
+
getPointerArbiter(): PointerArbiter;
|
|
184
|
+
/** The app's pointer display settings; `set` changes them at run time. */
|
|
185
|
+
getPointerDisplay(): PointerDisplay;
|
|
186
|
+
/** Whether a source is pressing or grabbing this frame (the selection that recolours its pointer). */
|
|
187
|
+
isSelecting(sourceId: string): boolean;
|
|
107
188
|
update(dt: number): void;
|
|
108
189
|
dispose(): void;
|
|
190
|
+
/**
|
|
191
|
+
* The targeting half of the eye-gaze rule (`@realitycollective/webxr-input`
|
|
192
|
+
* `eye-gaze.ts` states the whole of it): the cone's best candidate this
|
|
193
|
+
* frame, then the dwell consensus over the window, and nothing at all while
|
|
194
|
+
* a hand's near pointer is hovering or selecting (IWSDK
|
|
195
|
+
* `suppressWhenDirectPointerActive`). A frame with no valid gaze ray clears
|
|
196
|
+
* the record, as IWSDK resets its conecaster, unless a selection is held.
|
|
197
|
+
*/
|
|
198
|
+
private resolveEyeGaze;
|
|
199
|
+
/** A hand's near pointer is hovering or selecting: touch or grab owns the hand, or a grab is held. */
|
|
200
|
+
private directPointerActive;
|
|
201
|
+
/**
|
|
202
|
+
* Once a gaze selection is held, the pinching hand drives the pointer
|
|
203
|
+
* (IWSDK `pointerTransformFollowsHand`): the frame the hold starts, a ray
|
|
204
|
+
* from the hand's ray space is aimed at the gaze hit and its orientation
|
|
205
|
+
* relative to the hand recorded; every later frame the gaze snapshot's ray
|
|
206
|
+
* is that hand's ray space turned by the same amount. When the hand could
|
|
207
|
+
* not reach the hit, or the option is off, the ray stays where the
|
|
208
|
+
* selection was made. Consumers of `onSample` and `getSource`, and every
|
|
209
|
+
* behaviour, see the followed ray.
|
|
210
|
+
*/
|
|
211
|
+
private followHand;
|
|
212
|
+
/** Start, keep or end the gaze hold from the gaze source's press and grab state this frame. */
|
|
213
|
+
private trackGazeHold;
|
|
109
214
|
private sourceState;
|
|
110
215
|
private interactorInfo;
|
|
111
|
-
/**
|
|
216
|
+
/**
|
|
217
|
+
* Resolve this source's target and its active pointer. The pointer is the
|
|
218
|
+
* core rule (`near-pointer.ts`): touch, then grab, then ray, the first with
|
|
219
|
+
* a candidate, locked while it presses or grabs. The target is a provider
|
|
220
|
+
* hint when there is one (provider power: IWSDK's mesh-accurate tags), else
|
|
221
|
+
* the active pointer's candidate. An eye-gaze source targets through the
|
|
222
|
+
* cone and consensus instead.
|
|
223
|
+
*/
|
|
112
224
|
private resolveTarget;
|
|
225
|
+
/**
|
|
226
|
+
* The touch pointer's candidate this frame, advancing the source's touch
|
|
227
|
+
* machine: the nearest target within the widest touch band, hovering by
|
|
228
|
+
* that target's own enter and exit distances. One proximity query at the
|
|
229
|
+
* largest radius any interactable asked for, then the nearest hit must be
|
|
230
|
+
* inside ITS OWN band; the trade is that a farther target with a wider
|
|
231
|
+
* band is not found behind a nearer one with a narrower band.
|
|
232
|
+
*/
|
|
233
|
+
private touchCandidate;
|
|
234
|
+
/** The grab pointer's candidate: the nearest target whose surface the grip sphere reaches. */
|
|
235
|
+
private grabCandidate;
|
|
236
|
+
/** The ray pointer's candidate: the nearest target along the ray. */
|
|
237
|
+
private rayCandidate;
|
|
113
238
|
private targetable;
|
|
114
|
-
/** The
|
|
115
|
-
private
|
|
239
|
+
/** The widest touch exit band among enabled interactables; the default exit distance when none is registered. */
|
|
240
|
+
private touchQueryRadius;
|
|
116
241
|
private flags;
|
|
117
242
|
private updateSource;
|
|
118
243
|
private gazeAllows;
|