gps-plus-slam-app-framework 1.4.0 → 1.8.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 (243) hide show
  1. package/dist/absolute-orientation-BAxc15J6.d.ts +54 -0
  2. package/dist/{accuracy-circles-Pi2YeCrM.d.ts → accuracy-circles-DkWBkF5g.d.ts} +16 -6
  3. package/dist/{app-selectors-CrOucL00.d.ts → app-selectors-JqydaUMN.d.ts} +1 -1
  4. package/dist/ar/bresenham3d.d.ts +1 -1
  5. package/dist/ar/camera-blit-capture.d.ts +1 -1
  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-grid-lookup.d.ts +1 -1
  10. package/dist/ar/depth-occluder.d.ts +127 -0
  11. package/dist/ar/depth-occluder.js +299 -0
  12. package/dist/ar/depth-sampler.d.ts +2 -2
  13. package/dist/ar/depth-sampler.js +24 -4
  14. package/dist/ar/depth-unprojection.d.ts +1 -1
  15. package/dist/ar/depth-unprojection.js +52 -21
  16. package/dist/ar/enable-gps-ar.d.ts +1 -1
  17. package/dist/ar/enable-gps-ar.js +1 -1
  18. package/dist/ar/frame-loop.d.ts +1 -1
  19. package/dist/ar/frame-loop.js +12 -2
  20. package/dist/ar/image-capture.d.ts +2 -2
  21. package/dist/ar/image-capture.js +1 -154
  22. package/dist/ar/image-quality.d.ts +2 -0
  23. package/dist/ar/image-quality.js +169 -0
  24. package/dist/ar/index.d.ts +32 -30
  25. package/dist/ar/index.js +7 -5
  26. package/dist/ar/occlusion-mesh-worker.d.ts +58 -0
  27. package/dist/ar/occlusion-mesh-worker.js +129 -0
  28. package/dist/ar/occupancy-grid.d.ts +2 -2
  29. package/dist/ar/occupancy-grid.js +287 -31
  30. package/dist/ar/occupancy-mesher.d.ts +2 -0
  31. package/dist/ar/occupancy-mesher.js +638 -0
  32. package/dist/ar/planar-pnp.d.ts +1 -1
  33. package/dist/ar/qr-debug-view.d.ts +1 -1
  34. package/dist/ar/qr-derived-pose.d.ts +1 -1
  35. package/dist/ar/qr-detection-controller.d.ts +1 -1
  36. package/dist/ar/qr-frontend.d.ts +1 -1
  37. package/dist/ar/qr-pose.d.ts +1 -1
  38. package/dist/ar/qr-size-depth-context.d.ts +2 -2
  39. package/dist/ar/qr-size-measurer.d.ts +1 -1
  40. package/dist/ar/replay-scene.d.ts +1 -1
  41. package/dist/ar/replay-scene.js +1 -1
  42. package/dist/ar/scene-node-names.d.ts +1 -1
  43. package/dist/ar/webxr-nue-basis.d.ts +1 -1
  44. package/dist/ar/webxr-session.d.ts +4 -4
  45. package/dist/ar/webxr-session.js +2 -2
  46. package/dist/ar/xr-camera-texture.d.ts +1 -1
  47. package/dist/ar/xr-error-handler.d.ts +1 -1
  48. package/dist/ar/xr-frame-loop.d.ts +1 -1
  49. package/dist/ar/xr-frame-loop.js +12 -2
  50. package/dist/{ar-Dc72Ksiu.js → ar-BqCudtqE.js} +4 -3
  51. package/dist/{ar-world-group-alignment-DDLkgNf_.d.ts → ar-world-group-alignment-BlvGcIAk.d.ts} +1 -1
  52. package/dist/capture-motion-gate-BGoAMkmf.d.ts +24 -0
  53. package/dist/capture-motion-gate-_NdTSwKi.js +106 -0
  54. package/dist/cell-key-DIB4rOTV.js +51 -0
  55. package/dist/core/index.d.ts +2 -2
  56. package/dist/core/index.js +2 -2
  57. package/dist/{create-slam-app-store-Ce2FMKCp.d.ts → create-slam-app-store-D3nzh9Wb.d.ts} +50 -40
  58. package/dist/create-slam-app-store-DXiAnJwR.js +156 -0
  59. package/dist/{depth-grid-lookup-vMDrGTk_.d.ts → depth-grid-lookup-D5atpNZ3.d.ts} +1 -1
  60. package/dist/{depth-sampler-DwfPYzS7.d.ts → depth-sampler-CwObAwEA.d.ts} +33 -1
  61. package/dist/{depth-unprojection-CK3b9htH.d.ts → depth-unprojection-DRdBU90S.d.ts} +1 -1
  62. package/dist/{enable-gps-ar-CjZ1nEdu.d.ts → enable-gps-ar-DvGzBOrp.d.ts} +4 -4
  63. package/dist/{frame-conversions-D_Af8Dt3.d.ts → frame-conversions-C4bnNOEY.d.ts} +1 -1
  64. package/dist/{fused-path-C0FHe67t.d.ts → fused-path-C5pp0PJU.d.ts} +1 -1
  65. package/dist/geo/h3-proximity.d.ts +1 -1
  66. package/dist/geo/index.d.ts +1 -1
  67. package/dist/geodesic-angle-BTldfFgs.js +39 -0
  68. package/dist/{gps-anchor-C6I9NDHA.d.ts → gps-anchor-BjECcYnD.d.ts} +1 -1
  69. package/dist/{gps-ar-pose-sampler-DaHg2aPZ.d.ts → gps-ar-pose-sampler-DEGNk2CI.d.ts} +1 -1
  70. package/dist/{gps-event-coordinator-C7hwHyLA.d.ts → gps-event-coordinator-CHtIkhb7.d.ts} +21 -11
  71. package/dist/{image-capture-C2Feywet.d.ts → image-capture-Bp6Pd4Md.d.ts} +103 -3
  72. package/dist/image-capture-DVcuqMtX.js +374 -0
  73. package/dist/image-quality-BMx2ko9-.d.ts +158 -0
  74. package/dist/index-D4vrgV4b.d.ts +2 -0
  75. package/dist/index-D74JTX4I.d.ts +22 -0
  76. package/dist/{index-m_LneUSi.d.ts → index-DlsOo2IB.d.ts} +1 -1
  77. package/dist/{index-BvLo2poy.d.ts → index-cCQWuYqy.d.ts} +3 -3
  78. package/dist/index.d.ts +82 -77
  79. package/dist/index.js +15 -10
  80. package/dist/{leaflet-map-overlay-bnb2U1NQ.d.ts → leaflet-map-overlay-BOZFW5kT.d.ts} +64 -10
  81. package/dist/leaflet-map-overlay-CBWPvaX9.js +452 -0
  82. package/dist/lerp-utils-Qs8OhumS.d.ts +42 -0
  83. package/dist/map-data-D7rpMC6f.js +120 -0
  84. package/dist/{map-data-DdjPiSav.d.ts → map-data-PNfGKGWx.d.ts} +22 -2
  85. package/dist/{map-overlay-draw-CPVdAsFq.d.ts → map-overlay-draw-DiroaJed.d.ts} +1 -1
  86. package/dist/{null-storage-backend-CuSdYFsM.d.ts → null-storage-backend-B1-jlpin.d.ts} +2 -2
  87. package/dist/occlusion-mesh-b28lRJ6S.d.ts +194 -0
  88. package/dist/occupancy-grid-BV7RhB7I.d.ts +194 -0
  89. package/dist/occupancy-mesher-C2ecWhNp.d.ts +100 -0
  90. package/dist/{opfs-storage-DMtlt57S.d.ts → opfs-storage-CTkxvxN4.d.ts} +5 -0
  91. package/dist/{opfs-storage-backend-CeQAb0SD.d.ts → opfs-storage-backend-D6Z03fML.d.ts} +2 -2
  92. package/dist/{persistence-middleware-5eearhSV.d.ts → persistence-middleware-CG-t08X4.d.ts} +1 -1
  93. package/dist/{planar-pnp-cjthe7Be.d.ts → planar-pnp-D7bc_rLX.d.ts} +1 -1
  94. package/dist/{qr-debug-view-B8gTzjMm.d.ts → qr-debug-view-BJooqbiP.d.ts} +1 -1
  95. package/dist/{qr-derived-pose-DinKE600.d.ts → qr-derived-pose-BwNWn8oV.d.ts} +2 -2
  96. package/dist/{qr-detected-slice-Cpv-dg82.d.ts → qr-detected-slice-TzVb8bij.d.ts} +4 -4
  97. package/dist/{qr-detection-controller-Cd2Ih4cC.d.ts → qr-detection-controller-Bo4XfQTP.d.ts} +3 -3
  98. package/dist/{qr-frontend-DufZp2xS.d.ts → qr-frontend-BPURlh3S.d.ts} +1 -1
  99. package/dist/{qr-pose-aggregation-CFlmLm6w.d.ts → qr-pose-aggregation-CypnmjA-.d.ts} +1 -1
  100. package/dist/{qr-pose-aggregation-DXR9STHs.js → qr-pose-aggregation-DBiaLW0O.js} +1 -10
  101. package/dist/{qr-size-depth-context-onu36E9s.d.ts → qr-size-depth-context-apAOmbrZ.d.ts} +2 -2
  102. package/dist/{qr-size-measurer-D7739ZpB.d.ts → qr-size-measurer-DxiMqRUw.d.ts} +3 -3
  103. package/dist/recording-options-vASCFzc5.d.ts +667 -0
  104. package/dist/{recording-replayer-CUEK82tM.d.ts → recording-replayer-D0V9WkmY.d.ts} +2 -2
  105. package/dist/{recording-slice-B-uCgGoh.d.ts → recording-slice-DTUtSVgd.d.ts} +2 -2
  106. package/dist/{replay-engine-BSVd7Zzb.d.ts → replay-engine-xdrUgWJi.d.ts} +1 -1
  107. package/dist/{replay-scene-CPt56pps.d.ts → replay-scene-zJ8SFDjE.d.ts} +3 -3
  108. package/dist/sensors/absolute-orientation.d.ts +2 -0
  109. package/dist/sensors/absolute-orientation.js +162 -0
  110. package/dist/sensors/gps-error-handler.d.ts +1 -1
  111. package/dist/sensors/gps.d.ts +1 -1
  112. package/dist/sensors/index.d.ts +5 -4
  113. package/dist/sensors/index.js +2 -1
  114. package/dist/sensors/permission-checker.d.ts +1 -1
  115. package/dist/state/app-selectors.d.ts +1 -1
  116. package/dist/state/combined-root-state.d.ts +1 -1
  117. package/dist/state/create-slam-app-store.d.ts +1 -1
  118. package/dist/state/create-slam-app-store.js +1 -74
  119. package/dist/state/gps-ar-pose-sampler.d.ts +3 -3
  120. package/dist/state/gps-event-coordinator.d.ts +2 -2
  121. package/dist/state/gps-event-coordinator.js +27 -17
  122. package/dist/state/index.d.ts +20 -20
  123. package/dist/state/index.js +4 -4
  124. package/dist/state/persistence-middleware.d.ts +1 -1
  125. package/dist/state/persistence-middleware.js +30 -19
  126. package/dist/state/qr-detected-slice.d.ts +3 -3
  127. package/dist/state/qr-detected-slice.js +1 -1
  128. package/dist/state/recording-options.d.ts +2 -2
  129. package/dist/state/recording-options.js +286 -25
  130. package/dist/state/recording-replayer.d.ts +2 -2
  131. package/dist/state/recording-replayer.js +1 -1
  132. package/dist/state/recording-slice.d.ts +1 -1
  133. package/dist/state/replay-engine.d.ts +1 -1
  134. package/dist/state/store-subscribers.d.ts +2 -2
  135. package/dist/state/store-subscribers.js +2 -1
  136. package/dist/state/subscribe-to-selector.d.ts +1 -1
  137. package/dist/state/tracking-quality.d.ts +2 -2
  138. package/dist/state/tracking-quality.js +49 -128
  139. package/dist/state/tracking-slice.d.ts +1 -1
  140. package/dist/{state-IEw0ZsBu.js → state-BeKvn4B-.js} +1 -1
  141. package/dist/storage/file-system-utils.d.ts +1 -1
  142. package/dist/storage/index.d.ts +8 -8
  143. package/dist/storage/null-storage-backend.d.ts +1 -1
  144. package/dist/storage/opfs-storage-backend.d.ts +1 -1
  145. package/dist/storage/opfs-storage.d.ts +1 -1
  146. package/dist/storage/opfs-storage.js +25 -1
  147. package/dist/storage/storage-backend.d.ts +1 -1
  148. package/dist/storage/zip-coverage-embed.d.ts +1 -1
  149. package/dist/storage/zip-export.d.ts +1 -1
  150. package/dist/storage/zip-reader.d.ts +1 -1
  151. package/dist/{storage-backend-CTJHNdMp.d.ts → storage-backend-sEtmM4yW.d.ts} +1 -1
  152. package/dist/{store-subscribers-BZP81hjh.d.ts → store-subscribers-DScA7tdT.d.ts} +2 -2
  153. package/dist/{subscribe-to-selector-Bp3sLx8L.d.ts → subscribe-to-selector-B9ylM5Oh.d.ts} +1 -1
  154. package/dist/test-utils/browser-mocks.d.ts +1 -1
  155. package/dist/test-utils/zip-round-trip-helpers.d.ts +1 -1
  156. package/dist/{tracking-slice-DJZ9qeGg.d.ts → tracking-slice-DMkHlbny.d.ts} +1 -1
  157. package/dist/types/ar-types.d.ts +1 -1
  158. package/dist/types/geo-types.d.ts +1 -1
  159. package/dist/types/index.d.ts +2 -2
  160. package/dist/utils/concurrency.d.ts +1 -1
  161. package/dist/utils/failure-tracker.d.ts +1 -1
  162. package/dist/utils/format-file-size.d.ts +1 -1
  163. package/dist/utils/fused-path.d.ts +1 -1
  164. package/dist/utils/index.d.ts +8 -7
  165. package/dist/utils/index.js +2 -1
  166. package/dist/utils/list-formatter.d.ts +1 -1
  167. package/dist/utils/logger.d.ts +1 -1
  168. package/dist/visualization/accuracy-circles.d.ts +1 -1
  169. package/dist/visualization/accuracy-circles.js +17 -8
  170. package/dist/visualization/alignment-lerper.d.ts +1 -1
  171. package/dist/visualization/ar-world-group-alignment.d.ts +1 -1
  172. package/dist/visualization/ar-world-group-alignment.js +1 -1
  173. package/dist/visualization/camera-follower.d.ts +1 -1
  174. package/dist/visualization/css3d-renderer-manager.d.ts +1 -1
  175. package/dist/visualization/frame-conversions.d.ts +1 -1
  176. package/dist/visualization/frustum-visibility.d.ts +1 -1
  177. package/dist/visualization/gps-anchor.d.ts +1 -1
  178. package/dist/visualization/gps-compass-cubes.d.ts +1 -1
  179. package/dist/visualization/gps-event-markers.d.ts +1 -1
  180. package/dist/visualization/gps-event-markers.js +1 -1
  181. package/dist/visualization/hit-test-reticle.d.ts +1 -1
  182. package/dist/visualization/index.d.ts +20 -19
  183. package/dist/visualization/index.js +4 -3
  184. package/dist/visualization/leaflet-map-overlay.d.ts +1 -1
  185. package/dist/visualization/leaflet-map-overlay.js +1 -282
  186. package/dist/visualization/lerp-utils.d.ts +2 -2
  187. package/dist/visualization/lerp-utils.js +27 -3
  188. package/dist/visualization/map-data.d.ts +1 -1
  189. package/dist/visualization/map-data.js +1 -32
  190. package/dist/visualization/map-overlay-draw.d.ts +1 -1
  191. package/dist/visualization/map-overlay-draw.js +24 -2
  192. package/dist/visualization/map-overlay.d.ts +1 -1
  193. package/dist/visualization/occluder-mesh-driver.d.ts +130 -0
  194. package/dist/visualization/occluder-mesh-driver.js +219 -0
  195. package/dist/visualization/occlusion-mesh.d.ts +2 -0
  196. package/dist/visualization/occlusion-mesh.js +437 -0
  197. package/dist/visualization/three-dispose.d.ts +1 -1
  198. package/dist/visualization/vis-colors.d.ts +1 -1
  199. package/dist/{webxr-session-CJF9Nijp.js → webxr-session-BCP2XK7k.js} +110 -14
  200. package/dist/{webxr-session-COsOjJwn.d.ts → webxr-session-cRG2SYAQ.d.ts} +83 -10
  201. package/package.json +4 -4
  202. package/dist/index-eaI1Z1KR.d.ts +0 -2
  203. package/dist/lerp-utils-kLxMJ2Xj.d.ts +0 -20
  204. package/dist/occupancy-grid-Bvi2Gw1U.d.ts +0 -82
  205. package/dist/recording-options-D93kksJU.d.ts +0 -369
  206. /package/dist/{alignment-lerper-BUTsQ2yu.d.ts → alignment-lerper-gD70k7Fq.d.ts} +0 -0
  207. /package/dist/{ar-types-BckI1E-O.d.ts → ar-types-Cc5yvBwn.d.ts} +0 -0
  208. /package/dist/{bresenham3d-ByjcmEgM.d.ts → bresenham3d-DGc4nIb-.d.ts} +0 -0
  209. /package/dist/{camera-blit-capture-CqWe1G7s.d.ts → camera-blit-capture-BimSdyih.d.ts} +0 -0
  210. /package/dist/{camera-follower-BTb2Yp12.d.ts → camera-follower-DfBm2J5i.d.ts} +0 -0
  211. /package/dist/{capability-checker-CXqoJYtb.d.ts → capability-checker-BTTpygvZ.d.ts} +0 -0
  212. /package/dist/{capture-failure-tracker-BL5SoG2a.d.ts → capture-failure-tracker-_BVfhpxv.d.ts} +0 -0
  213. /package/dist/{chromium-camera-access-workaround-BNK189P0.d.ts → chromium-camera-access-workaround-C6yJLXz9.d.ts} +0 -0
  214. /package/dist/{concurrency-DqAlKX4j.d.ts → concurrency-DPeGBH0X.d.ts} +0 -0
  215. /package/dist/{css3d-renderer-manager-m-ab19vp.d.ts → css3d-renderer-manager-CPpVtvgs.d.ts} +0 -0
  216. /package/dist/{failure-tracker-Cnc_xWUw.d.ts → failure-tracker-CeKf5HIc.d.ts} +0 -0
  217. /package/dist/{file-system-utils-Bs7lpeVP.d.ts → file-system-utils-DueXHxo9.d.ts} +0 -0
  218. /package/dist/{format-file-size-BFUWvir1.d.ts → format-file-size-DxN-EBLc.d.ts} +0 -0
  219. /package/dist/{frame-loop-BEO9_q3k.d.ts → frame-loop-7GBMBu68.d.ts} +0 -0
  220. /package/dist/{frustum-visibility-D_RGmH98.d.ts → frustum-visibility-CokkgS_i.d.ts} +0 -0
  221. /package/dist/{geo-types-DqiBzPO9.d.ts → geo-types-DrGVu8nS.d.ts} +0 -0
  222. /package/dist/{gps-C5inWeNV.d.ts → gps-CDKDQokE.d.ts} +0 -0
  223. /package/dist/{gps-compass-cubes-BExxrWJ-.d.ts → gps-compass-cubes-1mooS3BS.d.ts} +0 -0
  224. /package/dist/{gps-error-handler-C2bS69oJ.d.ts → gps-error-handler-C34GQuTu.d.ts} +0 -0
  225. /package/dist/{gps-event-markers-Dd0G2bD0.d.ts → gps-event-markers-Di2nBzx-.d.ts} +0 -0
  226. /package/dist/{h3-proximity-B57C6rYL.d.ts → h3-proximity-D0GTHQmr.d.ts} +0 -0
  227. /package/dist/{hit-test-reticle-Dqak9hiR.d.ts → hit-test-reticle-CaQbrhbk.d.ts} +0 -0
  228. /package/dist/{list-formatter-ct8txwvR.d.ts → list-formatter-DLFDnS70.d.ts} +0 -0
  229. /package/dist/{logger-CzQrCe6C.d.ts → logger-CqTd5Kpk.d.ts} +0 -0
  230. /package/dist/{map-overlay-Dci4yvm3.d.ts → map-overlay-BCQgvIgU.d.ts} +0 -0
  231. /package/dist/{permission-checker-CtPtPQRq.d.ts → permission-checker-B-VtjnBA.d.ts} +0 -0
  232. /package/dist/{qr-pose-Bt2reSw7.d.ts → qr-pose-y3vaEd6x.d.ts} +0 -0
  233. /package/dist/{scene-node-names-CdNeQjOE.d.ts → scene-node-names-CGb-ylBd.d.ts} +0 -0
  234. /package/dist/{session-disposers-D8wnO-uW.js → session-disposers-BPrMplIC.js} +0 -0
  235. /package/dist/{three-dispose-BCZkSJp2.d.ts → three-dispose-BMOAmPhb.d.ts} +0 -0
  236. /package/dist/{vis-colors-BZsSLI4i.d.ts → vis-colors-C7HI0u9_.d.ts} +0 -0
  237. /package/dist/{webxr-nue-basis-CW1wsbm6.d.ts → webxr-nue-basis-CaCnYxuq.d.ts} +0 -0
  238. /package/dist/{xr-camera-texture-BmrP7iNZ.d.ts → xr-camera-texture-q0eIDSRg.d.ts} +0 -0
  239. /package/dist/{xr-error-handler-BEjRgZjt.d.ts → xr-error-handler-uVGDu9IX.d.ts} +0 -0
  240. /package/dist/{xr-frame-loop-K8ebdjLw.d.ts → xr-frame-loop-DX3JPpAU.d.ts} +0 -0
  241. /package/dist/{zip-coverage-embed-tFohI2J4.d.ts → zip-coverage-embed-ncVbaZAz.d.ts} +0 -0
  242. /package/dist/{zip-export-DDWXInTR.d.ts → zip-export-CtiUnAlJ.d.ts} +0 -0
  243. /package/dist/{zip-reader-Q2ue6zlC.d.ts → zip-reader-CvruaGvD.d.ts} +0 -0
