gps-plus-slam-app-framework 1.3.0 → 1.7.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-CAEh840t.d.ts +54 -0
- package/dist/{app-selectors-B8VQoMDY.d.ts → app-selectors-ikTUQZpe.d.ts} +1 -1
- package/dist/ar/bresenham3d.d.ts +1 -1
- package/dist/ar/camera-blit-capture.d.ts +2 -2
- package/dist/ar/camera-blit-capture.js +74 -9
- 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 +2 -0
- package/dist/ar/depth-grid-lookup.js +43 -0
- package/dist/ar/depth-sampler.d.ts +2 -2
- package/dist/ar/depth-sampler.js +2 -4
- package/dist/ar/depth-unprojection.d.ts +1 -1
- package/dist/ar/enable-gps-ar.d.ts +1 -1
- package/dist/ar/enable-gps-ar.js +26 -1
- package/dist/ar/frame-loop.d.ts +1 -1
- 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 +168 -0
- package/dist/ar/index.d.ts +31 -19
- package/dist/ar/index.js +17 -5
- package/dist/ar/occupancy-grid.d.ts +1 -1
- package/dist/ar/occupancy-grid.js +29 -2
- package/dist/ar/planar-pnp.d.ts +2 -0
- package/dist/ar/planar-pnp.js +425 -0
- package/dist/ar/qr-debug-view.d.ts +2 -0
- package/dist/ar/qr-debug-view.js +89 -0
- package/dist/ar/qr-derived-pose.d.ts +2 -0
- package/dist/ar/qr-derived-pose.js +114 -0
- package/dist/ar/qr-detection-controller.d.ts +2 -0
- package/dist/ar/qr-detection-controller.js +2 -0
- package/dist/ar/qr-frontend.d.ts +2 -0
- package/dist/ar/qr-frontend.js +60 -0
- package/dist/ar/qr-pose.d.ts +2 -0
- package/dist/ar/qr-pose.js +267 -0
- package/dist/ar/qr-size-depth-context.d.ts +3 -0
- package/dist/ar/qr-size-depth-context.js +42 -0
- package/dist/ar/qr-size-measurer.d.ts +2 -0
- package/dist/ar/qr-size-measurer.js +2 -0
- 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-MFpFlV7j.js +382 -0
- package/dist/{ar-world-group-alignment-C9lwwIyI.d.ts → ar-world-group-alignment-69fgnB39.d.ts} +1 -1
- package/dist/{camera-blit-capture-CnxUDbKx.d.ts → camera-blit-capture-NKT9H5-e.d.ts} +57 -4
- package/dist/capture-motion-gate-0Yl2NpQG.d.ts +24 -0
- package/dist/capture-motion-gate-4EscNZCw.js +94 -0
- package/dist/concurrency-By0oOTZt.d.ts +50 -0
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +2 -2
- package/dist/create-slam-app-store-DkCHOpvb.js +149 -0
- package/dist/{create-slam-app-store-BljusOua.d.ts → create-slam-app-store-n_VF63D5.d.ts} +50 -40
- package/dist/depth-grid-lookup-DQ-IYhO3.d.ts +21 -0
- package/dist/{depth-sampler-Bhanfwla.d.ts → depth-sampler-asc8exHt.d.ts} +1 -1
- package/dist/{depth-unprojection-Y6i_mJuT.d.ts → depth-unprojection-DjsbJW9L.d.ts} +1 -1
- package/dist/{enable-gps-ar-BfUJOgpW.d.ts → enable-gps-ar-DkNhFv4Y.d.ts} +4 -4
- package/dist/{frame-conversions-BfsafGWR.d.ts → frame-conversions-DObhj8Wf.d.ts} +1 -1
- package/dist/{fused-path-DJoVu98b.d.ts → fused-path-v6eOmLl3.d.ts} +1 -1
- package/dist/geo/h3-proximity.d.ts +2 -2
- package/dist/geo/h3-proximity.js +61 -2
- package/dist/geo/index.d.ts +2 -2
- package/dist/geo/index.js +2 -2
- package/dist/geodesic-angle-BTldfFgs.js +39 -0
- package/dist/{gps-anchor-dYeCvvp5.d.ts → gps-anchor-BeFO2RYa.d.ts} +1 -1
- package/dist/{gps-ar-pose-sampler-DVb96iOy.d.ts → gps-ar-pose-sampler-DHMZmYAc.d.ts} +1 -1
- package/dist/{gps-event-coordinator-CWIDOk4U.d.ts → gps-event-coordinator-CjWW636x.d.ts} +21 -11
- package/dist/{gps-event-markers-Dc9FT-q1.d.ts → gps-event-markers-Cz9RMA2k.d.ts} +18 -0
- package/dist/{h3-proximity-I1msGPgE.d.ts → h3-proximity-C-j0pw1j.d.ts} +39 -1
- package/dist/image-capture-B2-b6tKK.js +374 -0
- package/dist/{image-capture-qAZ5Fh5X.d.ts → image-capture-CNZDrQXG.d.ts} +103 -3
- package/dist/image-quality-smCBoFj1.d.ts +158 -0
- package/dist/index-BAPokxMb.d.ts +22 -0
- package/dist/{index-BYYUdf9v.d.ts → index-BODDUTvD.d.ts} +1 -1
- package/dist/index-BneSj4_5.d.ts +399 -0
- package/dist/index-_Rhgoqkh.d.ts +2 -0
- package/dist/index.d.ts +80 -67
- package/dist/index.js +24 -12
- package/dist/{leaflet-map-overlay-6CJC-E1l.d.ts → leaflet-map-overlay-CmIyiE8n.d.ts} +1 -1
- package/dist/{map-data-CkloBxIl.d.ts → map-data-B4oEehUh.d.ts} +1 -1
- package/dist/{map-overlay-draw-krOKwDAq.d.ts → map-overlay-draw-ymz1s7fG.d.ts} +1 -1
- package/dist/{null-storage-backend-uKQE8tl7.d.ts → null-storage-backend-B9rtyDfG.d.ts} +2 -2
- package/dist/{occupancy-grid-BRNRaPyT.d.ts → occupancy-grid-DnOc-6PU.d.ts} +12 -2
- package/dist/{opfs-storage-D5zAn_lx.d.ts → opfs-storage-3VZh-qH6.d.ts} +23 -4
- package/dist/{opfs-storage-backend-6aYMUZlJ.d.ts → opfs-storage-backend-JJiUJo-3.d.ts} +3 -3
- package/dist/{persistence-middleware-B9ei4Nhx.d.ts → persistence-middleware-DAPTTwB2.d.ts} +1 -1
- package/dist/planar-pnp-my9bqULS.d.ts +67 -0
- package/dist/qr-debug-view-Brl73HGO.d.ts +29 -0
- package/dist/qr-derived-pose-CmxTP2m4.d.ts +122 -0
- package/dist/qr-detected-slice-VQssZmQJ.d.ts +166 -0
- package/dist/qr-detection-controller-B-tLVd-S.js +119 -0
- package/dist/qr-detection-controller-CDurHv8E.d.ts +48 -0
- package/dist/qr-frontend--fFX25mV.d.ts +58 -0
- package/dist/qr-pose-D2rfu5DA.d.ts +138 -0
- package/dist/qr-pose-aggregation-B7tWJq7j.d.ts +90 -0
- package/dist/qr-pose-aggregation-DBiaLW0O.js +191 -0
- package/dist/qr-size-depth-context-CeAG53fy.d.ts +13 -0
- package/dist/qr-size-measurer-DLb33geH.js +596 -0
- package/dist/qr-size-measurer-DQdAsid1.d.ts +165 -0
- package/dist/recording-options-BPCpMHLK.d.ts +514 -0
- package/dist/{recording-replayer-r0DZ9ALp.d.ts → recording-replayer-DQKpAGyQ.d.ts} +2 -2
- package/dist/recording-slice-DaxHZvNu.d.ts +55 -0
- package/dist/{replay-engine-XpDa0dy1.d.ts → replay-engine-CmKh_4wf.d.ts} +7 -4
- package/dist/{replay-scene-Bw58VL6p.d.ts → replay-scene-DBlDrV_x.d.ts} +3 -3
- package/dist/sensors/absolute-orientation.d.ts +2 -0
- package/dist/sensors/absolute-orientation.js +145 -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/sensors/permission-checker.js +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 -17
- package/dist/state/index.js +5 -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 +4 -0
- package/dist/state/qr-detected-slice.js +311 -0
- package/dist/state/recording-options.d.ts +2 -2
- package/dist/state/recording-options.js +273 -7
- 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/recording-slice.js +5 -1
- package/dist/state/replay-engine.d.ts +1 -1
- package/dist/state/replay-engine.js +7 -3
- package/dist/state/store-subscribers.d.ts +2 -2
- 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 +40 -128
- package/dist/state/tracking-slice.d.ts +1 -1
- package/dist/{state-BkIfpiyA.js → state-E51QT_-j.js} +2 -1
- package/dist/storage/file-system-utils.d.ts +1 -1
- package/dist/storage/index.d.ts +9 -9
- package/dist/storage/index.js +4 -4
- 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-backend.js +3 -3
- package/dist/storage/opfs-storage.d.ts +1 -1
- package/dist/storage/opfs-storage.js +5 -4
- package/dist/storage/storage-backend.d.ts +1 -1
- package/dist/storage/zip-coverage-embed.d.ts +2 -0
- package/dist/storage/zip-coverage-embed.js +88 -0
- package/dist/storage/zip-export.d.ts +2 -2
- package/dist/storage/zip-export.js +37 -59
- package/dist/storage/zip-reader.d.ts +1 -1
- package/dist/{storage-backend-Do6YDpNo.d.ts → storage-backend-BdVLD0dU.d.ts} +1 -1
- package/dist/{store-subscribers-0YWyOLvV.d.ts → store-subscribers-CA6AJH_8.d.ts} +2 -2
- package/dist/{subscribe-to-selector-CZ6k1NFP.d.ts → subscribe-to-selector-B6EUmQ3I.d.ts} +1 -1
- package/dist/test-utils/browser-mocks.d.ts +7 -1
- package/dist/test-utils/browser-mocks.js +6 -0
- package/dist/test-utils/zip-round-trip-helpers.d.ts +9 -1
- package/dist/test-utils/zip-round-trip-helpers.js +5 -1
- package/dist/{tracking-slice-CdK8c-8P.d.ts → tracking-slice-BuAZxXZ5.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 +2 -2
- package/dist/utils/concurrency.js +66 -2
- 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/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 +40 -5
- package/dist/visualization/hit-test-reticle.d.ts +1 -1
- package/dist/visualization/hit-test-reticle.js +1 -0
- package/dist/visualization/index.d.ts +18 -18
- package/dist/visualization/leaflet-map-overlay.d.ts +1 -1
- package/dist/visualization/leaflet-map-overlay.js +15 -3
- package/dist/visualization/lerp-utils.d.ts +1 -1
- package/dist/visualization/map-data.d.ts +1 -1
- package/dist/visualization/map-overlay-draw.d.ts +1 -1
- package/dist/visualization/map-overlay.d.ts +1 -1
- package/dist/visualization/three-dispose.d.ts +1 -1
- package/dist/visualization/vis-colors.d.ts +1 -1
- package/dist/{webxr-session-TIaD883f.js → webxr-session-DG-eNfny.js} +244 -11
- package/dist/{webxr-session-GjZ2BIpS.d.ts → webxr-session-Do5L6G3B.d.ts} +86 -8
- package/dist/zip-coverage-embed-DCAREVmw.d.ts +41 -0
- package/dist/{zip-export-G14hkSPG.d.ts → zip-export-ol0S_Ofd.d.ts} +27 -23
- package/package.json +2 -2
- package/dist/concurrency-CO1FQvs3.d.ts +0 -24
- package/dist/file-system-CwXTNHmB.d.ts +0 -114
- package/dist/index-CF05QV2m.d.ts +0 -2
- package/dist/recording-options-DJBXuMxV.d.ts +0 -182
- package/dist/recording-slice-89O07ebs.d.ts +0 -36
- package/dist/storage/file-system.d.ts +0 -2
- package/dist/storage/file-system.js +0 -289
- /package/dist/{accuracy-circles-BpwWAs8G.d.ts → accuracy-circles-BtqOP_v1.d.ts} +0 -0
- /package/dist/{alignment-lerper-Dm9G7JJO.d.ts → alignment-lerper-Bil6vbVw.d.ts} +0 -0
- /package/dist/{ar-types-Dan1MVTi.d.ts → ar-types-BFX2r1wk.d.ts} +0 -0
- /package/dist/{bresenham3d-CFfd7n6B.d.ts → bresenham3d-CU1Gv2W4.d.ts} +0 -0
- /package/dist/{camera-follower-C2mwEn7c.d.ts → camera-follower-CqJJ7zRj.d.ts} +0 -0
- /package/dist/{capability-checker-CQXJHxXg.d.ts → capability-checker-DUQLg6gq.d.ts} +0 -0
- /package/dist/{capture-failure-tracker-CUeU3L_M.d.ts → capture-failure-tracker-gsm8q7yi.d.ts} +0 -0
- /package/dist/{chromium-camera-access-workaround-Jqbn0apH.d.ts → chromium-camera-access-workaround-D4K2uwwQ.d.ts} +0 -0
- /package/dist/{css3d-renderer-manager-ptu_t3B3.d.ts → css3d-renderer-manager-B3kGNtcn.d.ts} +0 -0
- /package/dist/{failure-tracker-Cn8Fo9D4.d.ts → failure-tracker-BO7M7jF_.d.ts} +0 -0
- /package/dist/{file-system-utils-CXIoL-DM.d.ts → file-system-utils-DOajnMm-.d.ts} +0 -0
- /package/dist/{format-file-size-CLCZz3lC.d.ts → format-file-size-Dc98ulzB.d.ts} +0 -0
- /package/dist/{frame-loop-CMewS8cY.d.ts → frame-loop-CpVjQ1dH.d.ts} +0 -0
- /package/dist/{frustum-visibility-CmI96ESS.d.ts → frustum-visibility-CeoCZchO.d.ts} +0 -0
- /package/dist/{geo-types-SaPnIEUM.d.ts → geo-types-3Ip1NzVR.d.ts} +0 -0
- /package/dist/{gps-VuezJWK3.d.ts → gps-BBO3hKfT.d.ts} +0 -0
- /package/dist/{gps-compass-cubes-BM1uGiYV.d.ts → gps-compass-cubes-DSN87L8p.d.ts} +0 -0
- /package/dist/{gps-error-handler-CJl_2MVm.d.ts → gps-error-handler-BbPsfw-_.d.ts} +0 -0
- /package/dist/{hit-test-reticle-BEzEybxZ.d.ts → hit-test-reticle-DV-2TpB9.d.ts} +0 -0
- /package/dist/{lerp-utils-BpV-0Dtk.d.ts → lerp-utils-BLmHhkVe.d.ts} +0 -0
- /package/dist/{list-formatter-RRcBe8Vg.d.ts → list-formatter-KNMS0ay2.d.ts} +0 -0
- /package/dist/{logger-BEHXsnnq.d.ts → logger-CLnPrHiD.d.ts} +0 -0
- /package/dist/{map-overlay-bcogY5fK.d.ts → map-overlay-Dh2B97MP.d.ts} +0 -0
- /package/dist/{permission-checker-GpAiSCwd.d.ts → permission-checker-CDuI8BwC.d.ts} +0 -0
- /package/dist/{scene-node-names-Bu0S66H1.d.ts → scene-node-names-BiShOQDB.d.ts} +0 -0
- /package/dist/{session-disposers-M-oashRH.js → session-disposers-zv9OGXPB.js} +0 -0
- /package/dist/{three-dispose-D950t71B.d.ts → three-dispose-Bt1k7WKh.d.ts} +0 -0
- /package/dist/{vis-colors-D6omC64K.d.ts → vis-colors-Obw2pLqT.d.ts} +0 -0
- /package/dist/{webxr-nue-basis-CqKQmZzT.d.ts → webxr-nue-basis-3nfjDJJp.d.ts} +0 -0
- /package/dist/{xr-camera-texture-DBwCaKTd.d.ts → xr-camera-texture-Cva1q3Qj.d.ts} +0 -0
- /package/dist/{xr-error-handler-C0AQRmxs.d.ts → xr-error-handler-CtvHpjfm.d.ts} +0 -0
- /package/dist/{xr-frame-loop-BtYytZJg.d.ts → xr-frame-loop-DIYtRcUa.d.ts} +0 -0
- /package/dist/{zip-reader-DSEXt7K6.d.ts → zip-reader-D9iPQwdQ.d.ts} +0 -0
|
@@ -0,0 +1,149 @@
|
|
|
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 reducer = {
|
|
101
|
+
gpsData: gpsDataReducer,
|
|
102
|
+
gpsElements: gpsElementsReducer,
|
|
103
|
+
arElements: arElementsReducer,
|
|
104
|
+
recording: recordingReducer,
|
|
105
|
+
tracking: trackingReducer,
|
|
106
|
+
trackingQuality: trackingQualityReducer,
|
|
107
|
+
...extraReducers ?? {}
|
|
108
|
+
};
|
|
109
|
+
const trackingQualityMiddleware = createTrackingQualityListenerMiddleware(trackingQualityOptions);
|
|
110
|
+
const compassOptIns = [];
|
|
111
|
+
if (enableCompassColdStartOverride) compassOptIns.push({
|
|
112
|
+
isSet: (s) => s.gpsData?.coldStartOverrideEnabled === true,
|
|
113
|
+
apply: (dispatch) => dispatch(setColdStartOverrideEnabled(true))
|
|
114
|
+
});
|
|
115
|
+
if (enableCompassRotationPrior) compassOptIns.push({
|
|
116
|
+
isSet: (s) => s.gpsData?.compassRotationPriorEnabled === true,
|
|
117
|
+
apply: (dispatch) => dispatch(setCompassRotationPriorEnabled(true))
|
|
118
|
+
});
|
|
119
|
+
if (enableCompassWebXRConsistency) compassOptIns.push({
|
|
120
|
+
isSet: (s) => s.gpsData?.compassWebXRConsistencyEnabled === true,
|
|
121
|
+
apply: (dispatch) => dispatch(setCompassWebXRConsistencyEnabled(true))
|
|
122
|
+
});
|
|
123
|
+
const prependedListeners = [trackingQualityMiddleware];
|
|
124
|
+
if (compassOptIns.length > 0) prependedListeners.push(createSlamAppStoreListenerMiddleware(compassOptIns));
|
|
125
|
+
const store = configureStore({
|
|
126
|
+
reducer,
|
|
127
|
+
middleware: (getDefaultMiddleware) => getDefaultMiddleware({
|
|
128
|
+
serializableCheck: enableDevChecks,
|
|
129
|
+
immutableCheck: enableDevChecks
|
|
130
|
+
}).prepend(...prependedListeners).concat(createPersistenceMiddleware({
|
|
131
|
+
storageBackend,
|
|
132
|
+
onWriteFailure,
|
|
133
|
+
persistedPrefixes: [...BUILTIN_PERSISTED_PREFIXES, ...persistedExtraPrefixes ?? []]
|
|
134
|
+
}), ...extraMiddleware ?? []),
|
|
135
|
+
devTools: {
|
|
136
|
+
actionSanitizer: sanitizeForDevTools,
|
|
137
|
+
stateSanitizer: sanitizeForDevTools
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
return {
|
|
141
|
+
getState: () => store.getState(),
|
|
142
|
+
dispatch: store.dispatch,
|
|
143
|
+
subscribe: (listener) => store.subscribe(listener),
|
|
144
|
+
writeFrame: (blob, index) => storageBackend.writeFrame(blob, index),
|
|
145
|
+
writeSessionMetadata: (metadata) => storageBackend.writeSessionMetadata(metadata)
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
//#endregion
|
|
149
|
+
export { createSlamAppStore as t };
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { n as
|
|
5
|
-
import { n as StorageBackend } from "./storage-backend-Do6YDpNo.js";
|
|
1
|
+
import { a as TrackingSliceState } from "./tracking-slice-BuAZxXZ5.js";
|
|
2
|
+
import { t as RecordingState } from "./recording-slice-DaxHZvNu.js";
|
|
3
|
+
import { n as SessionMetadata } from "./opfs-storage-3VZh-qH6.js";
|
|
4
|
+
import { n as StorageBackend } from "./storage-backend-BdVLD0dU.js";
|
|
6
5
|
import { GpsPoint, LatLong, Matrix4, RootState, Vector3 } from "gps-plus-slam-js";
|
|
7
6
|
import * as _$_reduxjs_toolkit0 from "@reduxjs/toolkit";
|
|
8
7
|
import { Middleware, Reducer, ReducersMapObject, configureStore } from "@reduxjs/toolkit";
|
|
@@ -18,8 +17,7 @@ interface TrackingQualityReport {
|
|
|
18
17
|
confidence: number;
|
|
19
18
|
subScores: {
|
|
20
19
|
convergence: number;
|
|
21
|
-
residualConsensus: number;
|
|
22
|
-
compassAgreement: number | null;
|
|
20
|
+
residualConsensus: number;
|
|
23
21
|
gpsAccuracy: number;
|
|
24
22
|
coverage: number;
|
|
25
23
|
};
|
|
@@ -37,9 +35,7 @@ interface TrackingQualityReport {
|
|
|
37
35
|
medianResidualM: number;
|
|
38
36
|
medianRecentGpsAccuracyM: number;
|
|
39
37
|
walkedDistanceM: number;
|
|
40
|
-
directionSpreadDeg: number;
|
|
41
|
-
headingDeltaDeg: number | null;
|
|
42
|
-
compassDriftDetected: boolean;
|
|
38
|
+
directionSpreadDeg: number;
|
|
43
39
|
observationsSeen: number; /** §4.6 — diagnostic only, never feeds the aggregate. */
|
|
44
40
|
gpsVsFusedMaxDivergenceM: number;
|
|
45
41
|
};
|
|
@@ -61,10 +57,6 @@ interface TrackingQualityOptions {
|
|
|
61
57
|
convergenceRotationWarnDeg?: number;
|
|
62
58
|
/** §4.1 ΣΔtranslation (m) at/below which convergence scores 1.0. Fail ramp ends at 4×. */
|
|
63
59
|
convergenceTranslationWarnM?: number;
|
|
64
|
-
/** §4.3 EMA threshold (deg) below which compass scores 1.0. */
|
|
65
|
-
compassWarnDeg?: number;
|
|
66
|
-
/** §4.3 EMA threshold (deg) above which compass scores 0.0. */
|
|
67
|
-
compassFailDeg?: number;
|
|
68
60
|
/** Minimum GPS observations before leaving `warming-up`. */
|
|
69
61
|
warmupMinObservations?: number;
|
|
70
62
|
/** Minimum coverage score before leaving `warming-up`. */
|
|
@@ -73,8 +65,6 @@ interface TrackingQualityOptions {
|
|
|
73
65
|
residualConfidenceTargetM?: number;
|
|
74
66
|
/** §4.4 GPS-accuracy floor used to normalise residuals (m). */
|
|
75
67
|
gpsAccuracyFloorM?: number;
|
|
76
|
-
/** §4.3 consecutive compass-agreeing observations for first agreement. */
|
|
77
|
-
firstAgreementMinStreak?: number;
|
|
78
68
|
/** §4.8 consecutive sub-threshold observations before ok → degraded. */
|
|
79
69
|
degradedHoldoff?: number;
|
|
80
70
|
/**
|
|
@@ -97,7 +87,6 @@ interface AlignmentSnapshot {
|
|
|
97
87
|
}
|
|
98
88
|
interface TrackingQualitySliceState {
|
|
99
89
|
recentAlignments: AlignmentSnapshot[];
|
|
100
|
-
firstAgreementObservationIndex: number | null;
|
|
101
90
|
report: TrackingQualityReport | null;
|
|
102
91
|
/** §4.8 — consecutive observations with raw confidence < degradedThreshold. */
|
|
103
92
|
degradedConsecutiveCount: number;
|
|
@@ -143,7 +132,8 @@ declare function computeConvergence(snapshots: readonly AlignmentSnapshot[], opt
|
|
|
143
132
|
* the column-major layout that gl-matrix and the framework use.
|
|
144
133
|
*
|
|
145
134
|
* Rotation: extract the orientation quaternion with `mat4.getRotation`
|
|
146
|
-
* and compute the relative angle via `
|
|
135
|
+
* and compute the relative angle via the shared `geodesicAngleRad` kernel
|
|
136
|
+
* (radians, converted to degrees here).
|
|
147
137
|
* Translation: extract the origin column with `mat4.getTranslation` and
|
|
148
138
|
* compute the Euclidean distance.
|
|
149
139
|
*
|
|
@@ -206,25 +196,6 @@ declare function computeCoverage(odometryPositions: readonly Vector3[], options?
|
|
|
206
196
|
walkedDistanceM?: number;
|
|
207
197
|
directionSpreadDeg?: number;
|
|
208
198
|
}): CoverageResult;
|
|
209
|
-
interface CompassAgreementResult {
|
|
210
|
-
/** `null` when `absolute !== true` on the sensor orientation. */
|
|
211
|
-
score: number | null;
|
|
212
|
-
headingDeltaDeg: number | null;
|
|
213
|
-
}
|
|
214
|
-
/**
|
|
215
|
-
* §4.3 — Compare the alignment matrix's mapping of AR-forward to ENU
|
|
216
|
-
* against the device compass heading. Returns `score = null` whenever
|
|
217
|
-
* the sensor's `absolute !== true` so the aggregate excludes the
|
|
218
|
-
* sub-score (per Q2 in the plan).
|
|
219
|
-
*
|
|
220
|
-
* AR-forward in our `ARPose` convention is `(0, 0, -1)` (right-handed,
|
|
221
|
-
* Y-up). We rotate that by the alignment's 3×3 rotation, take its
|
|
222
|
-
* North/East components, and compare against the compass alpha.
|
|
223
|
-
*/
|
|
224
|
-
declare function computeCompassAgreement(alignmentMatrix: Matrix4 | null, sensorOrientation: DeviceOrientation | null, arPose: ARPose | null, options?: {
|
|
225
|
-
warnDeg?: number;
|
|
226
|
-
failDeg?: number;
|
|
227
|
-
}): CompassAgreementResult;
|
|
228
199
|
/**
|
|
229
200
|
* §4.6 — Maximum lateral distance between raw GPS positions and the
|
|
230
201
|
* positions you would predict by transforming the matching odometry
|
|
@@ -234,14 +205,13 @@ declare function computeCompassAgreement(alignmentMatrix: Matrix4 | null, sensor
|
|
|
234
205
|
declare function computeGpsVsFusedDivergence(alignmentMatrix: Matrix4 | null, gpsPositions: readonly GpsPoint[], odometryPositions: readonly Vector3[], zeroRef: LatLong | null, windowSize?: number): number;
|
|
235
206
|
declare const snapshotPushed: _$_reduxjs_toolkit0.ActionCreatorWithPayload<AlignmentSnapshot, "trackingQuality/snapshotPushed">, snapshotsTrimmed: _$_reduxjs_toolkit0.ActionCreatorWithPayload<{
|
|
236
207
|
size: number;
|
|
237
|
-
}, "trackingQuality/snapshotsTrimmed">, reportUpdated: _$_reduxjs_toolkit0.ActionCreatorWithPayload<TrackingQualityReport | null, "trackingQuality/reportUpdated">,
|
|
208
|
+
}, "trackingQuality/snapshotsTrimmed">, reportUpdated: _$_reduxjs_toolkit0.ActionCreatorWithPayload<TrackingQualityReport | null, "trackingQuality/reportUpdated">, degradedCountUpdated: _$_reduxjs_toolkit0.ActionCreatorWithPayload<number, "trackingQuality/degradedCountUpdated">, resetTrackingQuality: _$_reduxjs_toolkit0.ActionCreatorWithoutPayload<"trackingQuality/resetTrackingQuality">;
|
|
238
209
|
declare const trackingQualityReducer: _$_reduxjs_toolkit0.Reducer<TrackingQualitySliceState>;
|
|
239
210
|
interface RootWithTrackingQuality {
|
|
240
211
|
trackingQuality?: TrackingQualitySliceState;
|
|
241
212
|
}
|
|
242
213
|
declare function selectTrackingQuality(state: RootWithTrackingQuality): TrackingQualityReport | null;
|
|
243
214
|
declare function selectRecentAlignments(state: RootWithTrackingQuality): readonly AlignmentSnapshot[];
|
|
244
|
-
declare function selectFirstAgreementObservationIndex(state: RootWithTrackingQuality): number | null;
|
|
245
215
|
/**
|
|
246
216
|
* Pure function — produces a {@link TrackingQualityReport} from a root
|
|
247
217
|
* state and options. The Investigation harness imports this directly to
|
|
@@ -339,6 +309,46 @@ interface SlamAppStoreOptions<ExtraReducers extends ReducersMapObject = Record<s
|
|
|
339
309
|
* @see docs/2026-05-16-tracking-quality-metrics-plan.md
|
|
340
310
|
*/
|
|
341
311
|
trackingQualityOptions?: Partial<TrackingQualityOptions>;
|
|
312
|
+
/**
|
|
313
|
+
* Enable the library's Phase-4 **Stage-0** cold-start compass yaw override.
|
|
314
|
+
* **Default `true`** — Stage 0 is a field-validated, default-on feature: at
|
|
315
|
+
* cold start the GPS-derived yaw is unobservable (clustered fixes ⇒ a yaw set
|
|
316
|
+
* by noise that flips as the user looks around), so the compass heading gives
|
|
317
|
+
* a roughly-correct, stable orientation immediately ("open app, stand still,
|
|
318
|
+
* look around" works). It is an observability-gated handover — once a walked
|
|
319
|
+
* baseline conditions the GPS yaw, the solve hands back to GPS — so it does no
|
|
320
|
+
* harm once GPS is observable. Pass `false` to opt out (the recorder exposes
|
|
321
|
+
* this as a settings toggle).
|
|
322
|
+
*
|
|
323
|
+
* When enabled the factory dispatches `setColdStartOverrideEnabled(true)` the
|
|
324
|
+
* first time `gpsData` becomes non-null (right after the first `setZeroPos`,
|
|
325
|
+
* since the flag lives on that slice and cannot be set before it exists).
|
|
326
|
+
*
|
|
327
|
+
* Replay/determinism: the library's `DefaultAlignmentConfig` stays OFF, so
|
|
328
|
+
* historical recordings replay unchanged; default-on lives here as a recorded
|
|
329
|
+
* `gpsData` action. A recording made with this on therefore replays with the
|
|
330
|
+
* override on. **For Stage-A/§6a field-calibration recordings, turn this OFF**
|
|
331
|
+
* (recorder settings) so the captured compass behaviour is unmodified.
|
|
332
|
+
*
|
|
333
|
+
* @see GpsPlusSlamJs_Docs/docs/2026-06-26-stage0-field-collection-and-enablement.md
|
|
334
|
+
*/
|
|
335
|
+
enableCompassColdStartOverride?: boolean;
|
|
336
|
+
/**
|
|
337
|
+
* **Debug/experiment flag** — enable the library's Phase-4 **Stage-C**
|
|
338
|
+
* trust-gated compass rotation prior (keeps a steady compass vote once GPS yaw
|
|
339
|
+
* is observable + the compass is trusted; supersedes Stage 0). Dispatches
|
|
340
|
+
* `setCompassRotationPriorEnabled(true)` once `gpsData` exists. Default `false`
|
|
341
|
+
* ⇒ byte-identical. Like the Stage-0 flag, the action persists into recordings.
|
|
342
|
+
*/
|
|
343
|
+
enableCompassRotationPrior?: boolean;
|
|
344
|
+
/**
|
|
345
|
+
* **Debug/experiment flag** — enable the library's GPS-free compass↔WebXR
|
|
346
|
+
* consistency gate. When on, the compass override (Stage 0 / Stage C) abstains
|
|
347
|
+
* unless the compass is rotating in lock-step with the WebXR pose. Dispatches
|
|
348
|
+
* `setCompassWebXRConsistencyEnabled(true)` once `gpsData` exists. Default
|
|
349
|
+
* `false` ⇒ byte-identical. The action persists into recordings.
|
|
350
|
+
*/
|
|
351
|
+
enableCompassWebXRConsistency?: boolean;
|
|
342
352
|
}
|
|
343
353
|
/**
|
|
344
354
|
* Combined root state: the framework's base state plus any caller-supplied
|
|
@@ -368,4 +378,4 @@ interface SlamAppStore<ExtraReducers extends ReducersMapObject = Record<string,
|
|
|
368
378
|
*/
|
|
369
379
|
declare function createSlamAppStore<ExtraReducers extends ReducersMapObject = Record<string, never>>(options: SlamAppStoreOptions<ExtraReducers>): SlamAppStore<ExtraReducers>;
|
|
370
380
|
//#endregion
|
|
371
|
-
export {
|
|
381
|
+
export { snapshotPushed as A, createTrackingQualityListenerMiddleware as C, resetTrackingQuality as D, reportUpdated as E, trackingQualityReducer as M, CombinedRootState as N, selectRecentAlignments as O, computeTrackingQualityReport as S, matrixDelta as T, computeConvergence as _, SlamAppStoreOptions as a, computeGpsVsFusedDivergence as b, ConvergenceResult as c, GpsAccuracyResult as d, ResidualConsensusResult as f, TrackingQualityState as g, TrackingQualitySliceState as h, SlamAppStore as i, snapshotsTrimmed as j, selectTrackingQuality as k, CoverageResult as l, TrackingQualityReport as m, SlamAppMiddleware as n, createSlamAppStore as o, TrackingQualityOptions as p, SlamAppRootState as r, AlignmentSnapshot as s, SlamAppCombinedState as t, DEFAULT_TRACKING_QUALITY_OPTIONS as u, computeCoverage as v, degradedCountUpdated as w, computeResidualConsensus as x, computeGpsAccuracy as y };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { r as DepthPoint } from "./ar-types-BFX2r1wk.js";
|
|
2
|
+
|
|
3
|
+
//#region ../src/ar/depth-grid-lookup.d.ts
|
|
4
|
+
interface DepthGridLookup {
|
|
5
|
+
/**
|
|
6
|
+
* Bilinearly-interpolated depth (m) at a normalized screen point (top-left
|
|
7
|
+
* origin), or `null` when the point is outside the node grid or any of the
|
|
8
|
+
* four surrounding nodes has no valid (finite, positive) depth.
|
|
9
|
+
*/
|
|
10
|
+
depthAt(screenX: number, screenY: number): number | null;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Build a bilinear lookup over a depth-sampler grid.
|
|
14
|
+
*
|
|
15
|
+
* @param points - the grid `DepthPoint`s (row-major, `row·g + col`).
|
|
16
|
+
* @param gridSize - points-per-side; inferred as `round(√points.length)` when
|
|
17
|
+
* omitted (the sampler always emits a full `g²` grid).
|
|
18
|
+
*/
|
|
19
|
+
declare function createDepthGridLookup(points: readonly DepthPoint[], gridSize?: number): DepthGridLookup;
|
|
20
|
+
//#endregion
|
|
21
|
+
export { createDepthGridLookup as n, DepthGridLookup as t };
|
|
@@ -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-BPCpMHLK.js";
|
|
2
|
+
import { n as RawDeviceOrientation, t as GpsPosition } from "./gps-BBO3hKfT.js";
|
|
3
|
+
import { n as PermissionStatus } from "./permission-checker-CDuI8BwC.js";
|
|
4
|
+
import { r as SessionFeatureOptions } from "./webxr-session-Do5L6G3B.js";
|
|
5
5
|
|
|
6
6
|
//#region ../src/ar/enable-gps-ar.d.ts
|
|
7
7
|
/**
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { H3_RESOLUTION, KnownGeoAnchor, approxDistanceMetres, findNearbyGeoAnchor, gpsToH3, h3CellsMatch, isH3Index };
|
|
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-C-j0pw1j.js";
|
|
2
|
+
export { H3_RESOLUTION, KnownGeoAnchor, approxDistanceMetres, clusterCellsByZoom, findNearbyGeoAnchor, gpsPathToCoverageCells, gpsToH3, h3CellsMatch, isH3Index };
|
package/dist/geo/h3-proximity.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { gridDisk, latLngToCell } from "h3-js";
|
|
1
|
+
import { cellToParent, getResolution, gridDisk, isValidCell, latLngToCell } from "h3-js";
|
|
2
2
|
//#region ../src/geo/h3-proximity.ts
|
|
3
3
|
/**
|
|
4
4
|
* H3 Geo-Anchor Proximity Matching
|
|
@@ -22,6 +22,65 @@ function gpsToH3(lat, lng) {
|
|
|
22
22
|
return latLngToCell(lat, lng, 11);
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
+
* Reduce a recorded GPS path to the deduplicated set of res-11 H3 cells it
|
|
26
|
+
* crossed — the per-tour "coverage index" stored in `SessionMetadata.h3Cells`.
|
|
27
|
+
*
|
|
28
|
+
* Cells are returned in first-seen (chronological) order and deduplicated, so a
|
|
29
|
+
* tour that dwells in one ~25 m cell contributes a single cell. This keeps the
|
|
30
|
+
* stored index small (see the metadata-size risk in the map-browser feedback
|
|
31
|
+
* doc, §7). Non-finite coordinates are skipped defensively — bad sensor data
|
|
32
|
+
* must not poison the index or throw inside `latLngToCell`.
|
|
33
|
+
*/
|
|
34
|
+
function gpsPathToCoverageCells(path) {
|
|
35
|
+
const seen = /* @__PURE__ */ new Set();
|
|
36
|
+
const cells = [];
|
|
37
|
+
for (const p of path) {
|
|
38
|
+
if (!Number.isFinite(p.lat) || !Number.isFinite(p.lng)) continue;
|
|
39
|
+
const cell = gpsToH3(p.lat, p.lng);
|
|
40
|
+
if (!seen.has(cell)) {
|
|
41
|
+
seen.add(cell);
|
|
42
|
+
cells.push(cell);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return cells;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Coarsen res-11 coverage cells to a target resolution for map zoom-clustering.
|
|
49
|
+
*
|
|
50
|
+
* Uses h3-js `cellToParent` — the ONLY correct way to coarsen an H3 cell. NEVER
|
|
51
|
+
* truncate the hex-string id: resolution is encoded in the high bits with
|
|
52
|
+
* trailing `f` padding, so slicing yields INVALID cells, not parents (verified;
|
|
53
|
+
* see the D1 gotcha in
|
|
54
|
+
* docs/2026-06-14-map-centric-recording-browser-and-h3-index-user-feedback.md).
|
|
55
|
+
*
|
|
56
|
+
* `targetRes` is clamped to `[0, H3_RESOLUTION]` because `cellToParent` throws
|
|
57
|
+
* when asked for a FINER resolution than the cell, and res 11 is the finest
|
|
58
|
+
* data stored — so the highest map zooms render the stored cells unclustered
|
|
59
|
+
* (`cellToParent(cell, 11) === cell`). A non-finite `targetRes` (e.g. a bad
|
|
60
|
+
* zoom→res mapping) degrades to unclustered output rather than throwing.
|
|
61
|
+
* Invalid input cells (corrupt/legacy metadata) are skipped defensively, as are
|
|
62
|
+
* valid cells coarser than `res` (which have no parent at the finer target res
|
|
63
|
+
* and would otherwise throw "incompatible resolutions").
|
|
64
|
+
*
|
|
65
|
+
* Output is deduplicated in first-seen order: sibling cells under one parent
|
|
66
|
+
* collapse to a single tile.
|
|
67
|
+
*/
|
|
68
|
+
function clusterCellsByZoom(cells, targetRes) {
|
|
69
|
+
const res = Number.isFinite(targetRes) ? Math.max(0, Math.min(11, Math.floor(targetRes))) : 11;
|
|
70
|
+
const seen = /* @__PURE__ */ new Set();
|
|
71
|
+
const parents = [];
|
|
72
|
+
for (const cell of cells) {
|
|
73
|
+
if (!isValidCell(cell)) continue;
|
|
74
|
+
if (getResolution(cell) < res) continue;
|
|
75
|
+
const parent = cellToParent(cell, res);
|
|
76
|
+
if (!seen.has(parent)) {
|
|
77
|
+
seen.add(parent);
|
|
78
|
+
parents.push(parent);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return parents;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
25
84
|
* Approximate distance in metres using equirectangular projection.
|
|
26
85
|
* Accurate to < 0.1% for distances under 1 km at mid-latitudes,
|
|
27
86
|
* which is sufficient for ranking ref point candidates within a
|
|
@@ -75,4 +134,4 @@ function isH3Index(id) {
|
|
|
75
134
|
return H3_INDEX_PATTERN.test(id);
|
|
76
135
|
}
|
|
77
136
|
//#endregion
|
|
78
|
-
export { H3_RESOLUTION, approxDistanceMetres, findNearbyGeoAnchor, gpsToH3, h3CellsMatch, isH3Index };
|
|
137
|
+
export { H3_RESOLUTION, approxDistanceMetres, clusterCellsByZoom, findNearbyGeoAnchor, gpsPathToCoverageCells, gpsToH3, h3CellsMatch, isH3Index };
|
package/dist/geo/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { H3_RESOLUTION, type KnownGeoAnchor, approxDistanceMetres, findNearbyGeoAnchor, gpsToH3, h3CellsMatch, isH3Index };
|
|
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-C-j0pw1j.js";
|
|
2
|
+
export { H3_RESOLUTION, type KnownGeoAnchor, approxDistanceMetres, clusterCellsByZoom, findNearbyGeoAnchor, gpsPathToCoverageCells, gpsToH3, h3CellsMatch, isH3Index };
|
package/dist/geo/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { H3_RESOLUTION, approxDistanceMetres, findNearbyGeoAnchor, gpsToH3, h3CellsMatch, isH3Index } from "./h3-proximity.js";
|
|
2
|
-
export { H3_RESOLUTION, approxDistanceMetres, findNearbyGeoAnchor, gpsToH3, h3CellsMatch, isH3Index };
|
|
1
|
+
import { H3_RESOLUTION, approxDistanceMetres, clusterCellsByZoom, findNearbyGeoAnchor, gpsPathToCoverageCells, gpsToH3, h3CellsMatch, isH3Index } from "./h3-proximity.js";
|
|
2
|
+
export { H3_RESOLUTION, 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-BFX2r1wk.js";
|
|
2
|
+
import { n as RawDeviceOrientation, t as GpsPosition } from "./gps-BBO3hKfT.js";
|
|
3
|
+
import { t as AbsoluteOrientationReading } from "./absolute-orientation-CAEh840t.js";
|
|
4
|
+
import { i as SlamAppStore } from "./create-slam-app-store-n_VF63D5.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 };
|
|
@@ -24,6 +24,14 @@ declare class GpsEventVisualizer {
|
|
|
24
24
|
private zeroRef;
|
|
25
25
|
private eventCounter;
|
|
26
26
|
private snapshotCounter;
|
|
27
|
+
/**
|
|
28
|
+
* Whether the debug markers are drawn. Toggled by {@link setVisible} for the
|
|
29
|
+
* recorder's `visualization.gpsAlignmentMarkers` opt-out (Finding B). Applied
|
|
30
|
+
* to every marker at creation so events spawned later by the live
|
|
31
|
+
* store-subscriber inherit the current state instead of popping into view.
|
|
32
|
+
* Default `true` — the markers render exactly as before until opted out.
|
|
33
|
+
*/
|
|
34
|
+
private markersVisible;
|
|
27
35
|
/**
|
|
28
36
|
* Set the GPS zero reference (origin for coordinate conversion).
|
|
29
37
|
* Must be called before adding GPS events.
|
|
@@ -64,6 +72,16 @@ declare class GpsEventVisualizer {
|
|
|
64
72
|
* Create a sphere marker mesh with the given color and name.
|
|
65
73
|
*/
|
|
66
74
|
private createMarkerMesh;
|
|
75
|
+
/**
|
|
76
|
+
* Show or hide ALL debug markers — raw GPS (yellow), fused (cyan), and
|
|
77
|
+
* alignment-snapshot (red) — and remember the state so markers added later
|
|
78
|
+
* inherit it. Used by the recorder's `visualization.gpsAlignmentMarkers`
|
|
79
|
+
* opt-out, read once at Enter-AR (live only; replay keeps markers visible).
|
|
80
|
+
*
|
|
81
|
+
* This only changes rendering: capture, GPS-event recording, counts, and the
|
|
82
|
+
* snapshot positions read back at session end are all unaffected.
|
|
83
|
+
*/
|
|
84
|
+
setVisible(visible: boolean): void;
|
|
67
85
|
/**
|
|
68
86
|
* Clear all markers from the scene and reset state.
|
|
69
87
|
*/
|