gps-plus-slam-app-framework 1.0.7 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/README.md +69 -15
  2. package/dist/{app-selectors-BeU0_ab8.d.ts → app-selectors-CcZ5UV-8.d.ts} +1 -1
  3. package/dist/ar/bresenham3d.d.ts +2 -0
  4. package/dist/ar/bresenham3d.js +81 -0
  5. package/dist/ar/camera-blit-capture.d.ts +1 -1
  6. package/dist/ar/camera-blit-capture.js +30 -2
  7. package/dist/ar/capability-checker.d.ts +2 -0
  8. package/dist/ar/capability-checker.js +25 -0
  9. package/dist/ar/capture-failure-tracker.d.ts +1 -1
  10. package/dist/ar/chromium-camera-access-workaround.d.ts +2 -2
  11. package/dist/ar/chromium-camera-access-workaround.js +197 -8
  12. package/dist/ar/depth-sampler.d.ts +3 -3
  13. package/dist/ar/depth-sampler.js +63 -8
  14. package/dist/ar/depth-unprojection.d.ts +2 -0
  15. package/dist/ar/depth-unprojection.js +94 -0
  16. package/dist/ar/enable-gps-ar.d.ts +2 -0
  17. package/dist/ar/enable-gps-ar.js +137 -0
  18. package/dist/ar/frame-loop.d.ts +1 -1
  19. package/dist/ar/frame-loop.js +24 -1
  20. package/dist/ar/image-capture.d.ts +1 -1
  21. package/dist/ar/image-capture.js +2 -1
  22. package/dist/ar/index.d.ts +19 -12
  23. package/dist/ar/index.js +11 -4
  24. package/dist/ar/occupancy-grid.d.ts +2 -0
  25. package/dist/ar/occupancy-grid.js +164 -0
  26. package/dist/ar/replay-scene.d.ts +1 -1
  27. package/dist/ar/replay-scene.js +1 -1
  28. package/dist/ar/scene-node-names.d.ts +1 -1
  29. package/dist/ar/webxr-nue-basis.d.ts +2 -0
  30. package/dist/ar/webxr-nue-basis.js +54 -0
  31. package/dist/ar/webxr-session.d.ts +4 -4
  32. package/dist/ar/webxr-session.js +1 -984
  33. package/dist/ar/xr-camera-texture.d.ts +1 -1
  34. package/dist/ar/xr-error-handler.d.ts +1 -1
  35. package/dist/ar/xr-frame-loop.d.ts +2 -0
  36. package/dist/ar/xr-frame-loop.js +69 -0
  37. package/dist/{ar-types-Ck8LEq8k.d.ts → ar-types-Dan1MVTi.d.ts} +28 -5
  38. package/dist/ar-world-group-alignment-ColHB_5A.d.ts +43 -0
  39. package/dist/bresenham3d-CFfd7n6B.d.ts +59 -0
  40. package/dist/{camera-blit-capture-C1V9I1Z6.d.ts → camera-blit-capture-D5_mNhla.d.ts} +20 -0
  41. package/dist/capability-checker-CQXJHxXg.d.ts +43 -0
  42. package/dist/chromium-camera-access-workaround-Jqbn0apH.d.ts +137 -0
  43. package/dist/core/index.d.ts +1 -1
  44. package/dist/{create-slam-app-store-CNsZ3rcX.d.ts → create-slam-app-store-qNXQ-tq5.d.ts} +5 -5
  45. package/dist/depth-sampler-Bhanfwla.d.ts +169 -0
  46. package/dist/depth-unprojection-Y6i_mJuT.d.ts +52 -0
  47. package/dist/enable-gps-ar-CapmADVN.d.ts +95 -0
  48. package/dist/{file-system-DTApEv_Q.d.ts → file-system-C_eqiEA8.d.ts} +1 -1
  49. package/dist/frame-conversions-BfsafGWR.d.ts +54 -0
  50. package/dist/{frame-loop-QJIxLin0.d.ts → frame-loop-CMewS8cY.d.ts} +5 -0
  51. package/dist/{fused-path-CB4DwWOG.d.ts → fused-path-DJoVu98b.d.ts} +1 -1
  52. package/dist/geo/h3-proximity.d.ts +1 -1
  53. package/dist/geo/index.d.ts +1 -1
  54. package/dist/{gps-anchor-CnWY61XK.d.ts → gps-anchor-dYeCvvp5.d.ts} +12 -1
  55. package/dist/{gps-ar-pose-sampler-DsM1Kb2N.d.ts → gps-ar-pose-sampler-DVb96iOy.d.ts} +1 -1
  56. package/dist/{gps-event-coordinator-DBx9I1YX.d.ts → gps-event-coordinator-BDIQde2x.d.ts} +3 -3
  57. package/dist/hit-test-reticle-BEzEybxZ.d.ts +45 -0
  58. package/dist/{image-capture-DZLxHDN9.d.ts → image-capture-DZ4HFRgS.d.ts} +15 -2
  59. package/dist/index-KJq2amHF.d.ts +37 -0
  60. package/dist/index.d.ts +67 -58
  61. package/dist/index.js +18 -8
  62. package/dist/{leaflet-map-overlay-Cew7XAoO.d.ts → leaflet-map-overlay-6CJC-E1l.d.ts} +1 -1
  63. package/dist/{map-data-sih4g64v.d.ts → map-data-CkloBxIl.d.ts} +1 -1
  64. package/dist/{map-overlay-draw-B3R1c9Xp.d.ts → map-overlay-draw-krOKwDAq.d.ts} +1 -1
  65. package/dist/{null-storage-backend-BKOS7JpL.d.ts → null-storage-backend-CxEjZHRW.d.ts} +2 -2
  66. package/dist/occupancy-grid-BRNRaPyT.d.ts +72 -0
  67. package/dist/{opfs-storage-backend-Cj_7PWoq.d.ts → opfs-storage-backend-CjSAuqg_.d.ts} +2 -2
  68. package/dist/{persistence-middleware-BkHIqvaA.d.ts → persistence-middleware-DwyzgfWr.d.ts} +1 -1
  69. package/dist/{recording-options-Dq6IsKAv.d.ts → recording-options-DJBXuMxV.d.ts} +22 -6
  70. package/dist/{recording-replayer-CmCIIZ02.d.ts → recording-replayer-BL-x-7k0.d.ts} +2 -2
  71. package/dist/{recording-slice-d6OtCrza.d.ts → recording-slice-89O07ebs.d.ts} +10 -2
  72. package/dist/{replay-engine-BGBRQyXs.d.ts → replay-engine-BRqszuQ9.d.ts} +1 -1
  73. package/dist/{replay-scene-DpAGcAKY.d.ts → replay-scene-Bw58VL6p.d.ts} +3 -3
  74. package/dist/sensors/gps-error-handler.d.ts +1 -1
  75. package/dist/sensors/gps.d.ts +1 -1
  76. package/dist/sensors/index.d.ts +3 -3
  77. package/dist/sensors/index.js +1 -1
  78. package/dist/sensors/permission-checker.d.ts +1 -1
  79. package/dist/session-disposers-M-oashRH.js +57 -0
  80. package/dist/state/app-selectors.d.ts +1 -1
  81. package/dist/state/combined-root-state.d.ts +1 -1
  82. package/dist/state/create-slam-app-store.d.ts +1 -1
  83. package/dist/state/gps-ar-pose-sampler.d.ts +3 -3
  84. package/dist/state/gps-event-coordinator.d.ts +1 -1
  85. package/dist/state/index.d.ts +17 -17
  86. package/dist/state/index.js +2 -2
  87. package/dist/state/persistence-middleware.d.ts +1 -1
  88. package/dist/state/persistence-middleware.js +9 -3
  89. package/dist/state/recording-options.d.ts +1 -1
  90. package/dist/state/recording-options.js +5 -3
  91. package/dist/state/recording-replayer.d.ts +2 -2
  92. package/dist/state/recording-slice.d.ts +1 -1
  93. package/dist/state/recording-slice.js +11 -2
  94. package/dist/state/replay-engine.d.ts +1 -1
  95. package/dist/state/store-subscribers.d.ts +2 -2
  96. package/dist/state/subscribe-to-selector.d.ts +1 -1
  97. package/dist/state/tracking-quality.d.ts +1 -1
  98. package/dist/state/tracking-quality.js +1 -1
  99. package/dist/state/tracking-slice.d.ts +1 -1
  100. package/dist/state-BkIfpiyA.js +87 -0
  101. package/dist/storage/file-system-utils.d.ts +1 -1
  102. package/dist/storage/file-system.d.ts +1 -1
  103. package/dist/storage/index.d.ts +8 -8
  104. package/dist/storage/null-storage-backend.d.ts +1 -1
  105. package/dist/storage/opfs-storage-backend.d.ts +1 -1
  106. package/dist/storage/opfs-storage.d.ts +1 -1
  107. package/dist/storage/storage-backend.d.ts +1 -1
  108. package/dist/storage/zip-export.d.ts +1 -1
  109. package/dist/storage/zip-reader.d.ts +1 -1
  110. package/dist/{storage-backend-CT0Il_AB.d.ts → storage-backend-C9HLFtgz.d.ts} +1 -1
  111. package/dist/{store-subscribers-Dce_CoJF.d.ts → store-subscribers-rrkLkFTt.d.ts} +2 -2
  112. package/dist/{subscribe-to-selector-DIHm3BRZ.d.ts → subscribe-to-selector-CP3BKSbF.d.ts} +1 -1
  113. package/dist/test-utils/browser-mocks.d.ts +1 -1
  114. package/dist/test-utils/zip-round-trip-helpers.d.ts +1 -1
  115. package/dist/{tracking-slice-utCNND8t.d.ts → tracking-slice-CdK8c-8P.d.ts} +1 -1
  116. package/dist/types/ar-types.d.ts +2 -2
  117. package/dist/types/geo-types.d.ts +1 -1
  118. package/dist/types/index.d.ts +3 -3
  119. package/dist/utils/concurrency.d.ts +1 -1
  120. package/dist/utils/failure-tracker.d.ts +1 -1
  121. package/dist/utils/format-file-size.d.ts +1 -1
  122. package/dist/utils/fused-path.d.ts +1 -1
  123. package/dist/utils/index.d.ts +6 -6
  124. package/dist/utils/list-formatter.d.ts +1 -1
  125. package/dist/utils/logger.d.ts +1 -1
  126. package/dist/visualization/accuracy-circles.d.ts +1 -1
  127. package/dist/visualization/alignment-lerper.d.ts +1 -1
  128. package/dist/visualization/ar-world-group-alignment.d.ts +2 -0
  129. package/dist/visualization/ar-world-group-alignment.js +51 -0
  130. package/dist/visualization/camera-follower.d.ts +1 -1
  131. package/dist/visualization/css3d-renderer-manager.d.ts +1 -1
  132. package/dist/visualization/frame-conversions.d.ts +2 -0
  133. package/dist/visualization/frame-conversions.js +88 -0
  134. package/dist/visualization/frustum-visibility.d.ts +1 -1
  135. package/dist/visualization/gps-anchor.d.ts +1 -1
  136. package/dist/visualization/gps-anchor.js +216 -1
  137. package/dist/visualization/gps-compass-cubes.d.ts +1 -1
  138. package/dist/visualization/gps-event-markers.d.ts +1 -1
  139. package/dist/visualization/gps-event-markers.js +1 -1
  140. package/dist/visualization/hit-test-reticle.d.ts +2 -0
  141. package/dist/visualization/hit-test-reticle.js +81 -0
  142. package/dist/visualization/index.d.ts +19 -17
  143. package/dist/visualization/index.js +5 -2
  144. package/dist/visualization/leaflet-map-overlay.d.ts +1 -1
  145. package/dist/visualization/lerp-utils.d.ts +1 -1
  146. package/dist/visualization/map-data.d.ts +1 -1
  147. package/dist/visualization/map-overlay-draw.d.ts +1 -1
  148. package/dist/visualization/map-overlay.d.ts +1 -1
  149. package/dist/visualization/three-dispose.d.ts +1 -1
  150. package/dist/visualization/vis-colors.d.ts +1 -1
  151. package/dist/webxr-nue-basis-CqKQmZzT.d.ts +23 -0
  152. package/dist/{webxr-session-DB5cIZnN.d.ts → webxr-session-DtAG9srK.d.ts} +45 -13
  153. package/dist/webxr-session-QhcSSub5.js +1041 -0
  154. package/dist/xr-frame-loop-BtYytZJg.d.ts +70 -0
  155. package/package.json +1 -1
  156. package/dist/chromium-camera-access-workaround-NvChXsi8.d.ts +0 -61
  157. package/dist/depth-sampler-xg8vna6c.d.ts +0 -108
  158. package/dist/gps-anchor-DjC_3MoT.js +0 -257
  159. package/dist/index-pOjUCKEP.d.ts +0 -24
  160. /package/dist/{accuracy-circles-CsxE_7x1.d.ts → accuracy-circles-BpwWAs8G.d.ts} +0 -0
  161. /package/dist/{alignment-lerper-L1RbWHQ4.d.ts → alignment-lerper-Dm9G7JJO.d.ts} +0 -0
  162. /package/dist/{camera-follower-1k8mnw5J.d.ts → camera-follower-C2mwEn7c.d.ts} +0 -0
  163. /package/dist/{capture-failure-tracker-JKb1ST8E.d.ts → capture-failure-tracker-CUeU3L_M.d.ts} +0 -0
  164. /package/dist/{concurrency-CMw8pSaG.d.ts → concurrency-CO1FQvs3.d.ts} +0 -0
  165. /package/dist/{css3d-renderer-manager-CkjCb2HW.d.ts → css3d-renderer-manager-ptu_t3B3.d.ts} +0 -0
  166. /package/dist/{failure-tracker-DnHXOIRa.d.ts → failure-tracker-Cn8Fo9D4.d.ts} +0 -0
  167. /package/dist/{file-system-utils-CtKiiw27.d.ts → file-system-utils-DyAmcuvX.d.ts} +0 -0
  168. /package/dist/{format-file-size-BTS5H62-.d.ts → format-file-size-CLCZz3lC.d.ts} +0 -0
  169. /package/dist/{frustum-visibility-BHnFrSKQ.d.ts → frustum-visibility-CmI96ESS.d.ts} +0 -0
  170. /package/dist/{geo-types-CTuF23gG.d.ts → geo-types-SaPnIEUM.d.ts} +0 -0
  171. /package/dist/{gps-DXoQb9Kf.d.ts → gps-VuezJWK3.d.ts} +0 -0
  172. /package/dist/{gps-compass-cubes-CsLtFv4B.d.ts → gps-compass-cubes-BM1uGiYV.d.ts} +0 -0
  173. /package/dist/{gps-error-handler-Dg3ziRVY.d.ts → gps-error-handler-CJl_2MVm.d.ts} +0 -0
  174. /package/dist/{gps-event-markers-BGr-iLvi.d.ts → gps-event-markers-Dc9FT-q1.d.ts} +0 -0
  175. /package/dist/{h3-proximity-BhfgNbdW.d.ts → h3-proximity-I1msGPgE.d.ts} +0 -0
  176. /package/dist/{index-gzWJMVpo.d.ts → index-CF05QV2m.d.ts} +0 -0
  177. /package/dist/{lerp-utils-B8MM8zCR.d.ts → lerp-utils-BpV-0Dtk.d.ts} +0 -0
  178. /package/dist/{list-formatter-CNqi-E2j.d.ts → list-formatter-RRcBe8Vg.d.ts} +0 -0
  179. /package/dist/{logger-C9vugiQb.d.ts → logger-BEHXsnnq.d.ts} +0 -0
  180. /package/dist/{map-overlay-DAmreyA3.d.ts → map-overlay-bcogY5fK.d.ts} +0 -0
  181. /package/dist/{opfs-storage-DOPkzgeT.d.ts → opfs-storage-DYm8WHq7.d.ts} +0 -0
  182. /package/dist/{permission-checker-DPJl_RU6.d.ts → permission-checker-GpAiSCwd.d.ts} +0 -0
  183. /package/dist/{scene-node-names-6LBjPmwz.d.ts → scene-node-names-Bu0S66H1.d.ts} +0 -0
  184. /package/dist/{three-dispose-C2drkp6f.d.ts → three-dispose-D950t71B.d.ts} +0 -0
  185. /package/dist/{vis-colors-CHQhjal3.d.ts → vis-colors-D6omC64K.d.ts} +0 -0
  186. /package/dist/{xr-camera-texture-BU5YvKAM.d.ts → xr-camera-texture-DBwCaKTd.d.ts} +0 -0
  187. /package/dist/{xr-error-handler-CDY5QxGb.d.ts → xr-error-handler-C0AQRmxs.d.ts} +0 -0
  188. /package/dist/{zip-export-QO4iLIi7.d.ts → zip-export-D_5HAvFz.d.ts} +0 -0
  189. /package/dist/{zip-reader-D1105idz.d.ts → zip-reader-DSEXt7K6.d.ts} +0 -0