@@ -0,0 +1,54 @@
1
+ //#region ../src/sensors/absolute-orientation.d.ts
2
+ /**
3
+ * AbsoluteOrientationSensor capture (Generic Sensor API).
4
+ *
5
+ * Phase 1 of the AbsoluteOrientationSensor plan
6
+ * (GpsPlusSlamJs_Docs/docs/2026-06-25-absolute-orientation-sensor-plan.md §5.1):
7
+ * passive instrumentation that fuses accelerometer + gyroscope + magnetometer
8
+ * into an Earth-referenced (ENU) quaternion, captured per GPS event as an
9
+ * independent north reference. No production behaviour change.
10
+ *
11
+ * Platform: Chrome Android only. Safari / Firefox / desktop / iOS lack the
12
+ * sensor → this module degrades to a clean, reported no-op.
13
+ *
14
+ * The raw `'device'` reference frame is recorded together with the screen angle
15
+ * so either the device- or screen-compensated form can be reconstructed offline
16
+ * (plan §8 Q1).
17
+ */
18
+ /** A single absolute-orientation reading, snapshotted into the GPS event payload. */
19
+ interface AbsoluteOrientationReading {
20
+ /** [x,y,z,w] quaternion mapping device-frame → Earth ENU (+X East, +Y North, +Z Up). */
21
+ quaternion: [number, number, number, number];
22
+ /** Reference frame the quaternion is expressed in. We record raw 'device'. */
23
+ referenceFrame: 'device' | 'screen';
24
+ /** screen.orientation.angle at sample time — needed to reconstruct the 'device' frame. */
25
+ screenAngleDeg: number;
26
+ /** Capture time (epoch ms) — pairs with the GPS event; lets warm-up be detected offline. */
27
+ timestamp: number;
28
+ }
29
+ /** Lifecycle status surfaced to the caller (HUD/observability). */
30
+ type AbsoluteOrientationStatus = {
31
+ state: 'active';
32
+ } | {
33
+ state: 'unavailable';
34
+ reason: string;
35
+ } | {
36
+ state: 'error';
37
+ reason: string;
38
+ };
39
+ /** True when the sensor can plausibly run here (secure context + API present). */
40
+ declare function isAbsoluteOrientationAvailable(): boolean;
41
+ /**
42
+ * Start capturing absolute orientation. Idempotent (stops any prior watch first).
43
+ *
44
+ * On platforms without the sensor — or when permissions are denied — it reports
45
+ * `unavailable`/`error` via `onStatus` and stays a no-op; the recorder keeps
46
+ * working unchanged. Never throws.
47
+ */
48
+ declare function startAbsoluteOrientationWatch(onStatus?: (status: AbsoluteOrientationStatus) => void): Promise<void>;
49
+ /** Latest reading, or null when unavailable/not-yet-warmed-up. Mirrors getLastDeviceOrientation. */
50
+ declare function getLatestAbsoluteOrientation(): AbsoluteOrientationReading | null;
51
+ /** Stop the watch and clear cached state. Idempotent. */
52
+ declare function stopAbsoluteOrientationWatch(): void;
53
+ //#endregion
54
+ export { startAbsoluteOrientationWatch as a, isAbsoluteOrientationAvailable as i, AbsoluteOrientationStatus as n, stopAbsoluteOrientationWatch as o, getLatestAbsoluteOrientation as r, AbsoluteOrientationReading as t };
@@ -4,9 +4,19 @@ import L from "leaflet";
4
4
  /**
5
5
  * Style for per-event GPS accuracy circles. Radius comes from the GPS event's
6
6
  * horizontal accuracy in meters; larger circles mean lower-quality fixes.
7
- * Filled and stroked with a highly transparent variant of the path color so
8
- * overlapping circles remain legible without obscuring the basemap or the
9
- * polyline drawn on top.
7
+ *
8
+ * Circles are drawn **stroke-only** (no fill): Leaflet composites overlapping
9
+ * semi-transparent SVG fills, so N stacked filled circles reach
10
+ * `1 − (1 − fillOpacity)^N` opacity and paint the basemap solid on dense
11
+ * recordings (~90 % at ~18 overlaps with the old 0.12 fill). A fill-free
12
+ * outline never accumulates an opaque interior, so the basemap shows through at
13
+ * ANY overlap density while each accuracy radius stays readable.
14
+ */
15
+ /**
16
+ * @deprecated Stroke-only since 2026-06-28 (Finding 1) — accuracy circles no
17
+ * longer render a fill, so this opacity is unused. Retained as an exported
18
+ * constant only to preserve the published API surface; do not pass it to
19
+ * `L.circle`. See `2026-06-28-map-rings-transparency-and-view-direction-user-feedback.md`.
10
20
  */
