gps-plus-slam-app-framework 1.1.0 → 1.3.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 (180) hide show
  1. package/dist/{app-selectors-CR9OWodf.d.ts → app-selectors-B8VQoMDY.d.ts} +1 -1
  2. package/dist/ar/bresenham3d.d.ts +2 -0
  3. package/dist/ar/bresenham3d.js +81 -0
  4. package/dist/ar/camera-blit-capture.d.ts +1 -1
  5. package/dist/ar/camera-blit-capture.js +43 -2
  6. package/dist/ar/capability-checker.d.ts +1 -1
  7. package/dist/ar/capture-failure-tracker.d.ts +1 -1
  8. package/dist/ar/chromium-camera-access-workaround.d.ts +1 -1
  9. package/dist/ar/depth-sampler.d.ts +3 -3
  10. package/dist/ar/depth-sampler.js +63 -8
  11. package/dist/ar/depth-unprojection.d.ts +2 -0
  12. package/dist/ar/depth-unprojection.js +94 -0
  13. package/dist/ar/enable-gps-ar.d.ts +1 -1
  14. package/dist/ar/enable-gps-ar.js +1 -1
  15. package/dist/ar/frame-loop.d.ts +1 -1
  16. package/dist/ar/image-capture.d.ts +2 -2
  17. package/dist/ar/image-capture.js +9 -6
  18. package/dist/ar/index.d.ts +19 -16
  19. package/dist/ar/index.js +7 -4
  20. package/dist/ar/occupancy-grid.d.ts +2 -0
  21. package/dist/ar/occupancy-grid.js +164 -0
  22. package/dist/ar/replay-scene.d.ts +1 -1
  23. package/dist/ar/replay-scene.js +1 -1
  24. package/dist/ar/scene-node-names.d.ts +1 -1
  25. package/dist/ar/webxr-nue-basis.d.ts +2 -0
  26. package/dist/{webxr-nue-basis-BbdqhqY6.js → ar/webxr-nue-basis.js} +1 -1
  27. package/dist/ar/webxr-session.d.ts +3 -3
  28. package/dist/ar/webxr-session.js +1 -969
  29. package/dist/ar/xr-camera-texture.d.ts +1 -1
  30. package/dist/ar/xr-error-handler.d.ts +1 -1
  31. package/dist/ar/xr-frame-loop.d.ts +1 -1
  32. package/dist/{ar-types-isPsQptb.d.ts → ar-types-Dan1MVTi.d.ts} +28 -5
  33. package/dist/{ar-world-group-alignment-Dn4rQk_c.d.ts → ar-world-group-alignment-C9lwwIyI.d.ts} +1 -1
  34. package/dist/bresenham3d-CFfd7n6B.d.ts +59 -0
  35. package/dist/{camera-blit-capture-75WWa9Xb.d.ts → camera-blit-capture-CnxUDbKx.d.ts} +29 -0
  36. package/dist/core/index.d.ts +1 -1
  37. package/dist/{create-slam-app-store-B76AGaI0.d.ts → create-slam-app-store-BljusOua.d.ts} +5 -5
  38. package/dist/depth-sampler-Bhanfwla.d.ts +169 -0
  39. package/dist/depth-unprojection-Y6i_mJuT.d.ts +52 -0
  40. package/dist/{enable-gps-ar-DNbO0zbg.d.ts → enable-gps-ar-BfUJOgpW.d.ts} +4 -4
  41. package/dist/{file-system-TIsDfamK.d.ts → file-system-CwXTNHmB.d.ts} +1 -1
  42. package/dist/file-system-utils-CXIoL-DM.d.ts +40 -0
  43. package/dist/{frame-conversions-D2EYjeNa.d.ts → frame-conversions-BfsafGWR.d.ts} +1 -1
  44. package/dist/{fused-path-BdVHmDW9.d.ts → fused-path-DJoVu98b.d.ts} +1 -1
  45. package/dist/geo/h3-proximity.d.ts +1 -1
  46. package/dist/geo/index.d.ts +1 -1
  47. package/dist/{gps-anchor-CISLPQIb.d.ts → gps-anchor-dYeCvvp5.d.ts} +1 -1
  48. package/dist/{gps-ar-pose-sampler-BypXZUqC.d.ts → gps-ar-pose-sampler-DVb96iOy.d.ts} +1 -1
  49. package/dist/{gps-event-coordinator-BKVZbS1M.d.ts → gps-event-coordinator-CWIDOk4U.d.ts} +3 -3
  50. package/dist/{image-capture-BXZUmj7j.d.ts → image-capture-qAZ5Fh5X.d.ts} +44 -5
  51. package/dist/{index-BB2KEWbN2.d.ts → index-BYYUdf9v.d.ts} +1 -1
  52. package/dist/index.d.ts +67 -64
  53. package/dist/index.js +7 -4
  54. package/dist/{leaflet-map-overlay-jNt-kcUr.d.ts → leaflet-map-overlay-6CJC-E1l.d.ts} +1 -1
  55. package/dist/{map-data-BGIBM5rv.d.ts → map-data-CkloBxIl.d.ts} +1 -1
  56. package/dist/{map-overlay-draw-DcGs9UV3.d.ts → map-overlay-draw-krOKwDAq.d.ts} +1 -1
  57. package/dist/{null-storage-backend-BPQJSaMw.d.ts → null-storage-backend-uKQE8tl7.d.ts} +2 -2
  58. package/dist/occupancy-grid-BRNRaPyT.d.ts +72 -0
  59. package/dist/{opfs-storage-LsLY6VZV.d.ts → opfs-storage-D5zAn_lx.d.ts} +2 -2
  60. package/dist/{opfs-storage-backend-u4QKvgvP.d.ts → opfs-storage-backend-6aYMUZlJ.d.ts} +2 -2
  61. package/dist/{persistence-middleware-xB5sTllJ.d.ts → persistence-middleware-B9ei4Nhx.d.ts} +1 -1
  62. package/dist/{recording-options-BSFpBSZH.d.ts → recording-options-DJBXuMxV.d.ts} +12 -2
  63. package/dist/{recording-replayer-CTL2cUE0.d.ts → recording-replayer-r0DZ9ALp.d.ts} +2 -2
  64. package/dist/{recording-slice-Cb888P6d.d.ts → recording-slice-89O07ebs.d.ts} +10 -2
  65. package/dist/{replay-engine-B-5GIZtc.d.ts → replay-engine-XpDa0dy1.d.ts} +1 -1
  66. package/dist/{replay-scene-DkNfldPh.d.ts → replay-scene-Bw58VL6p.d.ts} +3 -3
  67. package/dist/sensors/gps-error-handler.d.ts +1 -1
  68. package/dist/sensors/gps.d.ts +1 -1
  69. package/dist/sensors/index.d.ts +3 -3
  70. package/dist/sensors/permission-checker.d.ts +1 -1
  71. package/dist/state/app-selectors.d.ts +1 -1
  72. package/dist/state/app-selectors.js +3 -1
  73. package/dist/state/combined-root-state.d.ts +1 -1
  74. package/dist/state/create-slam-app-store.d.ts +1 -1
  75. package/dist/state/gps-ar-pose-sampler.d.ts +3 -3
  76. package/dist/state/gps-event-coordinator.d.ts +1 -1
  77. package/dist/state/index.d.ts +16 -16
  78. package/dist/state/persistence-middleware.d.ts +1 -1
  79. package/dist/state/recording-options.d.ts +1 -1
  80. package/dist/state/recording-options.js +5 -3
  81. package/dist/state/recording-replayer.d.ts +2 -2
  82. package/dist/state/recording-slice.d.ts +1 -1
  83. package/dist/state/recording-slice.js +11 -2
  84. package/dist/state/replay-engine.d.ts +1 -1
  85. package/dist/state/store-subscribers.d.ts +2 -2
  86. package/dist/state/subscribe-to-selector.d.ts +1 -1
  87. package/dist/state/tracking-quality.d.ts +1 -1
  88. package/dist/state/tracking-quality.js +1 -1
  89. package/dist/state/tracking-slice.d.ts +1 -1
  90. package/dist/storage/file-system-utils.d.ts +2 -2
  91. package/dist/storage/file-system-utils.js +17 -1
  92. package/dist/storage/file-system.d.ts +1 -1
  93. package/dist/storage/file-system.js +2 -2
  94. package/dist/storage/index.d.ts +8 -8
  95. package/dist/storage/null-storage-backend.d.ts +1 -1
  96. package/dist/storage/opfs-storage-backend.d.ts +1 -1
  97. package/dist/storage/opfs-storage.d.ts +1 -1
  98. package/dist/storage/opfs-storage.js +4 -4
  99. package/dist/storage/storage-backend.d.ts +1 -1
  100. package/dist/storage/zip-export.d.ts +1 -1
  101. package/dist/storage/zip-export.js +1 -1
  102. package/dist/storage/zip-reader.d.ts +1 -1
  103. package/dist/storage/zip-reader.js +1 -1
  104. package/dist/{storage-backend-yDSKafAQ.d.ts → storage-backend-Do6YDpNo.d.ts} +1 -1
  105. package/dist/{store-subscribers-swlbtg_z.d.ts → store-subscribers-0YWyOLvV.d.ts} +2 -2
  106. package/dist/{subscribe-to-selector--KQpgLXG.d.ts → subscribe-to-selector-CZ6k1NFP.d.ts} +1 -1
  107. package/dist/test-utils/browser-mocks.d.ts +1 -1
  108. package/dist/test-utils/zip-round-trip-helpers.d.ts +1 -1
  109. package/dist/test-utils/zip-round-trip-helpers.js +3 -2
  110. package/dist/{tracking-slice-CDREeoZ_.d.ts → tracking-slice-CdK8c-8P.d.ts} +1 -1
  111. package/dist/types/ar-types.d.ts +2 -2
  112. package/dist/types/geo-types.d.ts +1 -1
  113. package/dist/types/index.d.ts +3 -3
  114. package/dist/utils/concurrency.d.ts +1 -1
  115. package/dist/utils/failure-tracker.d.ts +1 -1
  116. package/dist/utils/format-file-size.d.ts +1 -1
  117. package/dist/utils/fused-path.d.ts +1 -1
  118. package/dist/utils/index.d.ts +6 -6
  119. package/dist/utils/list-formatter.d.ts +1 -1
  120. package/dist/utils/logger.d.ts +1 -1
  121. package/dist/visualization/accuracy-circles.d.ts +1 -1
  122. package/dist/visualization/alignment-lerper.d.ts +1 -1
  123. package/dist/visualization/ar-world-group-alignment.d.ts +1 -1
  124. package/dist/visualization/camera-follower.d.ts +1 -1
  125. package/dist/visualization/css3d-renderer-manager.d.ts +1 -1
  126. package/dist/visualization/frame-conversions.d.ts +1 -1
  127. package/dist/visualization/frustum-visibility.d.ts +1 -1
  128. package/dist/visualization/gps-anchor.d.ts +1 -1
  129. package/dist/visualization/gps-compass-cubes.d.ts +1 -1
  130. package/dist/visualization/gps-event-markers.d.ts +1 -1
  131. package/dist/visualization/gps-event-markers.js +1 -1
  132. package/dist/visualization/hit-test-reticle.d.ts +1 -1
  133. package/dist/visualization/hit-test-reticle.js +1 -1
  134. package/dist/visualization/index.d.ts +18 -18
  135. package/dist/visualization/leaflet-map-overlay.d.ts +1 -1
  136. package/dist/visualization/lerp-utils.d.ts +1 -1
  137. package/dist/visualization/map-data.d.ts +1 -1
  138. package/dist/visualization/map-overlay-draw.d.ts +1 -1
  139. package/dist/visualization/map-overlay.d.ts +1 -1
  140. package/dist/visualization/three-dispose.d.ts +1 -1
  141. package/dist/visualization/vis-colors.d.ts +1 -1
  142. package/dist/{webxr-session-BnArFCew.d.ts → webxr-session-GjZ2BIpS.d.ts} +21 -10
  143. package/dist/webxr-session-TIaD883f.js +1054 -0
  144. package/dist/{zip-export-C0_ErAqT.d.ts → zip-export-G14hkSPG.d.ts} +1 -1
  145. package/package.json +2 -2
  146. package/dist/depth-sampler-CqrkRr2N.d.ts +0 -108
  147. package/dist/file-system-utils-BH6uIs2i.d.ts +0 -24
  148. /package/dist/{accuracy-circles-DEKr0Hoh.d.ts → accuracy-circles-BpwWAs8G.d.ts} +0 -0
  149. /package/dist/{alignment-lerper-CCZJf-Xb.d.ts → alignment-lerper-Dm9G7JJO.d.ts} +0 -0
  150. /package/dist/{camera-follower-B-nS1sr6.d.ts → camera-follower-C2mwEn7c.d.ts} +0 -0
  151. /package/dist/{capability-checker-BTCmZRL4.d.ts → capability-checker-CQXJHxXg.d.ts} +0 -0
  152. /package/dist/{capture-failure-tracker-D4HYJNOj.d.ts → capture-failure-tracker-CUeU3L_M.d.ts} +0 -0
  153. /package/dist/{chromium-camera-access-workaround-MifIwK9x.d.ts → chromium-camera-access-workaround-Jqbn0apH.d.ts} +0 -0
  154. /package/dist/{concurrency-Bsmv53qw.d.ts → concurrency-CO1FQvs3.d.ts} +0 -0
  155. /package/dist/{css3d-renderer-manager-EDED3nvg.d.ts → css3d-renderer-manager-ptu_t3B3.d.ts} +0 -0
  156. /package/dist/{failure-tracker-D7ELffm0.d.ts → failure-tracker-Cn8Fo9D4.d.ts} +0 -0
  157. /package/dist/{format-file-size-B7gJb3Md.d.ts → format-file-size-CLCZz3lC.d.ts} +0 -0
  158. /package/dist/{frame-loop-BTeRpDm4.d.ts → frame-loop-CMewS8cY.d.ts} +0 -0
  159. /package/dist/{frustum-visibility-DGnJqls0.d.ts → frustum-visibility-CmI96ESS.d.ts} +0 -0
  160. /package/dist/{geo-types-clgzl8b5.d.ts → geo-types-SaPnIEUM.d.ts} +0 -0
  161. /package/dist/{gps-B7AlMPz5.d.ts → gps-VuezJWK3.d.ts} +0 -0
  162. /package/dist/{gps-compass-cubes-Ys6Hf1nc.d.ts → gps-compass-cubes-BM1uGiYV.d.ts} +0 -0
  163. /package/dist/{gps-error-handler-BLDhUlw5.d.ts → gps-error-handler-CJl_2MVm.d.ts} +0 -0
  164. /package/dist/{gps-event-markers-BlHq3jYV.d.ts → gps-event-markers-Dc9FT-q1.d.ts} +0 -0
  165. /package/dist/{h3-proximity-DJnghtir.d.ts → h3-proximity-I1msGPgE.d.ts} +0 -0
  166. /package/dist/{hit-test-reticle-BGXOxrUh.d.ts → hit-test-reticle-BEzEybxZ.d.ts} +0 -0
  167. /package/dist/{index--ldLph4V.d.ts → index-CF05QV2m.d.ts} +0 -0
  168. /package/dist/{lerp-utils-DNcSmKVX.d.ts → lerp-utils-BpV-0Dtk.d.ts} +0 -0
  169. /package/dist/{list-formatter-CsqcU4v5.d.ts → list-formatter-RRcBe8Vg.d.ts} +0 -0
  170. /package/dist/{logger-BZ44Dhd3.d.ts → logger-BEHXsnnq.d.ts} +0 -0
  171. /package/dist/{map-overlay-yrFsUtGv.d.ts → map-overlay-bcogY5fK.d.ts} +0 -0
  172. /package/dist/{permission-checker-CfV7INCa.d.ts → permission-checker-GpAiSCwd.d.ts} +0 -0
  173. /package/dist/{scene-node-names-IHW7HR4S.d.ts → scene-node-names-Bu0S66H1.d.ts} +0 -0
  174. /package/dist/{three-dispose-WTBAfaWU.d.ts → three-dispose-D950t71B.d.ts} +0 -0
  175. /package/dist/{vis-colors-Djnyg_UH.d.ts → vis-colors-D6omC64K.d.ts} +0 -0
  176. /package/dist/{index-22v8MXJX.d.ts → webxr-nue-basis-CqKQmZzT.d.ts} +0 -0
  177. /package/dist/{xr-camera-texture-BU5YvKAM.d.ts → xr-camera-texture-DBwCaKTd.d.ts} +0 -0
  178. /package/dist/{xr-error-handler-DcUFshNF.d.ts → xr-error-handler-C0AQRmxs.d.ts} +0 -0
  179. /package/dist/{xr-frame-loop-BKckC7xC.d.ts → xr-frame-loop-BtYytZJg.d.ts} +0 -0
  180. /package/dist/{zip-reader-B2lzN8F5.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-DcUFshNF.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 };
