@realitycollective/webxr-interactions 0.1.1-preview.3 → 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 +18 -2
- package/README.md +16 -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 +15 -0
- package/dist/contract-cases.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -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-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.js +48 -0
- package/dist/near-pointer-contract-cases.js.map +1 -1
- package/dist/near-pointer.d.ts +9 -52
- package/dist/near-pointer.js +9 -23
- package/dist/near-pointer.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 +38 -2
- package/dist/runtime.js +75 -15
- 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
package/dist/index.js
CHANGED
|
@@ -23,6 +23,7 @@ export * from "./contract-cases.js";
|
|
|
23
23
|
export * from "./gaze.js";
|
|
24
24
|
export * from "./descriptor.js";
|
|
25
25
|
export * from "./velocity-tracker.js";
|
|
26
|
+
export * from "./release-velocity.js";
|
|
26
27
|
export * from "./runtime.js";
|
|
27
28
|
export * from "./pointer-bridge.js";
|
|
28
29
|
export * from "./near-pointer.js";
|
|
@@ -30,6 +31,9 @@ export * from "./near-pointer-contract-cases.js";
|
|
|
30
31
|
export * from "./physics.js";
|
|
31
32
|
export * from "./physics-contract-cases.js";
|
|
32
33
|
export * from "./memory-physics.js";
|
|
34
|
+
export * from "./memory-ports.js";
|
|
35
|
+
export * from "./trace.js";
|
|
36
|
+
export * from "./trace-synthetic.js";
|
|
33
37
|
export * from "./behaviours/behaviour.js";
|
|
34
38
|
export * from "./behaviours/press.js";
|
|
35
39
|
export * from "./behaviours/pulse.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,cAAc,gCAAgC,CAAC;AAE/C,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC;AACpC,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kCAAkC,CAAC;AACjD,cAAc,cAAc,CAAC;AAC7B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,cAAc,gCAAgC,CAAC;AAE/C,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC;AACpC,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kCAAkC,CAAC;AACjD,cAAc,cAAc,CAAC;AAC7B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,sBAAsB,CAAC;AAErC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yBAAyB,CAAC","sourcesContent":["/**\n * @realitycollective/webxr-interactions - the engine-free interaction core.\n *\n * Layers (each strictly on the one below):\n * - behaviours (press, pulse, hinge, dial, slide, grab, tossScore) -\n * pure state machines driven by `update(dt)`;\n * - the runtime/binder - targeting, press/grab transitions, gaze gating\n * + dwell, capability negotiation, events, feedback intents;\n * - the shared input contracts, re-exported from\n * `@realitycollective/webxr-input`.\n *\n * Pair with an engine adapter: `@realitycollective/threejs-interactions`\n * (standalone WebXR + three.js), `@realitycollective/iwsdk-interactions`\n * (Meta IWSDK) or `@realitycollective/xrblocks-interactions` (Google XR\n * Blocks, experimental).\n */\nexport * from \"@realitycollective/webxr-input\";\n\nexport * from \"./math.js\";\nexport * from \"./events.js\";\nexport * from \"./feedback.js\";\nexport * from \"./ports.js\";\nexport * from \"./contract-cases.js\";\nexport * from \"./gaze.js\";\nexport * from \"./descriptor.js\";\nexport * from \"./velocity-tracker.js\";\nexport * from \"./release-velocity.js\";\nexport * from \"./runtime.js\";\nexport * from \"./pointer-bridge.js\";\nexport * from \"./near-pointer.js\";\nexport * from \"./near-pointer-contract-cases.js\";\nexport * from \"./physics.js\";\nexport * from \"./physics-contract-cases.js\";\nexport * from \"./memory-physics.js\";\nexport * from \"./memory-ports.js\";\nexport * from \"./trace.js\";\nexport * from \"./trace-synthetic.js\";\n\nexport * from \"./behaviours/behaviour.js\";\nexport * from \"./behaviours/press.js\";\nexport * from \"./behaviours/pulse.js\";\nexport * from \"./behaviours/hinge.js\";\nexport * from \"./behaviours/dial.js\";\nexport * from \"./behaviours/slide.js\";\nexport * from \"./behaviours/grab.js\";\nexport * from \"./behaviours/toss-score.js\";\nexport * from \"./behaviours/factory.js\";\n"]}
|
package/dist/math.d.ts
CHANGED
|
@@ -60,6 +60,14 @@ export declare function vApplyQuat(v: Vec3Tuple, q: QuatTuple): Vec3Tuple;
|
|
|
60
60
|
/** Transform a world-space point into the local frame of `pose`. */
|
|
61
61
|
export declare function worldToLocal(point: Vec3Tuple, posePosition: Vec3Tuple, poseQuaternion: QuatTuple): Vec3Tuple;
|
|
62
62
|
/** Closest-approach distance from a ray to a point, and the along-ray t. */
|
|
63
|
+
/**
|
|
64
|
+
* The point on a sphere's surface nearest `query`: the centre moved `radius`
|
|
65
|
+
* toward the query. A query at the centre exactly takes the point straight
|
|
66
|
+
* above it. This is the `point` a proximity hit reports, where the touch
|
|
67
|
+
* cursor sits (IWSDK's sphere intersector reports the point on the mesh,
|
|
68
|
+
* never the centre).
|
|
69
|
+
*/
|
|
70
|
+
export declare function surfacePointOnSphere(centre: Vec3Tuple, radius: number, query: Vec3Tuple): Vec3Tuple;
|
|
63
71
|
export declare function rayPointDistance(ray: RayTuple, point: Vec3Tuple): {
|
|
64
72
|
distance: number;
|
|
65
73
|
t: number;
|
package/dist/math.js
CHANGED
|
@@ -147,6 +147,23 @@ export function worldToLocal(point, posePosition, poseQuaternion) {
|
|
|
147
147
|
return vApplyQuat(vSub(point, posePosition), quatConjugate(poseQuaternion));
|
|
148
148
|
}
|
|
149
149
|
/** Closest-approach distance from a ray to a point, and the along-ray t. */
|
|
150
|
+
/**
|
|
151
|
+
* The point on a sphere's surface nearest `query`: the centre moved `radius`
|
|
152
|
+
* toward the query. A query at the centre exactly takes the point straight
|
|
153
|
+
* above it. This is the `point` a proximity hit reports, where the touch
|
|
154
|
+
* cursor sits (IWSDK's sphere intersector reports the point on the mesh,
|
|
155
|
+
* never the centre).
|
|
156
|
+
*/
|
|
157
|
+
export function surfacePointOnSphere(centre, radius, query) {
|
|
158
|
+
const dx = query[0] - centre[0];
|
|
159
|
+
const dy = query[1] - centre[1];
|
|
160
|
+
const dz = query[2] - centre[2];
|
|
161
|
+
const length = Math.hypot(dx, dy, dz);
|
|
162
|
+
if (length < 1e-9)
|
|
163
|
+
return [centre[0], centre[1] + radius, centre[2]];
|
|
164
|
+
const k = radius / length;
|
|
165
|
+
return [centre[0] + dx * k, centre[1] + dy * k, centre[2] + dz * k];
|
|
166
|
+
}
|
|
150
167
|
export function rayPointDistance(ray, point) {
|
|
151
168
|
const toPoint = vSub(point, ray.origin);
|
|
152
169
|
const t = vDot(toPoint, ray.direction);
|
package/dist/math.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"math.js","sourceRoot":"","sources":["../src/math.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pure interaction math - the proven motion functions extracted from the\n * Pale Signal research (springs, hinge/dial normalisation, hoop scoring),\n * plus the tuple vector/quaternion helpers the binder and behaviours need.\n * No engine imports; everything is unit-tested headless.\n */\nimport type { QuatTuple, RayTuple, Vec3Tuple } from \"@realitycollective/webxr-input\";\n\n/** Clamp to the unit interval. */\nexport function clamp01(t: number): number {\n return t < 0 ? 0 : t > 1 ? 1 : t;\n}\n\n/**\n * Normalize a measured displacement/angle against a signed range to 0..1.\n * Sign-agnostic: a range of `-π/2` maps `-π/2 → 1`.\n */\nexport function normalize01(value: number, range: number): number {\n if (range === 0) return 0;\n return clamp01(value / range);\n}\n\n/**\n * Did a tossed object pass *through* a hoop this frame? True when it crossed\n * the hoop plane in the scoring direction (downward - `prev` above, `curr`\n * at/below) while inside the ring (`radial <= radius`).\n */\nexport function passedThroughHoop(\n prevAlong: number,\n currAlong: number,\n planeAlong: number,\n radial: number,\n radius: number,\n): boolean {\n const crossedDown = prevAlong > planeAlong && currAlong <= planeAlong;\n return crossedDown && radial <= radius;\n}\n\n/**\n * One step of a damped harmonic oscillator (semi-implicit Euler).\n * `out[0]` = next value, `out[1]` = next velocity. An under-damped spring\n * (damping ratio ≈ 0.3-0.5) is what gives a pressed button its bounce.\n * `dt` is clamped to 1/30 s so a long frame can't blow the integrator up.\n */\nexport function springStep(\n value: number,\n velocity: number,\n target: number,\n stiffness: number,\n damping: number,\n dt: number,\n out: Float32Array = new Float32Array(2),\n): Float32Array {\n const h = Math.min(dt, 1 / 30);\n const accel = -stiffness * (value - target) - damping * velocity;\n const nextVelocity = velocity + accel * h;\n out[0] = value + nextVelocity * h;\n out[1] = nextVelocity;\n return out;\n}\n\n/** Exponential decay of `value` toward 0 over `dt` at `ratePerSec`. */\nexport function decay(value: number, ratePerSec: number, dt: number): number {\n const next = value * Math.exp(-ratePerSec * Math.max(dt, 0));\n return next < 1e-4 ? 0 : next;\n}\n\n/** Penner's ease-out-bounce on `t ∈ [0,1]`. */\nexport function easeOutBounce(t: number): number {\n const n1 = 7.5625;\n const d1 = 2.75;\n let x = clamp01(t);\n if (x < 1 / d1) return n1 * x * x;\n if (x < 2 / d1) return n1 * (x -= 1.5 / d1) * x + 0.75;\n if (x < 2.5 / d1) return n1 * (x -= 2.25 / d1) * x + 0.9375;\n return n1 * (x -= 2.625 / d1) * x + 0.984375;\n}\n\n/** Clamp a value to the symmetric range `[-|mag|, +|mag|]`. */\nexport function clampSym(x: number, mag: number): number {\n const m = Math.abs(mag);\n return x < -m ? -m : x > m ? m : x;\n}\n\n/**\n * Hinge angle for a position-driven lever: given the hand's offset from the\n * pivot resolved into the hinge plane - `alongRest` (toward the arm's rest\n * direction) and `alongSwing` (the in-plane perpendicular) - the angle the\n * arm should take to point at the hand, clamped to `±|maxMag|`.\n */\nexport function hingeAngleToHand(alongRest: number, alongSwing: number, maxMag: number): number {\n return clampSym(Math.atan2(alongSwing, alongRest), maxMag);\n}\n\n/**\n * Map a centered hinge angle in `[-|mag|, +|mag|]` to a 0..1 analog value\n * with the rest (0) at 0.5 - a bidirectional lever as one normalized value.\n */\nexport function centeredUnit(angle: number, mag: number): number {\n const m = Math.abs(mag);\n if (m < 1e-6) return 0.5;\n return clamp01(0.5 + (0.5 * angle) / m);\n}\n\n// ---------------------------------------------------------------------------\n// Tuple vector / quaternion helpers (allocation-light, no engine types).\n// ---------------------------------------------------------------------------\n\nexport function vSub(a: Vec3Tuple, b: Vec3Tuple): Vec3Tuple {\n return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];\n}\n\nexport function vAdd(a: Vec3Tuple, b: Vec3Tuple): Vec3Tuple {\n return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];\n}\n\nexport function vScale(a: Vec3Tuple, s: number): Vec3Tuple {\n return [a[0] * s, a[1] * s, a[2] * s];\n}\n\nexport function vDot(a: Vec3Tuple, b: Vec3Tuple): number {\n return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];\n}\n\nexport function vCross(a: Vec3Tuple, b: Vec3Tuple): Vec3Tuple {\n return [\n a[1] * b[2] - a[2] * b[1],\n a[2] * b[0] - a[0] * b[2],\n a[0] * b[1] - a[1] * b[0],\n ];\n}\n\nexport function vLength(a: Vec3Tuple): number {\n return Math.hypot(a[0], a[1], a[2]);\n}\n\nexport function vDistance(a: Vec3Tuple, b: Vec3Tuple): number {\n return Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]);\n}\n\nexport function vNormalize(a: Vec3Tuple): Vec3Tuple {\n const l = vLength(a);\n return l < 1e-9 ? [0, 0, 0] : [a[0] / l, a[1] / l, a[2] / l];\n}\n\n/** Quaternion from an axis (normalised) and an angle in radians. */\nexport function quatFromAxisAngle(axis: Vec3Tuple, angle: number): QuatTuple {\n const half = angle / 2;\n const s = Math.sin(half);\n return [axis[0] * s, axis[1] * s, axis[2] * s, Math.cos(half)];\n}\n\nexport function quatMultiply(a: QuatTuple, b: QuatTuple): QuatTuple {\n const [ax, ay, az, aw] = a;\n const [bx, by, bz, bw] = b;\n return [\n aw * bx + ax * bw + ay * bz - az * by,\n aw * by - ax * bz + ay * bw + az * bx,\n aw * bz + ax * by - ay * bx + az * bw,\n aw * bw - ax * bx - ay * by - az * bz,\n ];\n}\n\nexport function quatConjugate(q: QuatTuple): QuatTuple {\n return [-q[0], -q[1], -q[2], q[3]];\n}\n\n/** Rotate a vector by a quaternion. */\nexport function vApplyQuat(v: Vec3Tuple, q: QuatTuple): Vec3Tuple {\n const [x, y, z] = v;\n const [qx, qy, qz, qw] = q;\n // t = 2 * cross(q.xyz, v)\n const tx = 2 * (qy * z - qz * y);\n const ty = 2 * (qz * x - qx * z);\n const tz = 2 * (qx * y - qy * x);\n // v + qw * t + cross(q.xyz, t)\n return [\n x + qw * tx + qy * tz - qz * ty,\n y + qw * ty + qz * tx - qx * tz,\n z + qw * tz + qx * ty - qy * tx,\n ];\n}\n\n/** Transform a world-space point into the local frame of `pose`. */\nexport function worldToLocal(\n point: Vec3Tuple,\n posePosition: Vec3Tuple,\n poseQuaternion: QuatTuple,\n): Vec3Tuple {\n return vApplyQuat(vSub(point, posePosition), quatConjugate(poseQuaternion));\n}\n\n/** Closest-approach distance from a ray to a point, and the along-ray t. */\nexport function rayPointDistance(ray: RayTuple, point: Vec3Tuple): { distance: number; t: number } {\n const toPoint = vSub(point, ray.origin);\n const t = vDot(toPoint, ray.direction);\n if (t <= 0) return { distance: vLength(toPoint), t: 0 };\n const closest = vAdd(ray.origin, vScale(ray.direction, t));\n return { distance: vDistance(closest, point), t };\n}\n\n/** Angular offset (radians) between a ray direction and the direction to a point. */\nexport function rayAngleTo(ray: RayTuple, point: Vec3Tuple): number {\n const dir = vNormalize(vSub(point, ray.origin));\n const d = vDot(vNormalize(ray.direction), dir);\n return Math.acos(Math.min(1, Math.max(-1, d)));\n}\n\n/*\n * Velocity between two poses lives in `@realitycollective/webxr-input` as\n * `velocityBetween`, re-exported from this package's index. The local copy\n * that stood in for it before Input 0.1.1 is gone.\n */\n\n/** Zero out a signal below `threshold`, leaving larger values untouched. */\nexport function clampDeadzone(value: number, threshold: number): number {\n return Math.abs(value) < threshold ? 0 : value;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"math.js","sourceRoot":"","sources":["../src/math.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pure interaction math - the proven motion functions extracted from the\n * Pale Signal research (springs, hinge/dial normalisation, hoop scoring),\n * plus the tuple vector/quaternion helpers the binder and behaviours need.\n * No engine imports; everything is unit-tested headless.\n */\nimport type { QuatTuple, RayTuple, Vec3Tuple } from \"@realitycollective/webxr-input\";\n\n/** Clamp to the unit interval. */\nexport function clamp01(t: number): number {\n return t < 0 ? 0 : t > 1 ? 1 : t;\n}\n\n/**\n * Normalize a measured displacement/angle against a signed range to 0..1.\n * Sign-agnostic: a range of `-π/2` maps `-π/2 → 1`.\n */\nexport function normalize01(value: number, range: number): number {\n if (range === 0) return 0;\n return clamp01(value / range);\n}\n\n/**\n * Did a tossed object pass *through* a hoop this frame? True when it crossed\n * the hoop plane in the scoring direction (downward - `prev` above, `curr`\n * at/below) while inside the ring (`radial <= radius`).\n */\nexport function passedThroughHoop(\n prevAlong: number,\n currAlong: number,\n planeAlong: number,\n radial: number,\n radius: number,\n): boolean {\n const crossedDown = prevAlong > planeAlong && currAlong <= planeAlong;\n return crossedDown && radial <= radius;\n}\n\n/**\n * One step of a damped harmonic oscillator (semi-implicit Euler).\n * `out[0]` = next value, `out[1]` = next velocity. An under-damped spring\n * (damping ratio ≈ 0.3-0.5) is what gives a pressed button its bounce.\n * `dt` is clamped to 1/30 s so a long frame can't blow the integrator up.\n */\nexport function springStep(\n value: number,\n velocity: number,\n target: number,\n stiffness: number,\n damping: number,\n dt: number,\n out: Float32Array = new Float32Array(2),\n): Float32Array {\n const h = Math.min(dt, 1 / 30);\n const accel = -stiffness * (value - target) - damping * velocity;\n const nextVelocity = velocity + accel * h;\n out[0] = value + nextVelocity * h;\n out[1] = nextVelocity;\n return out;\n}\n\n/** Exponential decay of `value` toward 0 over `dt` at `ratePerSec`. */\nexport function decay(value: number, ratePerSec: number, dt: number): number {\n const next = value * Math.exp(-ratePerSec * Math.max(dt, 0));\n return next < 1e-4 ? 0 : next;\n}\n\n/** Penner's ease-out-bounce on `t ∈ [0,1]`. */\nexport function easeOutBounce(t: number): number {\n const n1 = 7.5625;\n const d1 = 2.75;\n let x = clamp01(t);\n if (x < 1 / d1) return n1 * x * x;\n if (x < 2 / d1) return n1 * (x -= 1.5 / d1) * x + 0.75;\n if (x < 2.5 / d1) return n1 * (x -= 2.25 / d1) * x + 0.9375;\n return n1 * (x -= 2.625 / d1) * x + 0.984375;\n}\n\n/** Clamp a value to the symmetric range `[-|mag|, +|mag|]`. */\nexport function clampSym(x: number, mag: number): number {\n const m = Math.abs(mag);\n return x < -m ? -m : x > m ? m : x;\n}\n\n/**\n * Hinge angle for a position-driven lever: given the hand's offset from the\n * pivot resolved into the hinge plane - `alongRest` (toward the arm's rest\n * direction) and `alongSwing` (the in-plane perpendicular) - the angle the\n * arm should take to point at the hand, clamped to `±|maxMag|`.\n */\nexport function hingeAngleToHand(alongRest: number, alongSwing: number, maxMag: number): number {\n return clampSym(Math.atan2(alongSwing, alongRest), maxMag);\n}\n\n/**\n * Map a centered hinge angle in `[-|mag|, +|mag|]` to a 0..1 analog value\n * with the rest (0) at 0.5 - a bidirectional lever as one normalized value.\n */\nexport function centeredUnit(angle: number, mag: number): number {\n const m = Math.abs(mag);\n if (m < 1e-6) return 0.5;\n return clamp01(0.5 + (0.5 * angle) / m);\n}\n\n// ---------------------------------------------------------------------------\n// Tuple vector / quaternion helpers (allocation-light, no engine types).\n// ---------------------------------------------------------------------------\n\nexport function vSub(a: Vec3Tuple, b: Vec3Tuple): Vec3Tuple {\n return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];\n}\n\nexport function vAdd(a: Vec3Tuple, b: Vec3Tuple): Vec3Tuple {\n return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];\n}\n\nexport function vScale(a: Vec3Tuple, s: number): Vec3Tuple {\n return [a[0] * s, a[1] * s, a[2] * s];\n}\n\nexport function vDot(a: Vec3Tuple, b: Vec3Tuple): number {\n return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];\n}\n\nexport function vCross(a: Vec3Tuple, b: Vec3Tuple): Vec3Tuple {\n return [\n a[1] * b[2] - a[2] * b[1],\n a[2] * b[0] - a[0] * b[2],\n a[0] * b[1] - a[1] * b[0],\n ];\n}\n\nexport function vLength(a: Vec3Tuple): number {\n return Math.hypot(a[0], a[1], a[2]);\n}\n\nexport function vDistance(a: Vec3Tuple, b: Vec3Tuple): number {\n return Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]);\n}\n\nexport function vNormalize(a: Vec3Tuple): Vec3Tuple {\n const l = vLength(a);\n return l < 1e-9 ? [0, 0, 0] : [a[0] / l, a[1] / l, a[2] / l];\n}\n\n/** Quaternion from an axis (normalised) and an angle in radians. */\nexport function quatFromAxisAngle(axis: Vec3Tuple, angle: number): QuatTuple {\n const half = angle / 2;\n const s = Math.sin(half);\n return [axis[0] * s, axis[1] * s, axis[2] * s, Math.cos(half)];\n}\n\nexport function quatMultiply(a: QuatTuple, b: QuatTuple): QuatTuple {\n const [ax, ay, az, aw] = a;\n const [bx, by, bz, bw] = b;\n return [\n aw * bx + ax * bw + ay * bz - az * by,\n aw * by - ax * bz + ay * bw + az * bx,\n aw * bz + ax * by - ay * bx + az * bw,\n aw * bw - ax * bx - ay * by - az * bz,\n ];\n}\n\nexport function quatConjugate(q: QuatTuple): QuatTuple {\n return [-q[0], -q[1], -q[2], q[3]];\n}\n\n/** Rotate a vector by a quaternion. */\nexport function vApplyQuat(v: Vec3Tuple, q: QuatTuple): Vec3Tuple {\n const [x, y, z] = v;\n const [qx, qy, qz, qw] = q;\n // t = 2 * cross(q.xyz, v)\n const tx = 2 * (qy * z - qz * y);\n const ty = 2 * (qz * x - qx * z);\n const tz = 2 * (qx * y - qy * x);\n // v + qw * t + cross(q.xyz, t)\n return [\n x + qw * tx + qy * tz - qz * ty,\n y + qw * ty + qz * tx - qx * tz,\n z + qw * tz + qx * ty - qy * tx,\n ];\n}\n\n/** Transform a world-space point into the local frame of `pose`. */\nexport function worldToLocal(\n point: Vec3Tuple,\n posePosition: Vec3Tuple,\n poseQuaternion: QuatTuple,\n): Vec3Tuple {\n return vApplyQuat(vSub(point, posePosition), quatConjugate(poseQuaternion));\n}\n\n/** Closest-approach distance from a ray to a point, and the along-ray t. */\n/**\n * The point on a sphere's surface nearest `query`: the centre moved `radius`\n * toward the query. A query at the centre exactly takes the point straight\n * above it. This is the `point` a proximity hit reports, where the touch\n * cursor sits (IWSDK's sphere intersector reports the point on the mesh,\n * never the centre).\n */\nexport function surfacePointOnSphere(centre: Vec3Tuple, radius: number, query: Vec3Tuple): Vec3Tuple {\n const dx = query[0] - centre[0];\n const dy = query[1] - centre[1];\n const dz = query[2] - centre[2];\n const length = Math.hypot(dx, dy, dz);\n if (length < 1e-9) return [centre[0], centre[1] + radius, centre[2]];\n const k = radius / length;\n return [centre[0] + dx * k, centre[1] + dy * k, centre[2] + dz * k];\n}\n\nexport function rayPointDistance(ray: RayTuple, point: Vec3Tuple): { distance: number; t: number } {\n const toPoint = vSub(point, ray.origin);\n const t = vDot(toPoint, ray.direction);\n if (t <= 0) return { distance: vLength(toPoint), t: 0 };\n const closest = vAdd(ray.origin, vScale(ray.direction, t));\n return { distance: vDistance(closest, point), t };\n}\n\n/** Angular offset (radians) between a ray direction and the direction to a point. */\nexport function rayAngleTo(ray: RayTuple, point: Vec3Tuple): number {\n const dir = vNormalize(vSub(point, ray.origin));\n const d = vDot(vNormalize(ray.direction), dir);\n return Math.acos(Math.min(1, Math.max(-1, d)));\n}\n\n/*\n * Velocity between two poses lives in `@realitycollective/webxr-input` as\n * `velocityBetween`, re-exported from this package's index. The local copy\n * that stood in for it before Input 0.1.1 is gone.\n */\n\n/** Zero out a signal below `threshold`, leaving larger values untouched. */\nexport function clampDeadzone(value: number, threshold: number): number {\n return Math.abs(value) < threshold ? 0 : value;\n}\n"]}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Engine-free ports over sphere targets: the reference `HitTester` and
|
|
3
|
+
* `TransformPort` a trace replay, a synthetic recording, a harness run
|
|
4
|
+
* without a host, or a test can drive with no engine at all. They apply the
|
|
5
|
+
* contract exactly as every binding must (`hitTesterContractCases()` and
|
|
6
|
+
* `transformPortContractCases()` pass over them), so a difference between a
|
|
7
|
+
* binding and these is a difference in the binding.
|
|
8
|
+
*/
|
|
9
|
+
import type { PoseTuple, QuatTuple, RayTuple, Vec3Tuple } from "@realitycollective/webxr-input";
|
|
10
|
+
import type { HitTester, HoldRelease, InteractableHit, TransformPort } from "./ports.js";
|
|
11
|
+
/** IWSDK's radius for a target that declares none. */
|
|
12
|
+
export declare const MEMORY_DEFAULT_RADIUS = 0.1;
|
|
13
|
+
/** A sphere-target hit tester: IWSDK's `EntityHitTester` rules over plain spheres. */
|
|
14
|
+
export declare class MemoryHitTester implements HitTester {
|
|
15
|
+
private readonly targets;
|
|
16
|
+
/** Place a shown sphere target; a port makes its centre follow the port's live pose. */
|
|
17
|
+
place(id: string, centre: Vec3Tuple, radius?: number, port?: MemoryTransformPort): void;
|
|
18
|
+
remove(id: string): void;
|
|
19
|
+
setVisible(id: string, visible: boolean): void;
|
|
20
|
+
clear(): void;
|
|
21
|
+
private centreOf;
|
|
22
|
+
hitRay(ray: RayTuple): InteractableHit | null;
|
|
23
|
+
hitProximity(point: Vec3Tuple, radius: number): InteractableHit | null;
|
|
24
|
+
hitCone(ray: RayTuple, halfAngle: number, maxLength: number): InteractableHit | null;
|
|
25
|
+
}
|
|
26
|
+
/** A transform port over a pose it keeps itself, with the held-pose members recorded. */
|
|
27
|
+
export declare class MemoryTransformPort implements TransformPort {
|
|
28
|
+
private rest;
|
|
29
|
+
private live;
|
|
30
|
+
private offset;
|
|
31
|
+
private rotation;
|
|
32
|
+
private held;
|
|
33
|
+
/** Every release `endHold` received, in order. */
|
|
34
|
+
readonly releases: HoldRelease[];
|
|
35
|
+
/** The last effect written. */
|
|
36
|
+
effect: {
|
|
37
|
+
scale?: number;
|
|
38
|
+
emissive?: number;
|
|
39
|
+
};
|
|
40
|
+
constructor(rest: PoseTuple);
|
|
41
|
+
getWorldPose(): PoseTuple;
|
|
42
|
+
getRestWorldPose(): PoseTuple;
|
|
43
|
+
getLocalOffset(): Vec3Tuple;
|
|
44
|
+
setLocalOffset(offset: Vec3Tuple): void;
|
|
45
|
+
setLocalRotation(quaternion: QuatTuple): void;
|
|
46
|
+
setWorldPose(pose: PoseTuple): void;
|
|
47
|
+
setEffect(effect: {
|
|
48
|
+
scale?: number;
|
|
49
|
+
emissive?: number;
|
|
50
|
+
}): void;
|
|
51
|
+
recaptureRest(): void;
|
|
52
|
+
beginHold(): void;
|
|
53
|
+
endHold(release: HoldRelease): void;
|
|
54
|
+
get isHeld(): boolean;
|
|
55
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { coneHitForSpheres } from "./gaze.js";
|
|
2
|
+
import { quatMultiply, rayPointDistance, surfacePointOnSphere, vAdd, vApplyQuat } from "./math.js";
|
|
3
|
+
/** IWSDK's radius for a target that declares none. */
|
|
4
|
+
export const MEMORY_DEFAULT_RADIUS = 0.1;
|
|
5
|
+
/** A sphere-target hit tester: IWSDK's `EntityHitTester` rules over plain spheres. */
|
|
6
|
+
export class MemoryHitTester {
|
|
7
|
+
targets = new Map();
|
|
8
|
+
/** Place a shown sphere target; a port makes its centre follow the port's live pose. */
|
|
9
|
+
place(id, centre, radius = MEMORY_DEFAULT_RADIUS, port) {
|
|
10
|
+
this.targets.set(id, { id, centre: [centre[0], centre[1], centre[2]], radius, visible: true, ...(port ? { port } : {}) });
|
|
11
|
+
}
|
|
12
|
+
remove(id) {
|
|
13
|
+
this.targets.delete(id);
|
|
14
|
+
}
|
|
15
|
+
setVisible(id, visible) {
|
|
16
|
+
const target = this.targets.get(id);
|
|
17
|
+
if (target)
|
|
18
|
+
target.visible = visible;
|
|
19
|
+
}
|
|
20
|
+
clear() {
|
|
21
|
+
this.targets.clear();
|
|
22
|
+
}
|
|
23
|
+
centreOf(target) {
|
|
24
|
+
return target.port ? target.port.getWorldPose().position : target.centre;
|
|
25
|
+
}
|
|
26
|
+
hitRay(ray) {
|
|
27
|
+
let best = null;
|
|
28
|
+
for (const target of this.targets.values()) {
|
|
29
|
+
if (!target.visible)
|
|
30
|
+
continue;
|
|
31
|
+
const centre = this.centreOf(target);
|
|
32
|
+
const { distance, t } = rayPointDistance(ray, centre);
|
|
33
|
+
if (t <= 0 || distance > target.radius)
|
|
34
|
+
continue;
|
|
35
|
+
if (best === null || t < best.distance) {
|
|
36
|
+
// Where the ray enters the sphere.
|
|
37
|
+
const inside = Math.sqrt(Math.max(0, target.radius * target.radius - distance * distance));
|
|
38
|
+
const enter = t - inside;
|
|
39
|
+
best = {
|
|
40
|
+
interactableId: target.id,
|
|
41
|
+
distance: enter,
|
|
42
|
+
point: [ray.origin[0] + ray.direction[0] * enter, ray.origin[1] + ray.direction[1] * enter, ray.origin[2] + ray.direction[2] * enter],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return best;
|
|
47
|
+
}
|
|
48
|
+
hitProximity(point, radius) {
|
|
49
|
+
let best = null;
|
|
50
|
+
for (const target of this.targets.values()) {
|
|
51
|
+
if (!target.visible)
|
|
52
|
+
continue;
|
|
53
|
+
const centre = this.centreOf(target);
|
|
54
|
+
const surface = Math.max(0, Math.hypot(centre[0] - point[0], centre[1] - point[1], centre[2] - point[2]) - target.radius);
|
|
55
|
+
if (surface <= radius && (best === null || surface < best.distance)) {
|
|
56
|
+
best = { interactableId: target.id, distance: surface, point: surfacePointOnSphere(centre, target.radius, point) };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return best;
|
|
60
|
+
}
|
|
61
|
+
hitCone(ray, halfAngle, maxLength) {
|
|
62
|
+
const spheres = [];
|
|
63
|
+
for (const target of this.targets.values()) {
|
|
64
|
+
if (target.visible)
|
|
65
|
+
spheres.push({ id: target.id, center: this.centreOf(target), radius: target.radius });
|
|
66
|
+
}
|
|
67
|
+
const hit = coneHitForSpheres(ray, spheres, halfAngle, maxLength);
|
|
68
|
+
return hit ? { interactableId: hit.interactableId, distance: hit.distance, point: hit.point } : null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** A transform port over a pose it keeps itself, with the held-pose members recorded. */
|
|
72
|
+
export class MemoryTransformPort {
|
|
73
|
+
rest;
|
|
74
|
+
live;
|
|
75
|
+
offset = [0, 0, 0];
|
|
76
|
+
rotation = [0, 0, 0, 1];
|
|
77
|
+
held = false;
|
|
78
|
+
/** Every release `endHold` received, in order. */
|
|
79
|
+
releases = [];
|
|
80
|
+
/** The last effect written. */
|
|
81
|
+
effect = {};
|
|
82
|
+
constructor(rest) {
|
|
83
|
+
this.rest = copyPose(rest);
|
|
84
|
+
this.live = copyPose(rest);
|
|
85
|
+
}
|
|
86
|
+
getWorldPose() {
|
|
87
|
+
return copyPose(this.live);
|
|
88
|
+
}
|
|
89
|
+
getRestWorldPose() {
|
|
90
|
+
return copyPose(this.rest);
|
|
91
|
+
}
|
|
92
|
+
getLocalOffset() {
|
|
93
|
+
return [this.offset[0], this.offset[1], this.offset[2]];
|
|
94
|
+
}
|
|
95
|
+
setLocalOffset(offset) {
|
|
96
|
+
this.offset = [offset[0], offset[1], offset[2]];
|
|
97
|
+
this.live = { position: vAdd(this.rest.position, vApplyQuat(this.offset, this.rest.quaternion)), quaternion: quatMultiply(this.rest.quaternion, this.rotation) };
|
|
98
|
+
}
|
|
99
|
+
setLocalRotation(quaternion) {
|
|
100
|
+
this.rotation = [quaternion[0], quaternion[1], quaternion[2], quaternion[3]];
|
|
101
|
+
this.live = { position: vAdd(this.rest.position, vApplyQuat(this.offset, this.rest.quaternion)), quaternion: quatMultiply(this.rest.quaternion, this.rotation) };
|
|
102
|
+
}
|
|
103
|
+
setWorldPose(pose) {
|
|
104
|
+
this.live = copyPose(pose);
|
|
105
|
+
}
|
|
106
|
+
setEffect(effect) {
|
|
107
|
+
this.effect = { ...this.effect, ...effect };
|
|
108
|
+
}
|
|
109
|
+
recaptureRest() {
|
|
110
|
+
this.rest = copyPose(this.live);
|
|
111
|
+
this.offset = [0, 0, 0];
|
|
112
|
+
this.rotation = [0, 0, 0, 1];
|
|
113
|
+
}
|
|
114
|
+
beginHold() {
|
|
115
|
+
this.held = true;
|
|
116
|
+
}
|
|
117
|
+
endHold(release) {
|
|
118
|
+
this.held = false;
|
|
119
|
+
this.releases.push({ linearVelocity: [...release.linearVelocity], angularVelocity: [...release.angularVelocity] });
|
|
120
|
+
}
|
|
121
|
+
get isHeld() {
|
|
122
|
+
return this.held;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function copyPose(pose) {
|
|
126
|
+
return { position: [pose.position[0], pose.position[1], pose.position[2]], quaternion: [pose.quaternion[0], pose.quaternion[1], pose.quaternion[2], pose.quaternion[3]] };
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=memory-ports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-ports.js","sourceRoot":"","sources":["../src/memory-ports.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Engine-free ports over sphere targets: the reference `HitTester` and\n * `TransformPort` a trace replay, a synthetic recording, a harness run\n * without a host, or a test can drive with no engine at all. They apply the\n * contract exactly as every binding must (`hitTesterContractCases()` and\n * `transformPortContractCases()` pass over them), so a difference between a\n * binding and these is a difference in the binding.\n */\nimport type { PoseTuple, QuatTuple, RayTuple, Vec3Tuple } from \"@realitycollective/webxr-input\";\nimport { coneHitForSpheres, type SphereTarget } from \"./gaze.js\";\nimport { quatMultiply, rayPointDistance, surfacePointOnSphere, vAdd, vApplyQuat } from \"./math.js\";\nimport type { HitTester, HoldRelease, InteractableHit, TransformPort } from \"./ports.js\";\n\n/** IWSDK's radius for a target that declares none. */\nexport const MEMORY_DEFAULT_RADIUS = 0.1;\n\ninterface MemoryTarget {\n id: string;\n /** The port the target's live pose is read from, when one was placed with a port. */\n port?: MemoryTransformPort;\n centre: Vec3Tuple;\n radius: number;\n visible: boolean;\n}\n\n/** A sphere-target hit tester: IWSDK's `EntityHitTester` rules over plain spheres. */\nexport class MemoryHitTester implements HitTester {\n private readonly targets = new Map<string, MemoryTarget>();\n\n /** Place a shown sphere target; a port makes its centre follow the port's live pose. */\n place(id: string, centre: Vec3Tuple, radius: number = MEMORY_DEFAULT_RADIUS, port?: MemoryTransformPort): void {\n this.targets.set(id, { id, centre: [centre[0], centre[1], centre[2]], radius, visible: true, ...(port ? { port } : {}) });\n }\n\n remove(id: string): void {\n this.targets.delete(id);\n }\n\n setVisible(id: string, visible: boolean): void {\n const target = this.targets.get(id);\n if (target) target.visible = visible;\n }\n\n clear(): void {\n this.targets.clear();\n }\n\n private centreOf(target: MemoryTarget): Vec3Tuple {\n return target.port ? target.port.getWorldPose().position : target.centre;\n }\n\n hitRay(ray: RayTuple): InteractableHit | null {\n let best: InteractableHit | null = null;\n for (const target of this.targets.values()) {\n if (!target.visible) continue;\n const centre = this.centreOf(target);\n const { distance, t } = rayPointDistance(ray, centre);\n if (t <= 0 || distance > target.radius) continue;\n if (best === null || t < best.distance) {\n // Where the ray enters the sphere.\n const inside = Math.sqrt(Math.max(0, target.radius * target.radius - distance * distance));\n const enter = t - inside;\n best = {\n interactableId: target.id,\n distance: enter,\n point: [ray.origin[0] + ray.direction[0] * enter, ray.origin[1] + ray.direction[1] * enter, ray.origin[2] + ray.direction[2] * enter],\n };\n }\n }\n return best;\n }\n\n hitProximity(point: Vec3Tuple, radius: number): InteractableHit | null {\n let best: InteractableHit | null = null;\n for (const target of this.targets.values()) {\n if (!target.visible) continue;\n const centre = this.centreOf(target);\n const surface = Math.max(0, Math.hypot(centre[0] - point[0], centre[1] - point[1], centre[2] - point[2]) - target.radius);\n if (surface <= radius && (best === null || surface < best.distance)) {\n best = { interactableId: target.id, distance: surface, point: surfacePointOnSphere(centre, target.radius, point) };\n }\n }\n return best;\n }\n\n hitCone(ray: RayTuple, halfAngle: number, maxLength: number): InteractableHit | null {\n const spheres: SphereTarget[] = [];\n for (const target of this.targets.values()) {\n if (target.visible) spheres.push({ id: target.id, center: this.centreOf(target), radius: target.radius });\n }\n const hit = coneHitForSpheres(ray, spheres, halfAngle, maxLength);\n return hit ? { interactableId: hit.interactableId, distance: hit.distance, point: hit.point } : null;\n }\n}\n\n/** A transform port over a pose it keeps itself, with the held-pose members recorded. */\nexport class MemoryTransformPort implements TransformPort {\n private rest: PoseTuple;\n private live: PoseTuple;\n private offset: Vec3Tuple = [0, 0, 0];\n private rotation: QuatTuple = [0, 0, 0, 1];\n private held = false;\n /** Every release `endHold` received, in order. */\n readonly releases: HoldRelease[] = [];\n /** The last effect written. */\n effect: { scale?: number; emissive?: number } = {};\n\n constructor(rest: PoseTuple) {\n this.rest = copyPose(rest);\n this.live = copyPose(rest);\n }\n\n getWorldPose(): PoseTuple {\n return copyPose(this.live);\n }\n\n getRestWorldPose(): PoseTuple {\n return copyPose(this.rest);\n }\n\n getLocalOffset(): Vec3Tuple {\n return [this.offset[0], this.offset[1], this.offset[2]];\n }\n\n setLocalOffset(offset: Vec3Tuple): void {\n this.offset = [offset[0], offset[1], offset[2]];\n this.live = { position: vAdd(this.rest.position, vApplyQuat(this.offset, this.rest.quaternion)), quaternion: quatMultiply(this.rest.quaternion, this.rotation) };\n }\n\n setLocalRotation(quaternion: QuatTuple): void {\n this.rotation = [quaternion[0], quaternion[1], quaternion[2], quaternion[3]];\n this.live = { position: vAdd(this.rest.position, vApplyQuat(this.offset, this.rest.quaternion)), quaternion: quatMultiply(this.rest.quaternion, this.rotation) };\n }\n\n setWorldPose(pose: PoseTuple): void {\n this.live = copyPose(pose);\n }\n\n setEffect(effect: { scale?: number; emissive?: number }): void {\n this.effect = { ...this.effect, ...effect };\n }\n\n recaptureRest(): void {\n this.rest = copyPose(this.live);\n this.offset = [0, 0, 0];\n this.rotation = [0, 0, 0, 1];\n }\n\n beginHold(): void {\n this.held = true;\n }\n\n endHold(release: HoldRelease): void {\n this.held = false;\n this.releases.push({ linearVelocity: [...release.linearVelocity] as Vec3Tuple, angularVelocity: [...release.angularVelocity] as Vec3Tuple });\n }\n\n get isHeld(): boolean {\n return this.held;\n }\n}\n\nfunction copyPose(pose: PoseTuple): PoseTuple {\n return { position: [pose.position[0], pose.position[1], pose.position[2]], quaternion: [pose.quaternion[0], pose.quaternion[1], pose.quaternion[2], pose.quaternion[3]] };\n}\n"]}
|
|
@@ -221,6 +221,54 @@ const NEAR_POINTER_CASES = [
|
|
|
221
221
|
expectActive(r, "touch", "once released, touch takes the source");
|
|
222
222
|
},
|
|
223
223
|
},
|
|
224
|
+
{
|
|
225
|
+
name: "a panel nearer than the object takes the ray, the object is not hovered, and the visuals name the panel",
|
|
226
|
+
run(subject) {
|
|
227
|
+
// IWSDK's MultiPointer runs over every pointer-event object, panels
|
|
228
|
+
// included; the UI Extensions hosts offer their panels to the same
|
|
229
|
+
// arbiter. Here a stand-in panel set offers a ray hit nearer than the
|
|
230
|
+
// target 1 m out.
|
|
231
|
+
const r = rig(subject);
|
|
232
|
+
const panels = r.runtime.getPointerArbiter().registerSet("panels", "panel");
|
|
233
|
+
panels.offer("right", "ray", { targetId: "info", point: [0, 1, -0.5], distance: 0.5 });
|
|
234
|
+
const types = r.frame({ ray: "at" });
|
|
235
|
+
expectActive(r, "ray", "a ray through a panel and an object behind it");
|
|
236
|
+
assert(!types.includes("hoverEnter"), "the object behind the panel must not be hovered");
|
|
237
|
+
const v = r.visuals[0];
|
|
238
|
+
assert(v.ray && v.cursor && v.targetKind === "panel" && v.targetId === "info", `the visuals must name the panel, got ${JSON.stringify(v)}`);
|
|
239
|
+
assert(Math.abs(v.hitDistance - 0.5) < 1e-9, "the visuals carry the panel hit's distance");
|
|
240
|
+
panels.offer("right", "ray", null);
|
|
241
|
+
const after = r.frame({ ray: "at" });
|
|
242
|
+
assert(after.includes("hoverEnter"), "with the panel gone the object behind it is hovered");
|
|
243
|
+
assert(r.visuals[0].targetKind === "object", "the visuals name the object again");
|
|
244
|
+
panels.dispose();
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: "a fingertip on a panel retires the ray over an object, and a fingertip on an object retires the ray over a panel",
|
|
249
|
+
run(subject) {
|
|
250
|
+
const r = rig(subject);
|
|
251
|
+
const panels = r.runtime.getPointerArbiter().registerSet("panels", "panel");
|
|
252
|
+
r.frame({ ray: "at" });
|
|
253
|
+
expectActive(r, "ray", "the ray on the object");
|
|
254
|
+
panels.offer("right", "touch", { targetId: "info", point: [0.3, 1, -0.5], distance: 0.01 });
|
|
255
|
+
let types = r.frame({ ray: "at" });
|
|
256
|
+
expectActive(r, "touch", "a fingertip touching a panel while the ray is on an object");
|
|
257
|
+
assert(types.includes("hoverExit"), "the object under the ray loses its hover when the panel's touch takes the hand");
|
|
258
|
+
let v = r.visuals[0];
|
|
259
|
+
assert(!v.ray && v.cursor && v.targetKind === "panel", `the ray hides and the cursor sits on the panel, got ${JSON.stringify(v)}`);
|
|
260
|
+
panels.offer("right", "touch", null);
|
|
261
|
+
panels.offer("right", "ray", { targetId: "info", point: [0, 1, -0.5], distance: 0.5 });
|
|
262
|
+
r.frame({ ray: "at" });
|
|
263
|
+
expectActive(r, "ray", "the ray on the panel");
|
|
264
|
+
types = r.frame({ tip: outside(0.05), ray: "at" });
|
|
265
|
+
expectActive(r, "touch", "a fingertip touching the object while the ray is on a panel");
|
|
266
|
+
v = r.visuals[0];
|
|
267
|
+
assert(!v.ray && v.cursor && v.targetKind === "object" && v.targetId === "target", `the ray over the panel hides and the cursor sits on the object, got ${JSON.stringify(v)}`);
|
|
268
|
+
assert(types.includes("hoverEnter"), "the touched object is hovered");
|
|
269
|
+
panels.dispose();
|
|
270
|
+
},
|
|
271
|
+
},
|
|
224
272
|
{
|
|
225
273
|
name: "a controller's ray origin presses by touch exactly as a fingertip does",
|
|
226
274
|
run(subject) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"near-pointer-contract-cases.js","sourceRoot":"","sources":["../src/near-pointer-contract-cases.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The shared near-pointer conformance suite: the touch press, the grip grab,\n * pointer priority with its selection lock, and the ray and cursor visuals\n * (`near-pointer.ts`), run through a binding's own `HitTester` so every\n * platform proves the same rule over its own scene queries.\n *\n * Runner-free, like `hitTesterContractCases()`: an adapter's test file is a\n * loop over these cases with the same subject factory it already uses for\n * the hit-tester suite. The suite drives an `InteractionRuntime` itself, with\n * a scripted provider, so the only platform code under test is the hit\n * tester and the targets the driver places.\n *\n * Every distance below is metres to the target's SURFACE, the quantity the\n * touch and grab pointers compare (IWSDK's sphere intersector reports the\n * distance from the fingertip to the point on the mesh).\n */\nimport {\n NO_CAPABILITIES,\n type InputCapabilities,\n type InputProvider,\n type InputSourceSnapshot,\n type Vec3Tuple,\n} from \"@realitycollective/webxr-input\";\nimport type { HitTesterContractSubject } from \"./contract-cases.js\";\nimport type { InteractionEvent } from \"./events.js\";\nimport { NEAR_POINTER_DEFAULTS, type PointerVisuals } from \"./near-pointer.js\";\nimport { InteractionRuntime } from \"./runtime.js\";\n\n/** One check a binding's hit tester must pass under the near-pointer rule. */\nexport interface NearPointerContractCase {\n name: string;\n run(subject: HitTesterContractSubject): void;\n}\n\n/**\n * The shared near-pointer conformance suite. An adapter's test file:\n *\n * ```ts\n * for (const contractCase of nearPointerContractCases()) {\n * it(contractCase.name, () => contractCase.run(makeSubject()));\n * }\n * ```\n */\nexport function nearPointerContractCases(): readonly NearPointerContractCase[] {\n return NEAR_POINTER_CASES;\n}\n\n/** A provider whose sources a case sets frame by frame. */\nclass ScriptedProvider implements InputProvider {\n sources: InputSourceSnapshot[] = [];\n private readonly capabilities: InputCapabilities = {\n ...NO_CAPABILITIES,\n rays: true,\n pokes: true,\n grabs: \"poseOnly\",\n handJoints: true,\n pinch: true,\n headPose: false,\n gaze: false,\n };\n\n getCapabilities(): InputCapabilities {\n return this.capabilities;\n }\n\n onCapabilitiesChanged(): () => void {\n return () => undefined;\n }\n\n onSourcesChanged(): () => void {\n return () => undefined;\n }\n\n sample(): readonly InputSourceSnapshot[] {\n return this.sources;\n }\n}\n\nconst CENTRE: Vec3Tuple = [0, 1, -1];\nconst RADIUS = 0.1;\n/** A ray from the viewer straight through the target. */\nconst RAY_AT = { origin: [0, 1, 0] as Vec3Tuple, direction: [0, 0, -1] as Vec3Tuple };\n/** A ray pointing away from everything. */\nconst RAY_AWAY = { origin: [0, 1, 0] as Vec3Tuple, direction: [0, 0, 1] as Vec3Tuple };\nconst FAR: Vec3Tuple = [0, 1, 1];\n\n/** A point `distance` metres outside the target's surface, on its near side. */\nfunction outside(distance: number): Vec3Tuple {\n return [CENTRE[0], CENTRE[1], CENTRE[2] + RADIUS + distance];\n}\n\ninterface HandFrame {\n tip?: Vec3Tuple;\n grip?: Vec3Tuple;\n ray?: \"at\" | \"away\" | \"none\";\n select?: number;\n squeeze?: number;\n kind?: \"hand\" | \"controller\";\n}\n\nfunction hand(frame: HandFrame): InputSourceSnapshot {\n const snapshot: InputSourceSnapshot = {\n id: \"right\",\n kind: frame.kind ?? \"hand\",\n handedness: \"right\",\n select: frame.select ?? 0,\n squeeze: frame.squeeze ?? 0,\n };\n const ray = frame.ray ?? \"away\";\n if (ray !== \"none\") snapshot.ray = ray === \"at\" ? { ...RAY_AT } : { ...RAY_AWAY };\n snapshot.indexTip = frame.tip ?? FAR;\n snapshot.gripPose = { position: frame.grip ?? FAR, quaternion: [0, 0, 0, 1] };\n return snapshot;\n}\n\ninterface Rig {\n runtime: InteractionRuntime;\n provider: ScriptedProvider;\n events: InteractionEvent[];\n visuals: PointerVisuals[];\n /** Feed one frame and return the event types it produced. */\n frame(frame: HandFrame): string[];\n}\n\nfunction rig(subject: HitTesterContractSubject, behaviours: Array<{ kind: \"press\" } | { kind: \"grab\" }> = [{ kind: \"press\" }]): Rig {\n subject.driver.place(\"target\", CENTRE, RADIUS);\n const provider = new ScriptedProvider();\n const runtime = new InteractionRuntime({ provider, hitTester: subject.hitTester, velocity: false });\n const events: InteractionEvent[] = [];\n const visuals: PointerVisuals[] = [];\n runtime.onEvent((event) => events.push(event));\n runtime.onPointerVisuals((list) => {\n visuals.length = 0;\n visuals.push(...list);\n });\n runtime.registerInteractable({ id: \"target\", behaviours });\n return {\n runtime,\n provider,\n events,\n visuals,\n frame(frame) {\n const before = events.length;\n provider.sources = [hand(frame)];\n runtime.update(1 / 60);\n return events.slice(before).map((event) => event.type);\n },\n };\n}\n\nfunction assert(condition: boolean, message: string): asserts condition {\n if (!condition) throw new Error(message);\n}\n\nfunction expectActive(r: Rig, kind: string | null, when: string): void {\n const active = r.runtime.getActivePointer(\"right\");\n assert(active === kind, `${when}: the active pointer must be ${String(kind)}, got ${String(active)}`);\n}\n\nconst D = NEAR_POINTER_DEFAULTS;\n\nconst NEAR_POINTER_CASES: readonly NearPointerContractCase[] = [\n {\n name: \"a fingertip approaching from outside hovers at 0.15 m, presses at 0.02 m, releases on the way out and stops hovering beyond 0.2 m\",\n run(subject) {\n const r = rig(subject);\n r.frame({ tip: outside(0.3) });\n expectActive(r, null, \"at 0.3 m\");\n let types = r.frame({ tip: outside(D.touchHoverEnter - 0.01) });\n expectActive(r, \"touch\", \"inside the hover enter distance\");\n assert(types.includes(\"hoverEnter\"), \"entering the hover band must raise hoverEnter\");\n types = r.frame({ tip: outside(0.05) });\n assert(!types.includes(\"pressStart\"), \"5 cm out must not press\");\n types = r.frame({ tip: outside(D.touchDown) });\n assert(types.includes(\"pressStart\"), \"reaching the press distance must raise pressStart\");\n assert(r.runtime.getState(\"target\")?.pressed === true, \"the target must be pressed\");\n types = r.frame({ tip: outside(0.005) });\n assert(!types.includes(\"pressEnd\") && !types.includes(\"pressStart\"), \"going deeper must change nothing\");\n types = r.frame({ tip: outside(D.touchDown + 0.01) });\n assert(types.includes(\"pressEnd\"), \"coming back out past the press distance must release\");\n r.frame({ tip: outside(D.touchHoverEnter + 0.02) });\n expectActive(r, \"touch\", \"between enter and exit while hovering (hysteresis)\");\n types = r.frame({ tip: outside(D.touchHoverExit + 0.01) });\n expectActive(r, null, \"beyond the exit distance\");\n assert(types.includes(\"hoverExit\"), \"leaving the exit distance must raise hoverExit\");\n },\n },\n {\n name: \"a fingertip first seen inside the press band never presses until it has been outside it\",\n run(subject) {\n const r = rig(subject);\n let types = r.frame({ tip: outside(0.01) });\n assert(types.includes(\"hoverEnter\") && !types.includes(\"pressStart\"), \"a fingertip that appears inside the band hovers but must not press\");\n types = r.frame({ tip: outside(0.0) });\n assert(!types.includes(\"pressStart\"), \"staying inside must not press\");\n r.frame({ tip: outside(0.05) });\n types = r.frame({ tip: outside(0.015) });\n assert(types.includes(\"pressStart\"), \"coming back in from outside must press\");\n },\n },\n {\n name: \"a grip within 0.07 m of the surface makes grab the active pointer and a squeeze grabs that target\",\n run(subject) {\n const r = rig(subject, [{ kind: \"grab\" }]);\n r.frame({ grip: outside(D.grabRadius - 0.02), ray: \"at\" });\n expectActive(r, \"grab\", \"grip inside the grab radius, ray also hitting\");\n let types = r.frame({ grip: outside(D.grabRadius - 0.02), ray: \"at\", squeeze: 1 });\n assert(types.includes(\"grabStart\"), \"a squeeze while grab owns the hand must grab\");\n types = r.frame({ grip: outside(D.grabRadius - 0.02), ray: \"at\", squeeze: 0 });\n assert(types.includes(\"grabEnd\"), \"releasing the squeeze must end the grab\");\n r.frame({ grip: outside(D.grabRadius + 0.01), ray: \"away\" });\n expectActive(r, null, \"grip outside the grab radius with no ray hit\");\n },\n },\n {\n name: \"touch beats grab beats ray\",\n run(subject) {\n const r = rig(subject);\n r.frame({ tip: outside(0.1), grip: outside(0.05), ray: \"at\" });\n expectActive(r, \"touch\", \"fingertip, grip and ray all on the target\");\n r.frame({ grip: outside(0.05), ray: \"at\" });\n expectActive(r, \"grab\", \"grip and ray on the target\");\n r.frame({ ray: \"at\" });\n expectActive(r, \"ray\", \"only the ray on the target\");\n r.frame({ ray: \"away\" });\n expectActive(r, null, \"nothing on the target\");\n },\n },\n {\n name: \"the ray and its cursor show only while the ray owns the source, and the cursor sits at the active pointer's hit\",\n run(subject) {\n const r = rig(subject);\n r.frame({ ray: \"at\" });\n let v = r.visuals[0]!;\n assert(v.ray && v.cursor && v.cursorPoint !== null, \"with the ray active, the ray and its cursor show\");\n assert(Math.abs(v.cursorPoint![2] - (CENTRE[2] + RADIUS)) <= RADIUS + 0.05, \"the cursor sits near the ray's hit on the target\");\n r.frame({ tip: outside(0.05), ray: \"at\" });\n v = r.visuals[0]!;\n assert(!v.ray, \"with touch active, the ray is hidden\");\n assert(v.cursor && v.cursorPoint !== null, \"with touch active, the cursor shows at the touch point\");\n assert(Math.abs(v.cursorPoint![2] - (CENTRE[2] + RADIUS)) <= RADIUS + 0.05, \"the cursor sits on the surface under the fingertip\");\n r.frame({ grip: outside(0.05), ray: \"at\" });\n v = r.visuals[0]!;\n assert(!v.ray && v.cursor, \"with grab active, the ray is hidden and the cursor shows\");\n r.frame({ ray: \"away\" });\n v = r.visuals[0]!;\n assert(v.ray && !v.cursor && v.cursorPoint === null, \"with nothing active, the ray shows and the cursor does not\");\n r.frame({ ray: \"none\" });\n v = r.visuals[0]!;\n assert(!v.ray && !v.cursor, \"a source with no ray draws no ray\");\n },\n },\n {\n name: \"a pointer that is pressing keeps the source until it releases\",\n run(subject) {\n const r = rig(subject);\n r.frame({ ray: \"at\" });\n let types = r.frame({ ray: \"at\", select: 1 });\n assert(types.includes(\"pressStart\"), \"a select on the ray target presses it\");\n r.frame({ tip: outside(0.05), ray: \"at\", select: 1 });\n expectActive(r, \"ray\", \"a fingertip entering the band while the ray presses\");\n types = r.frame({ tip: outside(0.05), ray: \"at\", select: 0 });\n assert(types.includes(\"pressEnd\"), \"releasing select ends the ray press\");\n r.frame({ tip: outside(0.05), ray: \"at\" });\n expectActive(r, \"touch\", \"once released, touch takes the source\");\n },\n },\n {\n name: \"a controller's ray origin presses by touch exactly as a fingertip does\",\n run(subject) {\n const r = rig(subject);\n r.frame({ kind: \"controller\", tip: outside(0.1) });\n expectActive(r, \"touch\", \"a controller nose inside the hover band\");\n const types = r.frame({ kind: \"controller\", tip: outside(0.01) });\n assert(types.includes(\"pressStart\"), \"a controller nose reaching the press distance presses\");\n },\n },\n];\n"]}
|
|
1
|
+
{"version":3,"file":"near-pointer-contract-cases.js","sourceRoot":"","sources":["../src/near-pointer-contract-cases.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EACL,eAAe,GAKhB,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EAAE,qBAAqB,EAAuB,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAQlD;;;;;;;;GAQG;AACH,MAAM,UAAU,wBAAwB;IACtC,OAAO,kBAAkB,CAAC;AAC5B,CAAC;AAED,2DAA2D;AAC3D,MAAM,gBAAgB;IACpB,OAAO,GAA0B,EAAE,CAAC;IACnB,YAAY,GAAsB;QACjD,GAAG,eAAe;QAClB,IAAI,EAAE,IAAI;QACV,KAAK,EAAE,IAAI;QACX,KAAK,EAAE,UAAU;QACjB,UAAU,EAAE,IAAI;QAChB,KAAK,EAAE,IAAI;QACX,QAAQ,EAAE,KAAK;QACf,IAAI,EAAE,KAAK;KACZ,CAAC;IAEF,eAAe;QACb,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,qBAAqB;QACnB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC;IACzB,CAAC;IAED,gBAAgB;QACd,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC;IACzB,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;CACF;AAED,MAAM,MAAM,GAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AACrC,MAAM,MAAM,GAAG,GAAG,CAAC;AACnB,yDAAyD;AACzD,MAAM,MAAM,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAc,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAc,EAAE,CAAC;AACtF,2CAA2C;AAC3C,MAAM,QAAQ,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAc,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAc,EAAE,CAAC;AACvF,MAAM,GAAG,GAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;AAEjC,gFAAgF;AAChF,SAAS,OAAO,CAAC,QAAgB;IAC/B,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,QAAQ,CAAC,CAAC;AAC/D,CAAC;AAWD,SAAS,IAAI,CAAC,KAAgB;IAC5B,MAAM,QAAQ,GAAwB;QACpC,EAAE,EAAE,OAAO;QACX,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,MAAM;QAC1B,UAAU,EAAE,OAAO;QACnB,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC;QACzB,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,CAAC;KAC5B,CAAC;IACF,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI,MAAM,CAAC;IAChC,IAAI,GAAG,KAAK,MAAM;QAAE,QAAQ,CAAC,GAAG,GAAG,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,QAAQ,EAAE,CAAC;IAClF,QAAQ,CAAC,QAAQ,GAAG,KAAK,CAAC,GAAG,IAAI,GAAG,CAAC;IACrC,QAAQ,CAAC,QAAQ,GAAG,EAAE,QAAQ,EAAE,KAAK,CAAC,IAAI,IAAI,GAAG,EAAE,UAAU,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;IAC9E,OAAO,QAAQ,CAAC;AAClB,CAAC;AAWD,SAAS,GAAG,CAAC,OAAiC,EAAE,aAA0D,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAC3H,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C,MAAM,QAAQ,GAAG,IAAI,gBAAgB,EAAE,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,kBAAkB,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;IACpG,MAAM,MAAM,GAAuB,EAAE,CAAC;IACtC,MAAM,OAAO,GAAqB,EAAE,CAAC;IACrC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/C,OAAO,CAAC,gBAAgB,CAAC,CAAC,IAAI,EAAE,EAAE;QAChC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;QACnB,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,oBAAoB,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;IAC3D,OAAO;QACL,OAAO;QACP,QAAQ;QACR,MAAM;QACN,OAAO;QACP,KAAK,CAAC,KAAK;YACT,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;YAC7B,QAAQ,CAAC,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YACjC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;YACvB,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzD,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,MAAM,CAAC,SAAkB,EAAE,OAAe;IACjD,IAAI,CAAC,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,YAAY,CAAC,CAAM,EAAE,IAAmB,EAAE,IAAY;IAC7D,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACnD,MAAM,CAAC,MAAM,KAAK,IAAI,EAAE,GAAG,IAAI,gCAAgC,MAAM,CAAC,IAAI,CAAC,SAAS,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AACxG,CAAC;AAED,MAAM,CAAC,GAAG,qBAAqB,CAAC;AAEhC,MAAM,kBAAkB,GAAuC;IAC7D;QACE,IAAI,EAAE,mIAAmI;QACzI,GAAG,CAAC,OAAO;YACT,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;YACvB,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC/B,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;YAClC,IAAI,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,eAAe,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;YAChE,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,iCAAiC,CAAC,CAAC;YAC5D,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,+CAA+C,CAAC,CAAC;YACtF,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACxC,MAAM,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,yBAAyB,CAAC,CAAC;YACjE,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YAC/C,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,mDAAmD,CAAC,CAAC;YAC1F,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,KAAK,IAAI,EAAE,4BAA4B,CAAC,CAAC;YACrF,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACzC,MAAM,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,kCAAkC,CAAC,CAAC;YACzG,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;YACtD,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,sDAAsD,CAAC,CAAC;YAC3F,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,eAAe,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;YACpD,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,oDAAoD,CAAC,CAAC;YAC/E,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,cAAc,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;YAC3D,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,0BAA0B,CAAC,CAAC;YAClD,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,gDAAgD,CAAC,CAAC;QACxF,CAAC;KACF;IACD;QACE,IAAI,EAAE,yFAAyF;QAC/F,GAAG,CAAC,OAAO;YACT,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;YACvB,IAAI,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC5C,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,oEAAoE,CAAC,CAAC;YAC5I,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACvC,MAAM,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,+BAA+B,CAAC,CAAC;YACvE,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAChC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACzC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,wCAAwC,CAAC,CAAC;QACjF,CAAC;KACF;IACD;QACE,IAAI,EAAE,mGAAmG;QACzG,GAAG,CAAC,OAAO;YACT,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;YAC3C,CAAC,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,UAAU,GAAG,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YAC3D,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,+CAA+C,CAAC,CAAC;YACzE,IAAI,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,UAAU,GAAG,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;YACnF,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,8CAA8C,CAAC,CAAC;YACpF,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,UAAU,GAAG,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;YAC/E,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,yCAAyC,CAAC,CAAC;YAC7E,CAAC,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,UAAU,GAAG,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;YAC7D,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,8CAA8C,CAAC,CAAC;QACxE,CAAC;KACF;IACD;QACE,IAAI,EAAE,4BAA4B;QAClC,GAAG,CAAC,OAAO;YACT,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;YACvB,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YAC/D,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,2CAA2C,CAAC,CAAC;YACtE,CAAC,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YAC5C,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,4BAA4B,CAAC,CAAC;YACtD,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YACvB,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,4BAA4B,CAAC,CAAC;YACrD,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;YACzB,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,uBAAuB,CAAC,CAAC;QACjD,CAAC;KACF;IACD;QACE,IAAI,EAAE,iHAAiH;QACvH,GAAG,CAAC,OAAO;YACT,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;YACvB,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC;YACtB,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,WAAW,KAAK,IAAI,EAAE,kDAAkD,CAAC,CAAC;YACxG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,WAAY,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,MAAM,GAAG,IAAI,EAAE,kDAAkD,CAAC,CAAC;YAChI,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YAC3C,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC;YAClB,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,sCAAsC,CAAC,CAAC;YACvD,MAAM,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,WAAW,KAAK,IAAI,EAAE,wDAAwD,CAAC,CAAC;YACrG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,WAAY,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,MAAM,GAAG,IAAI,EAAE,oDAAoD,CAAC,CAAC;YAClI,CAAC,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YAC5C,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC;YAClB,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,EAAE,0DAA0D,CAAC,CAAC;YACvF,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;YACzB,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC;YAClB,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,WAAW,KAAK,IAAI,EAAE,4DAA4D,CAAC,CAAC;YACnH,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;YACzB,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC;YAClB,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,mCAAmC,CAAC,CAAC;QACnE,CAAC;KACF;IACD;QACE,IAAI,EAAE,+DAA+D;QACrE,GAAG,CAAC,OAAO;YACT,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;YACvB,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YACvB,IAAI,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;YAC9C,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,uCAAuC,CAAC,CAAC;YAC9E,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;YACtD,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,qDAAqD,CAAC,CAAC;YAC9E,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;YAC9D,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,qCAAqC,CAAC,CAAC;YAC1E,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YAC3C,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,uCAAuC,CAAC,CAAC;QACpE,CAAC;KACF;IACD;QACE,IAAI,EAAE,yGAAyG;QAC/G,GAAG,CAAC,OAAO;YACT,oEAAoE;YACpE,mEAAmE;YACnE,sEAAsE;YACtE,kBAAkB;YAClB,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;YACvB,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC5E,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC;YACvF,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YACrC,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,+CAA+C,CAAC,CAAC;YACxE,MAAM,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,iDAAiD,CAAC,CAAC;YACzF,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC;YACxB,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,UAAU,KAAK,OAAO,IAAI,CAAC,CAAC,QAAQ,KAAK,MAAM,EAAE,wCAAwC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC5I,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,WAAY,GAAG,GAAG,CAAC,GAAG,IAAI,EAAE,4CAA4C,CAAC,CAAC;YAC5F,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;YACnC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YACrC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,qDAAqD,CAAC,CAAC;YAC5F,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,UAAU,KAAK,QAAQ,EAAE,mCAAmC,CAAC,CAAC;YACnF,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,CAAC;KACF;IACD;QACE,IAAI,EAAE,kHAAkH;QACxH,GAAG,CAAC,OAAO;YACT,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;YACvB,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC5E,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YACvB,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,uBAAuB,CAAC,CAAC;YAChD,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;YAC5F,IAAI,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YACnC,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,4DAA4D,CAAC,CAAC;YACvF,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,gFAAgF,CAAC,CAAC;YACtH,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC;YACtB,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,UAAU,KAAK,OAAO,EAAE,uDAAuD,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACnI,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;YACrC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC;YACvF,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YACvB,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,sBAAsB,CAAC,CAAC;YAC/C,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YACnD,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,6DAA6D,CAAC,CAAC;YACxF,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC;YAClB,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,UAAU,KAAK,QAAQ,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ,EAAE,uEAAuE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC/K,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,+BAA+B,CAAC,CAAC;YACtE,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,CAAC;KACF;IACD;QACE,IAAI,EAAE,wEAAwE;QAC9E,GAAG,CAAC,OAAO;YACT,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;YACvB,CAAC,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACnD,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,yCAAyC,CAAC,CAAC;YACpE,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAClE,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,uDAAuD,CAAC,CAAC;QAChG,CAAC;KACF;CACF,CAAC","sourcesContent":["/**\n * The shared near-pointer conformance suite: the touch press, the grip grab,\n * pointer priority with its selection lock, and the ray and cursor visuals\n * (`near-pointer.ts`), run through a binding's own `HitTester` so every\n * platform proves the same rule over its own scene queries.\n *\n * Runner-free, like `hitTesterContractCases()`: an adapter's test file is a\n * loop over these cases with the same subject factory it already uses for\n * the hit-tester suite. The suite drives an `InteractionRuntime` itself, with\n * a scripted provider, so the only platform code under test is the hit\n * tester and the targets the driver places.\n *\n * Every distance below is metres to the target's SURFACE, the quantity the\n * touch and grab pointers compare (IWSDK's sphere intersector reports the\n * distance from the fingertip to the point on the mesh).\n */\nimport {\n NO_CAPABILITIES,\n type InputCapabilities,\n type InputProvider,\n type InputSourceSnapshot,\n type Vec3Tuple,\n} from \"@realitycollective/webxr-input\";\nimport type { HitTesterContractSubject } from \"./contract-cases.js\";\nimport type { InteractionEvent } from \"./events.js\";\nimport { NEAR_POINTER_DEFAULTS, type PointerVisuals } from \"./near-pointer.js\";\nimport { InteractionRuntime } from \"./runtime.js\";\n\n/** One check a binding's hit tester must pass under the near-pointer rule. */\nexport interface NearPointerContractCase {\n name: string;\n run(subject: HitTesterContractSubject): void;\n}\n\n/**\n * The shared near-pointer conformance suite. An adapter's test file:\n *\n * ```ts\n * for (const contractCase of nearPointerContractCases()) {\n * it(contractCase.name, () => contractCase.run(makeSubject()));\n * }\n * ```\n */\nexport function nearPointerContractCases(): readonly NearPointerContractCase[] {\n return NEAR_POINTER_CASES;\n}\n\n/** A provider whose sources a case sets frame by frame. */\nclass ScriptedProvider implements InputProvider {\n sources: InputSourceSnapshot[] = [];\n private readonly capabilities: InputCapabilities = {\n ...NO_CAPABILITIES,\n rays: true,\n pokes: true,\n grabs: \"poseOnly\",\n handJoints: true,\n pinch: true,\n headPose: false,\n gaze: false,\n };\n\n getCapabilities(): InputCapabilities {\n return this.capabilities;\n }\n\n onCapabilitiesChanged(): () => void {\n return () => undefined;\n }\n\n onSourcesChanged(): () => void {\n return () => undefined;\n }\n\n sample(): readonly InputSourceSnapshot[] {\n return this.sources;\n }\n}\n\nconst CENTRE: Vec3Tuple = [0, 1, -1];\nconst RADIUS = 0.1;\n/** A ray from the viewer straight through the target. */\nconst RAY_AT = { origin: [0, 1, 0] as Vec3Tuple, direction: [0, 0, -1] as Vec3Tuple };\n/** A ray pointing away from everything. */\nconst RAY_AWAY = { origin: [0, 1, 0] as Vec3Tuple, direction: [0, 0, 1] as Vec3Tuple };\nconst FAR: Vec3Tuple = [0, 1, 1];\n\n/** A point `distance` metres outside the target's surface, on its near side. */\nfunction outside(distance: number): Vec3Tuple {\n return [CENTRE[0], CENTRE[1], CENTRE[2] + RADIUS + distance];\n}\n\ninterface HandFrame {\n tip?: Vec3Tuple;\n grip?: Vec3Tuple;\n ray?: \"at\" | \"away\" | \"none\";\n select?: number;\n squeeze?: number;\n kind?: \"hand\" | \"controller\";\n}\n\nfunction hand(frame: HandFrame): InputSourceSnapshot {\n const snapshot: InputSourceSnapshot = {\n id: \"right\",\n kind: frame.kind ?? \"hand\",\n handedness: \"right\",\n select: frame.select ?? 0,\n squeeze: frame.squeeze ?? 0,\n };\n const ray = frame.ray ?? \"away\";\n if (ray !== \"none\") snapshot.ray = ray === \"at\" ? { ...RAY_AT } : { ...RAY_AWAY };\n snapshot.indexTip = frame.tip ?? FAR;\n snapshot.gripPose = { position: frame.grip ?? FAR, quaternion: [0, 0, 0, 1] };\n return snapshot;\n}\n\ninterface Rig {\n runtime: InteractionRuntime;\n provider: ScriptedProvider;\n events: InteractionEvent[];\n visuals: PointerVisuals[];\n /** Feed one frame and return the event types it produced. */\n frame(frame: HandFrame): string[];\n}\n\nfunction rig(subject: HitTesterContractSubject, behaviours: Array<{ kind: \"press\" } | { kind: \"grab\" }> = [{ kind: \"press\" }]): Rig {\n subject.driver.place(\"target\", CENTRE, RADIUS);\n const provider = new ScriptedProvider();\n const runtime = new InteractionRuntime({ provider, hitTester: subject.hitTester, velocity: false });\n const events: InteractionEvent[] = [];\n const visuals: PointerVisuals[] = [];\n runtime.onEvent((event) => events.push(event));\n runtime.onPointerVisuals((list) => {\n visuals.length = 0;\n visuals.push(...list);\n });\n runtime.registerInteractable({ id: \"target\", behaviours });\n return {\n runtime,\n provider,\n events,\n visuals,\n frame(frame) {\n const before = events.length;\n provider.sources = [hand(frame)];\n runtime.update(1 / 60);\n return events.slice(before).map((event) => event.type);\n },\n };\n}\n\nfunction assert(condition: boolean, message: string): asserts condition {\n if (!condition) throw new Error(message);\n}\n\nfunction expectActive(r: Rig, kind: string | null, when: string): void {\n const active = r.runtime.getActivePointer(\"right\");\n assert(active === kind, `${when}: the active pointer must be ${String(kind)}, got ${String(active)}`);\n}\n\nconst D = NEAR_POINTER_DEFAULTS;\n\nconst NEAR_POINTER_CASES: readonly NearPointerContractCase[] = [\n {\n name: \"a fingertip approaching from outside hovers at 0.15 m, presses at 0.02 m, releases on the way out and stops hovering beyond 0.2 m\",\n run(subject) {\n const r = rig(subject);\n r.frame({ tip: outside(0.3) });\n expectActive(r, null, \"at 0.3 m\");\n let types = r.frame({ tip: outside(D.touchHoverEnter - 0.01) });\n expectActive(r, \"touch\", \"inside the hover enter distance\");\n assert(types.includes(\"hoverEnter\"), \"entering the hover band must raise hoverEnter\");\n types = r.frame({ tip: outside(0.05) });\n assert(!types.includes(\"pressStart\"), \"5 cm out must not press\");\n types = r.frame({ tip: outside(D.touchDown) });\n assert(types.includes(\"pressStart\"), \"reaching the press distance must raise pressStart\");\n assert(r.runtime.getState(\"target\")?.pressed === true, \"the target must be pressed\");\n types = r.frame({ tip: outside(0.005) });\n assert(!types.includes(\"pressEnd\") && !types.includes(\"pressStart\"), \"going deeper must change nothing\");\n types = r.frame({ tip: outside(D.touchDown + 0.01) });\n assert(types.includes(\"pressEnd\"), \"coming back out past the press distance must release\");\n r.frame({ tip: outside(D.touchHoverEnter + 0.02) });\n expectActive(r, \"touch\", \"between enter and exit while hovering (hysteresis)\");\n types = r.frame({ tip: outside(D.touchHoverExit + 0.01) });\n expectActive(r, null, \"beyond the exit distance\");\n assert(types.includes(\"hoverExit\"), \"leaving the exit distance must raise hoverExit\");\n },\n },\n {\n name: \"a fingertip first seen inside the press band never presses until it has been outside it\",\n run(subject) {\n const r = rig(subject);\n let types = r.frame({ tip: outside(0.01) });\n assert(types.includes(\"hoverEnter\") && !types.includes(\"pressStart\"), \"a fingertip that appears inside the band hovers but must not press\");\n types = r.frame({ tip: outside(0.0) });\n assert(!types.includes(\"pressStart\"), \"staying inside must not press\");\n r.frame({ tip: outside(0.05) });\n types = r.frame({ tip: outside(0.015) });\n assert(types.includes(\"pressStart\"), \"coming back in from outside must press\");\n },\n },\n {\n name: \"a grip within 0.07 m of the surface makes grab the active pointer and a squeeze grabs that target\",\n run(subject) {\n const r = rig(subject, [{ kind: \"grab\" }]);\n r.frame({ grip: outside(D.grabRadius - 0.02), ray: \"at\" });\n expectActive(r, \"grab\", \"grip inside the grab radius, ray also hitting\");\n let types = r.frame({ grip: outside(D.grabRadius - 0.02), ray: \"at\", squeeze: 1 });\n assert(types.includes(\"grabStart\"), \"a squeeze while grab owns the hand must grab\");\n types = r.frame({ grip: outside(D.grabRadius - 0.02), ray: \"at\", squeeze: 0 });\n assert(types.includes(\"grabEnd\"), \"releasing the squeeze must end the grab\");\n r.frame({ grip: outside(D.grabRadius + 0.01), ray: \"away\" });\n expectActive(r, null, \"grip outside the grab radius with no ray hit\");\n },\n },\n {\n name: \"touch beats grab beats ray\",\n run(subject) {\n const r = rig(subject);\n r.frame({ tip: outside(0.1), grip: outside(0.05), ray: \"at\" });\n expectActive(r, \"touch\", \"fingertip, grip and ray all on the target\");\n r.frame({ grip: outside(0.05), ray: \"at\" });\n expectActive(r, \"grab\", \"grip and ray on the target\");\n r.frame({ ray: \"at\" });\n expectActive(r, \"ray\", \"only the ray on the target\");\n r.frame({ ray: \"away\" });\n expectActive(r, null, \"nothing on the target\");\n },\n },\n {\n name: \"the ray and its cursor show only while the ray owns the source, and the cursor sits at the active pointer's hit\",\n run(subject) {\n const r = rig(subject);\n r.frame({ ray: \"at\" });\n let v = r.visuals[0]!;\n assert(v.ray && v.cursor && v.cursorPoint !== null, \"with the ray active, the ray and its cursor show\");\n assert(Math.abs(v.cursorPoint![2] - (CENTRE[2] + RADIUS)) <= RADIUS + 0.05, \"the cursor sits near the ray's hit on the target\");\n r.frame({ tip: outside(0.05), ray: \"at\" });\n v = r.visuals[0]!;\n assert(!v.ray, \"with touch active, the ray is hidden\");\n assert(v.cursor && v.cursorPoint !== null, \"with touch active, the cursor shows at the touch point\");\n assert(Math.abs(v.cursorPoint![2] - (CENTRE[2] + RADIUS)) <= RADIUS + 0.05, \"the cursor sits on the surface under the fingertip\");\n r.frame({ grip: outside(0.05), ray: \"at\" });\n v = r.visuals[0]!;\n assert(!v.ray && v.cursor, \"with grab active, the ray is hidden and the cursor shows\");\n r.frame({ ray: \"away\" });\n v = r.visuals[0]!;\n assert(v.ray && !v.cursor && v.cursorPoint === null, \"with nothing active, the ray shows and the cursor does not\");\n r.frame({ ray: \"none\" });\n v = r.visuals[0]!;\n assert(!v.ray && !v.cursor, \"a source with no ray draws no ray\");\n },\n },\n {\n name: \"a pointer that is pressing keeps the source until it releases\",\n run(subject) {\n const r = rig(subject);\n r.frame({ ray: \"at\" });\n let types = r.frame({ ray: \"at\", select: 1 });\n assert(types.includes(\"pressStart\"), \"a select on the ray target presses it\");\n r.frame({ tip: outside(0.05), ray: \"at\", select: 1 });\n expectActive(r, \"ray\", \"a fingertip entering the band while the ray presses\");\n types = r.frame({ tip: outside(0.05), ray: \"at\", select: 0 });\n assert(types.includes(\"pressEnd\"), \"releasing select ends the ray press\");\n r.frame({ tip: outside(0.05), ray: \"at\" });\n expectActive(r, \"touch\", \"once released, touch takes the source\");\n },\n },\n {\n name: \"a panel nearer than the object takes the ray, the object is not hovered, and the visuals name the panel\",\n run(subject) {\n // IWSDK's MultiPointer runs over every pointer-event object, panels\n // included; the UI Extensions hosts offer their panels to the same\n // arbiter. Here a stand-in panel set offers a ray hit nearer than the\n // target 1 m out.\n const r = rig(subject);\n const panels = r.runtime.getPointerArbiter().registerSet(\"panels\", \"panel\");\n panels.offer(\"right\", \"ray\", { targetId: \"info\", point: [0, 1, -0.5], distance: 0.5 });\n const types = r.frame({ ray: \"at\" });\n expectActive(r, \"ray\", \"a ray through a panel and an object behind it\");\n assert(!types.includes(\"hoverEnter\"), \"the object behind the panel must not be hovered\");\n const v = r.visuals[0]!;\n assert(v.ray && v.cursor && v.targetKind === \"panel\" && v.targetId === \"info\", `the visuals must name the panel, got ${JSON.stringify(v)}`);\n assert(Math.abs(v.hitDistance! - 0.5) < 1e-9, \"the visuals carry the panel hit's distance\");\n panels.offer(\"right\", \"ray\", null);\n const after = r.frame({ ray: \"at\" });\n assert(after.includes(\"hoverEnter\"), \"with the panel gone the object behind it is hovered\");\n assert(r.visuals[0]!.targetKind === \"object\", \"the visuals name the object again\");\n panels.dispose();\n },\n },\n {\n name: \"a fingertip on a panel retires the ray over an object, and a fingertip on an object retires the ray over a panel\",\n run(subject) {\n const r = rig(subject);\n const panels = r.runtime.getPointerArbiter().registerSet(\"panels\", \"panel\");\n r.frame({ ray: \"at\" });\n expectActive(r, \"ray\", \"the ray on the object\");\n panels.offer(\"right\", \"touch\", { targetId: \"info\", point: [0.3, 1, -0.5], distance: 0.01 });\n let types = r.frame({ ray: \"at\" });\n expectActive(r, \"touch\", \"a fingertip touching a panel while the ray is on an object\");\n assert(types.includes(\"hoverExit\"), \"the object under the ray loses its hover when the panel's touch takes the hand\");\n let v = r.visuals[0]!;\n assert(!v.ray && v.cursor && v.targetKind === \"panel\", `the ray hides and the cursor sits on the panel, got ${JSON.stringify(v)}`);\n panels.offer(\"right\", \"touch\", null);\n panels.offer(\"right\", \"ray\", { targetId: \"info\", point: [0, 1, -0.5], distance: 0.5 });\n r.frame({ ray: \"at\" });\n expectActive(r, \"ray\", \"the ray on the panel\");\n types = r.frame({ tip: outside(0.05), ray: \"at\" });\n expectActive(r, \"touch\", \"a fingertip touching the object while the ray is on a panel\");\n v = r.visuals[0]!;\n assert(!v.ray && v.cursor && v.targetKind === \"object\" && v.targetId === \"target\", `the ray over the panel hides and the cursor sits on the object, got ${JSON.stringify(v)}`);\n assert(types.includes(\"hoverEnter\"), \"the touched object is hovered\");\n panels.dispose();\n },\n },\n {\n name: \"a controller's ray origin presses by touch exactly as a fingertip does\",\n run(subject) {\n const r = rig(subject);\n r.frame({ kind: \"controller\", tip: outside(0.1) });\n expectActive(r, \"touch\", \"a controller nose inside the hover band\");\n const types = r.frame({ kind: \"controller\", tip: outside(0.01) });\n assert(types.includes(\"pressStart\"), \"a controller nose reaching the press distance presses\");\n },\n },\n];\n"]}
|