11
21
  declare const ACCURACY_CIRCLE_FILL_OPACITY = 0.12;
12
22
  declare const ACCURACY_CIRCLE_STROKE_OPACITY = 0.5;
@@ -23,8 +33,8 @@ interface AccuracyCircleSample {
23
33
  readonly accuracy?: number;
24
34
  }
25
35
  /**
26
- * Draw one transparent circle per sample whose `accuracy` is a finite
27
- * positive number. Samples without a usable accuracy value are skipped so
36
+ * Draw one stroke-only (un-filled) circle per sample whose `accuracy` is a
37
+ * finite positive number. Samples without a usable accuracy value are skipped so
28
38
  * pre-accuracy recordings still render their polyline.
29
39
  *
30
40
  * Circles are added to `map` immediately. The caller is responsible for
@@ -34,7 +44,7 @@ interface AccuracyCircleSample {
34
44
  * @param map - The Leaflet map to add circles to.
35
45
  * @param samples - GPS samples to consider; non-positive / non-finite
36
46
  * `accuracy` values are silently skipped.
37
- * @param color - CSS color string used for both stroke and fill.
47
+ * @param color - CSS color string used for the stroke (no fill is drawn).
38
48
  * @returns The created `L.Circle` instances, in the order they were added.
39
49
  * Callers that track layers for cleanup can append these to their list.
40
50
  */
