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.
- package/dist/absolute-orientation-BAxc15J6.d.ts +54 -0
- package/dist/{accuracy-circles-Pi2YeCrM.d.ts → accuracy-circles-DkWBkF5g.d.ts} +16 -6
- package/dist/{app-selectors-CrOucL00.d.ts → app-selectors-JqydaUMN.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 +2 -2
- package/dist/ar/image-capture.js +1 -154
- package/dist/ar/image-quality.d.ts +2 -0
- package/dist/ar/image-quality.js +169 -0
- package/dist/ar/index.d.ts +32 -30
- package/dist/ar/index.js +7 -5
- 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-Dc72Ksiu.js → ar-BqCudtqE.js} +4 -3
- package/dist/{ar-world-group-alignment-DDLkgNf_.d.ts → ar-world-group-alignment-BlvGcIAk.d.ts} +1 -1
- package/dist/capture-motion-gate-BGoAMkmf.d.ts +24 -0
- package/dist/capture-motion-gate-_NdTSwKi.js +106 -0
- 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-Ce2FMKCp.d.ts → create-slam-app-store-D3nzh9Wb.d.ts} +50 -40
- package/dist/create-slam-app-store-DXiAnJwR.js +156 -0
- package/dist/{depth-grid-lookup-vMDrGTk_.d.ts → depth-grid-lookup-D5atpNZ3.d.ts} +1 -1
- package/dist/{depth-sampler-DwfPYzS7.d.ts → depth-sampler-CwObAwEA.d.ts} +33 -1
- package/dist/{depth-unprojection-CK3b9htH.d.ts → depth-unprojection-DRdBU90S.d.ts} +1 -1
- package/dist/{enable-gps-ar-CjZ1nEdu.d.ts → enable-gps-ar-DvGzBOrp.d.ts} +4 -4
- package/dist/{frame-conversions-D_Af8Dt3.d.ts → frame-conversions-C4bnNOEY.d.ts} +1 -1
- package/dist/{fused-path-C0FHe67t.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/geodesic-angle-BTldfFgs.js +39 -0
- package/dist/{gps-anchor-C6I9NDHA.d.ts → gps-anchor-BjECcYnD.d.ts} +1 -1
- package/dist/{gps-ar-pose-sampler-DaHg2aPZ.d.ts → gps-ar-pose-sampler-DEGNk2CI.d.ts} +1 -1
- package/dist/{gps-event-coordinator-C7hwHyLA.d.ts → gps-event-coordinator-CHtIkhb7.d.ts} +21 -11
- package/dist/{image-capture-C2Feywet.d.ts → image-capture-Bp6Pd4Md.d.ts} +103 -3
- package/dist/image-capture-DVcuqMtX.js +374 -0
- package/dist/image-quality-BMx2ko9-.d.ts +158 -0
- package/dist/index-D4vrgV4b.d.ts +2 -0
- package/dist/index-D74JTX4I.d.ts +22 -0
- package/dist/{index-m_LneUSi.d.ts → index-DlsOo2IB.d.ts} +1 -1
- package/dist/{index-BvLo2poy.d.ts → index-cCQWuYqy.d.ts} +3 -3
- package/dist/index.d.ts +82 -77
- package/dist/index.js +15 -10
- package/dist/{leaflet-map-overlay-bnb2U1NQ.d.ts → leaflet-map-overlay-BOZFW5kT.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-D7rpMC6f.js +120 -0
- package/dist/{map-data-DdjPiSav.d.ts → map-data-PNfGKGWx.d.ts} +22 -2
- package/dist/{map-overlay-draw-CPVdAsFq.d.ts → map-overlay-draw-DiroaJed.d.ts} +1 -1
- package/dist/{null-storage-backend-CuSdYFsM.d.ts → null-storage-backend-B1-jlpin.d.ts} +2 -2
- package/dist/occlusion-mesh-b28lRJ6S.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-DMtlt57S.d.ts → opfs-storage-CTkxvxN4.d.ts} +5 -0
- package/dist/{opfs-storage-backend-CeQAb0SD.d.ts → opfs-storage-backend-D6Z03fML.d.ts} +2 -2
- package/dist/{persistence-middleware-5eearhSV.d.ts → persistence-middleware-CG-t08X4.d.ts} +1 -1
- package/dist/{planar-pnp-cjthe7Be.d.ts → planar-pnp-D7bc_rLX.d.ts} +1 -1
- package/dist/{qr-debug-view-B8gTzjMm.d.ts → qr-debug-view-BJooqbiP.d.ts} +1 -1
- package/dist/{qr-derived-pose-DinKE600.d.ts → qr-derived-pose-BwNWn8oV.d.ts} +2 -2
- package/dist/{qr-detected-slice-Cpv-dg82.d.ts → qr-detected-slice-TzVb8bij.d.ts} +4 -4
- package/dist/{qr-detection-controller-Cd2Ih4cC.d.ts → qr-detection-controller-Bo4XfQTP.d.ts} +3 -3
- package/dist/{qr-frontend-DufZp2xS.d.ts → qr-frontend-BPURlh3S.d.ts} +1 -1
- package/dist/{qr-pose-aggregation-CFlmLm6w.d.ts → qr-pose-aggregation-CypnmjA-.d.ts} +1 -1
- package/dist/{qr-pose-aggregation-DXR9STHs.js → qr-pose-aggregation-DBiaLW0O.js} +1 -10
- package/dist/{qr-size-depth-context-onu36E9s.d.ts → qr-size-depth-context-apAOmbrZ.d.ts} +2 -2
- package/dist/{qr-size-measurer-D7739ZpB.d.ts → qr-size-measurer-DxiMqRUw.d.ts} +3 -3
- package/dist/recording-options-vASCFzc5.d.ts +667 -0
- package/dist/{recording-replayer-CUEK82tM.d.ts → recording-replayer-D0V9WkmY.d.ts} +2 -2
- package/dist/{recording-slice-B-uCgGoh.d.ts → recording-slice-DTUtSVgd.d.ts} +2 -2
- package/dist/{replay-engine-BSVd7Zzb.d.ts → replay-engine-xdrUgWJi.d.ts} +1 -1
- package/dist/{replay-scene-CPt56pps.d.ts → replay-scene-zJ8SFDjE.d.ts} +3 -3
- package/dist/sensors/absolute-orientation.d.ts +2 -0
- package/dist/sensors/absolute-orientation.js +162 -0
- package/dist/sensors/gps-error-handler.d.ts +1 -1
- package/dist/sensors/gps.d.ts +1 -1
- package/dist/sensors/index.d.ts +5 -4
- package/dist/sensors/index.js +2 -1
- 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 -74
- package/dist/state/gps-ar-pose-sampler.d.ts +3 -3
- package/dist/state/gps-event-coordinator.d.ts +2 -2
- package/dist/state/gps-event-coordinator.js +27 -17
- package/dist/state/index.d.ts +20 -20
- package/dist/state/index.js +4 -4
- package/dist/state/persistence-middleware.d.ts +1 -1
- package/dist/state/persistence-middleware.js +30 -19
- package/dist/state/qr-detected-slice.d.ts +3 -3
- package/dist/state/qr-detected-slice.js +1 -1
- package/dist/state/recording-options.d.ts +2 -2
- package/dist/state/recording-options.js +286 -25
- 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 +2 -1
- package/dist/state/subscribe-to-selector.d.ts +1 -1
- package/dist/state/tracking-quality.d.ts +2 -2
- package/dist/state/tracking-quality.js +49 -128
- package/dist/state/tracking-slice.d.ts +1 -1
- package/dist/{state-IEw0ZsBu.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 +25 -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 +1 -1
- package/dist/{storage-backend-CTJHNdMp.d.ts → storage-backend-sEtmM4yW.d.ts} +1 -1
- package/dist/{store-subscribers-BZP81hjh.d.ts → store-subscribers-DScA7tdT.d.ts} +2 -2
- package/dist/{subscribe-to-selector-Bp3sLx8L.d.ts → subscribe-to-selector-B9ylM5Oh.d.ts} +1 -1
- package/dist/test-utils/browser-mocks.d.ts +1 -1
- package/dist/test-utils/zip-round-trip-helpers.d.ts +1 -1
- package/dist/{tracking-slice-DJZ9qeGg.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 +8 -7
- package/dist/utils/index.js +2 -1
- 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-CJF9Nijp.js → webxr-session-BCP2XK7k.js} +110 -14
- package/dist/{webxr-session-COsOjJwn.d.ts → webxr-session-cRG2SYAQ.d.ts} +83 -10
- package/package.json +4 -4
- package/dist/index-eaI1Z1KR.d.ts +0 -2
- package/dist/lerp-utils-kLxMJ2Xj.d.ts +0 -20
- package/dist/occupancy-grid-Bvi2Gw1U.d.ts +0 -82
- package/dist/recording-options-D93kksJU.d.ts +0 -369
- /package/dist/{alignment-lerper-BUTsQ2yu.d.ts → alignment-lerper-gD70k7Fq.d.ts} +0 -0
- /package/dist/{ar-types-BckI1E-O.d.ts → ar-types-Cc5yvBwn.d.ts} +0 -0
- /package/dist/{bresenham3d-ByjcmEgM.d.ts → bresenham3d-DGc4nIb-.d.ts} +0 -0
- /package/dist/{camera-blit-capture-CqWe1G7s.d.ts → camera-blit-capture-BimSdyih.d.ts} +0 -0
- /package/dist/{camera-follower-BTb2Yp12.d.ts → camera-follower-DfBm2J5i.d.ts} +0 -0
- /package/dist/{capability-checker-CXqoJYtb.d.ts → capability-checker-BTTpygvZ.d.ts} +0 -0
- /package/dist/{capture-failure-tracker-BL5SoG2a.d.ts → capture-failure-tracker-_BVfhpxv.d.ts} +0 -0
- /package/dist/{chromium-camera-access-workaround-BNK189P0.d.ts → chromium-camera-access-workaround-C6yJLXz9.d.ts} +0 -0
- /package/dist/{concurrency-DqAlKX4j.d.ts → concurrency-DPeGBH0X.d.ts} +0 -0
- /package/dist/{css3d-renderer-manager-m-ab19vp.d.ts → css3d-renderer-manager-CPpVtvgs.d.ts} +0 -0
- /package/dist/{failure-tracker-Cnc_xWUw.d.ts → failure-tracker-CeKf5HIc.d.ts} +0 -0
- /package/dist/{file-system-utils-Bs7lpeVP.d.ts → file-system-utils-DueXHxo9.d.ts} +0 -0
- /package/dist/{format-file-size-BFUWvir1.d.ts → format-file-size-DxN-EBLc.d.ts} +0 -0
- /package/dist/{frame-loop-BEO9_q3k.d.ts → frame-loop-7GBMBu68.d.ts} +0 -0
- /package/dist/{frustum-visibility-D_RGmH98.d.ts → frustum-visibility-CokkgS_i.d.ts} +0 -0
- /package/dist/{geo-types-DqiBzPO9.d.ts → geo-types-DrGVu8nS.d.ts} +0 -0
- /package/dist/{gps-C5inWeNV.d.ts → gps-CDKDQokE.d.ts} +0 -0
- /package/dist/{gps-compass-cubes-BExxrWJ-.d.ts → gps-compass-cubes-1mooS3BS.d.ts} +0 -0
- /package/dist/{gps-error-handler-C2bS69oJ.d.ts → gps-error-handler-C34GQuTu.d.ts} +0 -0
- /package/dist/{gps-event-markers-Dd0G2bD0.d.ts → gps-event-markers-Di2nBzx-.d.ts} +0 -0
- /package/dist/{h3-proximity-B57C6rYL.d.ts → h3-proximity-D0GTHQmr.d.ts} +0 -0
- /package/dist/{hit-test-reticle-Dqak9hiR.d.ts → hit-test-reticle-CaQbrhbk.d.ts} +0 -0
- /package/dist/{list-formatter-ct8txwvR.d.ts → list-formatter-DLFDnS70.d.ts} +0 -0
- /package/dist/{logger-CzQrCe6C.d.ts → logger-CqTd5Kpk.d.ts} +0 -0
- /package/dist/{map-overlay-Dci4yvm3.d.ts → map-overlay-BCQgvIgU.d.ts} +0 -0
- /package/dist/{permission-checker-CtPtPQRq.d.ts → permission-checker-B-VtjnBA.d.ts} +0 -0
- /package/dist/{qr-pose-Bt2reSw7.d.ts → qr-pose-y3vaEd6x.d.ts} +0 -0
- /package/dist/{scene-node-names-CdNeQjOE.d.ts → scene-node-names-CGb-ylBd.d.ts} +0 -0
- /package/dist/{session-disposers-D8wnO-uW.js → session-disposers-BPrMplIC.js} +0 -0
- /package/dist/{three-dispose-BCZkSJp2.d.ts → three-dispose-BMOAmPhb.d.ts} +0 -0
- /package/dist/{vis-colors-BZsSLI4i.d.ts → vis-colors-C7HI0u9_.d.ts} +0 -0
- /package/dist/{webxr-nue-basis-CW1wsbm6.d.ts → webxr-nue-basis-CaCnYxuq.d.ts} +0 -0
- /package/dist/{xr-camera-texture-BmrP7iNZ.d.ts → xr-camera-texture-q0eIDSRg.d.ts} +0 -0
- /package/dist/{xr-error-handler-BEjRgZjt.d.ts → xr-error-handler-uVGDu9IX.d.ts} +0 -0
- /package/dist/{xr-frame-loop-K8ebdjLw.d.ts → xr-frame-loop-DX3JPpAU.d.ts} +0 -0
- /package/dist/{zip-coverage-embed-tFohI2J4.d.ts → zip-coverage-embed-ncVbaZAz.d.ts} +0 -0
- /package/dist/{zip-export-DDWXInTR.d.ts → zip-export-CtiUnAlJ.d.ts} +0 -0
- /package/dist/{zip-reader-Q2ue6zlC.d.ts → zip-reader-CvruaGvD.d.ts} +0 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { trackingReducer } from "./state/tracking-slice.js";
|
|
2
|
+
import { recordWriteFailure, recordingReducer } from "./state/recording-slice.js";
|
|
3
|
+
import { createTrackingQualityListenerMiddleware, trackingQualityReducer } from "./state/tracking-quality.js";
|
|
4
|
+
import { createPersistenceMiddleware, slicePrefixOf } from "./state/persistence-middleware.js";
|
|
5
|
+
import { arElementsReducer, gpsDataReducer, gpsElementsReducer, sanitizeForDevTools, setColdStartOverrideEnabled, setCompassRotationPriorEnabled, setCompassWebXRConsistencyEnabled, setZeroPos, validateLicenseKey } from "gps-plus-slam-js";
|
|
6
|
+
import { configureStore, createListenerMiddleware } from "@reduxjs/toolkit";
|
|
7
|
+
import { COMMUNITY_LICENSE_KEY } from "gps-plus-slam-js/community-license-key";
|
|
8
|
+
//#region ../src/state/slam-app-store-listener.ts
|
|
9
|
+
/**
|
|
10
|
+
* Build a listener middleware that applies the requested compass opt-ins once
|
|
11
|
+
* `gpsData` exists and a flag is still unset. Register it via `.prepend(...)`
|
|
12
|
+
* (so its effect dispatches *outside* the trigger's `next()` — see module doc).
|
|
13
|
+
*
|
|
14
|
+
* Behaviour:
|
|
15
|
+
* - **Predicate** fires whenever the `gpsData` *object reference* is new since
|
|
16
|
+
* the last apply AND at least one opt-in is still unset. Two terms, each load-
|
|
17
|
+
* bearing:
|
|
18
|
+
* - `s.gpsData !== lastApplied` makes the predicate edge-triggered on gpsData
|
|
19
|
+
* identity. A recreated `gpsData` (store swap / origin reset) is a fresh
|
|
20
|
+
* object, so it still re-triggers the apply — the re-apply semantics the
|
|
21
|
+
* 2026-06-27 field bug demands are preserved. Do NOT weaken this to a
|
|
22
|
+
* `null → non-null` transition: that would drop the re-apply. The point of
|
|
23
|
+
* keying on the *reference* (not on "a flag is unset") is that it also stops
|
|
24
|
+
* the effect from re-firing for the SAME gpsData.
|
|
25
|
+
* - `optIns.some((o) => !o.isSet(s))` keeps the no-op case (all flags already
|
|
26
|
+
* set) from scheduling an effect at all.
|
|
27
|
+
* Why the reference guard matters: without it the predicate is purely level-
|
|
28
|
+
* based, so if an opt-in's `apply` dispatches but `isSet` never flips true
|
|
29
|
+
* (e.g. consumer/library **version skew** where the action type no longer
|
|
30
|
+
* matches the reducer — the packages are published independently), the
|
|
31
|
+
* condition stays true forever and every effect dispatch re-arms it: an
|
|
32
|
+
* unbounded storm that freezes the app. The per-dispatch `isSet` re-check
|
|
33
|
+
* below guards only against dispatching an *already-set* flag twice; it does
|
|
34
|
+
* nothing when the flag never sets. The reference guard is what bounds it.
|
|
35
|
+
* - **Effect** records the gpsData reference it is acting on, then dispatches
|
|
36
|
+
* every still-unset opt-in as a top-level action. `isSet` is re-read against
|
|
37
|
+
* the *current* store state immediately before each dispatch (not one snapshot
|
|
38
|
+
* at effect entry): a dispatch is synchronous, so a flag is already set by the
|
|
39
|
+
* time the next check runs, and an opt-in's own dispatch can re-enter this
|
|
40
|
+
* effect before the loop finishes. Re-checking per dispatch makes that
|
|
41
|
+
* re-entrancy idempotent — a flag is dispatched only while still unset.
|
|
42
|
+
*/
|
|
43
|
+
function createSlamAppStoreListenerMiddleware(optIns) {
|
|
44
|
+
const listenerMiddleware = createListenerMiddleware();
|
|
45
|
+
let lastApplied = null;
|
|
46
|
+
listenerMiddleware.startListening({
|
|
47
|
+
predicate: (_action, currentState) => {
|
|
48
|
+
const s = currentState;
|
|
49
|
+
return s.gpsData !== null && s.gpsData !== lastApplied && optIns.some((optIn) => !optIn.isSet(s));
|
|
50
|
+
},
|
|
51
|
+
effect: (_action, api) => {
|
|
52
|
+
const entry = api.getState();
|
|
53
|
+
if (entry.gpsData === null) return;
|
|
54
|
+
lastApplied = entry.gpsData;
|
|
55
|
+
for (const optIn of optIns) {
|
|
56
|
+
const s = api.getState();
|
|
57
|
+
if (s.gpsData === null) return;
|
|
58
|
+
if (!optIn.isSet(s)) optIn.apply((action) => {
|
|
59
|
+
api.dispatch(action);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
return listenerMiddleware.middleware;
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region ../src/state/create-slam-app-store.ts
|
|
68
|
+
/**
|
|
69
|
+
* `createSlamAppStore` — composable Redux store factory for AR+GPS apps.
|
|
70
|
+
*
|
|
71
|
+
* Introduced in Iter 1 of the AppFramework/RecorderApp boundary migration.
|
|
72
|
+
* Wires the three library reducers (`gpsData`, `gpsElements`, `arElements`),
|
|
73
|
+
* the framework's recording lifecycle slice, and the persistence middleware.
|
|
74
|
+
*
|
|
75
|
+
* Recorder-only state (routing screen, ref-points, scenario) is plugged in
|
|
76
|
+
* by the consumer via `extraReducers` / `extraMiddleware`. The factory itself
|
|
77
|
+
* never references those concepts so apps that don't need them (POI viewers,
|
|
78
|
+
* navigation arrows, …) compose freely.
|
|
79
|
+
*
|
|
80
|
+
* The legacy `createRecorderStore` from `store.ts` is built on top of this
|
|
81
|
+
* factory and will move out of the framework in Iter 1D.
|
|
82
|
+
*
|
|
83
|
+
* @see docs/2026-05-03-appframework-vs-recorderapp-boundary-analysis.md — Iter 1
|
|
84
|
+
*/
|
|
85
|
+
/**
|
|
86
|
+
* Slice prefixes the framework always persists, derived from the actual
|
|
87
|
+
* library / framework action creators (never hand-typed). A rename of the
|
|
88
|
+
* `gpsData` or `recording` slice therefore propagates here automatically
|
|
89
|
+
* instead of silently dropping that slice's actions from recordings.
|
|
90
|
+
*/
|
|
91
|
+
const BUILTIN_PERSISTED_PREFIXES = [slicePrefixOf(setZeroPos.type), slicePrefixOf(recordWriteFailure.type)];
|
|
92
|
+
/**
|
|
93
|
+
* Build a Redux store wired with library + recording slices, persistence
|
|
94
|
+
* middleware, and any caller-supplied extras. See module docstring for the
|
|
95
|
+
* design rationale.
|
|
96
|
+
*/
|
|
97
|
+
function createSlamAppStore(options) {
|
|
98
|
+
const { storageBackend, extraReducers, extraMiddleware, persistedExtraPrefixes, onWriteFailure, enableDevChecks = true, licenseKey = COMMUNITY_LICENSE_KEY, trackingQualityOptions, enableCompassColdStartOverride = true, enableCompassRotationPrior = false, enableCompassWebXRConsistency = false } = options;
|
|
99
|
+
validateLicenseKey(licenseKey);
|
|
100
|
+
const builtins = {
|
|
101
|
+
gpsData: gpsDataReducer,
|
|
102
|
+
gpsElements: gpsElementsReducer,
|
|
103
|
+
arElements: arElementsReducer,
|
|
104
|
+
recording: recordingReducer,
|
|
105
|
+
tracking: trackingReducer,
|
|
106
|
+
trackingQuality: trackingQualityReducer
|
|
107
|
+
};
|
|
108
|
+
if (extraReducers) {
|
|
109
|
+
const collisions = Object.keys(extraReducers).filter((key) => Object.prototype.hasOwnProperty.call(builtins, key));
|
|
110
|
+
if (collisions.length > 0) throw new Error(`extraReducers must not overwrite framework-reserved slice(s): ${collisions.join(", ")}. Reserved keys: ${Object.keys(builtins).join(", ")}.`);
|
|
111
|
+
}
|
|
112
|
+
const reducer = {
|
|
113
|
+
...builtins,
|
|
114
|
+
...extraReducers ?? {}
|
|
115
|
+
};
|
|
116
|
+
const trackingQualityMiddleware = createTrackingQualityListenerMiddleware(trackingQualityOptions);
|
|
117
|
+
const compassOptIns = [];
|
|
118
|
+
if (enableCompassColdStartOverride) compassOptIns.push({
|
|
119
|
+
isSet: (s) => s.gpsData?.coldStartOverrideEnabled === true,
|
|
120
|
+
apply: (dispatch) => dispatch(setColdStartOverrideEnabled(true))
|
|
121
|
+
});
|
|
122
|
+
if (enableCompassRotationPrior) compassOptIns.push({
|
|
123
|
+
isSet: (s) => s.gpsData?.compassRotationPriorEnabled === true,
|
|
124
|
+
apply: (dispatch) => dispatch(setCompassRotationPriorEnabled(true))
|
|
125
|
+
});
|
|
126
|
+
if (enableCompassWebXRConsistency) compassOptIns.push({
|
|
127
|
+
isSet: (s) => s.gpsData?.compassWebXRConsistencyEnabled === true,
|
|
128
|
+
apply: (dispatch) => dispatch(setCompassWebXRConsistencyEnabled(true))
|
|
129
|
+
});
|
|
130
|
+
const prependedListeners = [trackingQualityMiddleware];
|
|
131
|
+
if (compassOptIns.length > 0) prependedListeners.push(createSlamAppStoreListenerMiddleware(compassOptIns));
|
|
132
|
+
const store = configureStore({
|
|
133
|
+
reducer,
|
|
134
|
+
middleware: (getDefaultMiddleware) => getDefaultMiddleware({
|
|
135
|
+
serializableCheck: enableDevChecks,
|
|
136
|
+
immutableCheck: enableDevChecks
|
|
137
|
+
}).prepend(...prependedListeners).concat(createPersistenceMiddleware({
|
|
138
|
+
storageBackend,
|
|
139
|
+
onWriteFailure,
|
|
140
|
+
persistedPrefixes: [...BUILTIN_PERSISTED_PREFIXES, ...persistedExtraPrefixes ?? []]
|
|
141
|
+
}), ...extraMiddleware ?? []),
|
|
142
|
+
devTools: {
|
|
143
|
+
actionSanitizer: sanitizeForDevTools,
|
|
144
|
+
stateSanitizer: sanitizeForDevTools
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
return {
|
|
148
|
+
getState: () => store.getState(),
|
|
149
|
+
dispatch: store.dispatch,
|
|
150
|
+
subscribe: (listener) => store.subscribe(listener),
|
|
151
|
+
writeFrame: (blob, index) => storageBackend.writeFrame(blob, index),
|
|
152
|
+
writeSessionMetadata: (metadata) => storageBackend.writeSessionMetadata(metadata)
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
//#endregion
|
|
156
|
+
export { createSlamAppStore as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as RgbTuple, i as DepthSample, t as ARPose } from "./ar-types-
|
|
1
|
+
import { a as RgbTuple, i as DepthSample, t as ARPose } from "./ar-types-Cc5yvBwn.js";
|
|
2
2
|
import { Matrix4 } from "gps-plus-slam-js";
|
|
3
3
|
|
|
4
4
|
//#region ../src/ar/depth-rgb-lookup.d.ts
|
|
@@ -66,6 +66,27 @@ interface DepthInfo {
|
|
|
66
66
|
getDepthInMeters: (x: number, y: number) => number;
|
|
67
67
|
/** Column-major projection matrix of the capturing XRView, if known. */
|
|
68
68
|
projectionMatrix?: Matrix4;
|
|
69
|
+
/**
|
|
70
|
+
* Raw depth buffer (`XRCPUDepthInformation.data`) — a **live reference**
|
|
71
|
+
* valid only within the originating XR frame callback (NOT copied, unlike
|
|
72
|
+
* the matrices: the per-frame buffer is too large to clone and the live
|
|
73
|
+
* depth occluder uploads it synchronously). Absent when the source carries
|
|
74
|
+
* no `data` (e.g. the sparse-only path). Plumbed for the live depth occluder
|
|
75
|
+
* (2026-06-14-webxr-depth-occlusion-plan.md §2); the sparse sampler ignores it.
|
|
76
|
+
*/
|
|
77
|
+
data?: ArrayBuffer;
|
|
78
|
+
/**
|
|
79
|
+
* `XRCPUDepthInformation.rawValueToMeters` — multiply a raw depth sample by
|
|
80
|
+
* this to get metres. Preserved only when a finite number. Occluder-only.
|
|
81
|
+
*/
|
|
82
|
+
rawValueToMeters?: number;
|
|
83
|
+
/**
|
|
84
|
+
* The `.matrix` (column-major 16-tuple) of
|
|
85
|
+
* `XRDepthInformation.normDepthBufferFromNormView` — the screen-UV → depth-UV
|
|
86
|
+
* transform the occluder shader needs. Copied + validated like
|
|
87
|
+
* `projectionMatrix` (the UA may reuse the backing array). Occluder-only.
|
|
88
|
+
*/
|
|
89
|
+
normDepthBufferFromNormView?: Matrix4;
|
|
69
90
|
}
|
|
70
91
|
/**
|
|
71
92
|
* Wrap a raw browser XRDepthInformation object into a {@link DepthInfo}.
|
|
@@ -76,11 +97,22 @@ interface DepthInfo {
|
|
|
76
97
|
* the UA may reuse across frames) is defensively validated and copied into
|
|
77
98
|
* a plain serializable 16-tuple; invalid input (wrong length, non-finite
|
|
78
99
|
* entries) yields a DepthInfo without a matrix rather than an error.
|
|
100
|
+
* - The live-occluder metadata (`data`, `rawValueToMeters`,
|
|
101
|
+
* `normDepthBufferFromNormView`) is preserved when present and valid: `data`
|
|
102
|
+
* by live reference (no clone — see {@link DepthInfo.data}), the scale only
|
|
103
|
+
* when finite, and the UV transform's `.matrix` copied like
|
|
104
|
+
* `projectionMatrix`. The sparse grid sampler ignores all three; a source
|
|
105
|
+
* lacking them (e.g. the existing test doubles) wraps exactly as before.
|
|
79
106
|
*/
|
|
80
107
|
declare function wrapXRDepthInfo(raw: {
|
|
81
108
|
width: number;
|
|
82
109
|
height: number;
|
|
83
110
|
getDepthInMeters: (x: number, y: number) => number;
|
|
111
|
+
data?: ArrayBuffer;
|
|
112
|
+
rawValueToMeters?: number;
|
|
113
|
+
normDepthBufferFromNormView?: {
|
|
114
|
+
matrix?: ArrayLike<number>;
|
|
115
|
+
} | null;
|
|
84
116
|
}, projectionMatrix: ArrayLike<number> | undefined): DepthInfo;
|
|
85
117
|
/**
|
|
86
118
|
* Samples sparse depth points from WebXR depth sensing.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { t as ArCrashIsolationOptions } from "./recording-options-
|
|
2
|
-
import { n as RawDeviceOrientation, t as GpsPosition } from "./gps-
|
|
3
|
-
import { n as PermissionStatus } from "./permission-checker-
|
|
4
|
-
import {
|
|
1
|
+
import { t as ArCrashIsolationOptions } from "./recording-options-vASCFzc5.js";
|
|
2
|
+
import { n as RawDeviceOrientation, t as GpsPosition } from "./gps-CDKDQokE.js";
|
|
3
|
+
import { n as PermissionStatus } from "./permission-checker-B-VtjnBA.js";
|
|
4
|
+
import { s as SessionFeatureOptions } from "./webxr-session-cRG2SYAQ.js";
|
|
5
5
|
|
|
6
6
|
//#region ../src/ar/enable-gps-ar.d.ts
|
|
7
7
|
/**
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as findNearbyGeoAnchor, c as h3CellsMatch, i as clusterCellsByZoom, l as isH3Index, n as KnownGeoAnchor, o as gpsPathToCoverageCells, r as approxDistanceMetres, s as gpsToH3, t as H3_RESOLUTION } from "../h3-proximity-
|
|
1
|
+
import { a as findNearbyGeoAnchor, c as h3CellsMatch, i as clusterCellsByZoom, l as isH3Index, n as KnownGeoAnchor, o as gpsPathToCoverageCells, r as approxDistanceMetres, s as gpsToH3, t as H3_RESOLUTION } from "../h3-proximity-D0GTHQmr.js";
|
|
2
2
|
export { H3_RESOLUTION, KnownGeoAnchor, approxDistanceMetres, clusterCellsByZoom, findNearbyGeoAnchor, gpsPathToCoverageCells, gpsToH3, h3CellsMatch, isH3Index };
|
package/dist/geo/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as findNearbyGeoAnchor, c as h3CellsMatch, i as clusterCellsByZoom, l as isH3Index, n as KnownGeoAnchor, o as gpsPathToCoverageCells, r as approxDistanceMetres, s as gpsToH3, t as H3_RESOLUTION } from "../h3-proximity-
|
|
1
|
+
import { a as findNearbyGeoAnchor, c as h3CellsMatch, i as clusterCellsByZoom, l as isH3Index, n as KnownGeoAnchor, o as gpsPathToCoverageCells, r as approxDistanceMetres, s as gpsToH3, t as H3_RESOLUTION } from "../h3-proximity-D0GTHQmr.js";
|
|
2
2
|
export { H3_RESOLUTION, type KnownGeoAnchor, approxDistanceMetres, clusterCellsByZoom, findNearbyGeoAnchor, gpsPathToCoverageCells, gpsToH3, h3CellsMatch, isH3Index };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { quat } from "gl-matrix";
|
|
2
|
+
//#region ../src/utils/geodesic-angle.ts
|
|
3
|
+
/**
|
|
4
|
+
* Geodesic-angle kernel — the single shared definition of the shortest-arc
|
|
5
|
+
* rotation angle between two unit quaternions.
|
|
6
|
+
*
|
|
7
|
+
* This used to be hand-rolled in (at least) two places:
|
|
8
|
+
* - `geodesicAngleRad` (private) in `ar/qr-pose-aggregation.ts`, and
|
|
9
|
+
* - `quat.getAngle(...)` + a NaN guard inside `state/tracking-quality.ts`'s
|
|
10
|
+
* `matrixDelta`.
|
|
11
|
+
*
|
|
12
|
+
* Both compute the same number — `acos(2·dot² − 1)` — so they are consolidated
|
|
13
|
+
* here so a third consumer (the capture motion gate's angular-velocity helper,
|
|
14
|
+
* `ar/pose-motion.ts`) reuses one definition instead of adding a fourth copy.
|
|
15
|
+
* See `GpsPlusSlamJs_Docs/docs/2026-06-23-followup-consolidate-geodesic-angle-kernel.md`.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Geodesic (shortest-arc) rotation angle in radians between two **unit**
|
|
19
|
+
* quaternions.
|
|
20
|
+
*
|
|
21
|
+
* Uses `cos θ = 2·dot² − 1`, which is invariant to the sign of either input
|
|
22
|
+
* (`q` ≡ `−q`, the quaternion double cover) because it depends only on `dot²`.
|
|
23
|
+
* The argument is clamped to `[-1, 1]` before `acos` so float error on a
|
|
24
|
+
* near-identical pair (where `2·dot² − 1` can drift slightly above 1) returns
|
|
25
|
+
* `0` rather than `NaN` — this is the guard `matrixDelta` previously applied
|
|
26
|
+
* explicitly around the raw `quat.getAngle`.
|
|
27
|
+
*
|
|
28
|
+
* Precondition: both inputs are (approximately) unit length. Callers that hold
|
|
29
|
+
* un-normalized quaternions must normalize first (gl-matrix `quat.normalize`).
|
|
30
|
+
*
|
|
31
|
+
* @returns angle in radians, in `[0, π]`. Never `NaN` for finite unit inputs.
|
|
32
|
+
*/
|
|
33
|
+
function geodesicAngleRad(a, b) {
|
|
34
|
+
const d = quat.dot(a, b);
|
|
35
|
+
const c = Math.min(1, Math.max(-1, 2 * d * d - 1));
|
|
36
|
+
return Math.acos(c);
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
export { geodesicAngleRad as t };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { t as ARPose } from "./ar-types-
|
|
2
|
-
import { n as RawDeviceOrientation, t as GpsPosition } from "./gps-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { t as ARPose } from "./ar-types-Cc5yvBwn.js";
|
|
2
|
+
import { n as RawDeviceOrientation, t as GpsPosition } from "./gps-CDKDQokE.js";
|
|
3
|
+
import { t as AbsoluteOrientationReading } from "./absolute-orientation-BAxc15J6.js";
|
|
4
|
+
import { i as SlamAppStore } from "./create-slam-app-store-D3nzh9Wb.js";
|
|
5
|
+
import { Quaternion, RawAbsoluteOrientation, RawGpsPoint, RecordGpsEventPayload, Vector3, eulerToQuaternion } from "gps-plus-slam-js";
|
|
5
6
|
import { ReducersMapObject } from "@reduxjs/toolkit";
|
|
6
7
|
|
|
7
8
|
//#region ../src/state/gps-event-coordinator.d.ts
|
|
@@ -44,28 +45,37 @@ declare function extractOdomRotation(arPose: ARPose): Quaternion;
|
|
|
44
45
|
/**
|
|
45
46
|
* Build a RawGpsPoint from GPS position data.
|
|
46
47
|
* Returns only raw sensor fields — no derived fields (coordinates, weight,
|
|
47
|
-
* zeroRef
|
|
48
|
-
*
|
|
48
|
+
* zeroRef). Derived fields are computed by the library reducer when the
|
|
49
|
+
* action is dispatched (raw-storage pattern).
|
|
49
50
|
*
|
|
50
51
|
* @param gpsPosition - GPS position from Geolocation API
|
|
51
|
-
* @param deviceOrientation -
|
|
52
|
+
* @param deviceOrientation - Accepted for signature stability; no longer
|
|
53
|
+
* read (the legacy `compassAbsolute` field it fed is no longer recorded)
|
|
52
54
|
* @returns RawGpsPoint ready for the action payload
|
|
53
55
|
*/
|
|
54
56
|
declare function buildRawGpsPoint(gpsPosition: GpsPosition, deviceOrientation: RawDeviceOrientation | null): RawGpsPoint;
|
|
57
|
+
/**
|
|
58
|
+
* Map a capture-module {@link AbsoluteOrientationReading} to the library's
|
|
59
|
+
* {@link RawAbsoluteOrientation} payload shape (the only difference is the
|
|
60
|
+
* timestamp field name: capture `timestamp` → payload `sampleTimestamp`).
|
|
61
|
+
* Returns `undefined` when the sensor produced no reading (most non-Chrome).
|
|
62
|
+
*/
|
|
63
|
+
declare function toRawAbsoluteOrientation(reading: AbsoluteOrientationReading | null): RawAbsoluteOrientation | undefined;
|
|
55
64
|
/**
|
|
56
65
|
* Build a RecordGpsEventPayload from GPS position and AR pose.
|
|
57
66
|
* This is a pure function for testability.
|
|
58
67
|
*
|
|
59
68
|
* Stores only raw sensor data in the payload. The library reducer
|
|
60
|
-
* computes derived fields (coordinates, weight
|
|
61
|
-
*
|
|
69
|
+
* computes derived fields (coordinates, weight) when the action is
|
|
70
|
+
* processed (raw-storage pattern).
|
|
62
71
|
*
|
|
63
72
|
* @param gpsPosition - GPS position from Geolocation API
|
|
64
73
|
* @param arPose - AR pose from WebXR
|
|
65
74
|
* @param deviceOrientation - Optional device orientation from sensors
|
|
75
|
+
* @param absoluteOrientation - Optional AbsoluteOrientationSensor reading
|
|
66
76
|
* @returns RecordGpsEventPayload ready for dispatch
|
|
67
77
|
*/
|
|
68
|
-
declare function buildRecordGpsEventPayload(gpsPosition: GpsPosition, arPose: ARPose, deviceOrientation: RawDeviceOrientation | null): RecordGpsEventPayload;
|
|
78
|
+
declare function buildRecordGpsEventPayload(gpsPosition: GpsPosition, arPose: ARPose, deviceOrientation: RawDeviceOrientation | null, absoluteOrientation?: AbsoluteOrientationReading | null): RecordGpsEventPayload;
|
|
69
79
|
/**
|
|
70
80
|
* Create a GPS position handler that dispatches combined GPS+AR events.
|
|
71
81
|
*
|
|
@@ -79,4 +89,4 @@ declare function buildRecordGpsEventPayload(gpsPosition: GpsPosition, arPose: AR
|
|
|
79
89
|
*/
|
|
80
90
|
declare function createGpsPositionHandler(config: RecordingCoordinatorConfig): (position: GpsPosition) => void;
|
|
81
91
|
//#endregion
|
|
82
|
-
export { eulerToQuaternion as a, getLastDeviceOrientation as c, createGpsPositionHandler as i, resetCoordinatorState as l, buildRawGpsPoint as n, extractOdomPosition as o, buildRecordGpsEventPayload as r, extractOdomRotation as s, RecordingCoordinatorConfig as t,
|
|
92
|
+
export { eulerToQuaternion as a, getLastDeviceOrientation as c, updateDeviceOrientation as d, createGpsPositionHandler as i, resetCoordinatorState as l, buildRawGpsPoint as n, extractOdomPosition as o, buildRecordGpsEventPayload as r, extractOdomRotation as s, RecordingCoordinatorConfig as t, toRawAbsoluteOrientation as u };
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { o as WebXRQuaternion, s as WebXRVec3, t as ARPose } from "./ar-types-
|
|
1
|
+
import { o as WebXRQuaternion, s as WebXRVec3, t as ARPose } from "./ar-types-Cc5yvBwn.js";
|
|
2
|
+
import { t as MotionFilterConfig } from "./capture-motion-gate-BGoAMkmf.js";
|
|
3
|
+
import { a as QualityFilterConfig } from "./image-quality-BMx2ko9-.js";
|
|
2
4
|
|
|
3
5
|
//#region ../src/ar/image-capture.d.ts
|
|
4
6
|
/**
|
|
@@ -26,6 +28,17 @@ interface ImageCaptureConfig {
|
|
|
26
28
|
* into this config so the whole user options section can flow through the
|
|
27
29
|
* capture seam as one object (see the field-drop audit, F3). */
|
|
28
30
|
resolutionDivisor: number;
|
|
31
|
+
/** Motion gate: skip motion-blurred frames by deferring a due capture until
|
|
32
|
+
* device motion settles (or `maxWaitMs` elapses). Mirrored in the persisted
|
|
33
|
+
* `ImageCaptureOptions.motionFilter` and flowed through the same capture
|
|
34
|
+
* seam as `resolutionDivisor`. See `capture-motion-gate.ts`. */
|
|
35
|
+
motionFilter: MotionFilterConfig;
|
|
36
|
+
/** Image-quality gate: drop a blurry/black frame (judged off-thread by the
|
|
37
|
+
* injected `analyzeFrame` callback) and retry the next acceptable frame,
|
|
38
|
+
* bounded by `maxWaitMs`. The increment ON TOP of the motion gate — only ever
|
|
39
|
+
* runs on frames that already passed motion gating. Mirrored in the persisted
|
|
40
|
+
* `ImageCaptureOptions.qualityFilter`. See `image-quality.ts`. */
|
|
41
|
+
qualityFilter: QualityFilterConfig;
|
|
29
42
|
}
|
|
30
43
|
/**
|
|
31
44
|
* Default capture configuration
|
|
@@ -79,6 +92,18 @@ interface CapturedFrame {
|
|
|
79
92
|
readonly width: number;
|
|
80
93
|
readonly height: number;
|
|
81
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Verdict returned by the injected off-thread {@link ImageCaptureCallbacks.analyzeFrame}
|
|
97
|
+
* analyzer. The manager only needs `accept`; `reason` is carried for logging/
|
|
98
|
+
* tuning. Structurally compatible with `image-quality.ts`'s `QualityVerdict`, so
|
|
99
|
+
* the recorder worker can return that type directly.
|
|
100
|
+
*/
|
|
101
|
+
interface FrameQualityVerdict {
|
|
102
|
+
/** `true` to save the frame, `false` to drop it and retry. */
|
|
103
|
+
readonly accept: boolean;
|
|
104
|
+
/** Optional debug reason (e.g. `'black'` | `'blurry'`); for logging only. */
|
|
105
|
+
readonly reason?: string | null;
|
|
106
|
+
}
|
|
82
107
|
/**
|
|
83
108
|
* Callbacks for image capture integration
|
|
84
109
|
*/
|
|
@@ -110,6 +135,22 @@ interface ImageCaptureCallbacks {
|
|
|
110
135
|
* @see docs/2026-02-06-bug-camera-frames-black.md
|
|
111
136
|
*/
|
|
112
137
|
captureFrame?: (quality: number) => Promise<CapturedFrame | null>;
|
|
138
|
+
/**
|
|
139
|
+
* Optional off-thread image-quality analyzer (blur/blackness gate). When
|
|
140
|
+
* provided AND `config.qualityFilter.enabled`, a freshly-encoded blob is NOT
|
|
141
|
+
* saved immediately: it is handed to this async callback (a Web Worker
|
|
142
|
+
* round-trip in the recorder) and saved only if the verdict accepts. A reject
|
|
143
|
+
* drops the blob and re-arms capture so the next acceptable (motion-calm)
|
|
144
|
+
* frame fills the slot, bounded by `qualityFilter.maxWaitMs` (after which the
|
|
145
|
+
* next frame is saved regardless, so an interval is never silently lost).
|
|
146
|
+
*
|
|
147
|
+
* The manager itself NEVER touches pixels — the off-main-thread guarantee. The
|
|
148
|
+
* callback MUST settle its promise; if it rejects, or does not settle within
|
|
149
|
+
* `captureTimeoutMs`, the manager fails open (saves the frame) so a hung
|
|
150
|
+
* analyzer cannot deadlock the pipeline. See `image-quality.ts` and
|
|
151
|
+
* `GpsPlusSlamJs_Docs/docs/2026-06-24-image-quality-gate-plan.md`.
|
|
152
|
+
*/
|
|
153
|
+
analyzeFrame?: (frame: CapturedFrame) => Promise<FrameQualityVerdict>;
|
|
113
154
|
}
|
|
114
155
|
/**
|
|
115
156
|
* Manages periodic image capture from a canvas.
|
|
@@ -129,6 +170,30 @@ declare class ImageCaptureManager {
|
|
|
129
170
|
private frameCount;
|
|
130
171
|
private captureInProgress;
|
|
131
172
|
private captureTimeoutId;
|
|
173
|
+
/** True while an `analyzeFrame` verdict is in flight; blocks a second capture
|
|
174
|
+
* for the same interval without throttling normal cadence (captureInProgress
|
|
175
|
+
* is released after encode, NOT held across the round-trip). */
|
|
176
|
+
private awaitingVerdict;
|
|
177
|
+
/** The current interval's capture was rejected by the quality gate; bypass the
|
|
178
|
+
* interval guard so the next acceptable frame is grabbed immediately. */
|
|
179
|
+
private retryPending;
|
|
180
|
+
/** Frame time (ms) of the FIRST quality attempt for the current interval; the
|
|
181
|
+
* quality `maxWaitMs` fallback measures from here, independent of the motion
|
|
182
|
+
* gate's own due clock. `null` between intervals. */
|
|
183
|
+
private qualityDeadlineBase;
|
|
184
|
+
/** Safety timeout for a hung `analyzeFrame` (fail-open). */
|
|
185
|
+
private verdictTimeoutId;
|
|
186
|
+
/** Sliding window of recent per-frame velocities for the motion gate. */
|
|
187
|
+
private readonly motionWindow;
|
|
188
|
+
/** Previous frame's pose, for the per-frame velocity delta. */
|
|
189
|
+
private prevPose;
|
|
190
|
+
/** Previous frame's timestamp (ms), for the per-frame velocity delta. */
|
|
191
|
+
private prevTime;
|
|
192
|
+
/** Whether THIS frame's velocity sample was rejected as a tracking glitch. */
|
|
193
|
+
private lastSampleWasGlitch;
|
|
194
|
+
/** When the current capture first became due (ms); null while not due. The
|
|
195
|
+
* `maxWaitMs` fallback measures from here, not from `lastCaptureTime`. */
|
|
196
|
+
private dueTime;
|
|
132
197
|
constructor(canvas: HTMLCanvasElement, callbacks: ImageCaptureCallbacks, config?: ImageCaptureConfig);
|
|
133
198
|
/**
|
|
134
199
|
* Start capturing images.
|
|
@@ -154,16 +219,51 @@ declare class ImageCaptureManager {
|
|
|
154
219
|
* @param time - Frame timestamp in milliseconds (from requestAnimationFrame)
|
|
155
220
|
*/
|
|
156
221
|
onFrame(time: number): void;
|
|
222
|
+
/**
|
|
223
|
+
* Sample per-frame device motion into the sliding window. Called every frame
|
|
224
|
+
* (even during an in-flight capture or before the interval elapses) so the
|
|
225
|
+
* window reflects truly instantaneous motion the moment a capture becomes
|
|
226
|
+
* due. A frame with no pose, or a non-positive dt (duplicate timestamp),
|
|
227
|
+
* records no sample and is treated as "not a glitch".
|
|
228
|
+
*/
|
|
229
|
+
private updateMotionWindow;
|
|
230
|
+
/**
|
|
231
|
+
* Apply the motion gate to a due capture. Returns `true` to capture now,
|
|
232
|
+
* `false` to defer to a later frame.
|
|
233
|
+
*
|
|
234
|
+
* - Filter disabled → always `true` (legacy behavior).
|
|
235
|
+
* - `maxWaitMs` elapsed → always `true` (never-calm safety fallback; an
|
|
236
|
+
* interval is never silently lost).
|
|
237
|
+
* - Current frame's sample was a tracking glitch → `false` (don't grab a
|
|
238
|
+
* relocalization-teleport frame; the fallback still guarantees progress).
|
|
239
|
+
* - No measurable motion yet (first capture / all-glitch) → `true` (the very
|
|
240
|
+
* first capture is never blocked; only measurable motion defers).
|
|
241
|
+
* - Otherwise → defer to {@link decideCapture} over the windowed maxima.
|
|
242
|
+
*/
|
|
243
|
+
private shouldCaptureNow;
|
|
157
244
|
/**
|
|
158
245
|
* Clear the safety timeout for captureInProgress.
|
|
159
246
|
* Called when capture completes (success or failure) before the timeout fires.
|
|
160
247
|
*/
|
|
161
248
|
private clearCaptureTimeout;
|
|
249
|
+
/** Clear the safety timeout for a hung `analyzeFrame` verdict. */
|
|
250
|
+
private clearVerdictTimeout;
|
|
162
251
|
/**
|
|
163
252
|
* Common handler for captured blobs (from either canvas.toBlob or captureFrame).
|
|
164
|
-
*
|
|
253
|
+
* Releases `captureInProgress` after the synchronous encode, then either saves
|
|
254
|
+
* immediately (legacy / gate-off) or runs the off-thread image-quality gate.
|
|
255
|
+
*
|
|
256
|
+
* @param time - the XR frame time the capture was dispatched at; the quality
|
|
257
|
+
* retry-deadline clock and `lastCaptureTime` are measured from it.
|
|
165
258
|
*/
|
|
166
259
|
private handleCapturedBlob;
|
|
260
|
+
/**
|
|
261
|
+
* Persist an accepted frame: allocate its 1-based index, run the suspicious-
|
|
262
|
+
* size belt-and-suspenders check, reset the per-interval gate clocks, and fire
|
|
263
|
+
* `onCaptured`. Called from the legacy path, the quality fallbacks, and an
|
|
264
|
+
* accepting verdict — the single place a frame becomes durable.
|
|
265
|
+
*/
|
|
266
|
+
private saveCapture;
|
|
167
267
|
}
|
|
168
268
|
//#endregion
|
|
169
|
-
export {
|
|
269
|
+
export { ImageCaptureCallbacks as a, MIN_VALID_IMAGE_BYTES as c, FrameQualityVerdict as i, CapturedImage as n, ImageCaptureConfig as o, DEFAULT_CAPTURE_CONFIG as r, ImageCaptureManager as s, CapturedFrame as t };
|