@@ -1,2 +1,2 @@
1
- import { i as acquireCameraTexture, n as RendererLike, r as XRCameraLike, t as CameraTextureResult } from "../xr-camera-texture-BU5YvKAM.js";
1
+ import { i as acquireCameraTexture, n as RendererLike, r as XRCameraLike, t as CameraTextureResult } from "../xr-camera-texture-DBwCaKTd.js";
2
2
  export { CameraTextureResult, RendererLike, XRCameraLike, acquireCameraTexture };
@@ -1,2 +1,2 @@
1
- import { n as XR_ERROR_MESSAGE_UNKNOWN, r as getXrErrorMessage, t as XR_ERROR_MESSAGES } from "../xr-error-handler-CDY5QxGb.js";
1
+ import { n as XR_ERROR_MESSAGE_UNKNOWN, r as getXrErrorMessage, t as XR_ERROR_MESSAGES } from "../xr-error-handler-C0AQRmxs.js";
2
2
  export { XR_ERROR_MESSAGES, XR_ERROR_MESSAGE_UNKNOWN, getXrErrorMessage };
@@ -0,0 +1,2 @@
1
+ import { a as runXrFrameUpdates, i as registerXrFrameUpdate, n as XrFrameUpdate, r as clearXrFrameUpdates, t as XrFrameContext } from "../xr-frame-loop-BtYytZJg.js";
2
+ export { XrFrameContext, XrFrameUpdate, clearXrFrameUpdates, registerXrFrameUpdate, runXrFrameUpdates };
@@ -0,0 +1,69 @@
1
+ import { createLogger } from "../utils/logger.js";
2
+ //#region ../src/ar/xr-frame-loop.ts
3
+ /**
4
+ * Per-frame **XR-access** callback registry — the safe seam that lets app
5
+ * code run standard WebXR work (hit-test, light estimation, depth, the WebXR
6
+ * Anchors API, …) without the framework wrapping each capability.
7
+ *
8
+ * The framework owns the single `renderer.setAnimationLoop(onXRFrame)` hook,
9
+ * and the live `XRFrame` never leaves `onXRFrame`. The plain `FrameUpdate`
10
+ * registry (`frame-loop.ts`) only passes `(dt, elapsed)`, which is enough for
11
+ * pose-free ticks but cannot drive hit-test. This registry additionally hands
12
+ * the callback the live `frame`, `referenceSpace`, and `session`.
13
+ *
14
+ * SAFETY CONTRACT (non-negotiable): `frame` / `referenceSpace` / `session` are
15
+ * valid **only synchronously inside the callback**. The `XRFrame` is
16
+ * use-after-frame-unsafe — storing it and reading it on a later tick throws or
17
+ * crashes. Passing them as arguments (never a stashable `getXrFrame()` getter)
18
+ * makes correct use the easy path and stashing the awkward one. Do NOT retain
19
+ * `ctx` or its fields beyond the callback's synchronous execution.
20
+ *
21
+ * See `2026-06-03-threejs-arbutton-minimal-ar-example-user-feedback.md`
22
+ * §6.2/§6.3 (option H-A2) for the design rationale.
23
+ */
24
+ const log = createLogger("XrFrameLoop");
25
+ const updates = /* @__PURE__ */ new Set();
26
+ /**
27
+ * Register a per-frame XR-access callback. Returns an unregister function.
28
+ *
29
+ * Registration is idempotent — registering the same `fn` twice is a no-op
30
+ * (it remains a single entry in the underlying `Set`).
31
+ */
32
+ function registerXrFrameUpdate(fn) {
33
+ updates.add(fn);
34
+ return () => {
35
+ updates.delete(fn);
36
+ };
37
+ }
38
+ /**
39
+ * Invoke all registered XR-access callbacks. Called by the WebXR session's
40
+ * `onXRFrame` once per frame, only when a live `frame` / `referenceSpace` /
41
+ * `session` are all available.
42
+ *
43
+ * The set is snapshotted before iterating so that
44
+ * `registerXrFrameUpdate` / unregister calls made by a handler during the
45
+ * same frame are deferred to the next tick — mirroring `runFrameUpdates`.
46
+ *
47
+ * Each callback is invoked in its own `try/catch`: this registry is the public
48
+ * app seam, so a bug in one app-registered callback (which throws every frame)
49
+ * must not abort the remaining callbacks nor propagate up through `onXRFrame`
50
+ * and kill the scene render for the whole session. Failures are logged and the
51
+ * loop continues — mirroring `runFrameUpdates`.
52
+ */
53
+ function runXrFrameUpdates(ctx) {
54
+ const snapshot = Array.from(updates);
55
+ for (const fn of snapshot) try {
56
+ fn(ctx);
57
+ } catch (error) {
58
+ log.error("A registered XrFrameUpdate threw; continuing the loop", error);
59
+ }
60
+ }
61
+ /**
62
+ * Clear all registrations. Called from `resetWebXRState()` so a fresh
63
+ * session starts with an empty registry.
64
+ */
65
+ function clearXrFrameUpdates() {
66
+ updates.clear();
67
+ }
68
+ //#endregion
69
+ export { clearXrFrameUpdates, registerXrFrameUpdate, runXrFrameUpdates };
@@ -1,4 +1,4 @@
1
- import { Quaternion, Vector3 } from "gps-plus-slam-js";
1
+ import { Matrix4, Quaternion, Vector3 } from "gps-plus-slam-js";
2
2
 