@@ -1,4 +1,4 @@
1
- import { L as CombinedRootState, r as SlamAppRootState } from "./create-slam-app-store-Ce2FMKCp.js";
1
+ import { N as CombinedRootState, r as SlamAppRootState } from "./create-slam-app-store-D3nzh9Wb.js";
2
2
  import * as _$gps_plus_slam_js0 from "gps-plus-slam-js";
3
3
  import { ArImageCapture, GpsPoint, LatLong, Matrix4, Quaternion, Vector3 } from "gps-plus-slam-js";
4
4
  import * as _$_reduxjs_toolkit0 from "@reduxjs/toolkit";
@@ -1,2 +1,2 @@
1
- import { n as MAX_TRACE_STEPS, r as bresenham3d, t as GridCell } from "../bresenham3d-ByjcmEgM.js";
1
+ import { n as MAX_TRACE_STEPS, r as bresenham3d, t as GridCell } from "../bresenham3d-DGc4nIb-.js";
2
2
  export { GridCell, MAX_TRACE_STEPS, bresenham3d };
@@ -1,2 +1,2 @@
1
- import { a as computeCaptureSize, i as computeAspectFitSize, n as CameraBlitCaptureConfig, r as DEFAULT_BLIT_CONFIG, t as CameraBlitCapture } from "../camera-blit-capture-CqWe1G7s.js";
1
+ import { a as computeCaptureSize, i as computeAspectFitSize, n as CameraBlitCaptureConfig, r as DEFAULT_BLIT_CONFIG, t as CameraBlitCapture } from "../camera-blit-capture-BimSdyih.js";
2
2
  export { CameraBlitCapture, CameraBlitCaptureConfig, DEFAULT_BLIT_CONFIG, computeAspectFitSize, computeCaptureSize };
@@ -1,2 +1,2 @@
1
- import { i as isFullySupported, n as CapabilitySupport, r as capabilityMessage, t as CapabilityMessageOptions } from "../capability-checker-CXqoJYtb.js";
1
+ import { i as isFullySupported, n as CapabilitySupport, r as capabilityMessage, t as CapabilityMessageOptions } from "../capability-checker-BTTpygvZ.js";
2
2
  export { CapabilityMessageOptions, CapabilitySupport, capabilityMessage, isFullySupported };
@@ -1,2 +1,2 @@
1
- import { a as createCaptureFailureTracker, i as DEFAULT_CAPTURE_TRACKER_CONFIG, n as CaptureFailureTracker, r as CaptureFailureTrackerConfig, t as CAPTURE_FAILURE_WARNING } from "../capture-failure-tracker-BL5SoG2a.js";
1
+ import { a as createCaptureFailureTracker, i as DEFAULT_CAPTURE_TRACKER_CONFIG, n as CaptureFailureTracker, r as CaptureFailureTrackerConfig, t as CAPTURE_FAILURE_WARNING } from "../capture-failure-tracker-_BVfhpxv.js";
2
2
  export { CAPTURE_FAILURE_WARNING, CaptureFailureTracker, CaptureFailureTrackerConfig, DEFAULT_CAPTURE_TRACKER_CONFIG, createCaptureFailureTracker };
