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,58 @@
1
+ import { t as GridCell } from "../bresenham3d-DGc4nIb-.js";
2
+ import { n as MeshMode } from "../occupancy-mesher-C2ecWhNp.js";
3
+ import { Vector3 } from "gps-plus-slam-js";
4
+
5
+ //#region ../src/ar/occlusion-mesh-worker.d.ts
6
+ /** Main-thread → worker: an occupied-cell snapshot to mesh. */
7
+ interface MeshWorkerRequest {
8
+ /** Correlates a response with its request (the driver coalesces by newest id). */
9
+ readonly id: number;
10
+ /** Flat occupied cells `[x0,y0,z0, x1,y1,z1, …]`. */
11
+ readonly cells: Int32Array;
12
+ readonly cellSizeM: number;
13
+ readonly mode: MeshMode;
14
+ /**
15
+ * Per-cell measured surface point, flat and parallel to `cells`
16
+ * (`[cx,cy,cz, …]`), or `null` for the cube modes that ignore it. A `NaN`
17
+ * triple marks a cell whose `getCellPoint` was `null` (geometric fallback).
18
+ */
19
+ readonly centroids: Float64Array | null;
20
+ }
21
+ /** Worker → main thread: the meshed geometry (typed arrays, transferable). */
22
+ interface MeshWorkerResponse {
23
+ readonly id: number;
24
+ readonly positions: Float32Array;
25
+ readonly indices: Uint32Array;
26
+ }
27
+ /**
28
+ * Pack an occupied-cell snapshot into a transferable {@link MeshWorkerRequest}
29
+ * (main thread). `transfer` is the list to pass as the second `postMessage` arg
30
+ * so the buffers move (not copy) to the worker.
31
+ *
32
+ * `cells` may be the classic tuple array or an already-flat
33
+ * `[x0,y0,z0, x1,y1,z1, …]` Int32Array (Step 1.3 of the 2026-07-03
34
+ * long-session fps plan — `OccupancyGrid.getOccupiedCellsFlat` hands the
35
+ * snapshot over flat, deleting the tuple intermediate this function used to
36
+ * re-flatten). A flat snapshot is used **zero-copy**: its buffer lands in
37
+ * `transfer` and is DETACHED after posting — callers must pass a fresh array
38
+ * they do not reuse. Throws `RangeError` when a flat snapshot's length is not
39
+ * a multiple of 3 (a truncated buffer must fail loudly, not mesh garbage).
40
+ *
41
+ * `getCellPoint` receives a **transient** cell tuple (a reused scratch on the
42
+ * flat path — see `MeshOccupiedCellsOptions.getCellPoint` for the contract):
43
+ * implementations must not retain the tuple beyond the call.
44
+ */
45
+ declare function packMeshRequest(id: number, cells: readonly GridCell[] | Int32Array, cellSizeM: number, mode: MeshMode, getCellPoint?: (cell: GridCell) => Vector3 | null): {
46
+ request: MeshWorkerRequest;
47
+ transfer: ArrayBufferLike[];
48
+ };
49
+ /**
50
+ * Mesh a {@link MeshWorkerRequest} (worker side). Returns the geometry plus the
51
+ * transfer list for posting back. Byte-identical to a direct `meshOccupiedCells`.
52
+ */
53
+ declare function runMeshRequest(request: MeshWorkerRequest): {
54
+ response: MeshWorkerResponse;
55
+ transfer: ArrayBufferLike[];
56
+ };
57
+ //#endregion
58
+ export { MeshWorkerRequest, MeshWorkerResponse, packMeshRequest, runMeshRequest };
@@ -0,0 +1,129 @@
1
+ import { a as packCellKey } from "../cell-key-DIB4rOTV.js";
2
+ import { meshOccupiedCells } from "./occupancy-mesher.js";
3
+ //#region ../src/ar/occlusion-mesh-worker.ts
4
+ /** Only the surface-hugging modes read `getCellPoint`. */
5
+ function needsCentroids(mode) {
6
+ return mode === "smooth" || mode === "corner-fit";
7
+ }
8
+ /**
9
+ * Pack an occupied-cell snapshot into a transferable {@link MeshWorkerRequest}
10
+ * (main thread). `transfer` is the list to pass as the second `postMessage` arg
11
+ * so the buffers move (not copy) to the worker.
12
+ *
13
+ * `cells` may be the classic tuple array or an already-flat
14
+ * `[x0,y0,z0, x1,y1,z1, …]` Int32Array (Step 1.3 of the 2026-07-03
15
+ * long-session fps plan — `OccupancyGrid.getOccupiedCellsFlat` hands the
16
+ * snapshot over flat, deleting the tuple intermediate this function used to
17
+ * re-flatten). A flat snapshot is used **zero-copy**: its buffer lands in
18
+ * `transfer` and is DETACHED after posting — callers must pass a fresh array
19
+ * they do not reuse. Throws `RangeError` when a flat snapshot's length is not
20
+ * a multiple of 3 (a truncated buffer must fail loudly, not mesh garbage).
21
+ *
22
+ * `getCellPoint` receives a **transient** cell tuple (a reused scratch on the
23
+ * flat path — see `MeshOccupiedCellsOptions.getCellPoint` for the contract):
24
+ * implementations must not retain the tuple beyond the call.
25
+ */
26
+ function packMeshRequest(id, cells, cellSizeM, mode, getCellPoint) {
27
+ let flat;
28
+ let n;
29
+ if (cells instanceof Int32Array) {
30
+ if (cells.length % 3 !== 0) throw new RangeError(`flat cell snapshot length must be a multiple of 3, got ${cells.length}`);
31
+ flat = cells;
32
+ n = cells.length / 3;
33
+ } else {
34
+ n = cells.length;
35
+ flat = new Int32Array(n * 3);
36
+ for (let i = 0; i < n; i++) {
37
+ const c = cells[i];
38
+ flat[i * 3] = c[0];
39
+ flat[i * 3 + 1] = c[1];
40
+ flat[i * 3 + 2] = c[2];
41
+ }
42
+ }
43
+ let centroids = null;
44
+ if (needsCentroids(mode) && getCellPoint) {
45
+ centroids = new Float64Array(n * 3);
46
+ const scratch = [
47
+ 0,
48
+ 0,
49
+ 0
50
+ ];
51
+ for (let i = 0; i < n; i++) {
52
+ let cell;
53
+ if (cells instanceof Int32Array) {
54
+ scratch[0] = flat[i * 3];
55
+ scratch[1] = flat[i * 3 + 1];
56
+ scratch[2] = flat[i * 3 + 2];
57
+ cell = scratch;
58
+ } else cell = cells[i];
59
+ const cp = getCellPoint(cell);
60
+ if (cp) {
61
+ centroids[i * 3] = cp[0];
62
+ centroids[i * 3 + 1] = cp[1];
63
+ centroids[i * 3 + 2] = cp[2];
64
+ } else {
65
+ centroids[i * 3] = NaN;
66
+ centroids[i * 3 + 1] = NaN;
67
+ centroids[i * 3 + 2] = NaN;
68
+ }
69
+ }
70
+ }
71
+ const request = {
72
+ id,
73
+ cells: flat,
74
+ cellSizeM,
75
+ mode,
76
+ centroids
77
+ };
78
+ const transfer = [flat.buffer];
79
+ if (centroids) transfer.push(centroids.buffer);
80
+ return {
81
+ request,
82
+ transfer
83
+ };
84
+ }
85
+ /**
86
+ * Mesh a {@link MeshWorkerRequest} (worker side). Returns the geometry plus the
87
+ * transfer list for posting back. Byte-identical to a direct `meshOccupiedCells`.
88
+ */
89
+ function runMeshRequest(request) {
90
+ const { id, cells: flat, cellSizeM, mode, centroids } = request;
91
+ const n = flat.length / 3;
92
+ const cells = [];
93
+ for (let i = 0; i < n; i++) cells.push([
94
+ flat[i * 3],
95
+ flat[i * 3 + 1],
96
+ flat[i * 3 + 2]
97
+ ]);
98
+ let getCellPoint;
99
+ const pts = centroids;
100
+ if (pts) {
101
+ const indexByKey = /* @__PURE__ */ new Map();
102
+ for (let i = 0; i < n; i++) indexByKey.set(packCellKey(flat[i * 3], flat[i * 3 + 1], flat[i * 3 + 2]), i);
103
+ getCellPoint = (cell) => {
104
+ const i = indexByKey.get(packCellKey(cell[0], cell[1], cell[2]));
105
+ if (i === void 0) return null;
106
+ const cx = pts[i * 3];
107
+ if (Number.isNaN(cx)) return null;
108
+ return [
109
+ cx,
110
+ pts[i * 3 + 1],
111
+ pts[i * 3 + 2]
112
+ ];
113
+ };
114
+ }
115
+ const mesh = meshOccupiedCells(cells, cellSizeM, {
116
+ mode,
117
+ getCellPoint
118
+ });
119
+ return {
120
+ response: {
121
+ id,
122
+ positions: mesh.positions,
123
+ indices: mesh.indices
124
+ },
125
+ transfer: [mesh.positions.buffer, mesh.indices.buffer]
126
+ };
127
+ }
128
+ //#endregion
129
+ export { packMeshRequest, runMeshRequest };
@@ -1,2 +1,2 @@
1
- import { n as OccupancyGridOptions, t as OccupancyGrid } from "../occupancy-grid-Bvi2Gw1U.js";
2
- export { OccupancyGrid, OccupancyGridOptions };
1
+ import { i as unpackCellKey, n as OccupancyGridOptions, r as cellKey, t as OccupancyGrid } from "../occupancy-grid-BV7RhB7I.js";
2
+ export { OccupancyGrid, OccupancyGridOptions, cellKey, unpackCellKey };
@@ -1,10 +1,56 @@
1
1
  import { createDepthUnprojector } from "./depth-unprojection.js";