@@ -1,2 +1,2 @@
1
- import { a as runXrFrameUpdates, i as registerXrFrameUpdate, n as XrFrameUpdate, r as clearXrFrameUpdates, t as XrFrameContext } from "../xr-frame-loop-BKckC7xC.js";
1
+ import { a as runXrFrameUpdates, i as registerXrFrameUpdate, n as XrFrameUpdate, r as clearXrFrameUpdates, t as XrFrameContext } from "../xr-frame-loop-BtYytZJg.js";
2
2
  export { XrFrameContext, XrFrameUpdate, 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 };
@@ -1,4 +1,4 @@
1
- import { t as SubscribableStore } from "./subscribe-to-selector--KQpgLXG.js";
1
+ import { t as SubscribableStore } from "./subscribe-to-selector-CZ6k1NFP.js";
2
2
  import * as THREE from "three";
3
3
 
4
4
  //#region ../src/visualization/ar-world-group-alignment.d.ts
@@ -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).
@@ -99,6 +119,15 @@ declare class CameraBlitCapture {
99
119
  * @returns true if resources were actually resized
100
120
  */
101
121
  resizeIfNeeded(newWidth: number, newHeight: number): boolean;
122
+ /**
123
+ * Current render-target width in pixels. This is exactly the width of the
124
+ * JPEG produced by {@link captureToBlob} (the encode canvas is sized to the
125
+ * render target), so callers can persist it as the captured image's true
126
+ * pixel width without decoding the blob.
127
+ */
128
+ getWidth(): number;
129
+ /** Current render-target height in pixels. See {@link getWidth}. */
130
+ getHeight(): number;
102
131
  /**
103
132
  * Dispose GPU resources. Call when AR session ends.
104
133
  */
@@ -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--ldLph4V.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-isPsQptb.js";
2
- import { a as TrackingSliceState, t as DeviceOrientation } from "./tracking-slice-CDREeoZ_.js";
3
- import { t as RecordingState } from "./recording-slice-Cb888P6d.js";
4
- import { n as SessionMetadata } from "./opfs-storage-LsLY6VZV.js";
5
- import { n as StorageBackend } from "./storage-backend-yDSKafAQ.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-D5zAn_lx.js";
5
+ import { n as StorageBackend } from "./storage-backend-Do6YDpNo.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 };
@@ -0,0 +1,52 @@
1
+ import { r as DepthPoint } from "./ar-types-Dan1MVTi.js";
2
+ import { Matrix4, Quaternion, Vector3 } from "gps-plus-slam-js";
3
+
4
+ //#region ../src/ar/depth-unprojection.d.ts
5
+ /**
6
+ * A sample-scoped unprojector: the camera pose and (inverse) projection are
7
+ * computed once, then reused for every point in the same `DepthSample`. See
8
+ * {@link createDepthUnprojector}.
9
+ */
10
+ interface DepthUnprojector {
11
+ /**
12
+ * Unproject one point into raw WebXR space, or `null` when the input is not
13
+ * usable (non-positive/non-finite depth, out-of-range screen coordinates,
14
+ * degenerate ray).
15
+ */
16
+ unproject(point: DepthPoint): Vector3 | null;
17
+ }
18
+ /**
19
+ * Build a {@link DepthUnprojector} for one depth sample. The projection
20
+ * inverse and camera quaternion/position are sample-invariant, so they are
21
+ * computed once here instead of per point (the per-point hot path then only
22
+ * does the cheap NDC→view→world transform). Callers that fold many points
23
+ * from the same sample — e.g. `OccupancyGrid.addSample` — should build the
24
+ * unprojector once and reuse it for all points.
25
+ *
26
+ * @param cameraPos - camera position, raw WebXR (`DepthSample.cameraPos`)
27
+ * @param cameraRot - camera quaternion [x,y,z,w], raw WebXR (`DepthSample.cameraRot`)
28
+ * @param projectionMatrix - column-major projection matrix of the capturing
29
+ * view (`DepthSample.projectionMatrix`). `undefined` for recordings made
30
+ * before intrinsics capture — those points cannot be unprojected.
31
+ * @returns an unprojector, or `null` when the sample cannot be unprojected at
32
+ * all (missing or singular projection matrix).
33
+ */
34
+ declare function createDepthUnprojector(cameraPos: Vector3, cameraRot: Quaternion, projectionMatrix: Matrix4 | undefined): DepthUnprojector | null;
35
+ /**
36
+ * Unproject a single depth point into raw WebXR space. Convenience wrapper
37
+ * over {@link createDepthUnprojector} for one-off callers; when unprojecting
38
+ * many points from the same sample, build the unprojector once instead.
39
+ *
40
+ * @param point - normalized view coordinates + depth in meters
41
+ * @param cameraPos - camera position, raw WebXR (`DepthSample.cameraPos`)
42
+ * @param cameraRot - camera quaternion [x,y,z,w], raw WebXR (`DepthSample.cameraRot`)
43
+ * @param projectionMatrix - column-major projection matrix of the capturing
44
+ * view (`DepthSample.projectionMatrix`). `undefined` for recordings made
45
+ * before intrinsics capture — those points cannot be unprojected.
46
+ * @returns the 3D point in raw WebXR space, or `null` when the input is not
47
+ * usable (missing/singular matrix, non-positive or non-finite depth,
48
+ * out-of-range screen coordinates).
49
+ */
50
+ declare function unprojectDepthPoint(point: DepthPoint, cameraPos: Vector3, cameraRot: Quaternion, projectionMatrix: Matrix4 | undefined): Vector3 | null;
51
+ //#endregion
52
+ export { createDepthUnprojector as n, unprojectDepthPoint as r, DepthUnprojector as t };
@@ -1,7 +1,7 @@
1
- import { t as ArCrashIsolationOptions } from "./recording-options-BSFpBSZH.js";
2
- import { n as RawDeviceOrientation, t as GpsPosition } from "./gps-B7AlMPz5.js";
3
- import { n as PermissionStatus } from "./permission-checker-CfV7INCa.js";
4
- import { t as SessionFeatureOptions } from "./webxr-session-BnArFCew.js";
1
+ import { t as ArCrashIsolationOptions } from "./recording-options-DJBXuMxV.js";
2
+ import { n as RawDeviceOrientation, t as GpsPosition } from "./gps-VuezJWK3.js";
3
+ import { n as PermissionStatus } from "./permission-checker-GpAiSCwd.js";
4
+ import { t as SessionFeatureOptions } from "./webxr-session-GjZ2BIpS.js";
5
5
 
6
6
  //#region ../src/ar/enable-gps-ar.d.ts
7
7
  /**
@@ -1,4 +1,4 @@
1
- import { n as SessionMetadata } from "./opfs-storage-LsLY6VZV.js";
1
+ import { n as SessionMetadata } from "./opfs-storage-D5zAn_lx.js";
2
2
 
3
3
  //#region ../src/storage/file-system.d.ts
4
4
  /**
@@ -0,0 +1,40 @@
1
+ //#region ../src/storage/file-system-utils.d.ts
2
+ /**
3
+ * File System Utilities - Pure Functions
4
+ *
5
+ * These are extracted from file-system.ts so they can be tested
6
+ * without mocking browser APIs.
7
+ */
8
+ /**
9
+ * Format a Date as a timestamp string for folder names
10
+ * @example "2025-02-28_14-30-11utc"
11
+ */
12
+ declare function formatTimestamp(date: Date): string;
13
+ /**
14
+ * Format an action file index as a zero-padded filename
15
+ * @example 42 -> "000042.json"
16
+ */
17
+ declare function formatActionFilename(index: number): string;
18
+ /**
19
+ * Format a frame index as a zero-padded filename
20
+ * @example 42 -> "frame-000042.jpg"
21
+ */
22
+ declare function formatFrameFilename(index: number): string;
23
+ /**
24
+ * Canonical session subdirectory for captured camera images, written by new
25
+ * recordings. Renamed from {@link LEGACY_SESSION_IMAGES_DIR} so an exported ZIP
26
+ * is a textbook COLMAP tree (`images/frame-NNNNNN.jpg` next to `sparse/0/`).
27
+ * Both the OPFS write path and the recorder's persisted `imageFile` value must
28
+ * use THIS constant so a recording stays self-consistent (COLMAP export plan
29
+ * Q5, gps-plus-slam/GpsPlusSlamJs_Docs/docs/2026-06-13-colmap-export-plan.md).
30
+ */
31
+ declare const SESSION_IMAGES_DIR = "images";
32
+ /**
33
+ * The pre-2026-06 session image subdirectory. Recordings made before the
34
+ * {@link SESSION_IMAGES_DIR} rename store frames under `frames/` (in both the
35
+ * on-disk dir and the persisted `imageFile`); readers fall back to this prefix
36
+ * so old recordings still replay.
37
+ */
38
+ declare const LEGACY_SESSION_IMAGES_DIR = "frames";
39
+ //#endregion
40
+ export { formatTimestamp as a, formatFrameFilename as i, SESSION_IMAGES_DIR as n, formatActionFilename as r, LEGACY_SESSION_IMAGES_DIR as t };
@@ -1,4 +1,4 @@
1
- import { i as LatLongAlt, r as LatLong } from "./index--ldLph4V.js";
1
+ import { i as LatLongAlt, r as LatLong } from "./index-CF05QV2m.js";
2
2
  import * as THREE from "three";
3
3
 
4
4
  //#region ../src/visualization/frame-conversions.d.ts
@@ -1,4 +1,4 @@
1
- import { t as GpsCoord } from "./geo-types-clgzl8b5.js";
1
+ import { t as GpsCoord } from "./geo-types-SaPnIEUM.js";
2
2
  import { LatLong, Matrix4, Vector3, fusedGpsFromOdom } from "gps-plus-slam-js";
3
3
 
4
4
  //#region ../src/utils/fused-path.d.ts
@@ -1,2 +1,2 @@
1
- import { a as gpsToH3, i as findNearbyGeoAnchor, n as KnownGeoAnchor, o as h3CellsMatch, r as approxDistanceMetres, s as isH3Index, t as H3_RESOLUTION } from "../h3-proximity-DJnghtir.js";
1
+ import { a as gpsToH3, i as findNearbyGeoAnchor, n as KnownGeoAnchor, o as h3CellsMatch, r as approxDistanceMetres, s as isH3Index, t as H3_RESOLUTION } from "../h3-proximity-I1msGPgE.js";
2
2
  export { H3_RESOLUTION, KnownGeoAnchor, approxDistanceMetres, findNearbyGeoAnchor, gpsToH3, h3CellsMatch, isH3Index };
@@ -1,2 +1,2 @@
1
- import { a as gpsToH3, i as findNearbyGeoAnchor, n as KnownGeoAnchor, o as h3CellsMatch, r as approxDistanceMetres, s as isH3Index, t as H3_RESOLUTION } from "../h3-proximity-DJnghtir.js";
1
+ import { a as gpsToH3, i as findNearbyGeoAnchor, n as KnownGeoAnchor, o as h3CellsMatch, r as approxDistanceMetres, s as isH3Index, t as H3_RESOLUTION } from "../h3-proximity-I1msGPgE.js";
2
2
  export { H3_RESOLUTION, type KnownGeoAnchor, approxDistanceMetres, findNearbyGeoAnchor, gpsToH3, h3CellsMatch, isH3Index };
@@ -1,4 +1,4 @@
1
- import { i as LatLongAlt, r as LatLong } from "./index--ldLph4V.js";
1
+ import { i as LatLongAlt, r as LatLong } from "./index-CF05QV2m.js";
2
2
  import * as THREE from "three";
3
3
 
4
4
  //#region ../src/visualization/gps-anchor.d.ts
@@ -1,4 +1,4 @@
1
- import { t as ARPose } from "./ar-types-isPsQptb.js";
1
+ import { t as ARPose } from "./ar-types-Dan1MVTi.js";
2
2
  import { Quaternion, Vector3 } from "gps-plus-slam-js";
3
3
 
4
4
  //#region ../src/state/gps-ar-pose-sampler.d.ts
@@ -1,6 +1,6 @@
1
- import { t as ARPose } from "./ar-types-isPsQptb.js";
2
- import { n as RawDeviceOrientation, t as GpsPosition } from "./gps-B7AlMPz5.js";
3
- import { i as SlamAppStore } from "./create-slam-app-store-B76AGaI0.js";
1
+ import { t as ARPose } from "./ar-types-Dan1MVTi.js";
2
+ import { n as RawDeviceOrientation, t as GpsPosition } from "./gps-VuezJWK3.js";
3
+ import { i as SlamAppStore } from "./create-slam-app-store-BljusOua.js";
4
4
  import { Quaternion, RawGpsPoint, RecordGpsEventPayload, Vector3, eulerToQuaternion } from "gps-plus-slam-js";
5
5
  import { ReducersMapObject } from "@reduxjs/toolkit";
6
6
 
@@ -1,4 +1,4 @@
1
- import { a as WebXRQuaternion, o as WebXRVec3, t as ARPose } from "./ar-types-isPsQptb.js";
1
+ import { o as WebXRQuaternion, s as WebXRVec3, t as ARPose } from "./ar-types-Dan1MVTi.js";
2
2
 
3
3
  //#region ../src/ar/image-capture.d.ts
4
4
  /**
@@ -20,13 +20,26 @@ interface ImageCaptureConfig {
20
20
  * If a capture promise doesn't resolve within this duration, the flag is
21
21
  * force-reset to prevent permanent pipeline deadlock. */
22
22
  captureTimeoutMs: number;
23
+ /** Resolution divisor for the captured frame: 1 = full native resolution,
24
+ * 2 = half, 4 = quarter (default: 1). Consumed by the blit pipeline in
25
+ * `startImageCapture`, not by ImageCaptureManager's timing loop. Folded
26
+ * into this config so the whole user options section can flow through the
27
+ * capture seam as one object (see the field-drop audit, F3). */
28
+ resolutionDivisor: number;
23
29
  }