3
3
  //#region ../src/types/ar-types.d.ts
4
4
  /**
@@ -43,6 +43,11 @@ interface ARPose {
43
43
  readonly position: WebXRVec3;
44
44
  readonly orientation: WebXRQuaternion;
45
45
  }
46
+ /**
47
+ * An sRGB color triple, 0–255 integers per channel. Kept as plain ints so
48
+ * persisted JSON stays compact (~3 bytes/point in practice).
49
+ */
50
+ type RgbTuple = readonly [number, number, number];
46
51
  /**
47
52
  * A single depth point sample from WebXR Depth API.
48
53
  * Used for 3D reconstruction and validating AR tracking accuracy.
@@ -54,6 +59,14 @@ interface DepthPoint {
54
59
  readonly screenY: number;
55
60
  /** Depth value in meters */
56
61
  readonly depthM: number;
62
+ /**
63
+ * Camera color at (screenX, screenY), sampled from the same XR frame as
64
+ * the depth read (occupancy-grid port plan Iter 8). Optional + additive:
65
+ * recordings made before 2026-06 (or with the RGB recording option off)
66
+ * carry no color; consumers must fall back (e.g. height-based cube
67
+ * coloring).
68
+ */
69
+ readonly rgb?: RgbTuple;
57
70
  }
