gps-plus-slam-app-framework 1.0.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/LICENSE +201 -0
- package/README.md +222 -0
- package/dist/alignment-lerper-D9BeDwZX.d.ts +29 -0
- package/dist/app-selectors-DyMzxCEC.d.ts +140 -0
- package/dist/ar/camera-blit-capture.d.ts +2 -0
- package/dist/ar/camera-blit-capture.js +253 -0
- package/dist/ar/capture-failure-tracker.d.ts +2 -0
- package/dist/ar/capture-failure-tracker.js +45 -0
- package/dist/ar/chromium-camera-access-workaround.d.ts +2 -0
- package/dist/ar/chromium-camera-access-workaround.js +32 -0
- package/dist/ar/depth-sampler.d.ts +3 -0
- package/dist/ar/depth-sampler.js +147 -0
- package/dist/ar/image-capture.d.ts +2 -0
- package/dist/ar/image-capture.js +152 -0
- package/dist/ar/index.d.ts +12 -0
- package/dist/ar/index.js +12 -0
- package/dist/ar/replay-scene.d.ts +2 -0
- package/dist/ar/replay-scene.js +325 -0
- package/dist/ar/scene-node-names.d.ts +2 -0
- package/dist/ar/scene-node-names.js +16 -0
- package/dist/ar/tracking-state.d.ts +2 -0
- package/dist/ar/tracking-state.js +164 -0
- package/dist/ar/webxr-session.d.ts +4 -0
- package/dist/ar/webxr-session.js +890 -0
- package/dist/ar/xr-camera-texture.d.ts +2 -0
- package/dist/ar/xr-camera-texture.js +39 -0
- package/dist/ar/xr-error-handler.d.ts +2 -0
- package/dist/ar/xr-error-handler.js +37 -0
- package/dist/ar-types-B-ORgk6Z.d.ts +82 -0
- package/dist/camera-blit-capture-DHuyrrxr.d.ts +108 -0
- package/dist/camera-follower-BMqPddxx.d.ts +29 -0
- package/dist/capture-failure-tracker-2Q6oI2uQ.d.ts +52 -0
- package/dist/chromium-camera-access-workaround-CU5zSKNr.d.ts +61 -0
- package/dist/community-license-key-ChZLCJyO.d.ts +21 -0
- package/dist/concurrency-p6gB30fT.d.ts +24 -0
- package/dist/css3d-renderer-manager-rgLMVAvd.d.ts +23 -0
- package/dist/depth-sampler-BNCtnEoP.d.ts +108 -0
- package/dist/failure-tracker-1sQ_Cgyw.d.ts +49 -0
- package/dist/file-system-BsQgYCU3.d.ts +128 -0
- package/dist/file-system-utils-5cWO8Xyv.d.ts +24 -0
- package/dist/format-file-size-onGq0k1Q.d.ts +24 -0
- package/dist/fused-path-DtqCSqJh.d.ts +25 -0
- package/dist/geo-types-Xx-LVcnw.d.ts +29 -0
- package/dist/gps-Ddzr2KlX.d.ts +54 -0
- package/dist/gps-compass-cubes-C-6JEnpR.d.ts +22 -0
- package/dist/gps-error-handler-VPuNOuf8.d.ts +40 -0
- package/dist/gps-event-markers-DpEy2qRd.d.ts +67 -0
- package/dist/h3-ref-point-CFhayNSZ.d.ts +59 -0
- package/dist/image-capture-2pKjYUS6.d.ts +130 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.js +61 -0
- package/dist/leaflet-map-overlay-CvfQXTye.d.ts +114 -0
- package/dist/lerp-utils-87NWjUOD.d.ts +20 -0
- package/dist/licensing/community-license-key.d.ts +2 -0
- package/dist/licensing/community-license-key.js +21 -0
- package/dist/licensing/index.d.ts +2 -0
- package/dist/licensing/index.js +2 -0
- package/dist/list-formatter-DKQQVLcl.d.ts +14 -0
- package/dist/logger-B81iwxx0.d.ts +74 -0
- package/dist/map-overlay-DuiZuCTx.d.ts +153 -0
- package/dist/null-storage-backend-CsWvQwN8.d.ts +11 -0
- package/dist/opfs-storage-ChOzF_Wp.d.ts +181 -0
- package/dist/opfs-storage-backend-CVcmfJHN.d.ts +11 -0
- package/dist/permission-checker-jO2ziCWC.d.ts +146 -0
- package/dist/persistence-middleware-DrptY02i.d.ts +31 -0
- package/dist/recorder-slice-fcwAXrsh.d.ts +35 -0
- package/dist/recording-coordinator-DzflP07_.d.ts +81 -0
- package/dist/recording-options-BYUFX8iU.d.ts +166 -0
- package/dist/recording-replayer-oOzPITrd.d.ts +32 -0
- package/dist/ref-point-importer-B_wn5ABk.d.ts +62 -0
- package/dist/ref-point-loader-Pt1smgHS.d.ts +117 -0
- package/dist/ref-points/h3-ref-point.d.ts +2 -0
- package/dist/ref-points/h3-ref-point.js +74 -0
- package/dist/ref-points/index.d.ts +2 -0
- package/dist/ref-points/index.js +2 -0
- package/dist/ref-points-slice-tC-Wd6XJ.d.ts +48 -0
- package/dist/reference-points-CFWy8_wj.d.ts +68 -0
- package/dist/replay-engine-DuX316ae.d.ts +103 -0
- package/dist/replay-scene-DtUm6bsr.d.ts +109 -0
- package/dist/routing-slice-BlHTM8eh.d.ts +23 -0
- package/dist/scene-node-names-DPsaE2z3.d.ts +14 -0
- package/dist/sensors/gps-error-handler.d.ts +2 -0
- package/dist/sensors/gps-error-handler.js +53 -0
- package/dist/sensors/gps.d.ts +2 -0
- package/dist/sensors/gps.js +93 -0
- package/dist/sensors/index.d.ts +4 -0
- package/dist/sensors/index.js +4 -0
- package/dist/sensors/permission-checker.d.ts +2 -0
- package/dist/sensors/permission-checker.js +397 -0
- package/dist/state/app-selectors.d.ts +2 -0
- package/dist/state/app-selectors.js +35 -0
- package/dist/state/index.d.ts +17 -0
- package/dist/state/index.js +13 -0
- package/dist/state/persistence-middleware.d.ts +2 -0
- package/dist/state/persistence-middleware.js +71 -0
- package/dist/state/recorder-slice.d.ts +2 -0
- package/dist/state/recorder-slice.js +47 -0
- package/dist/state/recording-coordinator.d.ts +2 -0
- package/dist/state/recording-coordinator.js +150 -0
- package/dist/state/recording-options.d.ts +2 -0
- package/dist/state/recording-options.js +192 -0
- package/dist/state/recording-replayer.d.ts +2 -0
- package/dist/state/recording-replayer.js +39 -0
- package/dist/state/ref-points-slice.d.ts +2 -0
- package/dist/state/ref-points-slice.js +44 -0
- package/dist/state/replay-engine.d.ts +2 -0
- package/dist/state/replay-engine.js +230 -0
- package/dist/state/routing-slice.d.ts +2 -0
- package/dist/state/routing-slice.js +13 -0
- package/dist/state/store-subscribers.d.ts +3 -0
- package/dist/state/store-subscribers.js +97 -0
- package/dist/state/store.d.ts +9 -0
- package/dist/state/store.js +71 -0
- package/dist/state/subscribe-to-selector.d.ts +2 -0
- package/dist/state/subscribe-to-selector.js +26 -0
- package/dist/storage/file-system-utils.d.ts +2 -0
- package/dist/storage/file-system-utils.js +31 -0
- package/dist/storage/file-system.d.ts +2 -0
- package/dist/storage/file-system.js +229 -0
- package/dist/storage/index.d.ts +11 -0
- package/dist/storage/index.js +10 -0
- package/dist/storage/null-storage-backend.d.ts +2 -0
- package/dist/storage/null-storage-backend.js +8 -0
- package/dist/storage/opfs-storage-backend.d.ts +2 -0
- package/dist/storage/opfs-storage-backend.js +15 -0
- package/dist/storage/opfs-storage.d.ts +2 -0
- package/dist/storage/opfs-storage.js +242 -0
- package/dist/storage/ref-point-importer.d.ts +2 -0
- package/dist/storage/ref-point-importer.js +163 -0
- package/dist/storage/ref-point-loader.d.ts +2 -0
- package/dist/storage/ref-point-loader.js +248 -0
- package/dist/storage/ref-point-recovery.d.ts +28 -0
- package/dist/storage/ref-point-recovery.js +159 -0
- package/dist/storage/storage-backend.d.ts +2 -0
- package/dist/storage/storage-backend.js +1 -0
- package/dist/storage/zip-export.d.ts +2 -0
- package/dist/storage/zip-export.js +214 -0
- package/dist/storage/zip-reader.d.ts +2 -0
- package/dist/storage/zip-reader.js +190 -0
- package/dist/storage-backend-BcEItnFr.d.ts +20 -0
- package/dist/store-C7PKOj8S.d.ts +96 -0
- package/dist/store-subscribers-CuoIzB5T.d.ts +77 -0
- package/dist/subscribe-to-selector-UTZ0KkAc.d.ts +26 -0
- package/dist/test-utils/browser-mocks.d.ts +294 -0
- package/dist/test-utils/browser-mocks.js +568 -0
- package/dist/test-utils/zip-round-trip-helpers.d.ts +60 -0
- package/dist/test-utils/zip-round-trip-helpers.js +207 -0
- package/dist/three-dispose-CCBHjE_a.d.ts +30 -0
- package/dist/tracking-state-B9QfuZCg.d.ts +101 -0
- package/dist/types/ar-types.d.ts +2 -0
- package/dist/types/ar-types.js +1 -0
- package/dist/types/geo-types.d.ts +2 -0
- package/dist/types/geo-types.js +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/concurrency.d.ts +2 -0
- package/dist/utils/concurrency.js +40 -0
- package/dist/utils/failure-tracker.d.ts +2 -0
- package/dist/utils/failure-tracker.js +48 -0
- package/dist/utils/format-file-size.d.ts +2 -0
- package/dist/utils/format-file-size.js +41 -0
- package/dist/utils/fused-path.d.ts +2 -0
- package/dist/utils/fused-path.js +44 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.js +7 -0
- package/dist/utils/list-formatter.d.ts +2 -0
- package/dist/utils/list-formatter.js +17 -0
- package/dist/utils/logger.d.ts +2 -0
- package/dist/utils/logger.js +226 -0
- package/dist/vis-colors-Qi2vlRGt.d.ts +63 -0
- package/dist/visualization/alignment-lerper.d.ts +2 -0
- package/dist/visualization/alignment-lerper.js +68 -0
- package/dist/visualization/camera-follower.d.ts +2 -0
- package/dist/visualization/camera-follower.js +51 -0
- package/dist/visualization/css3d-renderer-manager.d.ts +2 -0
- package/dist/visualization/css3d-renderer-manager.js +41 -0
- package/dist/visualization/gps-compass-cubes.d.ts +2 -0
- package/dist/visualization/gps-compass-cubes.js +129 -0
- package/dist/visualization/gps-event-markers.d.ts +2 -0
- package/dist/visualization/gps-event-markers.js +159 -0
- package/dist/visualization/index.d.ts +12 -0
- package/dist/visualization/index.js +12 -0
- package/dist/visualization/leaflet-map-overlay.d.ts +2 -0
- package/dist/visualization/leaflet-map-overlay.js +313 -0
- package/dist/visualization/lerp-utils.d.ts +2 -0
- package/dist/visualization/lerp-utils.js +22 -0
- package/dist/visualization/map-overlay.d.ts +2 -0
- package/dist/visualization/map-overlay.js +241 -0
- package/dist/visualization/reference-points.d.ts +2 -0
- package/dist/visualization/reference-points.js +160 -0
- package/dist/visualization/three-dispose.d.ts +2 -0
- package/dist/visualization/three-dispose.js +55 -0
- package/dist/visualization/vis-colors.d.ts +2 -0
- package/dist/visualization/vis-colors.js +74 -0
- package/dist/webxr-session-COlSrXdL.d.ts +316 -0
- package/dist/xr-camera-texture-BbukNE79.d.ts +53 -0
- package/dist/xr-error-handler-DTHfmvrl.d.ts +24 -0
- package/dist/zip-export-Rtpi75JJ.d.ts +87 -0
- package/dist/zip-reader-D7pkpX8K.d.ts +97 -0
- package/package.json +104 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { createSession, initOpfsStorage, resetOpfsStorage, writeAction, writeFrame, writeSessionMetadata } from "../storage/opfs-storage.js";
|
|
2
|
+
import { exportSessionAsZip } from "../storage/zip-export.js";
|
|
3
|
+
import { installOPFSMocks } from "./browser-mocks.js";
|
|
4
|
+
//#region ../src/test-utils/zip-round-trip-helpers.ts
|
|
5
|
+
/**
|
|
6
|
+
* Round-Trip Test Helpers — Produce realistic recording zips programmatically.
|
|
7
|
+
*
|
|
8
|
+
* Why this exists: Tests need recording zip files for the replay pipeline,
|
|
9
|
+
* zip-reader, and state verification. Rather than depending on static
|
|
10
|
+
* pre-recorded zips (which go stale as the app evolves), this helper
|
|
11
|
+
* produces zips using the real OPFS mock + export pipeline, ensuring the
|
|
12
|
+
* test data always matches the current production format.
|
|
13
|
+
*
|
|
14
|
+
* The helper writes actions via writeAction(), writes frames via writeFrame(),
|
|
15
|
+
* writes session metadata via writeSessionMetadata(), and exports via
|
|
16
|
+
* exportSessionAsZip(). OPFS mocks are installed and cleaned up internally
|
|
17
|
+
* — no side effects leak to the calling test.
|
|
18
|
+
*
|
|
19
|
+
* DESIGN BOUNDARY — VALID ZIPS ONLY
|
|
20
|
+
*
|
|
21
|
+
* This helper intentionally produces only valid, realistic zips. Do NOT add
|
|
22
|
+
* options to generate broken/incomplete zips (missing session.json, malformed
|
|
23
|
+
* JSON, missing actions, etc.). Its value comes from exercising the real
|
|
24
|
+
* production pipeline end-to-end — mixing in "intentionally broken" modes
|
|
25
|
+
* would undermine that contract.
|
|
26
|
+
*
|
|
27
|
+
* For error-handling / robustness tests, use hand-crafted zips via ZipWriter
|
|
28
|
+
* directly — see createZipWithActions() in zip-reader.test.ts for the
|
|
29
|
+
* established pattern. That approach gives precise byte-level control over
|
|
30
|
+
* exactly what's broken, which is strictly better for negative test cases.
|
|
31
|
+
*/
|
|
32
|
+
const DEFAULTS = {
|
|
33
|
+
scenarioName: "TestScenario",
|
|
34
|
+
sessionTimestamp: /* @__PURE__ */ new Date("2026-03-01T09:00:00Z"),
|
|
35
|
+
gpsEventCount: 10,
|
|
36
|
+
imagesBeforeSetZero: 2,
|
|
37
|
+
imagesAfterSetZero: 5,
|
|
38
|
+
frameCount: 2,
|
|
39
|
+
deviceInfo: "TestDevice Android 14",
|
|
40
|
+
zeroPos: {
|
|
41
|
+
lat: 50,
|
|
42
|
+
lon: 8
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Produce a realistic recording zip using the real OPFS mock + export pipeline.
|
|
47
|
+
*
|
|
48
|
+
* Action sequence produced (with defaults):
|
|
49
|
+
* 1. recorder/startSession (index 1)
|
|
50
|
+
* 2. gpsData/add2dImage × 2 (indices 2-3, before setZeroPos — dropped on replay)
|
|
51
|
+
* 3. gpsData/setZeroPos (index 4)
|
|
52
|
+
* 4. gpsData/recordGpsEvent × 10 (indices 5-14)
|
|
53
|
+
* 5. gpsData/add2dImage × 5 (indices 15-19, after setZeroPos — kept on replay)
|
|
54
|
+
*
|
|
55
|
+
* GPS events use varied odom/GPS positions that produce a non-identity
|
|
56
|
+
* alignment matrix when replayed through the library store.
|
|
57
|
+
*
|
|
58
|
+
* OPFS mocks are installed and cleaned up within this function.
|
|
59
|
+
*/
|
|
60
|
+
async function produceTestZip(opts) {
|
|
61
|
+
const options = {
|
|
62
|
+
...DEFAULTS,
|
|
63
|
+
...opts
|
|
64
|
+
};
|
|
65
|
+
const { cleanup } = installOPFSMocks();
|
|
66
|
+
try {
|
|
67
|
+
await initOpfsStorage();
|
|
68
|
+
const { scenarioName, sessionName } = await createSession(options.scenarioName, options.sessionTimestamp);
|
|
69
|
+
const allActions = [];
|
|
70
|
+
let actionIndex = 0;
|
|
71
|
+
/** Increment index, persist to OPFS, and record for later assertions. */
|
|
72
|
+
const writeAndRecordAction = async (action) => {
|
|
73
|
+
actionIndex++;
|
|
74
|
+
await writeAction(action, actionIndex);
|
|
75
|
+
allActions.push(action);
|
|
76
|
+
};
|
|
77
|
+
await writeAndRecordAction({
|
|
78
|
+
type: "recorder/startSession",
|
|
79
|
+
payload: {
|
|
80
|
+
scenarioName: options.scenarioName,
|
|
81
|
+
sessionName,
|
|
82
|
+
startTime: options.sessionTimestamp.getTime(),
|
|
83
|
+
deviceInfo: options.deviceInfo
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
for (let i = 0; i < options.imagesBeforeSetZero; i++) await writeAndRecordAction({
|
|
87
|
+
type: "gpsData/add2dImage",
|
|
88
|
+
payload: {
|
|
89
|
+
imageFile: `frames/frame-${String(actionIndex + 1).padStart(6, "0")}.jpg`,
|
|
90
|
+
position: [
|
|
91
|
+
i * .5,
|
|
92
|
+
0,
|
|
93
|
+
0
|
|
94
|
+
],
|
|
95
|
+
rotation: [
|
|
96
|
+
0,
|
|
97
|
+
0,
|
|
98
|
+
0,
|
|
99
|
+
1
|
|
100
|
+
],
|
|
101
|
+
screenRotation: 0,
|
|
102
|
+
capturedAt: options.sessionTimestamp.getTime() + actionIndex * 1e3
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
await writeAndRecordAction({
|
|
106
|
+
type: "gpsData/setZeroPos",
|
|
107
|
+
payload: {
|
|
108
|
+
lat: options.zeroPos.lat,
|
|
109
|
+
lon: options.zeroPos.lon
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
for (let i = 0; i < options.gpsEventCount; i++) {
|
|
113
|
+
const t = options.sessionTimestamp.getTime() + (actionIndex + 1) * 1e3;
|
|
114
|
+
const angle = i / Math.max(options.gpsEventCount - 1, 1) * Math.PI * .5;
|
|
115
|
+
const radius = (i + 1) * 2;
|
|
116
|
+
await writeAndRecordAction({
|
|
117
|
+
type: "gpsData/recordGpsEvent",
|
|
118
|
+
payload: {
|
|
119
|
+
odomPosition: [
|
|
120
|
+
radius * Math.cos(angle),
|
|
121
|
+
0,
|
|
122
|
+
radius * Math.sin(angle)
|
|
123
|
+
],
|
|
124
|
+
odomRotation: [
|
|
125
|
+
0,
|
|
126
|
+
0,
|
|
127
|
+
0,
|
|
128
|
+
1
|
|
129
|
+
],
|
|
130
|
+
rawGpsPoint: {
|
|
131
|
+
id: `gps-event-${i + 1}`,
|
|
132
|
+
latitude: options.zeroPos.lat + (i + 1) * 1e-4,
|
|
133
|
+
longitude: options.zeroPos.lon + (i + 1) * 1e-4,
|
|
134
|
+
timestamp: t
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
const postZeroImageOffset = options.imagesBeforeSetZero;
|
|
140
|
+
for (let i = 0; i < options.imagesAfterSetZero; i++) {
|
|
141
|
+
const imgIdx = postZeroImageOffset + i + 1;
|
|
142
|
+
await writeAndRecordAction({
|
|
143
|
+
type: "gpsData/add2dImage",
|
|
144
|
+
payload: {
|
|
145
|
+
imageFile: `frames/frame-${String(imgIdx).padStart(6, "0")}.jpg`,
|
|
146
|
+
position: [
|
|
147
|
+
(i + 1) * 1,
|
|
148
|
+
(i + 1) * .5,
|
|
149
|
+
0
|
|
150
|
+
],
|
|
151
|
+
rotation: [
|
|
152
|
+
0,
|
|
153
|
+
0,
|
|
154
|
+
0,
|
|
155
|
+
1
|
|
156
|
+
],
|
|
157
|
+
screenRotation: i % 4,
|
|
158
|
+
capturedAt: options.sessionTimestamp.getTime() + actionIndex * 1e3
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
for (let i = 0; i < options.frameCount; i++) {
|
|
163
|
+
const jpegHeader = [
|
|
164
|
+
255,
|
|
165
|
+
216,
|
|
166
|
+
255,
|
|
167
|
+
224
|
|
168
|
+
];
|
|
169
|
+
const body = Array.from({ length: 10 }, (_, j) => i * 10 + j & 255);
|
|
170
|
+
const frameBytes = new Uint8Array([...jpegHeader, ...body]);
|
|
171
|
+
await writeFrame(new Blob([frameBytes], { type: "image/jpeg" }), i + 1);
|
|
172
|
+
}
|
|
173
|
+
await writeSessionMetadata({
|
|
174
|
+
version: 1,
|
|
175
|
+
startedAt: options.sessionTimestamp.toISOString(),
|
|
176
|
+
endedAt: new Date(options.sessionTimestamp.getTime() + actionIndex * 1e3 + 5e3).toISOString(),
|
|
177
|
+
scenarioName: options.scenarioName,
|
|
178
|
+
actionCount: actionIndex,
|
|
179
|
+
frameCount: options.frameCount,
|
|
180
|
+
userAgent: options.deviceInfo
|
|
181
|
+
});
|
|
182
|
+
const { blob } = await exportSessionAsZip(scenarioName, sessionName);
|
|
183
|
+
return {
|
|
184
|
+
zipData: new Uint8Array(await blob.arrayBuffer()),
|
|
185
|
+
totalActionCount: actionIndex,
|
|
186
|
+
gpsEventCount: options.gpsEventCount,
|
|
187
|
+
imageActions: {
|
|
188
|
+
totalCount: options.imagesBeforeSetZero + options.imagesAfterSetZero,
|
|
189
|
+
beforeSetZero: options.imagesBeforeSetZero,
|
|
190
|
+
afterSetZero: options.imagesAfterSetZero
|
|
191
|
+
},
|
|
192
|
+
scenarioName: options.scenarioName,
|
|
193
|
+
sessionName,
|
|
194
|
+
startTime: options.sessionTimestamp.getTime(),
|
|
195
|
+
deviceInfo: options.deviceInfo,
|
|
196
|
+
hasSessionJson: true,
|
|
197
|
+
zeroPos: options.zeroPos,
|
|
198
|
+
actions: allActions,
|
|
199
|
+
frameCount: options.frameCount
|
|
200
|
+
};
|
|
201
|
+
} finally {
|
|
202
|
+
resetOpfsStorage();
|
|
203
|
+
cleanup();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
//#endregion
|
|
207
|
+
export { produceTestZip };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
|
|
3
|
+
//#region ../src/visualization/three-dispose.d.ts
|
|
4
|
+
interface DisposeOptions {
|
|
5
|
+
/** When true, skip geometry.dispose() — useful for shared geometries. */
|
|
6
|
+
readonly skipGeometry?: boolean;
|
|
7
|
+
/** When true, skip material.dispose() (and its map texture) — useful for shared materials. */
|
|
8
|
+
readonly skipMaterial?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Traverse an Object3D and all its descendants, disposing GPU resources:
|
|
12
|
+
* geometries (Mesh), materials (Mesh + Sprite), and material textures.
|
|
13
|
+
*
|
|
14
|
+
* Does NOT remove root from its parent — callers handle that.
|
|
15
|
+
*
|
|
16
|
+
* @param root The Object3D tree root to clean up.
|
|
17
|
+
* @param opts Optional flags (e.g. skipGeometry for shared geometries).
|
|
18
|
+
*/
|
|
19
|
+
declare function disposeObject3D(root: THREE.Object3D, opts?: DisposeOptions): void;
|
|
20
|
+
/**
|
|
21
|
+
* Remove meshes from a parent, dispose their GPU resources, and clear
|
|
22
|
+
* the array in-place.
|
|
23
|
+
*
|
|
24
|
+
* @param meshes Array of meshes (mutated — emptied after disposal).
|
|
25
|
+
* @param parent The Object3D to remove meshes from. Pass null/undefined to skip removal.
|
|
26
|
+
* @param opts Optional flags (e.g. skipGeometry for shared geometries).
|
|
27
|
+
*/
|
|
28
|
+
declare function disposeMeshArray(meshes: THREE.Mesh[], parent?: THREE.Object3D | null, opts?: DisposeOptions): void;
|
|
29
|
+
//#endregion
|
|
30
|
+
export { disposeMeshArray as n, disposeObject3D as r, DisposeOptions as t };
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { t as ARPose } from "./ar-types-B-ORgk6Z.js";
|
|
2
|
+
import { OdometryTrackingRestartedPayload, Quaternion, Vector3 } from "gps-plus-slam-js";
|
|
3
|
+
|
|
4
|
+
//#region ../src/ar/tracking-state.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Possible tracking states
|
|
7
|
+
*/
|
|
8
|
+
declare enum TrackingState {
|
|
9
|
+
/** AR session started but no pose received yet */
|
|
10
|
+
INITIALIZING = "initializing",
|
|
11
|
+
/** Actively receiving valid poses */
|
|
12
|
+
TRACKING = "tracking",
|
|
13
|
+
/** Pose became unavailable (tracking lost) */
|
|
14
|
+
LOST = "lost"
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Device orientation from sensors (for sensor rotation in restart payload)
|
|
18
|
+
*/
|
|
19
|
+
interface DeviceOrientation {
|
|
20
|
+
alpha: number;
|
|
21
|
+
beta: number;
|
|
22
|
+
gamma: number;
|
|
23
|
+
absolute: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Serialized representation of the XRReferenceSpaceEvent.transform.
|
|
27
|
+
* The position and orientation of the new native origin expressed in
|
|
28
|
+
* the pre-reset coordinate system. `null` when the runtime cannot
|
|
29
|
+
* determine the delta.
|
|
30
|
+
*/
|
|
31
|
+
interface ResetTransformData {
|
|
32
|
+
position: Vector3;
|
|
33
|
+
orientation: Quaternion;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Callbacks for tracking state changes
|
|
37
|
+
*/
|
|
38
|
+
interface TrackingStateCallbacks {
|
|
39
|
+
/** Called when tracking is lost (pose became null) */
|
|
40
|
+
onTrackingLost: () => void;
|
|
41
|
+
/** Called when tracking resumes after a coordinate-frame reset (Case 2: relocalization) */
|
|
42
|
+
onTrackingRestarted: (payload: OdometryTrackingRestartedPayload) => void;
|
|
43
|
+
/** Called when tracking resumes seamlessly without origin reset (Case 1) */
|
|
44
|
+
onTrackingRecovered?: () => void;
|
|
45
|
+
/** Get current device orientation for sensor rotation */
|
|
46
|
+
getDeviceOrientation: () => DeviceOrientation;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Manages AR tracking state and detects loss/restart events.
|
|
50
|
+
*/
|
|
51
|
+
declare class TrackingStateManager {
|
|
52
|
+
private state;
|
|
53
|
+
private lastValidPose;
|
|
54
|
+
private lastSensorOrientation;
|
|
55
|
+
private lostFrameCount;
|
|
56
|
+
private originResetDuringLoss;
|
|
57
|
+
private resetTransform;
|
|
58
|
+
private callbacks;
|
|
59
|
+
constructor(callbacks: TrackingStateCallbacks);
|
|
60
|
+
/**
|
|
61
|
+
* Get current tracking state.
|
|
62
|
+
*/
|
|
63
|
+
getState(): TrackingState;
|
|
64
|
+
/**
|
|
65
|
+
* Get the last valid AR pose (before tracking was lost).
|
|
66
|
+
*/
|
|
67
|
+
getLastValidPose(): ARPose | null;
|
|
68
|
+
/**
|
|
69
|
+
* Get count of consecutive frames without pose (during LOST state).
|
|
70
|
+
*/
|
|
71
|
+
getLostFrameCount(): number;
|
|
72
|
+
/**
|
|
73
|
+
* Flag that an XRReferenceSpace reset event occurred during tracking loss.
|
|
74
|
+
* Only meaningful while in LOST state — ignored otherwise.
|
|
75
|
+
*
|
|
76
|
+
* @param transform — Serialized XRReferenceSpaceEvent.transform (post-reset
|
|
77
|
+
* origin in pre-reset coordinates). Pass `null` when the event's
|
|
78
|
+
* transform property is null (runtime can't determine delta).
|
|
79
|
+
* Omit entirely for backwards-compat with callers that don't have it.
|
|
80
|
+
*/
|
|
81
|
+
markOriginReset(transform?: ResetTransformData | null): void;
|
|
82
|
+
/**
|
|
83
|
+
* Called when a valid pose is received from WebXR.
|
|
84
|
+
*/
|
|
85
|
+
onPoseReceived(pose: ARPose): void;
|
|
86
|
+
/**
|
|
87
|
+
* Called when pose is null/unavailable from WebXR.
|
|
88
|
+
*/
|
|
89
|
+
onPoseLost(): void;
|
|
90
|
+
/**
|
|
91
|
+
* Reset the manager to initial state.
|
|
92
|
+
* Call this when starting a new session.
|
|
93
|
+
*/
|
|
94
|
+
reset(): void;
|
|
95
|
+
/**
|
|
96
|
+
* Handle the transition from LOST to TRACKING (restart detected).
|
|
97
|
+
*/
|
|
98
|
+
private handleTrackingRestarted;
|
|
99
|
+
}
|
|
100
|
+
//#endregion
|
|
101
|
+
export { TrackingStateManager as a, TrackingStateCallbacks as i, ResetTransformData as n, TrackingState as r, DeviceOrientation as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { a as WebXRQuaternion, i as DepthSample, n as ArPoseTuples, o as WebXRVec3, r as DepthPoint, t as ARPose } from "../ar-types-B-ORgk6Z.js";
|
|
2
|
+
import { n as RefPointMarker, t as GpsCoord } from "../geo-types-Xx-LVcnw.js";
|
|
3
|
+
export { ARPose, ArPoseTuples, DepthPoint, DepthSample, GpsCoord, RefPointMarker, WebXRQuaternion, WebXRVec3 };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//#region ../src/utils/concurrency.ts
|
|
2
|
+
/**
|
|
3
|
+
* Concurrency Utilities
|
|
4
|
+
*
|
|
5
|
+
* Provides helpers for limiting the number of concurrent async operations.
|
|
6
|
+
* Used to prevent excessive memory consumption when scanning many files
|
|
7
|
+
* in parallel (e.g., reading zip metadata during scenario discovery).
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Map over an array with a concurrency limit on the async mapper function.
|
|
11
|
+
*
|
|
12
|
+
* Behaves like `Promise.all(items.map(fn))` but limits how many mapper
|
|
13
|
+
* invocations run simultaneously. Results are returned in the same order
|
|
14
|
+
* as the input items.
|
|
15
|
+
*
|
|
16
|
+
* @param items - Array of items to process
|
|
17
|
+
* @param limit - Maximum number of concurrent mapper invocations
|
|
18
|
+
* @param mapper - Async function to apply to each item
|
|
19
|
+
* @returns Array of results in the same order as input items
|
|
20
|
+
* @throws Re-throws the first error from any mapper invocation (fail-fast)
|
|
21
|
+
*/
|
|
22
|
+
async function mapWithConcurrencyLimit(items, limit, mapper) {
|
|
23
|
+
if (limit < 1) throw new RangeError(`Concurrency limit must be >= 1, got ${limit}`);
|
|
24
|
+
const results = new Array(items.length);
|
|
25
|
+
if (items.length === 0) return results;
|
|
26
|
+
let nextIndex = 0;
|
|
27
|
+
const worker = async () => {
|
|
28
|
+
while (nextIndex < items.length) {
|
|
29
|
+
const currentIndex = nextIndex++;
|
|
30
|
+
results[currentIndex] = await mapper(items[currentIndex], currentIndex);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const workerCount = Math.min(limit, items.length);
|
|
34
|
+
const workers = [];
|
|
35
|
+
for (let i = 0; i < workerCount; i++) workers.push(worker());
|
|
36
|
+
await Promise.all(workers);
|
|
37
|
+
return results;
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
export { mapWithConcurrencyLimit };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createLogger } from "./logger.js";
|
|
2
|
+
//#region ../src/utils/failure-tracker.ts
|
|
3
|
+
/**
|
|
4
|
+
* Generic Failure Tracker Factory
|
|
5
|
+
*
|
|
6
|
+
* Shared pattern extracted from capture-failure-tracker and write-failure-tracker.
|
|
7
|
+
* Tracks consecutive failures, warns the user once when a threshold is exceeded,
|
|
8
|
+
* and supports session-level reset.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Create a generic failure tracker.
|
|
12
|
+
*
|
|
13
|
+
* @param config - Tracker configuration
|
|
14
|
+
* @returns A FailureTracker instance
|
|
15
|
+
*/
|
|
16
|
+
function createFailureTracker(config) {
|
|
17
|
+
const { label, warningMessage, defaultThreshold, onWarning, logLevel = "warn" } = config;
|
|
18
|
+
const threshold = config.failureThreshold ?? defaultThreshold;
|
|
19
|
+
const log = createLogger(label);
|
|
20
|
+
let consecutiveFailures = 0;
|
|
21
|
+
let warningShown = false;
|
|
22
|
+
return {
|
|
23
|
+
recordSuccess() {
|
|
24
|
+
consecutiveFailures = 0;
|
|
25
|
+
},
|
|
26
|
+
recordFailure(error) {
|
|
27
|
+
consecutiveFailures++;
|
|
28
|
+
if (logLevel === "error") log.error(`${label} failure #${consecutiveFailures}:`, error);
|
|
29
|
+
else log.warn(`${label} failure #${consecutiveFailures}`);
|
|
30
|
+
if (consecutiveFailures >= threshold && !warningShown) {
|
|
31
|
+
warningShown = true;
|
|
32
|
+
onWarning(warningMessage);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
getFailureCount() {
|
|
36
|
+
return consecutiveFailures;
|
|
37
|
+
},
|
|
38
|
+
hasWarned() {
|
|
39
|
+
return warningShown;
|
|
40
|
+
},
|
|
41
|
+
reset() {
|
|
42
|
+
consecutiveFailures = 0;
|
|
43
|
+
warningShown = false;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { createFailureTracker };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//#region ../src/utils/format-file-size.ts
|
|
2
|
+
/**
|
|
3
|
+
* Format File Size Utility
|
|
4
|
+
*
|
|
5
|
+
* Converts byte counts to human-readable file size strings (B, KB, MB, GB).
|
|
6
|
+
* Used by session summary to display ZIP file statistics.
|
|
7
|
+
*
|
|
8
|
+
* @see 2026-02-06 User Feedback Issue #3: ZIP stats on summary screen
|
|
9
|
+
*/
|
|
10
|
+
const UNITS = [
|
|
11
|
+
"B",
|
|
12
|
+
"KB",
|
|
13
|
+
"MB",
|
|
14
|
+
"GB"
|
|
15
|
+
];
|
|
16
|
+
const THRESHOLD = 1024;
|
|
17
|
+
/**
|
|
18
|
+
* Format a byte count into a human-readable string.
|
|
19
|
+
*
|
|
20
|
+
* @param bytes - Number of bytes (non-negative integer expected)
|
|
21
|
+
* @returns Formatted string like "512 B", "1.5 KB", "23.4 MB", "1.0 GB"
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* formatFileSize(0) // "0 B"
|
|
25
|
+
* formatFileSize(512) // "512 B"
|
|
26
|
+
* formatFileSize(1048576) // "1.0 MB"
|
|
27
|
+
* formatFileSize(2500000000) // "2.3 GB"
|
|
28
|
+
*/
|
|
29
|
+
function formatFileSize(bytes) {
|
|
30
|
+
if (bytes <= 0) return "0 B";
|
|
31
|
+
let value = bytes;
|
|
32
|
+
let unitIndex = 0;
|
|
33
|
+
while (value >= THRESHOLD && unitIndex < UNITS.length - 1) {
|
|
34
|
+
value /= THRESHOLD;
|
|
35
|
+
unitIndex++;
|
|
36
|
+
}
|
|
37
|
+
if (unitIndex === 0) return `${Math.round(value)} B`;
|
|
38
|
+
return `${value.toFixed(1)} ${UNITS[unitIndex]}`;
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { formatFileSize };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { calcGpsCoords, fusedGpsFromOdom } from "gps-plus-slam-js";
|
|
2
|
+
import { mat4, vec3 } from "gl-matrix";
|
|
3
|
+
//#region ../src/utils/fused-path.ts
|
|
4
|
+
/**
|
|
5
|
+
* Fused Path Calculation
|
|
6
|
+
*
|
|
7
|
+
* Transforms AR odometry positions into GPS coordinates using the alignment matrix.
|
|
8
|
+
* This enables displaying the "fused" SLAM+GPS trajectory on the session summary map.
|
|
9
|
+
*
|
|
10
|
+
* The alignment matrix (computed by the alignment solver in the library) transforms
|
|
11
|
+
* AR-local odometry positions into NUE (North-Up-East) meters relative to a
|
|
12
|
+
* GPS zero reference point. We then convert NUE to lat/lng for Leaflet.
|
|
13
|
+
*
|
|
14
|
+
* Internal coordinate convention: X=North, Y=Up, Z=East (right-handed).
|
|
15
|
+
*
|
|
16
|
+
* @see docs/2026-01-27-user-feedback.md Issue #4b
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Compute the fused GPS path from odometry positions using the alignment matrix.
|
|
20
|
+
*
|
|
21
|
+
* Each odometry position is transformed by the alignment matrix to get ENU
|
|
22
|
+
* coordinates (meters relative to zeroRef), then converted to GPS lat/lng.
|
|
23
|
+
*
|
|
24
|
+
* @param input - Odometry positions, alignment matrix, and GPS zero reference
|
|
25
|
+
* @returns Array of GPS coordinates suitable for Leaflet polyline
|
|
26
|
+
*/
|
|
27
|
+
function computeFusedPath(input) {
|
|
28
|
+
const { odometryPositions, alignmentMatrix, zeroRef } = input;
|
|
29
|
+
if (!alignmentMatrix || !zeroRef || odometryPositions.length === 0) return [];
|
|
30
|
+
const matrix = mat4.fromValues(...alignmentMatrix);
|
|
31
|
+
const odomVec = vec3.create();
|
|
32
|
+
const alignedVec = vec3.create();
|
|
33
|
+
return odometryPositions.map((odomPos) => {
|
|
34
|
+
vec3.set(odomVec, odomPos[0], odomPos[1], odomPos[2]);
|
|
35
|
+
vec3.transformMat4(alignedVec, odomVec, matrix);
|
|
36
|
+
const gpsCoord = calcGpsCoords(zeroRef, alignedVec);
|
|
37
|
+
return {
|
|
38
|
+
lat: gpsCoord.lat,
|
|
39
|
+
lng: gpsCoord.lon
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { computeFusedPath, fusedGpsFromOdom };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { a as createLogger, c as setGlobalLogLevel, i as clearLogBuffer, l as subscribeToLogs, n as LogLevel, o as getGlobalLogLevel, r as Logger, s as getLogBuffer, t as LogEntry } from "../logger-B81iwxx0.js";
|
|
2
|
+
import { n as computeFusedPath, r as fusedGpsFromOdom, t as FusedPathInput } from "../fused-path-DtqCSqJh.js";
|
|
3
|
+
import { n as FailureTrackerConfig, r as createFailureTracker, t as FailureTracker } from "../failure-tracker-1sQ_Cgyw.js";
|
|
4
|
+
import { t as mapWithConcurrencyLimit } from "../concurrency-p6gB30fT.js";
|
|
5
|
+
import { t as formatFileSize } from "../format-file-size-onGq0k1Q.js";
|
|
6
|
+
import { t as listFormatter } from "../list-formatter-DKQQVLcl.js";
|
|
7
|
+
export { type FailureTracker, type FailureTrackerConfig, type FusedPathInput, type LogEntry, LogLevel, type Logger, clearLogBuffer, computeFusedPath, createFailureTracker, createLogger, formatFileSize, fusedGpsFromOdom, getGlobalLogLevel, getLogBuffer, listFormatter, mapWithConcurrencyLimit, setGlobalLogLevel, subscribeToLogs };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { LogLevel, clearLogBuffer, createLogger, getGlobalLogLevel, getLogBuffer, setGlobalLogLevel, subscribeToLogs } from "./logger.js";
|
|
2
|
+
import { createFailureTracker } from "./failure-tracker.js";
|
|
3
|
+
import { computeFusedPath, fusedGpsFromOdom } from "./fused-path.js";
|
|
4
|
+
import { mapWithConcurrencyLimit } from "./concurrency.js";
|
|
5
|
+
import { formatFileSize } from "./format-file-size.js";
|
|
6
|
+
import { listFormatter } from "./list-formatter.js";
|
|
7
|
+
export { LogLevel, clearLogBuffer, computeFusedPath, createFailureTracker, createLogger, formatFileSize, fusedGpsFromOdom, getGlobalLogLevel, getLogBuffer, listFormatter, mapWithConcurrencyLimit, setGlobalLogLevel, subscribeToLogs };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region ../src/utils/list-formatter.ts
|
|
2
|
+
/**
|
|
3
|
+
* Shared list formatter for displaying comma-separated lists with "and".
|
|
4
|
+
*
|
|
5
|
+
* Uses English locale to match the app's UI language. If i18n support is added
|
|
6
|
+
* in the future, this should be updated to use the user's preferred locale.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* listFormatter.format(['Camera', 'Location']) // "Camera and Location"
|
|
10
|
+
* listFormatter.format(['A', 'B', 'C']) // "A, B, and C"
|
|
11
|
+
*/
|
|
12
|
+
const listFormatter = new Intl.ListFormat("en", {
|
|
13
|
+
style: "long",
|
|
14
|
+
type: "conjunction"
|
|
15
|
+
});
|
|
16
|
+
//#endregion
|
|
17
|
+
export { listFormatter };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as createLogger, c as setGlobalLogLevel, i as clearLogBuffer, l as subscribeToLogs, n as LogLevel, o as getGlobalLogLevel, r as Logger, s as getLogBuffer, t as LogEntry } from "../logger-B81iwxx0.js";
|
|
2
|
+
export { LogEntry, LogLevel, Logger, clearLogBuffer, createLogger, getGlobalLogLevel, getLogBuffer, setGlobalLogLevel, subscribeToLogs };
|