@@ -1,2 +1,2 @@
1
- import { a as applyChromiumProjectionLayerWorkaround, i as ChromiumProjectionLayerWorkaroundResult, n as BASELAYER_WINDOW_MIN, o as needsBaseLayerPersistence, r as ChromeVersion, s as parseChromeVersion, t as BASELAYER_WINDOW_MAX } from "../chromium-camera-access-workaround-BNK189P0.js";
1
+ import { a as applyChromiumProjectionLayerWorkaround, i as ChromiumProjectionLayerWorkaroundResult, n as BASELAYER_WINDOW_MIN, o as needsBaseLayerPersistence, r as ChromeVersion, s as parseChromeVersion, t as BASELAYER_WINDOW_MAX } from "../chromium-camera-access-workaround-C6yJLXz9.js";
2
2
  export { BASELAYER_WINDOW_MAX, BASELAYER_WINDOW_MIN, ChromeVersion, ChromiumProjectionLayerWorkaroundResult, applyChromiumProjectionLayerWorkaround, needsBaseLayerPersistence, parseChromeVersion };
@@ -1,2 +1,2 @@
1
- import { n as createDepthGridLookup, t as DepthGridLookup } from "../depth-grid-lookup-vMDrGTk_.js";
1
+ import { n as createDepthGridLookup, t as DepthGridLookup } from "../depth-grid-lookup-D5atpNZ3.js";
2
2
  export { DepthGridLookup, createDepthGridLookup };