58
71
  /**
59
72
  * A complete depth sample with camera pose and grid of depth points.
@@ -66,8 +79,10 @@ interface DepthSample {
66
79
  readonly timestamp: number;
67
80
  /**
68
81
  * Camera position in **raw WebXR** convention [x=East, y=Up, z=South].
69
- * NOT in NUE — the recordDepthSample reducer is a no-op, so no
70
- * webxrToNUE conversion is applied. Consumers must convert if needed.
82
+ * NOT in NUE — the recordDepthSample reducer is conversion-free (it only
83
+ * stores the latest sample for subscribers), so no webxrToNUE conversion
84
+ * is ever applied. Consumers needing NUE must convert themselves; the
85
+ * occupancy-grid pipeline works directly in this raw frame.
71
86
  */
72
87
  readonly cameraPos: Vector3;
73
88
  /**
@@ -76,7 +91,15 @@ interface DepthSample {
76
91
  */
77
92
  readonly cameraRot: Quaternion;
78
93
  /** Grid of depth points */
79
- readonly points: DepthPoint[];
94
+ readonly points: readonly DepthPoint[];
95
+ /**
96
+ * Projection matrix of the capturing XRView (16 floats, column-major,
97
+ * serializable tuple — not a THREE.Matrix4). Camera intrinsics needed to
98
+ * unproject (screenX, screenY, depthM) back into a 3D AR-space point.
99
+ * Optional: recordings made before 2026-06 do not carry it; consumers
100
+ * must skip unprojection for such samples.
101
+ */
102
+ readonly projectionMatrix?: Matrix4;
80
103
  }
81
104
  //#endregion
