gps-plus-slam-app-framework 1.7.0 → 1.9.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.
- package/dist/{accuracy-circles-BtqOP_v1.d.ts → accuracy-circles-DkWBkF5g.d.ts} +16 -6
- package/dist/{app-selectors-ikTUQZpe.d.ts → app-selectors-BbAiCRUT.d.ts} +1 -1
- package/dist/ar/bresenham3d.d.ts +1 -1
- package/dist/ar/camera-blit-capture.d.ts +1 -1
- package/dist/ar/capability-checker.d.ts +1 -1
- package/dist/ar/capture-failure-tracker.d.ts +1 -1
- package/dist/ar/chromium-camera-access-workaround.d.ts +1 -1
- package/dist/ar/depth-grid-lookup.d.ts +1 -1
- package/dist/ar/depth-occluder.d.ts +127 -0
- package/dist/ar/depth-occluder.js +299 -0
- package/dist/ar/depth-sampler.d.ts +2 -2
- package/dist/ar/depth-sampler.js +24 -4
- package/dist/ar/depth-unprojection.d.ts +1 -1
- package/dist/ar/depth-unprojection.js +52 -21
- package/dist/ar/enable-gps-ar.d.ts +1 -1
- package/dist/ar/enable-gps-ar.js +1 -1
- package/dist/ar/frame-loop.d.ts +1 -1
- package/dist/ar/frame-loop.js +12 -2
- package/dist/ar/image-capture.d.ts +1 -1
- package/dist/ar/image-capture.js +1 -1
- package/dist/ar/image-quality.d.ts +1 -1
- package/dist/ar/image-quality.js +2 -1
- package/dist/ar/index.d.ts +32 -31
- package/dist/ar/index.js +5 -4
- package/dist/ar/occlusion-mesh-worker.d.ts +58 -0
- package/dist/ar/occlusion-mesh-worker.js +129 -0
- package/dist/ar/occupancy-grid.d.ts +2 -2
- package/dist/ar/occupancy-grid.js +287 -31
- package/dist/ar/occupancy-mesher.d.ts +2 -0
- package/dist/ar/occupancy-mesher.js +638 -0
- package/dist/ar/planar-pnp.d.ts +1 -1
- package/dist/ar/qr-debug-view.d.ts +1 -1
- package/dist/ar/qr-derived-pose.d.ts +1 -1
- package/dist/ar/qr-detection-controller.d.ts +1 -1
- package/dist/ar/qr-frontend.d.ts +1 -1
- package/dist/ar/qr-pose.d.ts +1 -1
- package/dist/ar/qr-size-depth-context.d.ts +2 -2
- package/dist/ar/qr-size-measurer.d.ts +1 -1
- package/dist/ar/replay-scene.d.ts +1 -1
- package/dist/ar/replay-scene.js +1 -1
- package/dist/ar/scene-node-names.d.ts +1 -1
- package/dist/ar/webxr-nue-basis.d.ts +1 -1
- package/dist/ar/webxr-session.d.ts +4 -4
- package/dist/ar/webxr-session.js +2 -2
- package/dist/ar/xr-camera-texture.d.ts +1 -1
- package/dist/ar/xr-error-handler.d.ts +1 -1
- package/dist/ar/xr-frame-loop.d.ts +1 -1
- package/dist/ar/xr-frame-loop.js +12 -2
- package/dist/{ar-MFpFlV7j.js → ar-BqCudtqE.js} +3 -2
- package/dist/{ar-world-group-alignment-69fgnB39.d.ts → ar-world-group-alignment-Cfg0trC3.d.ts} +1 -1
- package/dist/{capture-motion-gate-4EscNZCw.js → capture-motion-gate-_NdTSwKi.js} +17 -5
- package/dist/cell-key-DIB4rOTV.js +51 -0
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +2 -2
- package/dist/{create-slam-app-store-n_VF63D5.d.ts → create-slam-app-store-D1QSMuKu.d.ts} +8 -9
- package/dist/{create-slam-app-store-DkCHOpvb.js → create-slam-app-store-DXiAnJwR.js} +9 -2
- package/dist/{depth-grid-lookup-DQ-IYhO3.d.ts → depth-grid-lookup-D5atpNZ3.d.ts} +1 -1
- package/dist/{depth-sampler-asc8exHt.d.ts → depth-sampler-CwObAwEA.d.ts} +33 -1
- package/dist/{depth-unprojection-DjsbJW9L.d.ts → depth-unprojection-DRdBU90S.d.ts} +1 -1
- package/dist/{enable-gps-ar-DkNhFv4Y.d.ts → enable-gps-ar-BBtRO2FJ.d.ts} +4 -4
- package/dist/{frame-conversions-DObhj8Wf.d.ts → frame-conversions-DDcNHkTb.d.ts} +1 -1
- package/dist/{fused-path-v6eOmLl3.d.ts → fused-path-C5pp0PJU.d.ts} +1 -1
- package/dist/geo/h3-proximity.d.ts +1 -1
- package/dist/geo/index.d.ts +1 -1
- package/dist/{gps-anchor-BeFO2RYa.d.ts → gps-anchor-CEjGqEVD.d.ts} +1 -1
- package/dist/{gps-ar-pose-sampler-DHMZmYAc.d.ts → gps-ar-pose-sampler-DEGNk2CI.d.ts} +1 -1
- package/dist/{gps-event-coordinator-CjWW636x.d.ts → gps-event-coordinator-BWpgBrHq.d.ts} +4 -4
- package/dist/{image-capture-CNZDrQXG.d.ts → image-capture-Bp6Pd4Md.d.ts} +3 -3
- package/dist/{image-capture-B2-b6tKK.js → image-capture-DVcuqMtX.js} +1 -1
- package/dist/{index-BODDUTvD.d.ts → index-B6iBnO_K.d.ts} +1 -1
- package/dist/index-DXPVzOpP.d.ts +2 -0
- package/dist/{index-BneSj4_5.d.ts → index-cCQWuYqy.d.ts} +3 -3
- package/dist/index.d.ts +82 -80
- package/dist/index.js +10 -8
- package/dist/{leaflet-map-overlay-CmIyiE8n.d.ts → leaflet-map-overlay-C8GS4cUy.d.ts} +64 -10
- package/dist/leaflet-map-overlay-CBWPvaX9.js +452 -0
- package/dist/lerp-utils-Qs8OhumS.d.ts +42 -0
- package/dist/map-data-1tF5gcYV.js +124 -0
- package/dist/map-data-CUnTzRIC.d.ts +78 -0
- package/dist/{map-overlay-draw-ymz1s7fG.d.ts → map-overlay-draw-6WJeq0Kg.d.ts} +1 -1
- package/dist/{null-storage-backend-B9rtyDfG.d.ts → null-storage-backend-B1-jlpin.d.ts} +2 -2
- package/dist/occlusion-mesh-kXCQ2FM1.d.ts +194 -0
- package/dist/occupancy-grid-BV7RhB7I.d.ts +194 -0
- package/dist/occupancy-mesher-C2ecWhNp.d.ts +100 -0
- package/dist/{opfs-storage-3VZh-qH6.d.ts → opfs-storage-CTkxvxN4.d.ts} +5 -0
- package/dist/{opfs-storage-backend-JJiUJo-3.d.ts → opfs-storage-backend-D6Z03fML.d.ts} +2 -2
- package/dist/{persistence-middleware-DAPTTwB2.d.ts → persistence-middleware-CG-t08X4.d.ts} +1 -1
- package/dist/{planar-pnp-my9bqULS.d.ts → planar-pnp-D7bc_rLX.d.ts} +1 -1
- package/dist/{qr-debug-view-Brl73HGO.d.ts → qr-debug-view-BJooqbiP.d.ts} +1 -1
- package/dist/{qr-derived-pose-CmxTP2m4.d.ts → qr-derived-pose-BwNWn8oV.d.ts} +2 -2
- package/dist/{qr-detected-slice-VQssZmQJ.d.ts → qr-detected-slice-TzVb8bij.d.ts} +4 -4
- package/dist/{qr-detection-controller-CDurHv8E.d.ts → qr-detection-controller-Bo4XfQTP.d.ts} +3 -3
- package/dist/{qr-frontend--fFX25mV.d.ts → qr-frontend-BPURlh3S.d.ts} +1 -1
- package/dist/{qr-pose-aggregation-B7tWJq7j.d.ts → qr-pose-aggregation-CypnmjA-.d.ts} +1 -1
- package/dist/{qr-size-depth-context-CeAG53fy.d.ts → qr-size-depth-context-apAOmbrZ.d.ts} +2 -2
- package/dist/{qr-size-measurer-DQdAsid1.d.ts → qr-size-measurer-DxiMqRUw.d.ts} +3 -3
- package/dist/{recording-options-BPCpMHLK.d.ts → recording-options-DYKWVI93.d.ts} +203 -24
- package/dist/{recording-replayer-DQKpAGyQ.d.ts → recording-replayer-BJ8Lf0QY.d.ts} +2 -2
- package/dist/{recording-slice-DaxHZvNu.d.ts → recording-slice-BW8SiLo1.d.ts} +2 -2
- package/dist/{replay-engine-CmKh_4wf.d.ts → replay-engine-DmCAc7WM.d.ts} +1 -1
- package/dist/{replay-scene-DBlDrV_x.d.ts → replay-scene-zJ8SFDjE.d.ts} +3 -3
- package/dist/sensors/absolute-orientation.d.ts +1 -1
- package/dist/sensors/absolute-orientation.js +23 -6
- package/dist/sensors/gps-error-handler.d.ts +1 -1
- package/dist/sensors/gps.d.ts +1 -1
- package/dist/sensors/index.d.ts +4 -4
- package/dist/sensors/permission-checker.d.ts +1 -1
- package/dist/state/app-selectors.d.ts +1 -1
- package/dist/state/combined-root-state.d.ts +1 -1
- package/dist/state/create-slam-app-store.d.ts +1 -1
- package/dist/state/create-slam-app-store.js +1 -1
- package/dist/state/gps-ar-pose-sampler.d.ts +3 -3
- package/dist/state/gps-event-coordinator.d.ts +1 -1
- package/dist/state/index.d.ts +19 -19
- package/dist/state/index.js +2 -2
- package/dist/state/persistence-middleware.d.ts +1 -1
- package/dist/state/qr-detected-slice.d.ts +3 -3
- package/dist/state/recording-options.d.ts +2 -2
- package/dist/state/recording-options.js +150 -19
- package/dist/state/recording-replayer.d.ts +2 -2
- package/dist/state/recording-replayer.js +1 -1
- package/dist/state/recording-slice.d.ts +1 -1
- package/dist/state/replay-engine.d.ts +1 -1
- package/dist/state/store-subscribers.d.ts +2 -2
- package/dist/state/store-subscribers.js +26 -12
- package/dist/state/subscribe-to-selector.d.ts +1 -1
- package/dist/state/tracking-quality.d.ts +1 -1
- package/dist/state/tracking-quality.js +14 -6
- package/dist/state/tracking-slice.d.ts +1 -1
- package/dist/{state-E51QT_-j.js → state-BeKvn4B-.js} +1 -1
- package/dist/storage/file-system-utils.d.ts +1 -1
- package/dist/storage/index.d.ts +8 -8
- package/dist/storage/null-storage-backend.d.ts +1 -1
- package/dist/storage/opfs-storage-backend.d.ts +1 -1
- package/dist/storage/opfs-storage.d.ts +1 -1
- package/dist/storage/opfs-storage.js +30 -1
- package/dist/storage/storage-backend.d.ts +1 -1
- package/dist/storage/zip-coverage-embed.d.ts +1 -1
- package/dist/storage/zip-export.d.ts +1 -1
- package/dist/storage/zip-reader.d.ts +2 -2
- package/dist/storage/zip-reader.js +20 -7
- package/dist/{storage-backend-BdVLD0dU.d.ts → storage-backend-sEtmM4yW.d.ts} +1 -1
- package/dist/{store-subscribers-CA6AJH_8.d.ts → store-subscribers-DJUErW-o.d.ts} +6 -3
- package/dist/{subscribe-to-selector-B6EUmQ3I.d.ts → subscribe-to-selector-dQV8SpsO.d.ts} +1 -1
- package/dist/test-utils/browser-mocks.d.ts +1 -1
- package/dist/test-utils/browser-mocks.js +2 -0
- package/dist/test-utils/zip-round-trip-helpers.d.ts +1 -1
- package/dist/{tracking-slice-BuAZxXZ5.d.ts → tracking-slice-DMkHlbny.d.ts} +1 -1
- package/dist/types/ar-types.d.ts +1 -1
- package/dist/types/geo-types.d.ts +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/utils/concurrency.d.ts +1 -1
- package/dist/utils/failure-tracker.d.ts +1 -1
- package/dist/utils/format-file-size.d.ts +1 -1
- package/dist/utils/fused-path.d.ts +1 -1
- package/dist/utils/index.d.ts +7 -7
- package/dist/utils/list-formatter.d.ts +1 -1
- package/dist/utils/logger.d.ts +1 -1
- package/dist/visualization/accuracy-circles.d.ts +1 -1
- package/dist/visualization/accuracy-circles.js +17 -8
- package/dist/visualization/alignment-lerper.d.ts +1 -1
- package/dist/visualization/ar-world-group-alignment.d.ts +1 -1
- package/dist/visualization/ar-world-group-alignment.js +1 -1
- package/dist/visualization/camera-follower.d.ts +1 -1
- package/dist/visualization/css3d-renderer-manager.d.ts +1 -1
- package/dist/visualization/frame-conversions.d.ts +1 -1
- package/dist/visualization/frustum-visibility.d.ts +1 -1
- package/dist/visualization/gps-anchor.d.ts +1 -1
- package/dist/visualization/gps-compass-cubes.d.ts +1 -1
- package/dist/visualization/gps-event-markers.d.ts +1 -1
- package/dist/visualization/gps-event-markers.js +1 -1
- package/dist/visualization/hit-test-reticle.d.ts +1 -1
- package/dist/visualization/index.d.ts +20 -19
- package/dist/visualization/index.js +4 -3
- package/dist/visualization/leaflet-map-overlay.d.ts +1 -1
- package/dist/visualization/leaflet-map-overlay.js +1 -282
- package/dist/visualization/lerp-utils.d.ts +2 -2
- package/dist/visualization/lerp-utils.js +27 -3
- package/dist/visualization/map-data.d.ts +1 -1
- package/dist/visualization/map-data.js +1 -32
- package/dist/visualization/map-overlay-draw.d.ts +1 -1
- package/dist/visualization/map-overlay-draw.js +24 -2
- package/dist/visualization/map-overlay.d.ts +1 -1
- package/dist/visualization/occluder-mesh-driver.d.ts +130 -0
- package/dist/visualization/occluder-mesh-driver.js +219 -0
- package/dist/visualization/occlusion-mesh.d.ts +2 -0
- package/dist/visualization/occlusion-mesh.js +437 -0
- package/dist/visualization/three-dispose.d.ts +1 -1
- package/dist/visualization/vis-colors.d.ts +1 -1
- package/dist/{webxr-session-DG-eNfny.js → webxr-session-BCP2XK7k.js} +88 -13
- package/dist/{webxr-session-Do5L6G3B.d.ts → webxr-session-BWCpGd-2.d.ts} +73 -10
- package/dist/{zip-reader-D9iPQwdQ.d.ts → zip-reader-BzENzMH6.d.ts} +36 -10
- package/package.json +9 -5
- package/dist/index-_Rhgoqkh.d.ts +0 -2
- package/dist/lerp-utils-BLmHhkVe.d.ts +0 -20
- package/dist/map-data-B4oEehUh.d.ts +0 -49
- package/dist/occupancy-grid-DnOc-6PU.d.ts +0 -82
- /package/dist/{absolute-orientation-CAEh840t.d.ts → absolute-orientation-BAxc15J6.d.ts} +0 -0
- /package/dist/{alignment-lerper-Bil6vbVw.d.ts → alignment-lerper-gD70k7Fq.d.ts} +0 -0
- /package/dist/{ar-types-BFX2r1wk.d.ts → ar-types-Cc5yvBwn.d.ts} +0 -0
- /package/dist/{bresenham3d-CU1Gv2W4.d.ts → bresenham3d-DGc4nIb-.d.ts} +0 -0
- /package/dist/{camera-blit-capture-NKT9H5-e.d.ts → camera-blit-capture-BimSdyih.d.ts} +0 -0
- /package/dist/{camera-follower-CqJJ7zRj.d.ts → camera-follower-DfBm2J5i.d.ts} +0 -0
- /package/dist/{capability-checker-DUQLg6gq.d.ts → capability-checker-BTTpygvZ.d.ts} +0 -0
- /package/dist/{capture-failure-tracker-gsm8q7yi.d.ts → capture-failure-tracker-_BVfhpxv.d.ts} +0 -0
- /package/dist/{capture-motion-gate-0Yl2NpQG.d.ts → capture-motion-gate-BGoAMkmf.d.ts} +0 -0
- /package/dist/{chromium-camera-access-workaround-D4K2uwwQ.d.ts → chromium-camera-access-workaround-C6yJLXz9.d.ts} +0 -0
- /package/dist/{concurrency-By0oOTZt.d.ts → concurrency-DPeGBH0X.d.ts} +0 -0
- /package/dist/{css3d-renderer-manager-B3kGNtcn.d.ts → css3d-renderer-manager-CPpVtvgs.d.ts} +0 -0
- /package/dist/{failure-tracker-BO7M7jF_.d.ts → failure-tracker-CeKf5HIc.d.ts} +0 -0
- /package/dist/{file-system-utils-DOajnMm-.d.ts → file-system-utils-DueXHxo9.d.ts} +0 -0
- /package/dist/{format-file-size-Dc98ulzB.d.ts → format-file-size-DxN-EBLc.d.ts} +0 -0
- /package/dist/{frame-loop-CpVjQ1dH.d.ts → frame-loop-7GBMBu68.d.ts} +0 -0
- /package/dist/{frustum-visibility-CeoCZchO.d.ts → frustum-visibility-CokkgS_i.d.ts} +0 -0
- /package/dist/{geo-types-3Ip1NzVR.d.ts → geo-types-DrGVu8nS.d.ts} +0 -0
- /package/dist/{gps-BBO3hKfT.d.ts → gps-CDKDQokE.d.ts} +0 -0
- /package/dist/{gps-compass-cubes-DSN87L8p.d.ts → gps-compass-cubes-1mooS3BS.d.ts} +0 -0
- /package/dist/{gps-error-handler-BbPsfw-_.d.ts → gps-error-handler-C34GQuTu.d.ts} +0 -0
- /package/dist/{gps-event-markers-Cz9RMA2k.d.ts → gps-event-markers-Di2nBzx-.d.ts} +0 -0
- /package/dist/{h3-proximity-C-j0pw1j.d.ts → h3-proximity-D0GTHQmr.d.ts} +0 -0
- /package/dist/{hit-test-reticle-DV-2TpB9.d.ts → hit-test-reticle-CaQbrhbk.d.ts} +0 -0
- /package/dist/{image-quality-smCBoFj1.d.ts → image-quality-BMx2ko9-.d.ts} +0 -0
- /package/dist/{index-BAPokxMb.d.ts → index-D74JTX4I.d.ts} +0 -0
- /package/dist/{list-formatter-KNMS0ay2.d.ts → list-formatter-DLFDnS70.d.ts} +0 -0
- /package/dist/{logger-CLnPrHiD.d.ts → logger-CqTd5Kpk.d.ts} +0 -0
- /package/dist/{map-overlay-Dh2B97MP.d.ts → map-overlay-BCQgvIgU.d.ts} +0 -0
- /package/dist/{permission-checker-CDuI8BwC.d.ts → permission-checker-B-VtjnBA.d.ts} +0 -0
- /package/dist/{qr-pose-D2rfu5DA.d.ts → qr-pose-y3vaEd6x.d.ts} +0 -0
- /package/dist/{scene-node-names-BiShOQDB.d.ts → scene-node-names-CGb-ylBd.d.ts} +0 -0
- /package/dist/{session-disposers-zv9OGXPB.js → session-disposers-BPrMplIC.js} +0 -0
- /package/dist/{three-dispose-Bt1k7WKh.d.ts → three-dispose-BMOAmPhb.d.ts} +0 -0
- /package/dist/{vis-colors-Obw2pLqT.d.ts → vis-colors-C7HI0u9_.d.ts} +0 -0
- /package/dist/{webxr-nue-basis-3nfjDJJp.d.ts → webxr-nue-basis-CaCnYxuq.d.ts} +0 -0
- /package/dist/{xr-camera-texture-Cva1q3Qj.d.ts → xr-camera-texture-q0eIDSRg.d.ts} +0 -0
- /package/dist/{xr-error-handler-CtvHpjfm.d.ts → xr-error-handler-uVGDu9IX.d.ts} +0 -0
- /package/dist/{xr-frame-loop-DIYtRcUa.d.ts → xr-frame-loop-DX3JPpAU.d.ts} +0 -0
- /package/dist/{zip-coverage-embed-DCAREVmw.d.ts → zip-coverage-embed-ncVbaZAz.d.ts} +0 -0
- /package/dist/{zip-export-ol0S_Ofd.d.ts → zip-export-CtiUnAlJ.d.ts} +0 -0
|
@@ -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 (!
|
|
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
|
-
/**
|
|
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
|
-
|
|
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.
|
|
93
|
-
record.
|
|
94
|
-
record.
|
|
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.
|
|
109
|
-
average(record.
|
|
110
|
-
average(record.
|
|
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(
|
|
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))
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
0
|
|
159
|
-
],
|
|
397
|
+
posSumX: 0,
|
|
398
|
+
posSumY: 0,
|
|
399
|
+
posSumZ: 0,
|
|
160
400
|
colorCount: 0,
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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.
|
|
171
|
-
|
|
172
|
-
|
|
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.
|
|
176
|
-
record.
|
|
177
|
-
record.
|
|
418
|
+
record.colorSumR += rgb[0];
|
|
419
|
+
record.colorSumG += rgb[1];
|
|
420
|
+
record.colorSumB += rgb[2];
|
|
178
421
|
}
|
|
179
422
|
}
|
|
180
423
|
};
|
|
181
|
-
function
|
|
182
|
-
return
|
|
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 };
|