@@ -0,0 +1,127 @@
1
+ import { t as DepthInfo } from "../depth-sampler-CwObAwEA.js";
2
+ import * as THREE from "three";
3
+
4
+ //#region ../src/ar/depth-occluder.d.ts
5
+ /** Render order of the full-screen depth writer — before virtual content
6
+ * (≥ 0) and at/after the persistent mesh (−2), so it composes with the mesh
7
+ * (nearer depth wins) and lays depth before content draws (plan §5). */
8
+ declare const OCCLUDER_RENDER_ORDER = -1;
9
+ /** The two upload formats `XRCPUDepthInformation` resolves to (plan §3a/§10). */
10
+ type DepthTextureFormat = 'r32f' | 'luminance-alpha';
11
+ /**
12
+ * Convert a **view-space perpendicular depth** `d` (metres in front of the camera)
13
+ * to a window-space depth in `[0, 1]` using the WebXR `XRView.projectionMatrix`
14
+ * `P` (column-major 16-tuple). This is the conversion both the per-material and
15
+ * the full-screen techniques need to compare the real surface against a virtual
16
+ * fragment's depth (plan §3b):
17
+ *
18
+ * ```
19
+ * z_clip = -d·P[10] + P[14]
20
+ * w_clip = -d·P[11] + P[15]
21
+ * z_ndc = z_clip / w_clip
22
+ * window = 0.5·z_ndc + 0.5
23
+ * ```
24
+ *
25
+ * Assumes the sampled value is the perpendicular z-distance (WebXR/ARCore depth
26
+ * semantics). Monotonic in `d` for a standard perspective `P`, and lands in
27
+ * `[0, 1]` for `d ∈ [near, far]` — both pinned by the property tests.
28
+ */
29
+ declare function metricDepthToWindowDepth(viewSpaceDepthMeters: number, projectionMatrix: ArrayLike<number>): number;
30
+ /**
31
+ * Map a normalized **screen** UV (`[0,1]²`, origin bottom-left) to the normalized
32
+ * **depth-buffer** UV via `XRDepthInformation.normDepthBufferFromNormView`'s
33
+ * `.matrix` (column-major 16-tuple). The depth texture is not 1:1 with the
34
+ * framebuffer (it is low-res and may be rotated), so the shader must apply this
35
+ * transform before sampling (plan §3c). Returns `[u, v]` after the perspective
36
+ * divide. The identity matrix is a fixed point (verified in tests).
37
+ */
38
+ declare function screenUvToDepthUv(u: number, v: number, matrix: ArrayLike<number>): [number, number];
39
+ /**
40
+ * Reconstruct metres from a 16-bit depth value packed across the `luminance`
41
+ * (low byte) + `alpha` (high byte) channels of an `RG8` texel, then scale by
42
+ * `XRCPUDepthInformation.rawValueToMeters` (plan §3a/§9). `lo`/`hi` are the two
43
+ * `0–255` bytes; round-trips a known raw value (property-tested).
44
+ */
45
+ declare function unpackLuminanceAlphaToMeters(lo: number, hi: number, rawValueToMeters: number): number;
46
+ /**
47
+ * Pick the `DataTexture` upload format from the resolved depth buffer's byte
48
+ * count (plan §3a). `float32` raw depth is 4 bytes/texel → `r32f`; the
49
+ * `luminance-alpha` 16-bit packing is 2 bytes/texel. We read the actual
50
+ * resolved layout at runtime rather than assuming the `dataFormatPreference`
51
+ * order, because the UA chooses (plan §3a "read it at runtime").
52
+ */
53
+ declare function selectDepthTextureFormat(width: number, height: number, byteLength: number): DepthTextureFormat;
54
+ /**
55
+ * GLSL for the **full-screen depth-write occluder** (v1 — the "do what the
56
+ * persistent mesh does" path). A clip-space quad whose fragment shader samples
57
+ * the live depth map and writes `gl_FragDepth` from the real surface depth, so
58
+ * the normal depth test then hides ALL virtual content behind real surfaces —
59
+ * no per-material patching (2026-06-29-occlusion-debug-viz-and-live-occluder
60
+ * user-feedback Finding 2).
61
+ *
62
+ * The fragment math **mirrors the CI-tested pure functions**:
63
+ * `screenUvToDepthUv` (the `uDepthUvFromScreenUv` transform + perspective
64
+ * divide), the `luminance-alpha`→metres unpack, the holes policy (no/invalid
65
+ * depth ⇒ `discard` ⇒ never occlude, so the persistent mesh shows through), and
66
+ * `metricDepthToWindowDepth` (the projection-matrix metres→window-depth step).
67
+ *
68
+ * The vertex shader maps the quad's NDC position to a `[0,1]` screen UV varying,
69
+ * so the shader needs **no resolution uniform** (the interpolated UV *is* the
70
+ * normalized screen coordinate). Mono AR (one `XRView`) — plan §3c.
71
+ *
72
+ * **Device-gated tuning** (the on-device gate, not CI): the depth-UV Y
73
+ * orientation, the float-vs-packed default, and that `gl_FragDepth` is writable
74
+ * on the target (it may need an extension/`glslVersion` on some renderers).
75
+ */
76
+ declare function buildFullscreenOcclusionShader(): {
77
+ vertexShader: string;
78
+ fragmentShader: string;
79
+ };
80
+ /**
81
+ * Manages the live occluder's GPU side: one small per-frame depth `DataTexture`,
82
+ * the shared uniform block, and the full-screen depth-write mesh. Construct once
83
+ * per AR session, `update(depthInfo)` each frame from the wrapped
84
+ * `XRCPUDepthInformation`, mount {@link getOcclusionMesh} in the AR scene, and
85
+ * `dispose()` on session end.
86
+ *
87
+ * The GLSL is a **device-gated first-light draft** (see the file header): its
88
+ * CPU-observable effects are unit-tested, but the on-device occlusion must be
89
+ * verified and tuned (plan §8 Iter 2–3) before `liveOcclusion` ships on.
90
+ */
91
+ declare class DepthOccluder {
92
+ private readonly uniforms;
93
+ private fullscreenMesh;
94
+ private texture;
95
+ private textureFormat;
96
+ private textureWidth;
97
+ private textureHeight;
98
+ private disposed;
99
+ constructor();
100
+ /** Whether a usable depth texture has been uploaded (occlusion is live). */
101
+ isEnabled(): boolean;
102
+ /** The current upload format, or null before the first {@link update}. */
103
+ getTextureFormat(): DepthTextureFormat | null;
104
+ /**
105
+ * The **full-screen depth-write occluder** mesh (v1 path) — lazily created and
106
+ * cached. Add it to the AR scene (its vertex shader ignores transforms, so the
107
+ * parent node is irrelevant; the recorder adds it under `arWorldGroup`). It is
108
+ * `colorWrite:false` / `depthWrite:true` / `depthTest:true` at
109
+ * {@link OCCLUDER_RENDER_ORDER}, so it lays the real-surface depth before
110
+ * virtual content draws and composes with the persistent mesh (nearer wins).
111
+ * Shares the live uniform block, so each {@link update} reaches it.
112
+ */
113
+ getOcclusionMesh(): THREE.Mesh;
114
+ /**
115
+ * Upload this frame's depth + metadata. No-op (and disables occlusion) when the
116
+ * depth info lacks the occluder fields (`data` / `rawValueToMeters` /
117
+ * `normDepthBufferFromNormView` / `projectionMatrix`) — e.g. a sparse-only
118
+ * frame — so a degraded frame can never occlude with stale or absent depth.
119
+ */
120
+ update(depthInfo: DepthInfo): void;
121
+ /** (Re)create the DataTexture on a size/format change, else refresh its data. */
122
+ private ensureTexture;
123
+ /** Release the depth texture and the full-screen mesh. */
124
+ dispose(): void;
125
+ }
126
+ //#endregion
127
+ export { DepthOccluder, DepthTextureFormat, OCCLUDER_RENDER_ORDER, buildFullscreenOcclusionShader, metricDepthToWindowDepth, screenUvToDepthUv, selectDepthTextureFormat, unpackLuminanceAlphaToMeters };
@@ -0,0 +1,299 @@
1
+ import * as THREE from "three";
2
+ //#region ../src/ar/depth-occluder.ts
3
+ /**
4
+ * Live CPU-depth occluder — hides virtual fragments behind the real surface the
5
+ * camera sees *this frame*.
6
+ *
7
+ * Companion to the **persistent** `OcclusionMesh` (`visualization/occlusion-mesh.ts`):
8
+ * that one meshes the accumulated occupancy grid (remembers out-of-view geometry,
9
+ * blocky, lagging); this one reads the per-frame `XRCPUDepthInformation` and
10
+ * occludes per-pixel against the surface currently in view (sharp, registration-
11
+ * free, no memory). They **compose** — both depth-only under `arWorldGroup`, the
12
+ * live occluder wins where this frame has depth, the mesh fills out-of-view / depth
13
+ * holes (2026-06-14-webxr-depth-occlusion-plan.md §5).
14
+ *
15
+ * **Two layers, two confidence levels:**
16
+ * - The **pure occlusion math** (this file's exported functions) is the CI-tested
17
+ * core (plan §9): the metric-depth→window-depth conversion, the screen-UV→depth-UV
18
+ * transform, the `luminance-alpha`→metres unpack, the depth-texture format
19
+ * selection, and the soft-margin / holes occlusion-strength policy. These are
20
+ * deterministic and property-tested.
21
+ * - The **{@link DepthOccluder} class** owns the per-frame `THREE.DataTexture`
22
+ * upload, the shared uniform block, the lifecycle (enable / dispose), and the
23
+ * **full-screen depth-write render path** ({@link DepthOccluder.getOcclusionMesh}):
24
+ * a clip-space quad that writes `gl_FragDepth` from the live depth map, so the
25
+ * normal depth test hides **all** virtual content behind real surfaces — the
26
+ * "do what the persistent mesh does" path the recorder uses (2026-06-29
27
+ * occlusion-debug-viz-and-live-occluder feedback Finding 2). Hard-edged.
28
+ * The JS-observable behaviour (texture (re)creation, uniform updates, mesh
29
+ * construction, disposal) is unit-tested in jsdom, but the **actual GLSL occlusion
30
+ * is device-gated** (plan §8 Iter 2–3): no headless GL renders it, so the shader is
31
+ * a first-light draft to verify/tune on-device (the `gl_FragDepth` sanity check,
32
+ * the depth-UV Y orientation, the float-vs-packed default). Keep
33
+ * `occupancy.liveOcclusion` OFF by default until that verification lands.
34
+ *
35
+ * (A second render path — a per-material soft-margin `onBeforeCompile`
36
+ * injection, "Phase B" — existed here as a never-wired draft until 2026-07-04
37
+ * and was deleted: dead code that review bots kept re-flagging. Recover it
38
+ * from git history if a per-material fade is ever actually built.)
39
+ *
40
+ * @see depth-occluder.ts.md for detailed documentation
41
+ * @see ar/depth-sampler.ts — `DepthInfo` / `wrapXRDepthInfo` (the per-frame source)
42
+ */
43
+ /** Render order of the full-screen depth writer — before virtual content
44
+ * (≥ 0) and at/after the persistent mesh (−2), so it composes with the mesh
45
+ * (nearer depth wins) and lays depth before content draws (plan §5). */
46
+ const OCCLUDER_RENDER_ORDER = -1;
47
+ /**
48
+ * Convert a **view-space perpendicular depth** `d` (metres in front of the camera)
49
+ * to a window-space depth in `[0, 1]` using the WebXR `XRView.projectionMatrix`
50
+ * `P` (column-major 16-tuple). This is the conversion both the per-material and
51
+ * the full-screen techniques need to compare the real surface against a virtual
52
+ * fragment's depth (plan §3b):
53
+ *
54
+ * ```
55
+ * z_clip = -d·P[10] + P[14]
56
+ * w_clip = -d·P[11] + P[15]
57
+ * z_ndc = z_clip / w_clip
58
+ * window = 0.5·z_ndc + 0.5
59
+ * ```
60
+ *
61
+ * Assumes the sampled value is the perpendicular z-distance (WebXR/ARCore depth
62
+ * semantics). Monotonic in `d` for a standard perspective `P`, and lands in
63
+ * `[0, 1]` for `d ∈ [near, far]` — both pinned by the property tests.
64
+ */
65
+ function metricDepthToWindowDepth(viewSpaceDepthMeters, projectionMatrix) {
66
+ const d = viewSpaceDepthMeters;
67
+ const p10 = projectionMatrix[10] ?? 0;
68
+ const p11 = projectionMatrix[11] ?? 0;
69
+ const p14 = projectionMatrix[14] ?? 0;
70
+ const p15 = projectionMatrix[15] ?? 0;
71
+ return .5 * ((-d * p10 + p14) / (-d * p11 + p15)) + .5;
72
+ }
73
+ /**
74
+ * Map a normalized **screen** UV (`[0,1]²`, origin bottom-left) to the normalized
75
+ * **depth-buffer** UV via `XRDepthInformation.normDepthBufferFromNormView`'s
76
+ * `.matrix` (column-major 16-tuple). The depth texture is not 1:1 with the
77
+ * framebuffer (it is low-res and may be rotated), so the shader must apply this
78
+ * transform before sampling (plan §3c). Returns `[u, v]` after the perspective
79
+ * divide. The identity matrix is a fixed point (verified in tests).
80
+ */
81
+ function screenUvToDepthUv(u, v, matrix) {
82
+ const at = (i) => matrix[i] ?? 0;
83
+ const x = at(0) * u + at(4) * v + at(12);
84
+ const y = at(1) * u + at(5) * v + at(13);
85
+ const w = at(3) * u + at(7) * v + at(15);
86
+ if (w !== 0 && Number.isFinite(w)) return [x / w, y / w];
87
+ return [x, y];
88
+ }
89
+ /**
90
+ * Reconstruct metres from a 16-bit depth value packed across the `luminance`
91
+ * (low byte) + `alpha` (high byte) channels of an `RG8` texel, then scale by
92
+ * `XRCPUDepthInformation.rawValueToMeters` (plan §3a/§9). `lo`/`hi` are the two
93
+ * `0–255` bytes; round-trips a known raw value (property-tested).
94
+ */
95
+ function unpackLuminanceAlphaToMeters(lo, hi, rawValueToMeters) {
96
+ return ((lo & 255) + (hi & 255) * 256) * rawValueToMeters;
97
+ }
98
+ /**
99
+ * Pick the `DataTexture` upload format from the resolved depth buffer's byte
100
+ * count (plan §3a). `float32` raw depth is 4 bytes/texel → `r32f`; the
101
+ * `luminance-alpha` 16-bit packing is 2 bytes/texel. We read the actual
102
+ * resolved layout at runtime rather than assuming the `dataFormatPreference`
103
+ * order, because the UA chooses (plan §3a "read it at runtime").
104
+ */
105
+ function selectDepthTextureFormat(width, height, byteLength) {
106
+ const texels = width * height;
107
+ if (texels > 0 && byteLength >= texels * 4) return "r32f";
108
+ return "luminance-alpha";
109
+ }
110
+ /**
111
+ * GLSL for the **full-screen depth-write occluder** (v1 — the "do what the
112
+ * persistent mesh does" path). A clip-space quad whose fragment shader samples
113
+ * the live depth map and writes `gl_FragDepth` from the real surface depth, so
114
+ * the normal depth test then hides ALL virtual content behind real surfaces —
115
+ * no per-material patching (2026-06-29-occlusion-debug-viz-and-live-occluder
116
+ * user-feedback Finding 2).
117
+ *
118
+ * The fragment math **mirrors the CI-tested pure functions**:
119
+ * `screenUvToDepthUv` (the `uDepthUvFromScreenUv` transform + perspective
120
+ * divide), the `luminance-alpha`→metres unpack, the holes policy (no/invalid
121
+ * depth ⇒ `discard` ⇒ never occlude, so the persistent mesh shows through), and
122
+ * `metricDepthToWindowDepth` (the projection-matrix metres→window-depth step).
123
+ *
124
+ * The vertex shader maps the quad's NDC position to a `[0,1]` screen UV varying,
125
+ * so the shader needs **no resolution uniform** (the interpolated UV *is* the
126
+ * normalized screen coordinate). Mono AR (one `XRView`) — plan §3c.
127
+ *
128
+ * **Device-gated tuning** (the on-device gate, not CI): the depth-UV Y
129
+ * orientation, the float-vs-packed default, and that `gl_FragDepth` is writable
130
+ * on the target (it may need an extension/`glslVersion` on some renderers).
131
+ */
132
+ function buildFullscreenOcclusionShader() {
133
+ return {
134
+ vertexShader: `
135
+ varying vec2 vScreenUv;
136
+ void main() {
137
+ // Fullscreen clip-space quad: 'position' (PlaneGeometry(2,2)) is already in
138
+ // NDC [-1,1]; the interpolated [0,1] uv is the normalized screen coordinate,
139
+ // so no resolution uniform is needed. View/projection are intentionally
140
+ // ignored — this writes gl_FragDepth, not geometry depth.
141
+ vScreenUv = position.xy * 0.5 + 0.5;
142
+ gl_Position = vec4(position.xy, 0.0, 1.0);
143
+ }
144
+ `,
145
+ fragmentShader: `
146
+ precision highp float;
147
+ uniform sampler2D uDepthTexture;
148
+ uniform float uRawValueToMeters;
149
+ uniform mat4 uDepthUvFromScreenUv;
150
+ uniform mat4 uProjectionMatrix;
151
+ uniform float uOccluderEnabled;
152
+ uniform float uPackedDepth;
153
+ varying vec2 vScreenUv;
154
+ void main() {
155
+ // No valid depth this frame → write nothing (frame-level holes policy).
156
+ if (uOccluderEnabled < 0.5) { discard; }
157
+ // Normalized screen UV → depth-buffer UV (low-res / possibly rotated), then
158
+ // perspective divide — mirrors screenUvToDepthUv.
159
+ vec4 duv = uDepthUvFromScreenUv * vec4(vScreenUv, 0.0, 1.0);
160
+ vec2 depthUv = duv.xy / duv.w;
161
+ vec4 texel = texture2D(uDepthTexture, depthUv);
162
+ // Reconstruct metres: packed luminance-alpha (lo + hi*256) or float R32F.
163
+ float realDepthMeters = uPackedDepth > 0.5
164
+ ? (texel.r * 255.0 + texel.g * 255.0 * 256.0) * uRawValueToMeters
165
+ : texel.r * uRawValueToMeters;
166
+ // Hole / invalid texel → never occlude (per-texel holes policy).
167
+ if (!(realDepthMeters > 0.0)) { discard; }
168
+ // metres → window depth via the WebXR projection matrix (column-major
169
+ // [col][row]) — mirrors metricDepthToWindowDepth.
170
+ float zClip = -realDepthMeters * uProjectionMatrix[2][2] + uProjectionMatrix[3][2];
171
+ float wClip = -realDepthMeters * uProjectionMatrix[2][3] + uProjectionMatrix[3][3];
172
+ gl_FragDepth = 0.5 * (zClip / wClip) + 0.5;
173
+ // colorWrite is off on the material; this is only here to satisfy GLSL1.
174
+ gl_FragColor = vec4(0.0);
175
+ }
176
+ `
177
+ };
178
+ }
179
+ /**
180
+ * Manages the live occluder's GPU side: one small per-frame depth `DataTexture`,
181
+ * the shared uniform block, and the full-screen depth-write mesh. Construct once
182
+ * per AR session, `update(depthInfo)` each frame from the wrapped
183
+ * `XRCPUDepthInformation`, mount {@link getOcclusionMesh} in the AR scene, and
184
+ * `dispose()` on session end.
185
+ *
186
+ * The GLSL is a **device-gated first-light draft** (see the file header): its
187
+ * CPU-observable effects are unit-tested, but the on-device occlusion must be
188
+ * verified and tuned (plan §8 Iter 2–3) before `liveOcclusion` ships on.
189
+ */
190
+ var DepthOccluder = class {
191
+ uniforms;
192
+ fullscreenMesh = null;
193
+ texture = null;
194
+ textureFormat = null;
195
+ textureWidth = 0;
196
+ textureHeight = 0;
197
+ disposed = false;
198
+ constructor() {
199
+ this.uniforms = {
200
+ uDepthTexture: { value: null },
201
+ uRawValueToMeters: { value: 1 },
202
+ uDepthUvFromScreenUv: { value: new THREE.Matrix4() },
203
+ uProjectionMatrix: { value: new THREE.Matrix4() },
204
+ uOccluderEnabled: { value: 0 },
205
+ uPackedDepth: { value: 0 }
206
+ };
207
+ }
208
+ /** Whether a usable depth texture has been uploaded (occlusion is live). */
209
+ isEnabled() {
210
+ return this.uniforms.uOccluderEnabled.value === 1;
211
+ }
212
+ /** The current upload format, or null before the first {@link update}. */
213
+ getTextureFormat() {
214
+ return this.textureFormat;
215
+ }
216
+ /**
217
+ * The **full-screen depth-write occluder** mesh (v1 path) — lazily created and
218
+ * cached. Add it to the AR scene (its vertex shader ignores transforms, so the
219
+ * parent node is irrelevant; the recorder adds it under `arWorldGroup`). It is
220
+ * `colorWrite:false` / `depthWrite:true` / `depthTest:true` at
221
+ * {@link OCCLUDER_RENDER_ORDER}, so it lays the real-surface depth before
222
+ * virtual content draws and composes with the persistent mesh (nearer wins).
223
+ * Shares the live uniform block, so each {@link update} reaches it.
224
+ */
225
+ getOcclusionMesh() {
226
+ if (!this.fullscreenMesh) {
227
+ const { vertexShader, fragmentShader } = buildFullscreenOcclusionShader();
228
+ const material = new THREE.ShaderMaterial({
229
+ uniforms: this.uniforms,
230
+ vertexShader,
231
+ fragmentShader,
232
+ colorWrite: false,
233
+ depthWrite: true,
234
+ depthTest: true
235
+ });
236
+ const mesh = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), material);
237
+ mesh.name = "live-depth-occluder";
238
+ mesh.frustumCulled = false;
239
+ mesh.renderOrder = -1;
240
+ this.fullscreenMesh = mesh;
241
+ }
242
+ return this.fullscreenMesh;
243
+ }
244
+ /**
245
+ * Upload this frame's depth + metadata. No-op (and disables occlusion) when the
246
+ * depth info lacks the occluder fields (`data` / `rawValueToMeters` /
247
+ * `normDepthBufferFromNormView` / `projectionMatrix`) — e.g. a sparse-only
248
+ * frame — so a degraded frame can never occlude with stale or absent depth.
249
+ */
250
+ update(depthInfo) {
251
+ if (this.disposed) return;
252
+ const { data, rawValueToMeters, normDepthBufferFromNormView } = depthInfo;
253
+ if (!data || typeof rawValueToMeters !== "number" || !normDepthBufferFromNormView || !depthInfo.projectionMatrix) {
254
+ this.uniforms.uOccluderEnabled.value = 0;
255
+ return;
256
+ }
257
+ const { width, height } = depthInfo;
258
+ const format = selectDepthTextureFormat(width, height, data.byteLength);
259
+ this.ensureTexture(width, height, format, data);
260
+ this.uniforms.uPackedDepth.value = format === "luminance-alpha" ? 1 : 0;
261
+ this.uniforms.uRawValueToMeters.value = rawValueToMeters;
262
+ this.uniforms.uDepthUvFromScreenUv.value.fromArray(normDepthBufferFromNormView);
263
+ this.uniforms.uProjectionMatrix.value.fromArray(depthInfo.projectionMatrix);
264
+ this.uniforms.uOccluderEnabled.value = 1;
265
+ }
266
+ /** (Re)create the DataTexture on a size/format change, else refresh its data. */
267
+ ensureTexture(width, height, format, data) {
268
+ if (this.texture === null || this.textureWidth !== width || this.textureHeight !== height || this.textureFormat !== format) {
269
+ this.texture?.dispose();
270
+ this.texture = format === "r32f" ? new THREE.DataTexture(new Float32Array(data), width, height, THREE.RedFormat, THREE.FloatType) : new THREE.DataTexture(new Uint8Array(data), width, height, THREE.RGFormat, THREE.UnsignedByteType);
271
+ this.texture.needsUpdate = true;
272
+ this.textureWidth = width;
273
+ this.textureHeight = height;
274
+ this.textureFormat = format;
275
+ this.uniforms.uDepthTexture.value = this.texture;
276
+ } else if (this.texture) {
277
+ const image = this.texture.image;
278
+ image.data = format === "r32f" ? new Float32Array(data) : new Uint8Array(data);
279
+ this.texture.needsUpdate = true;
280
+ }
281
+ }
282
+ /** Release the depth texture and the full-screen mesh. */
283
+ dispose() {
284
+ if (this.disposed) return;
285
+ this.disposed = true;
286
+ this.uniforms.uOccluderEnabled.value = 0;
287
+ this.uniforms.uDepthTexture.value = null;
288
+ this.texture?.dispose();
289
+ this.texture = null;
290
+ if (this.fullscreenMesh) {
291
+ this.fullscreenMesh.removeFromParent();
292
+ this.fullscreenMesh.geometry.dispose();
293
+ this.fullscreenMesh.material.dispose();
294
+ this.fullscreenMesh = null;
295
+ }
296
+ }
297
+ };
298
+ //#endregion
299
+ export { DepthOccluder, OCCLUDER_RENDER_ORDER, buildFullscreenOcclusionShader, metricDepthToWindowDepth, screenUvToDepthUv, selectDepthTextureFormat, unpackLuminanceAlphaToMeters };
@@ -1,3 +1,3 @@
1
- import { i as DepthSample } from "../ar-types-BckI1E-O.js";
2
- import { a as wrapXRDepthInfo, i as DepthSamplerConfig, n as DepthSampler, r as DepthSamplerCallbacks, t as DepthInfo } from "../depth-sampler-DwfPYzS7.js";
1
+ import { i as DepthSample } from "../ar-types-Cc5yvBwn.js";
2
+ import { a as wrapXRDepthInfo, i as DepthSamplerConfig, n as DepthSampler, r as DepthSamplerCallbacks, t as DepthInfo } from "../depth-sampler-CwObAwEA.js";
3
3
  export { DepthInfo, DepthSample, DepthSampler, DepthSamplerCallbacks, DepthSamplerConfig, wrapXRDepthInfo };
