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
|
@@ -77,27 +77,38 @@ function createPersistenceMiddleware(options) {
|
|
|
77
77
|
const excludedActionType = recordWriteFailure.type;
|
|
78
78
|
let actionIndex = 0;
|
|
79
79
|
const writeQueue = new WriteQueue();
|
|
80
|
+
let dispatchDepth = 0;
|
|
81
|
+
let hasWarnedReentrant = false;
|
|
80
82
|
const middleware = (store) => (next) => (action) => {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
log.error("Failed to persist action:", normalized);
|
|
96
|
-
store.dispatch(recordWriteFailure(normalized.message));
|
|
97
|
-
if (onWriteFailure) onWriteFailure(normalized);
|
|
83
|
+
dispatchDepth += 1;
|
|
84
|
+
try {
|
|
85
|
+
const typedAction = action;
|
|
86
|
+
const actionType = typeof typedAction.type === "string" ? typedAction.type : null;
|
|
87
|
+
const wasRecording = readIsRecording(store.getState());
|
|
88
|
+
const result = next(action);
|
|
89
|
+
if (!actionType) return result;
|
|
90
|
+
if (actionType === startSession.type) actionIndex = 0;
|
|
91
|
+
if (!isInPersistableSession(wasRecording, readIsRecording(store.getState()), actionType)) return result;
|
|
92
|
+
if (!(actionType !== excludedActionType && normalizedPrefixes.some((prefix) => actionType.startsWith(prefix)))) return result;
|
|
93
|
+
const index = ++actionIndex;
|
|
94
|
+
if (dispatchDepth > 1 && !hasWarnedReentrant) {
|
|
95
|
+
hasWarnedReentrant = true;
|
|
96
|
+
log.warn(`Persisted action "${actionType}" was dispatched re-entrantly (inside another dispatch's next()). Its recorded order can invert on replay — react to actions via a prepended listener middleware, not a synchronous store.subscribe dispatch. See 2026-06-28-subscriber-dispatch-persistence-ordering-review.md.`);
|
|
98
97
|
}
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
writeQueue.enqueue(async () => {
|
|
99
|
+
try {
|
|
100
|
+
await storageBackend.writeAction(typedAction, index);
|
|
101
|
+
} catch (err) {
|
|
102
|
+
const normalized = err instanceof Error ? err : new Error(String(err));
|
|
103
|
+
log.error("Failed to persist action:", normalized);
|
|
104
|
+
store.dispatch(recordWriteFailure(normalized.message));
|
|
105
|
+
if (onWriteFailure) onWriteFailure(normalized);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
return result;
|
|
109
|
+
} finally {
|
|
110
|
+
dispatchDepth -= 1;
|
|
111
|
+
}
|
|
101
112
|
};
|
|
102
113
|
return middleware;
|
|
103
114
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { d as QrSizeStatus, l as QrSizeEstimate } from "../qr-size-measurer-DQdAsid1.js";
|
|
2
|
+
import { a as QrPoseStability, o as QrPoseStabilityOptions, s as QrPoseStabilityStatus } from "../qr-pose-aggregation-B7tWJq7j.js";
|
|
3
|
+
import { C as setQrMaxHistory, S as selectStableQrPose, _ as selectQrPoseStability, a as RootWithQrDetected, b as selectResolvedQrSizeM, c as medianQrPosition, d as recordQrDetection, f as recordQrSizeEstimate, g as selectQrMarkers, h as selectQrMarker, i as QrMarkerState, l as pruneQrDetections, m as selectLatestQrDetection, n as QrDetectedState, o as clearAllQrMarkers, p as selectDerivedQrPlacement, r as QrDetectionEntry, s as clearQrMarker, t as DEFAULT_QR_MAX_HISTORY, u as qrDetectedReducer, v as selectQrRawObservations, x as selectSolvedQrPose, y as selectQrSize } from "../qr-detected-slice-VQssZmQJ.js";
|
|
4
|
+
export { DEFAULT_QR_MAX_HISTORY, QrDetectedState, QrDetectionEntry, QrMarkerState, QrPoseStability, QrPoseStabilityOptions, QrPoseStabilityStatus, QrSizeEstimate, QrSizeStatus, RootWithQrDetected, clearAllQrMarkers, clearQrMarker, medianQrPosition, pruneQrDetections, qrDetectedReducer, recordQrDetection, recordQrSizeEstimate, selectDerivedQrPlacement, selectLatestQrDetection, selectQrMarker, selectQrMarkers, selectQrPoseStability, selectQrRawObservations, selectQrSize, selectResolvedQrSizeM, selectSolvedQrPose, selectStableQrPose, setQrMaxHistory };
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { deriveQrPlacement, deriveSolvedQrPose } from "../ar/qr-derived-pose.js";
|
|
2
|
+
import { i as evaluateQrPoseStability } from "../qr-pose-aggregation-DBiaLW0O.js";
|
|
3
|
+
import { createSlice } from "@reduxjs/toolkit";
|
|
4
|
+
//#region ../src/state/qr-detected-slice.ts
|
|
5
|
+
/**
|
|
6
|
+
* `qrDetected` Redux slice — Note 3 of the QR-tracking follow-up plan
|
|
7
|
+
* (docs `2026-06-15-followup-qr-tracking-generalization-overlay-and-north.md`).
|
|
8
|
+
*
|
|
9
|
+
* A detection-agnostic, framework-level store of "what was detected, where, in
|
|
10
|
+
* 3D" that overlay / trigger / anchor consumers subscribe to INDEPENDENTLY of
|
|
11
|
+
* the GPS fusion. The QR-tracking controller dispatches into it on every lock
|
|
12
|
+
* (alongside, or instead of, the synthetic GPS vote — the vote is optional, the
|
|
13
|
+
* `qrDetected` emission is not). Both the QR-tracking demo and the Recorder
|
|
14
|
+
* consume the SAME slice — it is not duplicated per app and does NOT live in the
|
|
15
|
+
* closed-source fusion core (locked maintainer decision, 2026-06-15 interview).
|
|
16
|
+
*
|
|
17
|
+
* Shape (locked decision): a dictionary keyed by the decoded payload (text/URL),
|
|
18
|
+
* value = a per-marker BOUNDED RING BUFFER of detections + a size-lifecycle
|
|
19
|
+
* estimate. Identity is the payload itself (already unique, human-debuggable) —
|
|
20
|
+
* two physically-distinct markers sharing a payload merge by design, and a
|
|
21
|
+
* *moving* marker with one payload accumulates a motion path (also desired).
|
|
22
|
+
* The ring buffer is bounded by {@link QrDetectedState.maxHistory} so a naive
|
|
23
|
+
* consumer cannot leak; `pruneQrDetections` is the explicit custom-cleanup path.
|
|
24
|
+
*
|
|
25
|
+
* Detection-agnostic on purpose (Note 1): the same shape (world pose + label +
|
|
26
|
+
* confidence + timestamp) generalizes to future object detection (YOLO etc.),
|
|
27
|
+
* so the fields are deliberately not QR-specific beyond the payload key.
|
|
28
|
+
*
|
|
29
|
+
* This slice is OPT-IN: apps wire it via `createSlamAppStore({ extraReducers:
|
|
30
|
+
* { qrDetected: qrDetectedReducer } })`. It is not a built-in of the store
|
|
31
|
+
* factory, so framework consumers that never detect anything pay nothing.
|
|
32
|
+
*
|
|
33
|
+
* @see ../ar/qr-tracking-controller.ts — the producer (dispatches `recordQrDetection`).
|
|
34
|
+
* @see ../ar/qr-pose.ts — `Pose` / `QrPoseSolution` (the per-detection geometry).
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* Default ring-buffer cap per marker. Bounded so an overlay that never prunes
|
|
38
|
+
* still cannot leak; large enough to support a robust median over a sliding
|
|
39
|
+
* window and a short motion path. Override via {@link setQrMaxHistory}.
|
|
40
|
+
*/
|
|
41
|
+
const DEFAULT_QR_MAX_HISTORY = 32;
|
|
42
|
+
const initialSize = () => ({
|
|
43
|
+
status: "unknown",
|
|
44
|
+
estimateM: null,
|
|
45
|
+
sampleCount: 0,
|
|
46
|
+
spreadM: 0
|
|
47
|
+
});
|
|
48
|
+
const initialState = {
|
|
49
|
+
maxHistory: 32,
|
|
50
|
+
markers: {}
|
|
51
|
+
};
|
|
52
|
+
/** Trim a detection list to at most `cap`, dropping the oldest. */
|
|
53
|
+
function capDetections(detections, cap) {
|
|
54
|
+
if (cap <= 0) return [];
|
|
55
|
+
return detections.length > cap ? detections.slice(detections.length - cap) : detections.slice();
|
|
56
|
+
}
|
|
57
|
+
const qrDetectedSlice = createSlice({
|
|
58
|
+
name: "qrDetected",
|
|
59
|
+
initialState,
|
|
60
|
+
reducers: {
|
|
61
|
+
/**
|
|
62
|
+
* Append a detection to its marker's ring buffer (creating the marker on
|
|
63
|
+
* first sight), enforcing `maxHistory`. Returns a fresh state object rather
|
|
64
|
+
* than mutating the immer draft: the entry's `Pose` carries readonly
|
|
65
|
+
* `Vector3`/`Quaternion` tuples that immer's `WritableDraft` rejects (same
|
|
66
|
+
* reason `tracking-slice.originReset` returns new state).
|
|
67
|
+
*/
|
|
68
|
+
recordQrDetection(state, action) {
|
|
69
|
+
const entry = action.payload;
|
|
70
|
+
const existing = state.markers[entry.text];
|
|
71
|
+
const detections = capDetections(existing ? [...existing.detections, entry] : [entry], state.maxHistory);
|
|
72
|
+
const marker = {
|
|
73
|
+
text: entry.text,
|
|
74
|
+
detections,
|
|
75
|
+
size: existing?.size ?? initialSize()
|
|
76
|
+
};
|
|
77
|
+
return {
|
|
78
|
+
...state,
|
|
79
|
+
markers: {
|
|
80
|
+
...state.markers,
|
|
81
|
+
[entry.text]: marker
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
/**
|
|
86
|
+
* Set a marker's size estimate (the Note 3 size lifecycle). Creates the
|
|
87
|
+
* marker with an empty history if it has no detections yet (a size can be
|
|
88
|
+
* authored before any detection).
|
|
89
|
+
*/
|
|
90
|
+
recordQrSizeEstimate(state, action) {
|
|
91
|
+
const { text, estimate } = action.payload;
|
|
92
|
+
const existing = state.markers[text];
|
|
93
|
+
const marker = {
|
|
94
|
+
text,
|
|
95
|
+
detections: existing ? existing.detections.slice() : [],
|
|
96
|
+
size: estimate
|
|
97
|
+
};
|
|
98
|
+
return {
|
|
99
|
+
...state,
|
|
100
|
+
markers: {
|
|
101
|
+
...state.markers,
|
|
102
|
+
[text]: marker
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
/**
|
|
107
|
+
* Drop the oldest `count` detections from one marker (explicit custom
|
|
108
|
+
* cleanup, beyond the automatic ring-buffer cap). No-op for an unknown
|
|
109
|
+
* marker or `count <= 0`.
|
|
110
|
+
*/
|
|
111
|
+
pruneQrDetections(state, action) {
|
|
112
|
+
const { text, count } = action.payload;
|
|
113
|
+
const existing = state.markers[text];
|
|
114
|
+
if (!existing || count <= 0) return state;
|
|
115
|
+
const marker = {
|
|
116
|
+
...existing,
|
|
117
|
+
detections: existing.detections.slice(count)
|
|
118
|
+
};
|
|
119
|
+
return {
|
|
120
|
+
...state,
|
|
121
|
+
markers: {
|
|
122
|
+
...state.markers,
|
|
123
|
+
[text]: marker
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
},
|
|
127
|
+
/** Remove one marker entirely. */
|
|
128
|
+
clearQrMarker(state, action) {
|
|
129
|
+
const { [action.payload.text]: _removed, ...rest } = state.markers;
|
|
130
|
+
return {
|
|
131
|
+
...state,
|
|
132
|
+
markers: rest
|
|
133
|
+
};
|
|
134
|
+
},
|
|
135
|
+
/** Remove every marker (e.g. on session reset). */
|
|
136
|
+
clearAllQrMarkers(state) {
|
|
137
|
+
return {
|
|
138
|
+
...state,
|
|
139
|
+
markers: {}
|
|
140
|
+
};
|
|
141
|
+
},
|
|
142
|
+
/**
|
|
143
|
+
* Change the ring-buffer cap. Existing markers are re-trimmed immediately
|
|
144
|
+
* so the invariant "no marker exceeds `maxHistory`" holds at all times.
|
|
145
|
+
*/
|
|
146
|
+
setQrMaxHistory(state, action) {
|
|
147
|
+
const next = Math.max(0, Math.floor(action.payload));
|
|
148
|
+
const markers = {};
|
|
149
|
+
for (const [key, marker] of Object.entries(state.markers)) markers[key] = {
|
|
150
|
+
...marker,
|
|
151
|
+
detections: capDetections(marker.detections, next)
|
|
152
|
+
};
|
|
153
|
+
return {
|
|
154
|
+
...state,
|
|
155
|
+
maxHistory: next,
|
|
156
|
+
markers
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
const { recordQrDetection, recordQrSizeEstimate, pruneQrDetections, clearQrMarker, clearAllQrMarkers, setQrMaxHistory } = qrDetectedSlice.actions;
|
|
162
|
+
const qrDetectedReducer = qrDetectedSlice.reducer;
|
|
163
|
+
function selectQrMarkers(state) {
|
|
164
|
+
return state.qrDetected.markers;
|
|
165
|
+
}
|
|
166
|
+
function selectQrMarker(state, text) {
|
|
167
|
+
return state.qrDetected.markers[text];
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* The newest detection for a marker (the natural overlay-persistence source —
|
|
171
|
+
* an overlay reads this and keeps the last pose across detection misses rather
|
|
172
|
+
* than unmounting/flickering).
|
|
173
|
+
*/
|
|
174
|
+
function selectLatestQrDetection(state, text) {
|
|
175
|
+
const marker = state.qrDetected.markers[text];
|
|
176
|
+
if (!marker || marker.detections.length === 0) return void 0;
|
|
177
|
+
return marker.detections[marker.detections.length - 1];
|
|
178
|
+
}
|
|
179
|
+
function selectQrSize(state, text) {
|
|
180
|
+
return state.qrDetected.markers[text]?.size;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Resolve a marker's measured size for the high-weight GPS vote
|
|
184
|
+
* (framework-wiring-options Part B, Option a). Returns the running-median
|
|
185
|
+
* `estimateM` ONCE the size lifecycle reports `'estimated'`, else `null` ("keep
|
|
186
|
+
* scanning"). This is the bridge an app injects as the QR controller's
|
|
187
|
+
* `resolveSizeM`, so a size-less-but-geo level votes the moment the slice
|
|
188
|
+
* converges — **without** the `ar` controller ever importing the slice (which
|
|
189
|
+
* would close the `ar → state` cycle):
|
|
190
|
+
*
|
|
191
|
+
* ```ts
|
|
192
|
+
* resolveSizeM: (text) => selectResolvedQrSizeM(store.getState(), text),
|
|
193
|
+
* ```
|
|
194
|
+
*
|
|
195
|
+
* Per the Note 3 decision (confirmed) a reliably-estimated *measured* size
|
|
196
|
+
* drives the vote under the *same* gate as an authored size — no extra
|
|
197
|
+
* size-specific bar — because the §7 occupancy + reprojection + outlier-rejection
|
|
198
|
+
* backstops already bound a bad read.
|
|
199
|
+
*/
|
|
200
|
+
function selectResolvedQrSizeM(state, text) {
|
|
201
|
+
const size = state.qrDetected.markers[text]?.size;
|
|
202
|
+
return size?.status === "estimated" ? size.estimateM : null;
|
|
203
|
+
}
|
|
204
|
+
/** Per-axis median of a numeric list (returns the lower-middle for even n). */
|
|
205
|
+
function medianOf(values) {
|
|
206
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
207
|
+
return sorted[Math.floor((sorted.length - 1) / 2)];
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Robust per-axis median of the world positions across a marker's detection
|
|
211
|
+
* window — the "running estimate" the bounded buffer enables (Note 3). Outliers
|
|
212
|
+
* in fewer than half the samples cannot move the result outside the inlier
|
|
213
|
+
* range. Skips RAW-only entries that carry no solved pose (D-A); returns `null`
|
|
214
|
+
* for an empty window (or one with no solved poses at all).
|
|
215
|
+
*/
|
|
216
|
+
function medianQrPosition(entries) {
|
|
217
|
+
const poses = entries.map((e) => e.qrPoseWorld).filter((p) => p !== void 0);
|
|
218
|
+
if (poses.length === 0) return null;
|
|
219
|
+
const xs = poses.map((p) => p.position[0]);
|
|
220
|
+
const ys = poses.map((p) => p.position[1]);
|
|
221
|
+
const zs = poses.map((p) => p.position[2]);
|
|
222
|
+
return [
|
|
223
|
+
medianOf(xs),
|
|
224
|
+
medianOf(ys),
|
|
225
|
+
medianOf(zs)
|
|
226
|
+
];
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* The pose-stability lifecycle for a marker, derived from its raw detection ring
|
|
230
|
+
* buffer (NOT stored separately — the detections are the source of truth, unlike
|
|
231
|
+
* the depth-measured size which is accumulated outside the buffer). Mirrors the
|
|
232
|
+
* size lifecycle's `unknown → measuring → stable` shape so the HUD can show
|
|
233
|
+
* "converging…" vs "stable". See `ar/qr-pose-aggregation.ts`.
|
|
234
|
+
*/
|
|
235
|
+
function selectQrPoseStability(state, text, options) {
|
|
236
|
+
const marker = state.qrDetected.markers[text];
|
|
237
|
+
return evaluateQrPoseStability(marker ? marker.detections.map((d) => d.qrPoseWorld).filter((p) => p !== void 0) : [], options);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* The robust filtered QR world pose, exposed ONLY once the stability gate is
|
|
241
|
+
* `stable` (else `null` — "keep accumulating"). This is the pose analogue of
|
|
242
|
+
* {@link selectResolvedQrSizeM}: the `resolveStablePose`-style bridge an app
|
|
243
|
+
* injects into the QR controller / demo so the `ar` layer never imports the
|
|
244
|
+
* slice. The high-weight vote and the smooth overlay consume THIS, never the raw
|
|
245
|
+
* latest pose (which stays available via {@link selectLatestQrDetection} for
|
|
246
|
+
* scanning feedback / overlay persistence across misses).
|
|
247
|
+
*
|
|
248
|
+
* ```ts
|
|
249
|
+
* resolveStablePose: (text) => selectStableQrPose(store.getState(), text),
|
|
250
|
+
* ```
|
|
251
|
+
*/
|
|
252
|
+
function selectStableQrPose(state, text, options) {
|
|
253
|
+
const stability = selectQrPoseStability(state, text, options);
|
|
254
|
+
return stability.status === "stable" ? stability.pose : null;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Narrow a stored entry to a {@link RawQrObservation} — i.e. one that carries
|
|
258
|
+
* the full raw detector output. During the D-A-2 transition some entries may
|
|
259
|
+
* still be solved-pose-only (no raw fields); those are skipped by the derive
|
|
260
|
+
* selector. After WS-3 every entry is raw and this guard always holds.
|
|
261
|
+
*/
|
|
262
|
+
function toRawObservation(entry) {
|
|
263
|
+
if (!entry.corners || !entry.cameraPose || !entry.projectionMatrix || entry.imageWidth === void 0 || entry.imageHeight === void 0) return null;
|
|
264
|
+
return {
|
|
265
|
+
text: entry.text,
|
|
266
|
+
corners: entry.corners,
|
|
267
|
+
cameraPose: entry.cameraPose,
|
|
268
|
+
projectionMatrix: entry.projectionMatrix,
|
|
269
|
+
imageWidth: entry.imageWidth,
|
|
270
|
+
imageHeight: entry.imageHeight,
|
|
271
|
+
timestamp: entry.timestamp
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Derive the best-effort solved WORLD pose for a marker from its RAW observation
|
|
276
|
+
* history (decision D-A): accumulate size over the history against the injected
|
|
277
|
+
* `resolveDepthAt` as-of join, then PnP-solve the latest observation. Returns
|
|
278
|
+
* `null` when the marker is unknown, has no raw observations, cannot be sized
|
|
279
|
+
* yet, or PnP rejects the latest quad — the re-test-friendly replacement for the
|
|
280
|
+
* once-stored solved pose. Runs identically live and on replay (only
|
|
281
|
+
* `deps.resolveDepthAt` differs). See `ar/qr-derived-pose.ts`.
|
|
282
|
+
*/
|
|
283
|
+
function selectSolvedQrPose(state, text, deps) {
|
|
284
|
+
return deriveSolvedQrPose(text, selectQrRawObservations(state, text), deps);
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* The marker's stored entries narrowed to {@link RawQrObservation}s (entries
|
|
288
|
+
* carrying the full raw detector output), oldest→newest. Empty for an unknown
|
|
289
|
+
* marker or one with only solved-pose-only (transitional) entries. This is the
|
|
290
|
+
* shared mapping the derive-on-read selectors and the incremental deriver
|
|
291
|
+
* consume — exposed so a stateful consumer (the recorder's debug controller)
|
|
292
|
+
* can hand raw observations to `createIncrementalQrPlacement` without
|
|
293
|
+
* re-implementing the `toRawObservation` guard.
|
|
294
|
+
*/
|
|
295
|
+
function selectQrRawObservations(state, text) {
|
|
296
|
+
const marker = state.qrDetected.markers[text];
|
|
297
|
+
if (!marker) return [];
|
|
298
|
+
return marker.detections.map(toRawObservation).filter((o) => o !== null);
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Derive the best-effort {@link DerivedQrPlacement} (solved pose AND the metric
|
|
302
|
+
* size that solved it) for a marker from its RAW observation history. The debug
|
|
303
|
+
* viz needs the size to draw the cube, so this returns both in one history pass
|
|
304
|
+
* (decision D-A; see {@link deriveQrPlacement}). `null` under the same
|
|
305
|
+
* conditions as {@link selectSolvedQrPose}. Runs identically live and on replay.
|
|
306
|
+
*/
|
|
307
|
+
function selectDerivedQrPlacement(state, text, deps) {
|
|
308
|
+
return deriveQrPlacement(text, selectQrRawObservations(state, text), deps);
|
|
309
|
+
}
|
|
310
|
+
//#endregion
|
|
311
|
+
export { DEFAULT_QR_MAX_HISTORY, clearAllQrMarkers, clearQrMarker, medianQrPosition, pruneQrDetections, qrDetectedReducer, recordQrDetection, recordQrSizeEstimate, selectDerivedQrPlacement, selectLatestQrDetection, selectQrMarker, selectQrMarkers, selectQrPoseStability, selectQrRawObservations, selectQrSize, selectResolvedQrSizeM, selectSolvedQrPose, selectStableQrPose, setQrMaxHistory };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { ArCrashIsolationOptions, DEFAULT_RECORDING_OPTIONS, DEPTH_CONSTRAINTS, DepthCaptureOptions, IMAGE_CONSTRAINTS, ImageCaptureOptions, RecordingOptions, RecordingOptionsInput, STORAGE_KEY, cloneRecordingOptions, loadRecordingOptions, resetRecordingOptions, saveRecordingOptions, validateArCrashIsolationOptions, validateDepthOptions, validateImageOptions, validateRecordingOptions };
|
|
1
|
+
import { A as validateOccupancyOptions, C as saveRecordingOptions, D as validateFrameTileDisplayOptions, E as validateDepthOptions, M as validateQualityFilterOptions, N as validateRecordingOptions, O as validateImageOptions, P as validateVisualizationOptions, S as resetRecordingOptions, T as validateCompassDebugOptions, _ as RecordingOptionsInput, a as DepthCaptureOptions, b as cloneRecordingOptions, c as IMAGE_CONSTRAINTS, d as OCCUPANCY_CONSTRAINTS, f as OccupancyOptions, g as RecordingOptions, h as QrCaptureOptions, i as DEPTH_CONSTRAINTS, j as validateQrOptions, k as validateMotionFilterOptions, l as ImageCaptureOptions, m as QUALITY_FILTER_CONSTRAINTS, n as CompassDebugOptions, o as FRAME_TILE_DISPLAY_CONSTRAINTS, p as QR_CONSTRAINTS, r as DEFAULT_RECORDING_OPTIONS, s as FrameTileDisplayOptions, t as ArCrashIsolationOptions, u as MOTION_FILTER_CONSTRAINTS, v as STORAGE_KEY, w as validateArCrashIsolationOptions, x as loadRecordingOptions, y as VisualizationOptions } from "../recording-options-BPCpMHLK.js";
|
|
2
|
+
export { ArCrashIsolationOptions, CompassDebugOptions, DEFAULT_RECORDING_OPTIONS, DEPTH_CONSTRAINTS, DepthCaptureOptions, FRAME_TILE_DISPLAY_CONSTRAINTS, FrameTileDisplayOptions, IMAGE_CONSTRAINTS, ImageCaptureOptions, MOTION_FILTER_CONSTRAINTS, OCCUPANCY_CONSTRAINTS, OccupancyOptions, QR_CONSTRAINTS, QUALITY_FILTER_CONSTRAINTS, QrCaptureOptions, RecordingOptions, RecordingOptionsInput, STORAGE_KEY, VisualizationOptions, cloneRecordingOptions, loadRecordingOptions, resetRecordingOptions, saveRecordingOptions, validateArCrashIsolationOptions, validateCompassDebugOptions, validateDepthOptions, validateFrameTileDisplayOptions, validateImageOptions, validateMotionFilterOptions, validateOccupancyOptions, validateQrOptions, validateQualityFilterOptions, validateRecordingOptions, validateVisualizationOptions };
|