82
- export { WebXRQuaternion as a, DepthSample as i, ArPoseTuples as n, WebXRVec3 as o, DepthPoint as r, ARPose as t };
105
+ export { RgbTuple as a, DepthSample as i, ArPoseTuples as n, WebXRQuaternion as o, DepthPoint as r, WebXRVec3 as s, ARPose as t };
@@ -0,0 +1,43 @@
1
+ import { t as SubscribableStore } from "./subscribe-to-selector-CP3BKSbF.js";
2
+ import * as THREE from "three";
3
+
4
+ //#region ../src/visualization/ar-world-group-alignment.d.ts
5
+ interface ArWorldGroupAlignmentOptions {
6
+ /** The store whose alignment selector drives the lerper. */
7
+ readonly store: SubscribableStore;
8
+ /** The AR world group whose `.matrix` is lerped toward each alignment. */
9
+ readonly arWorldGroup: THREE.Object3D;
10
+ /**
11
+ * Lerp speed multiplier forwarded to {@link createAlignmentLerper}
12
+ * (default matches the lerper's own `DEFAULT_LERP_RATE`).
13
+ */
14
+ readonly lerpRate?: number;
15
+ }
16
+ interface ArWorldGroupAlignmentHandle {
17
+ /** Stop the subscription and the per-frame update. */
18
+ dispose(): void;
19
+ }
20
+ /**
21
+ * Begin applying the store's (lerped) alignment to `arWorldGroup`. Returns a
22
+ * handle whose `dispose()` removes both the store subscription and the
23
+ * per-frame update.
24
+ *
25
+ * Disposal is tied to the AR session lifecycle: the binding registers itself
26
+ * with the session-disposer registry that `resetWebXRState()` flushes on
27
+ * teardown, so a caller can `enableArWorldGroupAlignment(...)` once and never
28
+ * hold the handle — the binding cannot outlive the session (which is what two
29
+ * apps independently leaked: the store subscription survives a teardown because
30
+ * `clearFrameUpdates` only drops the per-frame tick). Because `initAR()` throws
31
+ * while a prior session is live, every restart passes through that flush. The
32
+ * handle is still returned for callers that want to stop alignment mid-session,
33
+ * and is idempotent + self-deregistering so a manual `dispose()` and the
34
+ * teardown flush never double-run. See
35
+ * gps-plus-slam/GpsPlusSlamJs_Docs/docs/2026-06-08-arworldgroup-alignment-session-scoped-disposal.md.
36
+ *
37
+ * Double-drive is still the caller's concern: the recorder owns its own lerper
38
+ * and must NOT also call this (it would double-lerp the same group). It does not
39
+ * call this helper, so flushing the registry on teardown never affects it.
40
+ */
41
+ declare function enableArWorldGroupAlignment(options: ArWorldGroupAlignmentOptions): ArWorldGroupAlignmentHandle;
42
+ //#endregion
43
+ export { ArWorldGroupAlignmentOptions as n, enableArWorldGroupAlignment as r, ArWorldGroupAlignmentHandle as t };
@@ -0,0 +1,59 @@
1
+ //#region ../src/ar/bresenham3d.d.ts
2
+ /**
3
+ * 3D Bresenham Line Tracer
4
+ *
5
+ * Direct port of the Unity occupancy-grid helper
6
+ * (`PointCloudHelpers.BresenhamsLineAlgorithm`, after
7
+ * http://members.chello.at/~easyfilter/bresenham.html): walks integer grid
8
+ * cells from `start` to `end`, invoking a visitor per cell. Used by the
9
+ * occupancy grid for free-space carving and raycasting.
10
+ *
11
+ * Semantics preserved from the Unity original:
12
+ * - The visitor runs on the start cell BEFORE the stop-distance check.
13
+ * - `stopDistance` is measured in dominant-axis (Chebyshev) steps from the
14
+ * end cell: tracing stops once the remaining distance reaches it, so the
15
+ * end cell and its `stopDistance`-neighborhood are not visited (except
16
+ * the unconditional start visit).
17
+ * - Error offsets use integer division (`floor(dm / 2)`).
18
+ *
19
+ * @see bresenham3d.ts.md for detailed documentation
20
+ */
21
+ /** Integer grid cell coordinate triple. */
22
+ type GridCell = readonly [number, number, number];
23
+ /**
24
+ * Main-thread safety cap: the maximum dominant-axis (Chebyshev) span a single
25
+ * trace may cover. The trace runs synchronously, one iteration per
26
+ * dominant-axis step, so an unbounded span freezes the UI. Finite-but-absurd
27
+ * coordinates (a tracking glitch, a corrupt projectionMatrix unprojecting to a
28
+ * huge world point) quantize to safe integers and pass the integer check, so
29
+ * this is the only thing standing between such input and a multi-second
30
+ * (potentially multi-billion-iteration) freeze.
31
+ *
32
+ * Chosen generously — at the grid's 0.15 m cells this is ~150 km, far beyond
33
+ * any real AR scene, so it never trips on legitimate carving or raycasting.
34
+ * It is a circuit breaker against programmer/data error, not a ray-length
35
+ * policy: exceeding it throws (loud — surfaces the upstream bug) rather than
36
+ * silently truncating the trace.
37
+ */
38
+ declare const MAX_TRACE_STEPS = 1000000;
39
+ /**
40
+ * Trace the line from `start` to `end`, calling `visitCell` per cell.
41
+ *
42
+ * @param visitCell - return `false` to stop the trace early.
43
+ * @param stopDistance - dominant-axis steps before `end` at which to stop
44
+ * (default 0 = trace all the way to `end`).
45
+ * @throws TypeError when a coordinate is not a safe integer (cells must be
46
+ * quantized before tracing — programmer error, not a data error).
47
+ * @throws RangeError when `stopDistance` is not a non-negative safe integer.
48
+ * The loop terminates on a counter (`i--`) that decrements unconditionally,
49
+ * so a negative or fractional value still terminates (it merely traces past
50
+ * `end`); but `NaN`/`-Infinity` never satisfy `i <= stopDistance` and would
51
+ * spin forever. Rejecting up front both prevents that freeze and enforces
52
+ * the "dominant-axis steps before end" contract.
53
+ * @throws RangeError when the dominant-axis span exceeds {@link MAX_TRACE_STEPS}
54
+ * (circuit breaker against a synchronous main-thread freeze from
55
+ * finite-but-absurd coordinates).
56
+ */
57
+ declare function bresenham3d(start: GridCell, end: GridCell, visitCell: (cell: GridCell) => boolean, stopDistance?: number): void;
58
+ //#endregion
59
+ export { MAX_TRACE_STEPS as n, bresenham3d as r, GridCell as t };
@@ -63,6 +63,26 @@ declare class CameraBlitCapture {
63
63
  * @returns JPEG Blob, or null if capture fails
64
64
  */
65
65
  captureToBlob(renderer: THREE.WebGLRenderer, cameraTexture: THREE.Texture, quality: number): Promise<Blob | null>;
66
+ /**
67
+ * Run the blit + readback (steps A+B of {@link captureToBlob}) and return
68
+ * the raw RGBA pixel buffer without JPEG encoding — the cheap path for
69
+ * per-point color sampling (occupancy-grid port plan Iter 8).
70
+ *
71
+ * IMPORTANT: Must be called within the XR animation frame callback, while
72
+ * the camera texture is valid. The returned `pixels` is the INTERNAL
73
+ * buffer — valid only until the next capture or `resizeIfNeeded`; consume
74
+ * it synchronously (e.g. via `createRgbLookup`) or copy it.
75
+ *
76
+ * Note: the buffer is in WebGL readback order (bottom-row-first / RGBA);
77
+ * `createRgbLookup` handles the y-flip.
78
+ *
79
+ * @returns the buffer with its dimensions, or null on failure/dispose.
80
+ */
81
+ captureToPixels(renderer: THREE.WebGLRenderer, cameraTexture: THREE.Texture): {
82
+ pixels: Uint8Array;
83
+ width: number;
84
+ height: number;
85
+ } | null;
66
86
  /**
67
87
  * Check if a pixel buffer is entirely black (all zeros).
68
88
  * Uses sampling for performance (checks BLACK_CHECK_SAMPLE_COUNT evenly-spaced pixels).
@@ -0,0 +1,43 @@
1
+ //#region ../src/ar/capability-checker.d.ts
2
+ /**
3
+ * Pure capability-gating helpers, promoted from the AnchorStarter app so the
4
+ * minimal AR example and AnchorStarter share the *decision* and *message*
5
+ * without sharing app-specific copy.
6
+ *
7
+ * The async feature probing (`isWebXRSupported()` / geolocation availability)
8
+ * stays in each app's `main.ts`; only the browser-free decision and the
9
+ * user-facing message live here so they can be unit-tested without a browser.
10
+ *
11
+ * See `2026-06-03-threejs-arbutton-minimal-ar-example-user-feedback.md` §6.4.
12
+ */
13
+ /** The two capabilities every GPS+AR demo requires to run. */
14
+ interface CapabilitySupport {
15
+ /** Browser supports a WebXR `immersive-ar` session. */
16
+ readonly webxr: boolean;
17
+ /** Geolocation API is available. */
18
+ readonly geolocation: boolean;
19
+ }
20
+ /** Options that let an app flavour the otherwise-neutral gating message. */
21
+ interface CapabilityMessageOptions {
22
+ /**
23
+ * App-specific "what this demo lets you do" phrase, appended as
24
+ * `…outdoors, to try ${contextLabel}.`. Omit for a neutral message that
25
+ * ends at `…outdoors.`. Example: `'the persistent-anchor flow'`.
26
+ */
27
+ readonly contextLabel?: string;
28
+ }
29
+ /** True only when both AR and GPS are available — the demo can run. */
30
+ declare function isFullySupported(support: CapabilitySupport): boolean;
31
+ /**
32
+ * Build the capability-gated message shown when the demo cannot run. Names
33
+ * exactly which capabilities are missing so the user understands *why*, and
34
+ * always points them at the supported environment (an AR phone, outdoors).
35
+ * Returns `null` when everything is supported (no message needed).
36
+ *
37
+ * The trailing "to try …" clause is app-specific and supplied via
38
+ * `options.contextLabel`; without it the message ends neutrally at
39
+ * "…outdoors.".
40
+ */
41
+ declare function capabilityMessage(support: CapabilitySupport, options?: CapabilityMessageOptions): string | null;
42
+ //#endregion
43
+ export { isFullySupported as i, CapabilitySupport as n, capabilityMessage as r, CapabilityMessageOptions as t };
@@ -0,0 +1,137 @@
1
+ //#region ../src/ar/chromium-camera-access-workaround.d.ts
2
+ /**
3
+ * Chromium WebXR camera-access tab-crash workaround.
4
+ *
5
+ * Background:
6
+ * Requesting `camera-access` as an optional WebXR feature on Android Chrome
7
+ * 147+ causes a fatal renderer-process crash (`CrRendererMain`) 1–2 seconds
8
+ * after entering AR. The crash reproduces in the upstream three.js
9
+ * `webxr_ar_hittest.html` example with `optionalFeatures: ['camera-access']`
10
+ * added — i.e. it is not specific to this app's session setup. See:
11
+ *
12
+ * - GpsPlusSlamJs_Docs/docs/2026-04-22-camera-access-reproducer-plan.md
13
+ * - GpsPlusSlamJs_Docs/docs/2026-06-04-camera-access-crash-regression-chrome-148.md
14
+ * - https://github.com/mrdoob/three.js/issues/33404
15
+ * - https://issues.chromium.org/issues/507508099 (root-cause, marked Fixed)
16
+ *
17
+ * There is NOT a single crash here — the Chromium tracker notes the crash on
18
+ * Chrome 147 is different from the crash on 148+, and the simple workaround
19
+ * below only helped on a subset of versions. The empirically-verified
20
+ * (on-device) timeline is:
21
+ *
22
+ * - 147 : deleting `createProjectionLayer` / `layers` forces three.js onto
23
+ * `XRWebGLLayer` and sidesteps the crash (deletes-only is sufficient).
24
+ * - 148.0.7778.12 .. 149.0.7821 : the delete-only trick is NOT sufficient on
25
+ * its own. An ADDITIONAL patch is needed: persist the `baseLayer`
26
+ * reference across `XRSession.prototype.updateRenderState` so three.js's
27
+ * later `depthNear`/`depthFar` update does not drop the active
28
+ * `glBaseLayer`. (Confirmed on-device on `148.0.7778.215`: BOTH the
29
+ * deletes and the baseLayer patch are required for the page not to
30
+ * crash. Earlier 148 builds (`< .7778.12`) are below the window per the
31
+ * tracker, which reported delete-only worked there.)
32
+ * - > 149.0.7821 (incl. Chrome 150) : the delete-only path is STILL required
33
+ * (confirmed on-device: Chrome 150 only stops crashing when the deletes
34
+ * are applied), but the extra baseLayer-persistence patch is NOT needed.
35
+ * The earlier assumption that Chromium fully fixed this on patched
36
+ * builds did not hold on real devices, so we no longer skip the
37
+ * workaround on "patched" Chrome.
38
+ *
39
+ * Resulting policy (see {@link applyChromiumProjectionLayerWorkaround}):
40
+ * - ALWAYS apply the deletes (every Chrome build, and unknown/non-Chromium
41
+ * environments — restoring the original always-on behavior).
42
+ * - Apply the baseLayer-persistence patch ONLY when a detected Chrome build
43
+ * falls inside the affected window
44
+ * [{@link BASELAYER_WINDOW_MIN} .. {@link BASELAYER_WINDOW_MAX}].
45
+ *
46
+ * Caveats:
47
+ * - This is a Chromium-specific hack and the upstream comment explicitly
48
+ * warns it "might break webxr on other devices" — keep it opt-in.
49
+ * - Must run BEFORE any WebXR session setup. Three.js reads these prototype
50
+ * members lazily when the first session starts, so calling this at app
51
+ * bootstrap (before `initAR()`) is sufficient.
52
+ * - Idempotent: safe to call repeatedly. Safe on environments where the
53
+ * prototypes do not exist (e.g. desktop browsers, jsdom).
54
+ */
55
+ /**
56
+ * Parsed Chrome version as `[major, minor, build, patch]`.
57
+ */
58
+ type ChromeVersion = [number, number, number, number];
59
+ /**
60
+ * Inclusive lower bound of the Chrome window that additionally needs the
61
+ * `baseLayer`-persistence patch (on top of the deletes).
62
+ *
63
+ * Set to `148.0.7778.12`, the issue tracker's figure for when the delete-only
64
+ * trick stopped being sufficient. On-device confirmation: `148.0.7778.215`
65
+ * (well inside this window) crashes with deletes-only and needs BOTH patches.
66
+ * Earlier 148 builds (`< .7778.12`) and all of Chrome 147 stay below this
67
+ * bound (deletes-only) per the documented timeline — we have no on-device
68
+ * evidence that they need the baseLayer patch, so we do not apply it there.
69
+ */
70
+ declare const BASELAYER_WINDOW_MIN: ChromeVersion;
71
+ /**
72
+ * Inclusive upper bound of the Chrome window that additionally needs the
73
+ * `baseLayer`-persistence patch. The crash is fixed after `149.0.7819.0` and
74
+ * the camera image is correctly populated after `149.0.7821.0`; above this
75
+ * build the extra patch is no longer required (the deletes still are — see
76
+ * the module header and the on-device matrix for Chrome 150).
77
+ *
78
+ * @see https://github.com/mrdoob/three.js/issues/33404
79
+ */
80
+ declare const BASELAYER_WINDOW_MAX: ChromeVersion;
81
+ /**
82
+ * Result of {@link applyChromiumProjectionLayerWorkaround}.
83
+ * Useful for logging and tests.
84
+ */
85
+ interface ChromiumProjectionLayerWorkaroundResult {
86
+ /** True if `XRWebGLBinding.prototype.createProjectionLayer` was deleted on this call. */
87
+ deletedCreateProjectionLayer: boolean;
88
+ /** True if `XRRenderState.prototype.layers` was deleted on this call. */
89
+ deletedRenderStateLayers: boolean;
90
+ /** True if `XRSession.prototype.updateRenderState` was wrapped on this call. */
91
+ patchedUpdateRenderState: boolean;
92
+ /** The detected Chrome version (`"major.minor.build.patch"`), or null. */
93
+ detectedChromeVersion: string | null;
94
+ }
95
+ /**
96
+ * Parse a Chrome/Chromium version from a user-agent string.
97
+ *
98
+ * Matches the `Chrome/<major>.<minor>.<build>.<patch>` token (and the iOS
99
+ * `CriOS/...` variant). Returns null for non-Chromium user agents.
100
+ */
101
+ declare function parseChromeVersion(userAgent: string): ChromeVersion | null;
102
+ /**
103
+ * True when the detected Chrome build falls inside the affected window
104
+ * [{@link BASELAYER_WINDOW_MIN} .. {@link BASELAYER_WINDOW_MAX}] (inclusive)
105
+ * and therefore additionally needs the `baseLayer`-persistence patch. Outside
106
+ * the window (including non-Chromium user agents) returns false — only the
107
+ * deletes are needed there.
108
+ */
109
+ declare function needsBaseLayerPersistence(userAgent: string): boolean;
110
+ /**
111
+ * Apply the Chromium camera-access tab-crash workaround.
112
+ *
113
+ * Policy (derived from on-device testing — see the module header):
114
+ *
115
+ * - ALWAYS removes `XRWebGLBinding.prototype.createProjectionLayer`
116
+ * (three.js r184) and `XRRenderState.prototype.layers` (three.js r158) so
117
+ * three.js falls back to `XRWebGLLayer`. This is required on every affected
118
+ * Chrome build observed so far, including Chrome 150, and is also applied
119
+ * on unknown/non-Chromium environments (restoring the original always-on
120
+ * behavior).
121
+ * - ADDITIONALLY persists the `baseLayer` across
122
+ * `XRSession.prototype.updateRenderState`, but ONLY when a detected Chrome
123
+ * build falls inside the affected window
124
+ * [{@link BASELAYER_WINDOW_MIN} .. {@link BASELAYER_WINDOW_MAX}]. Outside
125
+ * that window the extra patch is unnecessary (and is skipped on unknown
126
+ * environments to avoid touching projection-layer devices like Quest).
127
+ *
128
+ * Call once during bootstrap, before any `requestSession()` call.
129
+ *
130
+ * @param options.userAgent override the detected user agent (for testing).
131
+ * @returns which prototype members were actually changed on this call.
132
+ */
133
+ declare function applyChromiumProjectionLayerWorkaround(options?: {
134
+ userAgent?: string;
135
+ }): ChromiumProjectionLayerWorkaroundResult;
136
+ //#endregion
137
+ export { applyChromiumProjectionLayerWorkaround as a, ChromiumProjectionLayerWorkaroundResult as i, BASELAYER_WINDOW_MIN as n, needsBaseLayerPersistence as o, ChromeVersion as r, parseChromeVersion as s, BASELAYER_WINDOW_MAX as t };
@@ -1,2 +1,2 @@
1
- import { a as LibraryRootState, c as RootState, d as calcRelativeCoordsInMeters, f as createGpsSlamStore, g as webxrToNUE, h as validateLicenseKey, i as LatLongAlt, l as Vector3, m as odometryTrackingRestarted, n as GpsPoint, o as Matrix4, p as isIdentityMatrix4, r as LatLong, s as Quaternion, t as ArImageCapture, u as calcGpsCoords } from "../index-gzWJMVpo.js";
1
+ import { a as LibraryRootState, c as RootState, d as calcRelativeCoordsInMeters, f as createGpsSlamStore, g as webxrToNUE, h as validateLicenseKey, i as LatLongAlt, l as Vector3, m as odometryTrackingRestarted, n as GpsPoint, o as Matrix4, p as isIdentityMatrix4, r as LatLong, s as Quaternion, t as ArImageCapture, u as calcGpsCoords } from "../index-CF05QV2m.js";
2
2
  export { ArImageCapture, GpsPoint, LatLong, LatLongAlt, LibraryRootState, Matrix4, Quaternion, RootState, Vector3, calcGpsCoords, calcRelativeCoordsInMeters, createGpsSlamStore, isIdentityMatrix4, odometryTrackingRestarted, validateLicenseKey, webxrToNUE };
@@ -1,8 +1,8 @@
1
- import { t as ARPose } from "./ar-types-Ck8LEq8k.js";
2
- import { a as TrackingSliceState, t as DeviceOrientation } from "./tracking-slice-utCNND8t.js";
3
- import { t as RecordingState } from "./recording-slice-d6OtCrza.js";
4
- import { n as SessionMetadata } from "./opfs-storage-DOPkzgeT.js";
5
- import { n as StorageBackend } from "./storage-backend-CT0Il_AB.js";
1
+ import { t as ARPose } from "./ar-types-Dan1MVTi.js";
2
+ import { a as TrackingSliceState, t as DeviceOrientation } from "./tracking-slice-CdK8c-8P.js";
3
+ import { t as RecordingState } from "./recording-slice-89O07ebs.js";
4
+ import { n as SessionMetadata } from "./opfs-storage-DYm8WHq7.js";
5
+ import { n as StorageBackend } from "./storage-backend-C9HLFtgz.js";
6
6
  import { GpsPoint, LatLong, Matrix4, RootState, Vector3 } from "gps-plus-slam-js";
7
7
  import * as _$_reduxjs_toolkit0 from "@reduxjs/toolkit";
8
8
  import { Middleware, Reducer, ReducersMapObject, configureStore } from "@reduxjs/toolkit";
@@ -0,0 +1,169 @@
1
+ import { a as RgbTuple, i as DepthSample, t as ARPose } from "./ar-types-Dan1MVTi.js";
2
+ import { Matrix4 } from "gps-plus-slam-js";
3
+
4
+ //#region ../src/ar/depth-rgb-lookup.d.ts
5
+ /**
6
+ * Sample the camera color at normalized view coordinates (0–1, y=0 top).
7
+ * Returns null for out-of-range or non-finite coordinates.
8
+ */
9
+ type RgbLookup = (screenX: number, screenY: number) => RgbTuple | null;
10
+ //#endregion
11
+ //#region ../src/ar/depth-sampler.d.ts
12
+ /**
13
+ * Configuration for depth sampling behavior.
14
+ */
15
+ interface DepthSamplerConfig {
16
+ /** Interval between samples in milliseconds. Default: 1000ms */
17
+ intervalMs: number;
18
+ /**
19
+ * Number of points per dimension (gridSize x gridSize). Default: 16
20
+ * (256 pts at 1 Hz — dense enough to populate the AR-space occupancy
21
+ * grid for on-device verification, see the 2026-06-11 port plan §1).
22
+ */
23
+ gridSize: number;
24
+ /** Time in ms to wait before declaring depth unavailable. Default: 5000ms */
25
+ unavailabilityThresholdMs: number;
26
+ /**
27
+ * Whether to enrich each sampled point with the camera color at its view
28
+ * coordinates (occupancy-grid port plan Iter 8). Requires the
29
+ * `acquireRgbLookup` callback; gates the per-sample GPU blit+readback.
30
+ * Default: true.
31
+ */
32
+ rgb: boolean;
33
+ }
34
+ /**
35
+ * Callbacks for depth sampler events.
36
+ */
37
+ interface DepthSamplerCallbacks {
38
+ /** Called when a depth sample is captured */
39
+ onSampleCaptured: (sample: DepthSample) => void;
40
+ /** Returns the current AR pose, or null if not available */
41
+ getCurrentPose: () => ARPose | null;
42
+ /**
43
+ * Called once when depth is determined to be unavailable.
44
+ * Triggered after unavailabilityThresholdMs with no depth data.
45
+ * Field Test Readiness Issue #8.
46
+ */
47
+ onDepthUnavailable?: () => void;
48
+ /**
49
+ * Lazily acquire a camera-color lookup for the CURRENT XR frame
50
+ * (occupancy-grid port plan Iter 8). Invoked at most once per *emitted*
51
+ * sample (never per frame or per point — acquisition is a GPU-stall
52
+ * blit+readback) and only while `config.rgb` is true. Returning `null`
53
+ * (or throwing) yields color-less points; the returned lookup is used
54
+ * synchronously within the same frame callback.
55
+ */
56
+ acquireRgbLookup?: () => RgbLookup | null;
57
+ }
58
+ /**
59
+ * WebXR depth info interface (subset of XRDepthInformation), extended with
60
+ * the capturing view's projection matrix so each emitted DepthSample carries
61
+ * the intrinsics needed for unprojection. Build via {@link wrapXRDepthInfo}.
62
+ */
63
+ interface DepthInfo {
64
+ width: number;
65
+ height: number;
66
+ getDepthInMeters: (x: number, y: number) => number;
67
+ /** Column-major projection matrix of the capturing XRView, if known. */
68
+ projectionMatrix?: Matrix4;
69
+ }
70
+ /**
71
+ * Wrap a raw browser XRDepthInformation object into a {@link DepthInfo}.
72
+ *
73
+ * - `getDepthInMeters` is bound to the source object (browser
74
+ * implementations are this-sensitive).
75
+ * - `projectionMatrix` (typically `XRView.projectionMatrix`, a Float32Array
76
+ * the UA may reuse across frames) is defensively validated and copied into
77
+ * a plain serializable 16-tuple; invalid input (wrong length, non-finite
78
+ * entries) yields a DepthInfo without a matrix rather than an error.
79
+ */
80
+ declare function wrapXRDepthInfo(raw: {
81
+ width: number;
82
+ height: number;
83
+ getDepthInMeters: (x: number, y: number) => number;
84
+ }, projectionMatrix: ArrayLike<number> | undefined): DepthInfo;
85
+ /**
86
+ * Samples sparse depth points from WebXR depth sensing.
87
+ *
88
+ * Usage:
89
+ * ```ts
90
+ * const sampler = new DepthSampler({
91
+ * onSampleCaptured: (sample) => saveSample(sample),
92
+ * getCurrentPose: () => arSession.getCurrentPose(),
93
+ * });
94
+ * sampler.start();
95
+ * // In frame loop:
96
+ * sampler.onFrame(timestamp, depthInfo);
97
+ * ```
98
+ */
99
+ declare class DepthSampler {
100
+ private readonly callbacks;
101
+ private readonly config;
102
+ private running;
103
+ private sampleCount;
104
+ private lastSampleTime;
105
+ /** Timestamp when sampling started (for unavailability detection) */
106
+ private startTime;
107
+ /** Whether we've ever received valid depth data */
108
+ private depthReceived;
109
+ /** Whether we've already fired the unavailable callback */
110
+ private unavailableCallbackFired;
111
+ constructor(callbacks: DepthSamplerCallbacks, config?: Partial<DepthSamplerConfig>);
112
+ /**
113
+ * Start depth sampling.
114
+ */
115
+ start(): void;
116
+ /**
117
+ * Stop depth sampling.
118
+ */
119
+ stop(): void;
120
+ /**
121
+ * Check if sampler is currently running.
122
+ */
123
+ isRunning(): boolean;
124
+ /**
125
+ * Get the number of samples captured since start.
126
+ */
127
+ getSampleCount(): number;
128
+ /**
129
+ * Get the current configuration.
130
+ */
131
+ getConfig(): DepthSamplerConfig;
132
+ /**
133
+ * Apply partial configuration overrides (e.g. the user's recording
134
+ * options, plumbed in by `startDepthCapture` just before sampling
135
+ * starts). Invalid values are ignored defensively: every key requires a
136
+ * finite positive number, and `gridSize` additionally an integer.
137
+ */
138
+ updateConfig(config: Partial<DepthSamplerConfig>): void;
139
+ /**
140
+ * Called each frame with depth information.
141
+ *
142
+ * @param timestamp - Current frame timestamp in milliseconds
143
+ * @param depthInfo - WebXR depth information, or null if unavailable
144
+ */
145
+ onFrame(timestamp: number, depthInfo: DepthInfo | null): void;
146
+ /**
147
+ * Acquire the per-sample RGB lookup, gated by `config.rgb` and guarded so
148
+ * a failing acquisition (e.g. GL context loss during the blit) can never
149
+ * break the sample emission in the XR frame loop.
150
+ */
151
+ private acquireRgbLookupSafely;
152
+ /**
153
+ * Sample a grid of depth points from the depth buffer, attaching the
154
+ * camera color per point when a lookup is available.
155
+ */
156
+ private sampleGrid;
157
+ /**
158
+ * Check if depth has been unavailable for longer than the threshold.
159
+ * If so, fire the onDepthUnavailable callback (once).
160
+ */
161
+ private checkDepthUnavailability;
162
+ /**
163
+ * Check if depth data has ever been received.
164
+ * Useful for testing and status display.
165
+ */
166
+ hasReceivedDepth(): boolean;
167
+ }
168
+ //#endregion
169
+ export { wrapXRDepthInfo as a, DepthSamplerConfig as i, DepthSampler as n, DepthSamplerCallbacks as r, DepthInfo as t };