24
30
  /**
25
31
  * Default capture configuration
26
32
  */
27
33
  declare const DEFAULT_CAPTURE_CONFIG: ImageCaptureConfig;
28
34
  /**
29
- * Data returned when an image is captured
35
+ * Data returned when an image is captured.
36
+ *
37
+ * NOTE: every persistable field here is forwarded into the `add2dImage`
38
+ * action by the RecorderApp's `handleImageCaptured`, which rebuilds the
39
+ * payload field-by-field. A new field added here is therefore NOT persisted
40
+ * until it is threaded through that handler — see
41
+ * `2026-06-12-payload-rebuild-field-drop-audit.md` (F1/F2) and the forwarding
42
+ * test in `main.occupancy-cubes-wiring.test.ts`.
30
43
  */
31
44
  interface CapturedImage {
32
45
  /** The captured image as a Blob */
@@ -41,6 +54,30 @@ interface CapturedImage {
41
54
  readonly rotation: WebXRQuaternion;
42
55
  /** Device screen orientation (0, 90, 180, 270) */
43
56
  readonly screenRotation: number;
57
+ /**
58
+ * Width of the encoded JPEG in pixels — the blit/canvas dimensions the blob
59
+ * was produced from, so it equals the decoded image's width. Persisted as
60
+ * `ArImageCapture.width` so consumers (the 3D frame-tile visualizer) can
61
+ * render each frame at its true aspect ratio. Always present from the
62
+ * capture pipeline; optional so the field can be threaded without forcing
63
+ * every test/caller to supply it.
64
+ */
65
+ readonly width?: number;
66
+ /** Height of the encoded JPEG in pixels. See {@link CapturedImage.width}. */
67
+ readonly height?: number;
68
+ }
69
+ /**
70
+ * A captured frame blob together with the pixel dimensions it was encoded at.
71
+ * Returned by the optional `captureFrame` (blit) callback so the pose-invariant
72
+ * image size can be persisted as first-class metadata (frame-tile aspect-ratio
73
+ * fix, D1 of 2026-06-13-frame-tile-rendering-bugs-user-feedback.md). The
74
+ * dimensions are the blit render-target size — identical to the JPEG's own
75
+ * width/height — so no decode is needed to learn the aspect ratio.
76
+ */
77
+ interface CapturedFrame {
78
+ readonly blob: Blob;
79
+ readonly width: number;
80
+ readonly height: number;
44
81
  }
45
82
  /**
46
83
  * Callbacks for image capture integration
@@ -67,10 +104,12 @@ interface ImageCaptureCallbacks {
67
104
  * be read directly via canvas.toBlob() (which returns black pixels).
68
105
  *
69
106
  * @param quality - JPEG quality 0.0-1.0
70
- * @returns Promise resolving to a JPEG Blob, or null if capture fails
107
+ * @returns Promise resolving to the JPEG blob plus the pixel dimensions it
108
+ * was encoded at, or null if capture fails. The dimensions flow into the
109
+ * persisted `ArImageCapture.width`/`height` for aspect-correct rendering.
71
110
  * @see docs/2026-02-06-bug-camera-frames-black.md
72
111
  */
73
- captureFrame?: (quality: number) => Promise<Blob | null>;
112
+ captureFrame?: (quality: number) => Promise<CapturedFrame | null>;
74
113
  }
