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,194 @@
1
+ import { t as GridCell } from "./bresenham3d-DGc4nIb-.js";
2
+ import { f as OccluderDebugStyle } from "./recording-options-vASCFzc5.js";
3
+ import { n as MeshMode, t as Aabb } from "./occupancy-mesher-C2ecWhNp.js";
4
+ import * as THREE from "three";
5
+ import { Vector3 } from "gps-plus-slam-js";
6
+
7
+ //#region ../src/visualization/occlusion-mesh.d.ts
8
+ /**
9
+ * Depth-shaded debug-skin constants — **module constants, not user settings**
10
+ * (tune in code after the first on-device look; 2026-07-02 debug-viz-styles
11
+ * plan). Exported so the unit/property tests of the GLSL-mirror curves below
12
+ * pin the same numbers the shader bakes in.
13
+ */
14
+ declare const OCCLUDER_DEPTH_SHADE: {
15
+ /** Distance (m) where the fade starts — nearer stays full-bright cyan.
16
+ * Indoor-scale since the 2026-07-03 field pass (finding F1): the original
17
+ * 1.5 → 10 m range kept a whole room in the "near" band (94% brightness at
18
+ * 3 m), making depth-shaded indistinguishable from matcap on device. */
19
+ readonly FADE_START_M: 0.75;
20
+ /** Distance (m) where the fade bottoms out at {@link FADE_MIN_BRIGHTNESS}.
21
+ * ~Room depth, so indoor scenes span the full near→far gradient; beyond it
22
+ * everything sits uniformly at the floor (still separated from near). */
23
+ readonly FADE_END_M: 4.5;
24
+ /** Brightness floor far away — never fades to black, so distant mesh stays
25
+ * inspectable (the reason the fog-to-background idea was rejected). */
26
+ readonly FADE_MIN_BRIGHTNESS: 0.3; /** Fresnel exponent — higher hugs the silhouette tighter. */
27
+ readonly RIM_POWER: 2.5; /** White-rim contribution at a fully grazing view angle. */
28
+ readonly RIM_STRENGTH: 0.6;
29
+ };
30
+ /**
31
+ * Pure TS mirror of the shader's **distance fade**: 1 at/inside FADE_START_M,
32
+ * smoothly down to FADE_MIN_BRIGHTNESS at/after FADE_END_M. The fragment color
33
+ * is `mix(darkBlue, matcapColor, fade)` — near = bright cyan, far = dark blue.
34
+ * Mirrored so the curve is unit-testable without a GPU (the
35
+ * `buildFullscreenOcclusionShader` GLSL-mirror precedent).
36
+ */
37
+ declare function occluderDepthFade(distanceM: number): number;
38
+ /**
39
+ * Pure TS mirror of the shader's **fresnel rim**: 0 where the surface faces
40
+ * the camera head-on (|cos| = 1), RIM_STRENGTH at a fully grazing angle
41
+ * (cos = 0) — silhouettes brighten, so overlapping shells read as separate.
42
+ *
43
+ * @param cosViewAngle `dot(normal, viewDir)` — cosine of the angle between the
44
+ * surface normal and the view direction, in [-1, 1].
45
+ */
46
+ declare function occluderFresnelRim(cosViewAngle: number): number;
47
+ /**
48
+ * The fragment-shader snippet the depth-shaded material injects before
49
+ * `#include <opaque_fragment>` in three.js's matcap fragment shader — the
50
+ * point where `outgoingLight` (matcap-lit color), `normal` and `viewDir` /
51
+ * `vViewPosition` are all in scope. Exported so tests can pin the exact
52
+ * injected code. Constants are baked as literals (no uniforms — they are
53
+ * module constants by design, see {@link OCCLUDER_DEPTH_SHADE}).
54
+ */
55
+ declare function buildOccluderDepthShadeSnippet(): string;
56
+ interface OcclusionMeshOptions {
57
+ /**
58
+ * Merge coplanar faces (fewer triangles, same occluded volume). Default
59
+ * true — the occluder is invisible, so the coarser triangulation is free.
60
+ * Ignored when {@link OcclusionMeshOptions.mode} is set.
61
+ */
62
+ readonly greedy?: boolean;
63
+ /**
64
+ * Mesher strategy (additive opt-in; 2026-06-30 occluder-tuning, F2). When set
65
+ * it takes precedence over {@link greedy}. `'smooth'` selects the surface-nets
66
+ * mesher that hugs the measured per-cell centroids — pass a `getCellPoint`
67
+ * provider to {@link OcclusionMesh.update} for it to read. Left **unset by
68
+ * default** so existing behaviour (greedy cubes) is byte-for-byte unchanged
69
+ * until the smooth occluder is confirmed on-device.
70
+ */
71
+ readonly mode?: MeshMode;
72
+ /**
73
+ * `renderOrder` of the depth-only mesh. Must be below virtual content so the
74
+ * occluder lays down depth first. Default −1. (The live occluder, when it
75
+ * exists, sits between this and content — plan §5.)
76
+ */
77
+ readonly renderOrder?: number;
78
+ }
79
+ /**
80
+ * A depth-only occlusion mesh that rebuilds from an occupancy-grid snapshot.
81
+ * Mirrors {@link OccupancyCubesVisualizer}'s lifecycle (inject parent, `update`,
82
+ * `clear`, `dispose`) so the recorder can wire it the same way as the cubes.
83
+ */
84
+ declare class OcclusionMesh {
85
+ private readonly arSpaceNode;
86
+ private readonly greedy;
87
+ private readonly mode;
88
+ private readonly material;
89
+ private readonly mesh;
90
+ private geometry;
91
+ private lastAabbs;
92
+ private disposed;
93
+ private debugStyle;
94
+ private shadedSkin;
95
+ private wireframeSkin;
96
+ private matcapTexture;
97
+ private matcapMaterial;
98
+ private depthShadedMaterial;
99
+ private wireframeMaterial;
100
+ /**
101
+ * @param arSpaceNode the AR-odometry-NUE node that receives the alignment
102
+ * matrix (`arWorldGroup` live, `replaySceneState.arWorldGroup` in replay).
103
+ */
104
+ constructor(arSpaceNode: THREE.Object3D, options?: OcclusionMeshOptions);
105
+ /** The number of triangles currently drawn. */
106
+ getTriangleCount(): number;
107
+ /** The AABB list from the most recent {@link update} (physics export hook). */
108
+ getAabbs(): readonly Aabb[];
109
+ /**
110
+ * Re-mesh from a fresh occupied-cell snapshot. Pass
111
+ * `grid.getOccupiedCells(occupancy.minConfidence)` so the occluder shares the
112
+ * same noise floor as the cubes and the COLMAP export.
113
+ *
114
+ * @param getCellPoint optional per-cell measured-centroid provider
115
+ * (`grid.getCellPoint`); only consumed when this occluder was constructed
116
+ * with `mode: 'smooth'` (otherwise ignored). When omitted under `'smooth'`,
117
+ * the surface nets falls back to cell centres.
118
+ */
119
+ update(cells: Iterable<GridCell>, cellSizeM: number, getCellPoint?: (cell: GridCell) => Vector3 | null): void;
120
+ /**
121
+ * Apply **precomputed** geometry (positions/indices) without meshing — the
122
+ * entry point for the Web Worker offload: the driver posts the occupied-cell
123
+ * snapshot to a worker (`packMeshRequest`/`runMeshRequest`), and this swaps in
124
+ * the returned typed arrays off the render-critical path. Byte-identical result
125
+ * to {@link update} for the same input.
126
+ *
127
+ * The AABB physics hook is NOT populated on this path (the worker returns
128
+ * geometry only; AABBs are an export hook unused by rendering) — {@link getAabbs}
129
+ * returns empty here. Use the synchronous {@link update} if AABBs are needed.
130
+ */
131
+ applyMeshData(positions: Float32Array, indices: Uint32Array): void;
132
+ /**
133
+ * Replace the geometry wholesale from typed arrays — a full rebuild is the
134
+ * simple first cut; dispose the old buffers to avoid leaking GPU memory across
135
+ * refreshes.
136
+ */
137
+ private swapGeometry;
138
+ /** Point every active debug skin at the (new) shared geometry. */
139
+ private rebindSkinGeometry;
140
+ /**
141
+ * Select which **visible debug skin(s)** render the occluder mesh so its
142
+ * shape and structure can be judged on-device (2026-07-02 debug-viz-styles
143
+ * plan): the original `'matcap'` skin, a `'depth-shaded'` variant (distance
144
+ * fade + fresnel rim, separates overlapping layers), a `'wireframe'` overlay
145
+ * (the raw triangulation as GL lines), the combined
146
+ * `'depth-shaded-wireframe'`, or `'off'`.
147
+ *
148
+ * Additive by design: every style adds/removes separate skin meshes sharing
149
+ * the occluder's geometry and **never touches the invisible depth-only
150
+ * mesh**, so occlusion is byte-for-byte unchanged whichever style is active.
151
+ * The skins are `transparent` with `depthWrite:false` (the depth-only mesh
152
+ * already wrote the occluding depth), so they just paint where the occluder
153
+ * is the nearest geometry; the wireframe draws at renderOrder 1 so its lines
154
+ * overlay the shaded surface in the combined style.
155
+ *
156
+ * Vertex normals (the mesher emits none) are computed only for the
157
+ * matcap-based styles — `'wireframe'` is unlit, so like `'off'` it keeps the
158
+ * remesh path normal-free.
159
+ *
160
+ * Only meaningful when this occluder is actually meshing the grid (it is the
161
+ * persistent occluder's mesh); setting a style on an empty/disabled occluder
162
+ * is a harmless no-op until {@link update} feeds geometry.
163
+ */
164
+ setDebugStyle(style: OccluderDebugStyle): void;
165
+ /** Which matcap-based material the style shades with (null = no shaded skin). */
166
+ private shadedMaterialFor;
167
+ /** Add/retarget/remove the single shaded skin node for the desired material. */
168
+ private applyShadedSkin;
169
+ /** Add/remove the wireframe skin — drawn after the shaded skin (renderOrder
170
+ * 1) so its lines sit on top of the surface in the combined style. */
171
+ private applyWireframeSkin;
172
+ /**
173
+ * Toggle the matcap debug rendering of the occluder mesh.
174
+ *
175
+ * @deprecated Superseded by {@link setDebugStyle} (2026-07-02) — this is a
176
+ * thin wrapper mapping `enabled ? 'matcap' : 'off'`, kept so existing
177
+ * consumers of the boolean API keep working unchanged.
178
+ */
179
+ setDebugVisualization(enabled: boolean): void;
180
+ /** A debug skin mesh sharing the occluder's geometry, transform and culling
181
+ * behaviour — only name, renderOrder and material differ per skin. */
182
+ private createSkinMesh;
183
+ /** The procedural matcap texture, shared by both matcap-based materials. */
184
+ private getMatcapTexture;
185
+ private getMatcapMaterial;
186
+ private getDepthShadedMaterial;
187
+ private getWireframeMaterial;
188
+ /** Empty the mesh (e.g. on store swap); the node stays in the scene. */
189
+ clear(): void;
190
+ /** Remove the mesh from its parent and release GPU resources. */
191
+ dispose(): void;
192
+ }
193
+ //#endregion
194
+ export { occluderDepthFade as a, buildOccluderDepthShadeSnippet as i, OcclusionMesh as n, occluderFresnelRim as o, OcclusionMeshOptions as r, OCCLUDER_DEPTH_SHADE as t };
@@ -0,0 +1,194 @@
1
+ import { t as GridCell } from "./bresenham3d-DGc4nIb-.js";
2
+ import { a as RgbTuple, i as DepthSample } from "./ar-types-Cc5yvBwn.js";
3
+ import { Vector3 } from "gps-plus-slam-js";
4
+
5
+ //#region ../src/ar/cell-key.d.ts
6
+ /** Tuple convenience over {@link packCellKey}. */
7
+ declare function cellKey(cell: GridCell): number;
8
+ /**
9
+ * Exact inverse of {@link cellKey} over the ±{@link CELL_KEY_LIMIT} envelope
10
+ * (property-tested). Consumers that no longer store coordinate tuples recover
11
+ * them from the key through this function.
12
+ */
13
+ declare function unpackCellKey(key: number): GridCell;
14
+ //#endregion
15
+ //#region ../src/ar/occupancy-grid.d.ts
16
+ interface OccupancyGridOptions {
17
+ /** Edge length of a cubic grid cell in meters. Default 0.15 (Unity parity). */
18
+ readonly cellSizeM?: number;
19
+ /**
20
+ * Dominant-axis steps before a ray's endpoint at which free-space
21
+ * carving stops, to respect depth noise. Default 2 (Unity parity).
22
+ */
23
+ readonly carveStopCells?: number;
24
+ }
25
+ declare class OccupancyGrid {
26
+ readonly cellSizeM: number;
27
+ readonly carveStopCells: number;
28
+ private readonly cells;
29
+ /**
30
+ * Chunk index (Step 2 of the 2026-07-03 long-session fps plan): cell keys
31
+ * grouped by {@link CHUNK_EDGE_CELLS}³ chunk, maintained incrementally on
32
+ * add/carve/clear (O(1) per mutation). Lets {@link getOccupiedCellsWithin}
33
+ * visit only the chunks a query sphere touches — cost independent of total
34
+ * explored area. Empty chunk sets are dropped.
35
+ */
36
+ private readonly chunks;
37
+ /**
38
+ * Per-chunk dirty revision, bumped whenever a mutation inside the chunk
39
+ * could change some consumer's occupied set (same semantics as the global
40
+ * {@link revision}). This is the bookkeeping a future dirty-chunk remesh
41
+ * needs (2026-07-01 worker plan, Phase-2 sketch) — landed with the index so
42
+ * that plan becomes a consumer-side change only. Entries survive a chunk
43
+ * emptying (a consumer must still see "changed"); reset by {@link clear}.
44
+ */
45
+ private readonly chunkRevisions;
46
+ /**
47
+ * Monotonic counter bumped whenever the **occupied set** (at any
48
+ * `minConfidence ≤ MAX_RELEVANT_COUNT`) could have changed: a cell added, a
49
+ * cell removed by carving, a cell's count rising while still `≤
50
+ * MAX_RELEVANT_COUNT` (a possible threshold crossing), or `clear`. Re-observing
51
+ * an already-settled cell (count `> MAX_RELEVANT_COUNT`) does NOT bump it, so a
52
+ * consumer can cheaply skip a full re-derive (cube refresh / occluder re-mesh)
53
+ * when the revision is unchanged — the dominant idle-time saving over a long
54
+ * session (see `2026-06-30-occluder-tuning-followups.md`).
55
+ */
56
+ private revision;
57
+ /**
58
+ * Memo of the last {@link getOccupiedCells} walk (Step 1.2 of the
59
+ * 2026-07-03 long-session fps plan): with cubes + occluder both on, every
60
+ * throttled refresh triggers two identical full-grid walks with the same
61
+ * minConfidence floor — the second is answered from here. Valid only while
62
+ * `revision` is unchanged, so it is only used for floors the revision
63
+ * counter actually tracks (≤ {@link MAX_RELEVANT_COUNT}).
64
+ */
65
+ private snapshotCache;
66
+ constructor(options?: OccupancyGridOptions);
67
+ /** Number of occupied cells. */
68
+ get size(): number;
69
+ /**
70
+ * A monotonic version that changes only when the occupied set (at any
71
+ * `minConfidence ≤ 10`) could have changed. A consumer caches the value it
72
+ * last meshed/rendered and skips its full re-derive when it is unchanged — so
73
+ * a settled scene (already-observed cells being re-observed) costs nothing.
74
+ * See {@link revision}.
75
+ */
76
+ getRevision(): number;
77
+ /**
78
+ * Fold one depth sample into the grid: unproject each point, carve free
79
+ * space from the camera cell to the point cell, then count the point's
80
+ * cell as occupied. Points that cannot be unprojected (no
81
+ * projectionMatrix on old recordings, invalid depth/coords) are skipped.
82
+ *
83
+ * Carving and incrementing run as two separate passes over the sample's
84
+ * points: all rays are carved first, then every endpoint is incremented.
85
+ * A single interleaved pass would be order-dependent — a deeper point's
86
+ * carve could erase the endpoint a nearer point added earlier in the same
87
+ * sample. Splitting the passes makes the result deterministic and lets an
88
+ * endpoint observed within a sample survive other rays in that same
89
+ * sample. (Deeper-carves-nearer still applies ACROSS samples: a later
90
+ * sample's ray carves an earlier sample's endpoint as before.)
91
+ *
92
+ * @returns the number of points actually added.
93
+ */
94
+ addSample(sample: DepthSample): number;
95
+ /**
96
+ * Occupied cells observed at least `minObservations` times (default 1).
97
+ *
98
+ * The result is a **shared immutable snapshot**: a repeated call with the
99
+ * same floor on an unchanged grid returns the SAME array instance (the
100
+ * memo above), so callers must never mutate it. Floors above
101
+ * {@link MAX_RELEVANT_COUNT} bypass the memo — the revision counter does
102
+ * not track their threshold crossings, so a cached result could go stale.
103
+ */
104
+ getOccupiedCells(minObservations?: number): GridCell[];
105
+ /**
106
+ * Flat `[x0,y0,z0, x1,y1,z1, …]` variant of {@link getOccupiedCells} for the
107
+ * mesh-worker pack path (Step 1.3 of the 2026-07-03 long-session fps plan):
108
+ * `packMeshRequest` ships the snapshot to the worker as a transferable
109
+ * Int32Array, so handing it over flat deletes the tuple-array intermediate
110
+ * it used to re-flatten. Same cells, same order as the tuple API.
111
+ *
112
+ * Unlike {@link getOccupiedCells} this returns a **fresh array every call**
113
+ * — the pack path TRANSFERS the buffer to the worker (detaching it), so a
114
+ * shared/cached array would be destroyed under the grid. When the tuple
115
+ * memo is warm (the cubes refresh just snapshotted the same floor) the
116
+ * fresh array is flattened from it in O(matching cells) without a re-walk.
117
+ */
118
+ getOccupiedCellsFlat(minObservations?: number): Int32Array;
119
+ /**
120
+ * Occupied cells (≥ `minObservations`) whose **centers** lie within
121
+ * `radiusM` of `centerPos` — the viewer-local window that keeps consumer
122
+ * refresh cost independent of total explored area (Step 2 of the
123
+ * 2026-07-03 long-session fps plan). Walks only the chunks whose
124
+ * cell-center AABB intersects the query sphere; chunks entirely inside it
125
+ * skip the per-cell distance test. Result set ≡ the brute-force radius
126
+ * filter of {@link getOccupiedCells} (property-tested; iteration order may
127
+ * differ). Fresh array per call (no memo — the camera moves every frame).
128
+ *
129
+ * @throws RangeError for a non-finite or non-positive `radiusM` (a
130
+ * windowed query without a window is an upstream bug). A non-finite
131
+ * `centerPos` (tracking glitch) returns `[]` instead — mirrors
132
+ * {@link raycast}'s non-finite policy.
133
+ */
134
+ getOccupiedCellsWithin(centerPos: Vector3, radiusM: number, minObservations?: number): GridCell[];
135
+ /**
136
+ * Flat `[x0,y0,z0, …]` variant of {@link getOccupiedCellsWithin} for the
137
+ * mesh-worker pack path (same contract as {@link getOccupiedCellsFlat}:
138
+ * fresh array every call — packing transfers/detaches the buffer).
139
+ */
140
+ getOccupiedCellsWithinFlat(centerPos: Vector3, radiusM: number, minObservations?: number): Int32Array;
141
+ /**
142
+ * Per-chunk dirty revision (see the field docs): 0 for a never-touched
143
+ * chunk. `chunk` is in chunk coordinates (`floor(cell / 16)` per axis).
144
+ */
145
+ getChunkRevision(chunk: GridCell): number;
146
+ /** Shared sphere-window walk behind the tuple and flat queries. */
147
+ private forEachOccupiedCellWithin;
148
+ /** Quantize a raw-WebXR position to its grid cell (round per axis). */
149
+ cellForPosition(pos: Vector3): GridCell;
150
+ /** Center of a cell in raw WebXR space (round-consistent: cell · cellSizeM). */
151
+ getCellCenter(cell: GridCell): Vector3;
152
+ /**
153
+ * Running-average of the EXACT unprojected surface points observed in this
154
+ * cell (raw WebXR space), or null for an unknown cell. Unlike
155
+ * {@link getCellCenter} (the geometric 15 cm-lattice center) this hugs the
156
+ * real measured surface and noise-averages across viewpoints — used by the
157
+ * COLMAP `points3D` export and the debug cubes (follow-up Item A). Being a
158
+ * centroid of points that fell in the cell, it always lies within
159
+ * `cellSizeM/2` of the cell center per axis.
160
+ */
161
+ getCellPoint(cell: GridCell): Vector3 | null;
162
+ /**
163
+ * Running-average color of the cell's colored observations (Iter 8), or
164
+ * null when the cell is unknown or was only ever observed without color
165
+ * (rgb option off / pre-Iter-8 recordings) — consumers fall back to
166
+ * height-based coloring. Channels are rounded and clamped to 0–255.
167
+ */
168
+ getCellColor(cell: GridCell): RgbTuple | null;
169
+ /**
170
+ * Walk the grid from `startPos` to `endPos` and return the center of the
171
+ * first cell occupied at least `minObservations` times, or null.
172
+ * Port of Unity's `TryRaycast` (hook for cursor/floor-detection parity).
173
+ */
174
+ raycast(startPos: Vector3, endPos: Vector3, minObservations?: number): Vector3 | null;
175
+ /** Remove all occupied cells (e.g. on store swap / new session). */
176
+ clear(): void;
177
+ /**
178
+ * Delete occupied cells along the camera→point ray (the space was seen
179
+ * through, so it must be free), stopping `carveStopCells` dominant-axis
180
+ * steps before the endpoint. The endpoint cell itself is additionally
181
+ * protected so a current observation is never erased (relevant for
182
+ * carveStopCells = 0 and for the unconditional start-cell visit).
183
+ */
184
+ private carve;
185
+ /** Add a newly-occupied cell to its chunk's index set. */
186
+ private indexCell;
187
+ /** Remove a carved cell from its chunk (dropping an emptied chunk set). */
188
+ private unindexCell;
189
+ /** Mirror of the global revision bump, scoped to one chunk. */
190
+ private bumpChunkRevision;
191
+ private increment;
192
+ }
193
+ //#endregion
194
+ export { unpackCellKey as i, OccupancyGridOptions as n, cellKey as r, OccupancyGrid as t };
@@ -0,0 +1,100 @@
1
+ import { t as GridCell } from "./bresenham3d-DGc4nIb-.js";
2
+ import { Vector3 } from "gps-plus-slam-js";
3
+
4
+ //#region ../src/ar/occupancy-mesher.d.ts
5
+ /**
6
+ * An axis-aligned bounding box for one occupied cell (or, after greedy merge, a
7
+ * run of cells), in raw-WebXR metres. The neutral form a developer adapts into
8
+ * their physics engine's box collider — the framework adds no engine dependency.
9
+ */
10
+ interface Aabb {
11
+ readonly center: readonly [number, number, number];
12
+ readonly halfExtents: readonly [number, number, number];
13
+ }
14
+ /**
15
+ * Output of {@link meshOccupiedCells}: a non-indexed-friendly triangle soup
16
+ * (`positions`/`indices`, raw-WebXR metres) plus the per-cell AABB list. Typed
17
+ * arrays so the result is cheap to hand to `THREE.BufferGeometry` or transfer
18
+ * to a Web Worker.
19
+ */
20
+ interface OccupancyMeshResult {
21
+ /** Flat `[x0,y0,z0, x1,y1,z1, …]` vertex positions, 4 verts per emitted quad. */
22
+ readonly positions: Float32Array;
23
+ /** Triangle indices into `positions` (2 triangles / 6 indices per quad). */
24
+ readonly indices: Uint32Array;
25
+ /** One AABB per unique occupied cell. */
26
+ readonly aabbs: readonly Aabb[];
27
+ }
28
+ /**
29
+ * Selectable mesher strategy (2026-06-30 occluder-tuning session). All modes are
30
+ * simultaneously usable — none replaces another — so they can be perf/quality
31
+ * compared and a consumer can pick per use-case:
32
+ * - `'per-face'` — blocky, watertight, exact cell volume; the strict baseline.
33
+ * - `'greedy'` — fewest triangles, blocky; coplanar-face merge for memory.
34
+ * - `'smooth'` — standard surface nets (dual contouring): one welded vertex per
35
+ * boundary dual cell at the mean of its occupied corners' `getCellPoint`, with
36
+ * one quad per occupied↔empty crossing — so coverage matches the cubes.
37
+ * Continuous, hugs the measured surface, watertight for closed regions; a thin
38
+ * feature (the floor) collapses to a single smooth sheet (the smoothest mode),
39
+ * with only its single-occupied-corner dual vertices nudged apart so features
40
+ * thin in ≥2 dimensions keep a non-zero area (see SINGLE_CORNER_NUDGE_K).
41
+ * Uses `getCellPoint` to hug the surface (falls back to geometric centres).
42
+ *
43
+ * - `'corner-fit'` — the per-face cube mesher with each shared lattice corner
44
+ * nudged by the mean sub-cell offset (`getCellPoint − cellCentre`) of the cells
45
+ * touching it. Surface-hugging like `'smooth'` but **watertight** (identical
46
+ * face topology to `'per-face'`) and cube-thickness-preserving, at the per-face
47
+ * triangle cost. The "improve the cubes" path; needs `getCellPoint` (falls back
48
+ * to plain cubes without it).
49
+ */
50
+ type MeshMode = 'per-face' | 'greedy' | 'smooth' | 'corner-fit';
51
+ /** Options for {@link meshOccupiedCells}. */
52
+ interface MeshOccupiedCellsOptions {
53
+ /**
54
+ * @deprecated Prefer {@link MeshOccupiedCellsOptions.mode}. Back-compat shim:
55
+ * when `mode` is unset, `greedy:true` → `'greedy'`, otherwise `'per-face'`.
56
+ * Kept so existing callers/tests keep working unchanged.
57
+ */
58
+ readonly greedy?: boolean;
59
+ /**
60
+ * The mesher strategy. Takes precedence over {@link greedy}. Default resolves
61
+ * via the `greedy` shim above (so omitting both ⇒ `'per-face'`).
62
+ *
63
+ * Note: every mode still returns one `aabbs` box per cell (a 3-D greedy box
64
+ * merge for fewer colliders is a separate follow-on — see the plan §3E).
65
+ */
66
+ readonly mode?: MeshMode;
67
+ /**
68
+ * Per-cell measured surface point (the `OccupancyGrid.getCellPoint` bound
69
+ * method). Consumed by the surface-hugging modes `'smooth'` (dual vertex at the
70
+ * mean of its occupied corners' centroids) and `'corner-fit'` (corners nudged
71
+ * by the mean sub-cell offset) instead of the lattice centre. Ignored by
72
+ * `'per-face'`/`'greedy'`. When absent, both fall back to geometric positions.
73
+ * A `null` or non-finite result degrades that cell to its geometric position
74
+ * too — a NaN/Infinity centroid must not poison welded vertices (and NaN is
75
+ * the worker wire protocol's "no centroid" sentinel, so both paths agree).
76
+ *
77
+ * **Contract:** the `cell` tuple is only valid for the duration of the call —
78
+ * the meshers pass a reused scratch tuple on their allocation-free hot paths
79
+ * (PR #161 review), so implementations must read the coordinates and must NOT
80
+ * retain the tuple (no caching it as a key, no async use). Copy it if needed.
81
+ */
82
+ readonly getCellPoint?: (cell: GridCell) => Vector3 | null;
83
+ }
84
+ /**
85
+ * Mesh a snapshot of occupied cells into a face-culled surface + AABB list.
86
+ *
87
+ * Only faces whose neighbour cell is **not** in the occupied set are emitted
88
+ * (interior faces are dropped), so the triangle count scales with the surface
89
+ * area of the occupied set. Duplicate cells in `cells` are de-duplicated;
90
+ * cells with a non-finite or non-integer coordinate are skipped defensively (a
91
+ * tracking glitch upstream must not poison the mesh, and the packed cell keys
92
+ * are only collision-safe for integer coordinates).
93
+ *
94
+ * @param cells occupied cells (e.g. `grid.getOccupiedCells(minConfidence)`).
95
+ * @param cellSizeM cube edge length in metres (must be a positive finite number).
96
+ * @returns positions/indices (raw-WebXR metres) + one AABB per unique cell.
97
+ */
98
+ declare function meshOccupiedCells(cells: Iterable<GridCell>, cellSizeM: number, options?: MeshOccupiedCellsOptions): OccupancyMeshResult;
99
+ //#endregion
100
+ export { meshOccupiedCells as a, OccupancyMeshResult as i, MeshMode as n, MeshOccupiedCellsOptions as r, Aabb as t };
@@ -137,6 +137,11 @@ declare function getAppRootHandle(): FileSystemDirectoryHandle | null;
137
137
  * - /gps-plus-slam/sessions/recording-{timestamp}/actions/