@@ -1,6 +1,18 @@
1
1
  import { extractOdomPosition } from "../state/gps-event-coordinator.js";
2
2
  //#region ../src/ar/depth-sampler.ts
3
3
  /**
4
+ * Defensively copy a 16-element column-major matrix into a plain serializable
5
+ * tuple. Returns undefined for missing input, the wrong length, or any
6
+ * non-finite entry — so an invalid (or UA-reused/garbage) source degrades to
7
+ * "absent" rather than poisoning downstream maths. The copy also de-aliases
8
+ * Float32Arrays the UA reuses across frames.
9
+ */
10
+ function copyValidMatrix16(src) {
11
+ if (!src || src.length !== 16) return;
12
+ const copy = Array.from(src);
13
+ return copy.every((v) => Number.isFinite(v)) ? copy : void 0;
14
+ }
15
+ /**
4
16
  * Wrap a raw browser XRDepthInformation object into a {@link DepthInfo}.
5
17
  *
6
18
  * - `getDepthInMeters` is bound to the source object (browser
@@ -9,6 +21,12 @@ import { extractOdomPosition } from "../state/gps-event-coordinator.js";
9
21
  * the UA may reuse across frames) is defensively validated and copied into
10
22
  * a plain serializable 16-tuple; invalid input (wrong length, non-finite
11
23
  * entries) yields a DepthInfo without a matrix rather than an error.
24
+ * - The live-occluder metadata (`data`, `rawValueToMeters`,
25
+ * `normDepthBufferFromNormView`) is preserved when present and valid: `data`
26
+ * by live reference (no clone — see {@link DepthInfo.data}), the scale only
27
+ * when finite, and the UV transform's `.matrix` copied like
28
+ * `projectionMatrix`. The sparse grid sampler ignores all three; a source
29
+ * lacking them (e.g. the existing test doubles) wraps exactly as before.
12
30
  */