75
114
  /**
76
115
  * Manages periodic image capture from a canvas.
@@ -127,4 +166,4 @@ declare class ImageCaptureManager {
127
166
  private handleCapturedBlob;
128
167
  }
129
168
  //#endregion
130
- export { ImageCaptureManager as a, ImageCaptureConfig as i, DEFAULT_CAPTURE_CONFIG as n, MIN_VALID_IMAGE_BYTES as o, ImageCaptureCallbacks as r, CapturedImage as t };
169
+ export { ImageCaptureConfig as a, ImageCaptureCallbacks as i, CapturedImage as n, ImageCaptureManager as o, DEFAULT_CAPTURE_CONFIG as r, MIN_VALID_IMAGE_BYTES as s, CapturedFrame as t };
@@ -1,4 +1,4 @@
1
- import { L as CombinedRootState, h as TrackingQualityReport } from "./create-slam-app-store-B76AGaI0.js";
1
+ import { L as CombinedRootState, h as TrackingQualityReport } from "./create-slam-app-store-BljusOua.js";
2
2
  import { Add2dImagePayload, GpsPoint as GpsPoint$1, LatLong as LatLong$1, RawDeviceOrientation, RawGpsPoint as RawGpsPoint$1, RecordGpsEventPayload as RecordGpsEventPayload$1, add2dImage, calcRelativeCoordsInMeters, recordGpsEvent as recordGpsEvent$1, setZeroPos as setZeroPos$1 } from "gps-plus-slam-js";
3
3
 
4
4
  //#region ../src/state/onboarding-guidance.d.ts