138
138
  * - /gps-plus-slam/sessions/recording-{timestamp}/images/ (legacy: frames/)
139
139
  *
140
+ * The folder name is `recording-{timestamp}` at whole-second resolution; if a
141
+ * directory with that name already exists (two recordings started in the same
142
+ * UTC second), a numeric suffix (`-2`, `-3`, …) is appended so the new session
143
+ * never silently reuses and corrupts the earlier one's directory.
144
+ *
140
145
  * @param timestamp - Session start time (used for folder naming)
141
146
  * @param _contextTag - Opaque tag; not used by the flat layout but accepted
142
147
  * for interface compatibility with wrapping backends
@@ -1,5 +1,5 @@
1
- import { n as SessionMetadata } from "./opfs-storage-DMtlt57S.js";
2
- import { n as StorageBackend, t as CreateSessionResult } from "./storage-backend-CTJHNdMp.js";
1
+ import { n as SessionMetadata } from "./opfs-storage-CTkxvxN4.js";
2
+ import { n as StorageBackend, t as CreateSessionResult } from "./storage-backend-sEtmM4yW.js";
3
3
 
4
4
  //#region ../src/storage/opfs-storage-backend.d.ts
5
5
  declare class OpfsStorageBackend implements StorageBackend {
@@ -1,4 +1,4 @@
1
- import { n as StorageBackend } from "./storage-backend-CTJHNdMp.js";
1
+ import { n as StorageBackend } from "./storage-backend-sEtmM4yW.js";
2
2
  import { Middleware } from "@reduxjs/toolkit";
3
3
 
4
4
  //#region ../src/state/persistence-middleware.d.ts
@@ -1,4 +1,4 @@
1
- import { n as OpenCvPnpResult, r as Point2, s as SolvePnpSquare, t as CameraIntrinsics } from "./qr-pose-Bt2reSw7.js";
1
+ import { n as OpenCvPnpResult, r as Point2, s as SolvePnpSquare, t as CameraIntrinsics } from "./qr-pose-y3vaEd6x.js";
2
2
  import { Vector3 } from "gps-plus-slam-js";
3
3
 
4
4
  //#region ../src/ar/planar-pnp.d.ts
@@ -1,4 +1,4 @@
1
- import { i as Pose } from "./qr-pose-Bt2reSw7.js";
1
+ import { i as Pose } from "./qr-pose-y3vaEd6x.js";
2
2
  import { Object3D } from "three";
3
3
 
4
4
  //#region ../src/ar/qr-debug-view.d.ts
@@ -1,5 +1,5 @@
1
- import { i as Pose, r as Point2, s as SolvePnpSquare } from "./qr-pose-Bt2reSw7.js";
2
- import { a as QrSizeMeasurerOptions, n as QrSizeDepthContext } from "./qr-size-measurer-D7739ZpB.js";
1
+ import { i as Pose, r as Point2, s as SolvePnpSquare } from "./qr-pose-y3vaEd6x.js";
2
+ import { a as QrSizeMeasurerOptions, n as QrSizeDepthContext } from "./qr-size-measurer-DxiMqRUw.js";
3
3
  import { Matrix4 } from "gps-plus-slam-js";
4
4
 
5
5
  //#region ../src/ar/qr-derived-pose.d.ts
@@ -1,7 +1,7 @@
1
- import { i as Pose, r as Point2 } from "./qr-pose-Bt2reSw7.js";
2
- import { l as QrSizeEstimate } from "./qr-size-measurer-D7739ZpB.js";
3
- import { i as RawQrObservation, n as DerivedQrPlacement, t as DeriveQrPoseDeps } from "./qr-derived-pose-DinKE600.js";
4
- import { a as QrPoseStability, o as QrPoseStabilityOptions } from "./qr-pose-aggregation-CFlmLm6w.js";
1
+ import { i as Pose, r as Point2 } from "./qr-pose-y3vaEd6x.js";
2
+ import { l as QrSizeEstimate } from "./qr-size-measurer-DxiMqRUw.js";
3
+ import { i as RawQrObservation, n as DerivedQrPlacement, t as DeriveQrPoseDeps } from "./qr-derived-pose-BwNWn8oV.js";
4
+ import { a as QrPoseStability, o as QrPoseStabilityOptions } from "./qr-pose-aggregation-CypnmjA-.js";
5
5
  import { Matrix4, Vector3 } from "gps-plus-slam-js";
6
6
  import * as _$_reduxjs_toolkit0 from "@reduxjs/toolkit";
7
7
 
@@ -1,6 +1,6 @@
1
- import { i as Pose } from "./qr-pose-Bt2reSw7.js";
2
- import { i as QrDetection, o as RgbaImage } from "./qr-frontend-DufZp2xS.js";
3
- import { i as RawQrObservation } from "./qr-derived-pose-DinKE600.js";
1
+ import { i as Pose } from "./qr-pose-y3vaEd6x.js";
2
+ import { i as QrDetection, o as RgbaImage } from "./qr-frontend-BPURlh3S.js";
3
+ import { i as RawQrObservation } from "./qr-derived-pose-BwNWn8oV.js";
4
4
  import { Matrix4 } from "gps-plus-slam-js";
5
5
 
6
6
  //#region ../src/ar/qr-detection-controller.d.ts
@@ -1,4 +1,4 @@
1
- import { r as Point2 } from "./qr-pose-Bt2reSw7.js";
1
+ import { r as Point2 } from "./qr-pose-y3vaEd6x.js";
2
2
 
3
3
  //#region ../src/ar/qr-frontend.d.ts
4
4
  /** Raw RGBA pixels of the frame fed to detection (top-left origin). */
@@ -1,4 +1,4 @@
1
- import { i as Pose } from "./qr-pose-Bt2reSw7.js";
1
+ import { i as Pose } from "./qr-pose-y3vaEd6x.js";
2
2
  import { Quaternion } from "gps-plus-slam-js";
3
3
 
4
4
  //#region ../src/ar/qr-pose-aggregation.d.ts
@@ -1,3 +1,4 @@
1
+ import { t as geodesicAngleRad } from "./geodesic-angle-BTldfFgs.js";
1
2
  import { quat } from "gl-matrix";
2
3
  //#region ../src/ar/qr-pose-aggregation.ts
3
4
  /**
@@ -45,16 +46,6 @@ function toGl(q) {
45
46
  return quat.normalize(g, g);
46
47
  }
47
48
  /**
48
- * Geodesic (shortest-arc) angle in radians between two unit quaternions.
49
- * Double-cover safe: uses `2·dot² − 1 = cos θ`, which is invariant to the sign
50
- * of either quaternion (`q` ≡ `−q`), and clamps for float error before `acos`.
51
- */
52
- function geodesicAngleRad(a, b) {
53
- const d = quat.dot(a, b);
54
- const c = Math.min(1, Math.max(-1, 2 * d * d - 1));
55
- return Math.acos(c);
56
- }
57
- /**
58
49
  * Average the samples within `thresholdRad` of `ref`. Each inlier is flipped
59
50
  * into `ref`'s hemisphere (sign of the dot) BEFORE the linear average so the
60
51
  * double cover cannot cancel the sum; the normalized linear average equals the
@@ -1,5 +1,5 @@
1
- import { i as DepthSample } from "./ar-types-BckI1E-O.js";
2
- import { n as QrSizeDepthContext } from "./qr-size-measurer-D7739ZpB.js";
1
+ import { i as DepthSample } from "./ar-types-Cc5yvBwn.js";
2
+ import { n as QrSizeDepthContext } from "./qr-size-measurer-DxiMqRUw.js";
3
3
 
4
4
  //#region ../src/ar/qr-size-depth-context.d.ts
5
5
  /**
@@ -1,6 +1,6 @@
1
- import { r as DepthPoint } from "./ar-types-BckI1E-O.js";
2
- import { t as DepthUnprojector } from "./depth-unprojection-CK3b9htH.js";
3
- import { r as Point2 } from "./qr-pose-Bt2reSw7.js";
1
+ import { r as DepthPoint } from "./ar-types-Cc5yvBwn.js";
2
+ import { t as DepthUnprojector } from "./depth-unprojection-DRdBU90S.js";
3
+ import { r as Point2 } from "./qr-pose-y3vaEd6x.js";
4
4
  import { Vector3 } from "gps-plus-slam-js";
5
5
 
6
6
  //#region ../src/ar/qr-size-from-depth.d.ts