2
2
  import { bresenham3d } from "./bresenham3d.js";
3
+ import { i as cellKey, o as unpackCellCoord, r as cellCoordsInKeyRange, s as unpackCellKey, t as CELL_KEY_LIMIT } from "../cell-key-DIB4rOTV.js";
3
4
  //#region ../src/ar/occupancy-grid.ts
5
+ /**
6
+ * Highest `minConfidence` a consumer can ask for (mirrors
7
+ * `OCCUPANCY_CONSTRAINTS.minConfidence.max`). Once a cell's count exceeds this,
8
+ * further observations can no longer change ANY consumer's occupied set, so they
9
+ * do not bump {@link OccupancyGrid.getRevision} — letting a settled scene skip
10
+ * redundant re-meshing.
11
+ */
12
+ const MAX_RELEVANT_COUNT = 10;
4
13
  var OccupancyGrid = class {
5
14
  cellSizeM;
6
15
  carveStopCells;
7
16
  cells = /* @__PURE__ */ new Map();
17
+ /**
18
+ * Chunk index (Step 2 of the 2026-07-03 long-session fps plan): cell keys
19
+ * grouped by {@link CHUNK_EDGE_CELLS}³ chunk, maintained incrementally on
20
+ * add/carve/clear (O(1) per mutation). Lets {@link getOccupiedCellsWithin}
21
+ * visit only the chunks a query sphere touches — cost independent of total
22
+ * explored area. Empty chunk sets are dropped.
23
+ */
24
+ chunks = /* @__PURE__ */ new Map();
25
+ /**
26
+ * Per-chunk dirty revision, bumped whenever a mutation inside the chunk
27
+ * could change some consumer's occupied set (same semantics as the global
28
+ * {@link revision}). This is the bookkeeping a future dirty-chunk remesh
29
+ * needs (2026-07-01 worker plan, Phase-2 sketch) — landed with the index so
30
+ * that plan becomes a consumer-side change only. Entries survive a chunk
31
+ * emptying (a consumer must still see "changed"); reset by {@link clear}.
32
+ */
33
+ chunkRevisions = /* @__PURE__ */ new Map();
34
+ /**
35
+ * Monotonic counter bumped whenever the **occupied set** (at any
36
+ * `minConfidence ≤ MAX_RELEVANT_COUNT`) could have changed: a cell added, a
37
+ * cell removed by carving, a cell's count rising while still `≤
38
+ * MAX_RELEVANT_COUNT` (a possible threshold crossing), or `clear`. Re-observing
39
+ * an already-settled cell (count `> MAX_RELEVANT_COUNT`) does NOT bump it, so a
40
+ * consumer can cheaply skip a full re-derive (cube refresh / occluder re-mesh)
41
+ * when the revision is unchanged — the dominant idle-time saving over a long
42
+ * session (see `2026-06-30-occluder-tuning-followups.md`).
43
+ */
44
+ revision = 0;
45
+ /**
46
+ * Memo of the last {@link getOccupiedCells} walk (Step 1.2 of the
47
+ * 2026-07-03 long-session fps plan): with cubes + occluder both on, every
48
+ * throttled refresh triggers two identical full-grid walks with the same
49
+ * minConfidence floor — the second is answered from here. Valid only while
50
+ * `revision` is unchanged, so it is only used for floors the revision
51
+ * counter actually tracks (≤ {@link MAX_RELEVANT_COUNT}).
52
+ */
53
+ snapshotCache = null;
8
54
  constructor(options) {
9
55
  const cellSizeM = options?.cellSizeM ?? .15;
10
56
  const carveStopCells = options?.carveStopCells ?? 2;
@@ -18,6 +64,16 @@ var OccupancyGrid = class {
18
64
  return this.cells.size;
19
65
  }
20
66
  /**
67
+ * A monotonic version that changes only when the occupied set (at any
68
+ * `minConfidence ≤ 10`) could have changed. A consumer caches the value it
69
+ * last meshed/rendered and skips its full re-derive when it is unchanged — so
70
+ * a settled scene (already-observed cells being re-observed) costs nothing.
71
+ * See {@link revision}.
72
+ */
73
+ getRevision() {
74
+ return this.revision;
75
+ }
76
+ /**
21
77
  * Fold one depth sample into the grid: unproject each point, carve free
22
78
  * space from the camera cell to the point cell, then count the point's
23
79
  * cell as occupied. Points that cannot be unprojected (no
@@ -39,12 +95,21 @@ var OccupancyGrid = class {
39
95
  const unprojector = createDepthUnprojector(sample.cameraPos, sample.cameraRot, sample.projectionMatrix);
40
96
  if (!unprojector) return 0;
41
97
  const cameraCell = this.cellForPosition(sample.cameraPos);
98
+ const cameraInRange = cellInKeyRange(cameraCell);
99
+ const carvedEndpointKeys = /* @__PURE__ */ new Set();
42
100
  const endpoints = [];
43
101
  for (const point of sample.points) {
44
102
  const world = unprojector.unproject(point);
45
103
  if (!world) continue;
46
104
  const cell = this.cellForPosition(world);
47
- if (!cellsEqual(cameraCell, cell)) this.carve(cameraCell, cell);
105
+ if (!cellInKeyRange(cell)) continue;
106
+ if (cameraInRange && !cellsEqual(cameraCell, cell)) {
107
+ const key = cellKey(cell);
108
+ if (!carvedEndpointKeys.has(key)) {
109
+ carvedEndpointKeys.add(key);
110
+ this.carve(cameraCell, cell);
111
+ }
112
+ }
48
113
  endpoints.push({
49
114
  cell,
50
115
  world,
@@ -54,12 +119,152 @@ var OccupancyGrid = class {
54
119
  for (const endpoint of endpoints) this.increment(endpoint.cell, endpoint.world, endpoint.rgb);
55
120
  return endpoints.length;
56
121
  }
57
- /** Occupied cells observed at least `minObservations` times (default 1). */
122
+ /**
123
+ * Occupied cells observed at least `minObservations` times (default 1).
124
+ *
125
+ * The result is a **shared immutable snapshot**: a repeated call with the
126
+ * same floor on an unchanged grid returns the SAME array instance (the
127
+ * memo above), so callers must never mutate it. Floors above
128
+ * {@link MAX_RELEVANT_COUNT} bypass the memo — the revision counter does
129
+ * not track their threshold crossings, so a cached result could go stale.
130
+ */
58
131
  getOccupiedCells(minObservations = 1) {
132
+ const cache = this.snapshotCache;
133
+ if (cache && cache.revision === this.revision && cache.minObservations === minObservations) return cache.cells;
134
+ const result = [];
135
+ for (const [key, record] of this.cells) if (record.count >= minObservations) result.push(unpackCellKey(key));
136
+ if (minObservations <= MAX_RELEVANT_COUNT) this.snapshotCache = {
137
+ revision: this.revision,
138
+ minObservations,
139
+ cells: result
140
+ };
141
+ return result;
142
+ }
143
+ /**
144
+ * Flat `[x0,y0,z0, x1,y1,z1, …]` variant of {@link getOccupiedCells} for the
145
+ * mesh-worker pack path (Step 1.3 of the 2026-07-03 long-session fps plan):
146
+ * `packMeshRequest` ships the snapshot to the worker as a transferable
147
+ * Int32Array, so handing it over flat deletes the tuple-array intermediate
148
+ * it used to re-flatten. Same cells, same order as the tuple API.
149
+ *
150
+ * Unlike {@link getOccupiedCells} this returns a **fresh array every call**
151
+ * — the pack path TRANSFERS the buffer to the worker (detaching it), so a
152
+ * shared/cached array would be destroyed under the grid. When the tuple
153
+ * memo is warm (the cubes refresh just snapshotted the same floor) the
154
+ * fresh array is flattened from it in O(matching cells) without a re-walk.
155
+ */
156
+ getOccupiedCellsFlat(minObservations = 1) {
157
+ const cache = this.snapshotCache;
158
+ if (cache && cache.revision === this.revision && cache.minObservations === minObservations) {
159
+ const cells = cache.cells;
160
+ const flat = new Int32Array(cells.length * 3);
161
+ for (let i = 0; i < cells.length; i++) {
162
+ const c = cells[i];
163
+ flat[i * 3] = c[0];
164
+ flat[i * 3 + 1] = c[1];
165
+ flat[i * 3 + 2] = c[2];
166
+ }
167
+ return flat;
168
+ }
169
+ const flat = new Int32Array(this.cells.size * 3);
170
+ let used = 0;
171
+ for (const [key, record] of this.cells) if (record.count >= minObservations) {
172
+ flat[used] = unpackCellCoord(key, 0);
173
+ flat[used + 1] = unpackCellCoord(key, 1);
174
+ flat[used + 2] = unpackCellCoord(key, 2);
175
+ used += 3;
176
+ }
177
+ return used === flat.length ? flat : flat.slice(0, used);
178
+ }
179
+ /**
180
+ * Occupied cells (≥ `minObservations`) whose **centers** lie within
181
+ * `radiusM` of `centerPos` — the viewer-local window that keeps consumer
182
+ * refresh cost independent of total explored area (Step 2 of the
183
+ * 2026-07-03 long-session fps plan). Walks only the chunks whose
184
+ * cell-center AABB intersects the query sphere; chunks entirely inside it
185
+ * skip the per-cell distance test. Result set ≡ the brute-force radius
186
+ * filter of {@link getOccupiedCells} (property-tested; iteration order may
187
+ * differ). Fresh array per call (no memo — the camera moves every frame).
188
+ *
189
+ * @throws RangeError for a non-finite or non-positive `radiusM` (a
190
+ * windowed query without a window is an upstream bug). A non-finite
191
+ * `centerPos` (tracking glitch) returns `[]` instead — mirrors
192
+ * {@link raycast}'s non-finite policy.
193
+ */
194
+ getOccupiedCellsWithin(centerPos, radiusM, minObservations = 1) {
59
195
  const result = [];
60
- for (const record of this.cells.values()) if (record.count >= minObservations) result.push(record.cell);
196
+ this.forEachOccupiedCellWithin(centerPos, radiusM, minObservations, (x, y, z) => {
197
+ result.push([
198
+ x,
199
+ y,
200
+ z
201
+ ]);
202
+ });
61
203
  return result;
62
204
  }
205
+ /**
206
+ * Flat `[x0,y0,z0, …]` variant of {@link getOccupiedCellsWithin} for the
207
+ * mesh-worker pack path (same contract as {@link getOccupiedCellsFlat}:
208
+ * fresh array every call — packing transfers/detaches the buffer).
209
+ */
210
+ getOccupiedCellsWithinFlat(centerPos, radiusM, minObservations = 1) {
211
+ const coords = [];
212
+ this.forEachOccupiedCellWithin(centerPos, radiusM, minObservations, (x, y, z) => {
213
+ coords.push(x, y, z);
214
+ });
215
+ return Int32Array.from(coords);
216
+ }
217
+ /**
218
+ * Per-chunk dirty revision (see the field docs): 0 for a never-touched
219
+ * chunk. `chunk` is in chunk coordinates (`floor(cell / 16)` per axis).
220
+ */
221
+ getChunkRevision(chunk) {
222
+ return this.chunkRevisions.get(chunkKeyOf(chunk[0], chunk[1], chunk[2])) ?? 0;
223
+ }
224
+ /** Shared sphere-window walk behind the tuple and flat queries. */
225
+ forEachOccupiedCellWithin(centerPos, radiusM, minObservations, visit) {
226
+ if (!Number.isFinite(radiusM) || radiusM <= 0) throw new RangeError(`radiusM must be a positive finite number, got ${radiusM}`);
227
+ if (!isFiniteTriple(centerPos)) return;
228
+ const cs = this.cellSizeM;
229
+ const r2 = radiusM * radiusM;
230
+ const skipBeyond = (radiusM + cs * 1e-9) ** 2;
231
+ const wholeInsideR2 = (radiusM - cs * 1e-9) ** 2;
232
+ for (const [chunkKey, cellKeys] of this.chunks) {
233
+ const bx = unpackChunkCoord(chunkKey, 0) * CHUNK_EDGE_CELLS * cs;
234
+ const by = unpackChunkCoord(chunkKey, 1) * CHUNK_EDGE_CELLS * cs;
235
+ const bz = unpackChunkCoord(chunkKey, 2) * CHUNK_EDGE_CELLS * cs;
236
+ const extent = (CHUNK_EDGE_CELLS - 1) * cs;
237
+ let dMin2 = 0;
238
+ let dMax2 = 0;
239
+ for (let axis = 0; axis < 3; axis++) {
240
+ const lo = axis === 0 ? bx : axis === 1 ? by : bz;
241
+ const hi = lo + extent;
242
+ const v = centerPos[axis];
243
+ const below = lo - v;
244
+ const above = v - hi;
245
+ const dMin = Math.max(below, above, 0);
246
+ dMin2 += dMin * dMin;
247
+ const dMax = Math.max(Math.abs(v - lo), Math.abs(hi - v));
248
+ dMax2 += dMax * dMax;
249
+ }
250
+ if (dMin2 > skipBeyond) continue;
251
+ const wholeInside = dMax2 <= wholeInsideR2;
252
+ for (const key of cellKeys) {
253
+ const record = this.cells.get(key);
254
+ if (!record || record.count < minObservations) continue;
255
+ const x = unpackCellCoord(key, 0);
256
+ const y = unpackCellCoord(key, 1);
257
+ const z = unpackCellCoord(key, 2);
258
+ if (!wholeInside) {
259
+ const dx = x * cs - centerPos[0];
260
+ const dy = y * cs - centerPos[1];
261
+ const dz = z * cs - centerPos[2];
262
+ if (dx * dx + dy * dy + dz * dz > r2) continue;
263
+ }
264
+ visit(x, y, z);
265
+ }
266
+ }
267
+ }
63
268
  /** Quantize a raw-WebXR position to its grid cell (round per axis). */
64
269
  cellForPosition(pos) {
65
270
  return [
@@ -86,12 +291,13 @@ var OccupancyGrid = class {
86
291
  * `cellSizeM/2` of the cell center per axis.
87
292
  */
88
293
  getCellPoint(cell) {
294
+ if (!cellInKeyRange(cell)) return null;
89
295
  const record = this.cells.get(cellKey(cell));
90
296
  if (!record || record.count === 0) return null;
91
297
  return [
92
- record.posSum[0] / record.count,
93
- record.posSum[1] / record.count,
94
- record.posSum[2] / record.count
298
+ record.posSumX / record.count,
299
+ record.posSumY / record.count,
300
+ record.posSumZ / record.count
95
301
  ];
96
302
  }
97
303
  /**
@@ -101,13 +307,14 @@ var OccupancyGrid = class {
101
307
  * height-based coloring. Channels are rounded and clamped to 0–255.
102
308
  */
103
309
  getCellColor(cell) {
310
+ if (!cellInKeyRange(cell)) return null;
104
311
  const record = this.cells.get(cellKey(cell));
105
312
  if (!record || record.colorCount === 0) return null;
106
313
  const average = (sum) => Math.min(255, Math.max(0, Math.round(sum / record.colorCount)));
107
314
  return [
108
- average(record.colorSum[0]),
109
- average(record.colorSum[1]),
110
- average(record.colorSum[2])
315
+ average(record.colorSumR),
316
+ average(record.colorSumG),
317
+ average(record.colorSumB)
111
318
  ];
112
319
  }
113
320
  /**
@@ -117,8 +324,11 @@ var OccupancyGrid = class {
117
324
  */
118
325
  raycast(startPos, endPos, minObservations = 1) {
119
326
  if (!isFiniteTriple(startPos) || !isFiniteTriple(endPos)) return null;
327
+ const startCell = this.cellForPosition(startPos);
328
+ const endCell = this.cellForPosition(endPos);
329
+ if (!cellInKeyRange(startCell) || !cellInKeyRange(endCell)) return null;
120
330
  let hit = null;
121
- bresenham3d(this.cellForPosition(startPos), this.cellForPosition(endPos), (cell) => {
331
+ bresenham3d(startCell, endCell, (cell) => {
122
332
  const record = this.cells.get(cellKey(cell));
123
333
  if (record && record.count >= minObservations) {
124
334
  hit = cell;
@@ -130,7 +340,10 @@ var OccupancyGrid = class {
130
340
  }
131
341
  /** Remove all occupied cells (e.g. on store swap / new session). */
132
342
  clear() {
343
+ if (this.cells.size > 0) this.revision++;
133
344
  this.cells.clear();
345
+ this.chunks.clear();
346
+ this.chunkRevisions.clear();
134
347
  }
135
348
  /**
136
349
  * Delete occupied cells along the camera→point ray (the space was seen
@@ -141,45 +354,88 @@ var OccupancyGrid = class {
141
354
  */
142
355
  carve(cameraCell, pointCell) {
143
356
  bresenham3d(cameraCell, pointCell, (cell) => {
144
- if (!cellsEqual(cell, pointCell)) this.cells.delete(cellKey(cell));
357
+ if (!cellsEqual(cell, pointCell)) {
358
+ const key = cellKey(cell);
359
+ if (this.cells.delete(key)) {
360
+ this.revision++;
361
+ this.unindexCell(cell, key);
362
+ }
363
+ }
145
364
  return true;
146
365
  }, this.carveStopCells);
147
366
  }
367
+ /** Add a newly-occupied cell to its chunk's index set. */
368
+ indexCell(cell, key) {
369
+ const ck = chunkKeyOf(Math.floor(cell[0] / CHUNK_EDGE_CELLS), Math.floor(cell[1] / CHUNK_EDGE_CELLS), Math.floor(cell[2] / CHUNK_EDGE_CELLS));
370
+ let set = this.chunks.get(ck);
371
+ if (!set) {
372
+ set = /* @__PURE__ */ new Set();
373
+ this.chunks.set(ck, set);
374
+ }
375
+ set.add(key);
376
+ }
377
+ /** Remove a carved cell from its chunk (dropping an emptied chunk set). */
378
+ unindexCell(cell, key) {
379
+ const ck = chunkKeyOf(Math.floor(cell[0] / CHUNK_EDGE_CELLS), Math.floor(cell[1] / CHUNK_EDGE_CELLS), Math.floor(cell[2] / CHUNK_EDGE_CELLS));
380
+ const set = this.chunks.get(ck);
381
+ if (set) {
382
+ set.delete(key);
383
+ if (set.size === 0) this.chunks.delete(ck);
384
+ }
385
+ this.bumpChunkRevision(ck);
386
+ }
387
+ /** Mirror of the global revision bump, scoped to one chunk. */
388
+ bumpChunkRevision(chunkKey) {
389
+ this.chunkRevisions.set(chunkKey, (this.chunkRevisions.get(chunkKey) ?? 0) + 1);
390
+ }
148
391
  increment(cell, world, rgb) {
149
392
  const key = cellKey(cell);
150
393
  let record = this.cells.get(key);
151
394
  if (!record) {
152
395
  record = {
153
- cell,
154
396
  count: 0,
155
- posSum: [
156
- 0,
157
- 0,
158
- 0
159
- ],
397
+ posSumX: 0,
398
+ posSumY: 0,
399
+ posSumZ: 0,
160
400
  colorCount: 0,
161
- colorSum: [
162
- 0,
163
- 0,
164
- 0
165
- ]
401
+ colorSumR: 0,
402
+ colorSumG: 0,
403
+ colorSumB: 0
166
404
  };
167
405
  this.cells.set(key, record);
406
+ this.indexCell(cell, key);
168
407
  }
169
408
  record.count++;
170
- record.posSum[0] += world[0];
171
- record.posSum[1] += world[1];
172
- record.posSum[2] += world[2];
409
+ if (record.count <= MAX_RELEVANT_COUNT) {
410
+ this.revision++;
411
+ this.bumpChunkRevision(chunkKeyOf(Math.floor(cell[0] / CHUNK_EDGE_CELLS), Math.floor(cell[1] / CHUNK_EDGE_CELLS), Math.floor(cell[2] / CHUNK_EDGE_CELLS)));
412
+ }
413
+ record.posSumX += world[0];
414
+ record.posSumY += world[1];
415
+ record.posSumZ += world[2];
173
416
  if (rgb && isFiniteTriple(rgb)) {
174
417
  record.colorCount++;
175
- record.colorSum[0] += rgb[0];
176
- record.colorSum[1] += rgb[1];
177
- record.colorSum[2] += rgb[2];
418
+ record.colorSumR += rgb[0];
419
+ record.colorSumG += rgb[1];
420
+ record.colorSumB += rgb[2];
178
421
  }
179
422
  }
180
423
  };
181
- function cellKey(cell) {
182
- return `${cell[0]},${cell[1]},${cell[2]}`;
424
+ function cellInKeyRange(cell) {
425
+ return cellCoordsInKeyRange(cell, CELL_KEY_LIMIT);
426
+ }
427
+ const CHUNK_EDGE_CELLS = 16;
428
+ const CHUNK_KEY_OFFSET = 4096;
429
+ const CHUNK_KEY_FIELD = 8192;
430
+ const CHUNK_KEY_FIELD_SQ = CHUNK_KEY_FIELD * CHUNK_KEY_FIELD;
431
+ function chunkKeyOf(cx, cy, cz) {
432
+ return (cx + CHUNK_KEY_OFFSET) * CHUNK_KEY_FIELD_SQ + (cy + CHUNK_KEY_OFFSET) * CHUNK_KEY_FIELD + (cz + CHUNK_KEY_OFFSET);
433
+ }
434
+ /** Inverse of {@link chunkKeyOf} for one axis (0 = x, 1 = y, 2 = z). */
435
+ function unpackChunkCoord(key, axis) {
436
+ if (axis === 0) return Math.floor(key / CHUNK_KEY_FIELD_SQ) - CHUNK_KEY_OFFSET;
437
+ if (axis === 1) return Math.floor(key / CHUNK_KEY_FIELD) % CHUNK_KEY_FIELD - CHUNK_KEY_OFFSET;
438
+ return key % CHUNK_KEY_FIELD - CHUNK_KEY_OFFSET;
183
439
  }
184
440
  function cellsEqual(a, b) {
185
441
  return a[0] === b[0] && a[1] === b[1] && a[2] === b[2];
@@ -188,4 +444,4 @@ function isFiniteTriple(v) {
188
444
  return Number.isFinite(v[0]) && Number.isFinite(v[1]) && Number.isFinite(v[2]);
189
445
  }
190
446
  //#endregion
191
- export { OccupancyGrid };
447
+ export { OccupancyGrid, cellKey, unpackCellKey };
@@ -0,0 +1,2 @@
1
+ import { a as meshOccupiedCells, i as OccupancyMeshResult, n as MeshMode, r as MeshOccupiedCellsOptions, t as Aabb } from "../occupancy-mesher-C2ecWhNp.js";
2
+ export { Aabb, MeshMode, MeshOccupiedCellsOptions, OccupancyMeshResult, meshOccupiedCells };