13
31
  function wrapXRDepthInfo(raw, projectionMatrix) {
14
32
  const wrapped = {
@@ -16,10 +34,12 @@ function wrapXRDepthInfo(raw, projectionMatrix) {
16
34
  height: raw.height,
17
35
  getDepthInMeters: raw.getDepthInMeters.bind(raw)
18
36
  };
19
- if (projectionMatrix && projectionMatrix.length === 16) {
20
- const copy = Array.from(projectionMatrix);
21
- if (copy.every((v) => Number.isFinite(v))) wrapped.projectionMatrix = copy;
22
- }
37
+ const projection = copyValidMatrix16(projectionMatrix);
38
+ if (projection) wrapped.projectionMatrix = projection;
39
+ if (raw.data instanceof ArrayBuffer) wrapped.data = raw.data;
40
+ if (typeof raw.rawValueToMeters === "number" && Number.isFinite(raw.rawValueToMeters)) wrapped.rawValueToMeters = raw.rawValueToMeters;
41
+ const uvTransform = copyValidMatrix16(raw.normDepthBufferFromNormView?.matrix);
42
+ if (uvTransform) wrapped.normDepthBufferFromNormView = uvTransform;
23
43
  return wrapped;
24
44
  }
25
45
  const DEFAULT_CONFIG = {
@@ -1,2 +1,2 @@
1
- import { n as createDepthUnprojector, r as unprojectDepthPoint, t as DepthUnprojector } from "../depth-unprojection-CK3b9htH.js";
1
+ import { n as createDepthUnprojector, r as unprojectDepthPoint, t as DepthUnprojector } from "../depth-unprojection-DRdBU90S.js";
2
2
  export { DepthUnprojector, createDepthUnprojector, unprojectDepthPoint };