gps-plus-slam-app-framework 1.0.0 → 1.0.7

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.
Files changed (216) hide show
  1. package/LICENSE +4 -4
  2. package/README.md +249 -108
  3. package/dist/accuracy-circles-CsxE_7x1.d.ts +43 -0
  4. package/dist/{app-selectors-DyMzxCEC.d.ts → app-selectors-BeU0_ab8.d.ts} +14 -15
  5. package/dist/ar/camera-blit-capture.d.ts +1 -1
  6. package/dist/ar/capture-failure-tracker.d.ts +1 -1
  7. package/dist/ar/chromium-camera-access-workaround.d.ts +1 -1
  8. package/dist/ar/depth-sampler.d.ts +2 -2
  9. package/dist/ar/depth-sampler.js +1 -1
  10. package/dist/ar/frame-loop.d.ts +2 -0
  11. package/dist/ar/frame-loop.js +40 -0
  12. package/dist/ar/image-capture.d.ts +1 -1
  13. package/dist/ar/image-capture.js +1 -1
  14. package/dist/ar/index.d.ts +12 -12
  15. package/dist/ar/index.js +3 -3
  16. package/dist/ar/replay-scene.d.ts +1 -1
  17. package/dist/ar/scene-node-names.d.ts +1 -1
  18. package/dist/ar/webxr-session.d.ts +4 -4
  19. package/dist/ar/webxr-session.js +151 -56
  20. package/dist/ar/xr-camera-texture.d.ts +1 -1
  21. package/dist/ar/xr-error-handler.d.ts +1 -1
  22. package/dist/core/index.d.ts +2 -0
  23. package/dist/core/index.js +2 -0
  24. package/dist/create-slam-app-store-CNsZ3rcX.d.ts +371 -0
  25. package/dist/{depth-sampler-BNCtnEoP.d.ts → depth-sampler-xg8vna6c.d.ts} +1 -1
  26. package/dist/file-system-DTApEv_Q.d.ts +114 -0
  27. package/dist/frame-loop-QJIxLin0.d.ts +42 -0
  28. package/dist/frustum-visibility-BHnFrSKQ.d.ts +58 -0
  29. package/dist/{fused-path-DtqCSqJh.d.ts → fused-path-CB4DwWOG.d.ts} +1 -1
  30. package/dist/geo/h3-proximity.d.ts +2 -0
  31. package/dist/{ref-points/h3-ref-point.js → geo/h3-proximity.js} +16 -12
  32. package/dist/geo/index.d.ts +2 -0
  33. package/dist/geo/index.js +2 -0
  34. package/dist/{geo-types-Xx-LVcnw.d.ts → geo-types-CTuF23gG.d.ts} +16 -1
  35. package/dist/{gps-Ddzr2KlX.d.ts → gps-DXoQb9Kf.d.ts} +1 -1
  36. package/dist/gps-anchor-CnWY61XK.d.ts +48 -0
  37. package/dist/gps-anchor-DjC_3MoT.js +257 -0
  38. package/dist/gps-ar-pose-sampler-DsM1Kb2N.d.ts +43 -0
  39. package/dist/{recording-coordinator-DzflP07_.d.ts → gps-event-coordinator-DBx9I1YX.d.ts} +7 -6
  40. package/dist/gps-event-markers-BGr-iLvi.d.ts +103 -0
  41. package/dist/{h3-ref-point-CFhayNSZ.d.ts → h3-proximity-BhfgNbdW.d.ts} +16 -12
  42. package/dist/{image-capture-2pKjYUS6.d.ts → image-capture-DZLxHDN9.d.ts} +1 -1
  43. package/dist/index-gzWJMVpo.d.ts +2 -0
  44. package/dist/index-pOjUCKEP.d.ts +24 -0
  45. package/dist/index.d.ts +59 -56
  46. package/dist/index.js +22 -21
  47. package/dist/{leaflet-map-overlay-CvfQXTye.d.ts → leaflet-map-overlay-Cew7XAoO.d.ts} +32 -17
  48. package/dist/licensing/index.d.ts +1 -1
  49. package/dist/licensing/index.js +1 -1
  50. package/dist/{logger-B81iwxx0.d.ts → logger-C9vugiQb.d.ts} +7 -1
  51. package/dist/map-data-sih4g64v.d.ts +49 -0
  52. package/dist/map-overlay-draw-B3R1c9Xp.d.ts +42 -0
  53. package/dist/{null-storage-backend-CsWvQwN8.d.ts → null-storage-backend-BKOS7JpL.d.ts} +4 -2
  54. package/dist/{opfs-storage-ChOzF_Wp.d.ts → opfs-storage-DOPkzgeT.d.ts} +54 -51
  55. package/dist/opfs-storage-backend-Cj_7PWoq.d.ts +13 -0
  56. package/dist/{permission-checker-jO2ziCWC.d.ts → permission-checker-DPJl_RU6.d.ts} +41 -1
  57. package/dist/persistence-middleware-BkHIqvaA.d.ts +55 -0
  58. package/dist/{recording-replayer-oOzPITrd.d.ts → recording-replayer-CmCIIZ02.d.ts} +2 -2
  59. package/dist/recording-slice-d6OtCrza.d.ts +28 -0
  60. package/dist/{replay-engine-DuX316ae.d.ts → replay-engine-BGBRQyXs.d.ts} +4 -1
  61. package/dist/{replay-scene-DtUm6bsr.d.ts → replay-scene-DpAGcAKY.d.ts} +3 -3
  62. package/dist/sensors/gps-error-handler.d.ts +1 -1
  63. package/dist/sensors/gps.d.ts +1 -1
  64. package/dist/sensors/gps.js +2 -2
  65. package/dist/sensors/index.d.ts +3 -3
  66. package/dist/sensors/index.js +1 -1
  67. package/dist/sensors/permission-checker.d.ts +2 -2
  68. package/dist/sensors/permission-checker.js +79 -1
  69. package/dist/state/app-selectors.d.ts +2 -2
  70. package/dist/state/app-selectors.js +33 -3
  71. package/dist/state/combined-root-state.d.ts +2 -0
  72. package/dist/state/combined-root-state.js +1 -0
  73. package/dist/state/create-slam-app-store.d.ts +2 -0
  74. package/dist/state/create-slam-app-store.js +75 -0
  75. package/dist/state/gps-ar-pose-sampler.d.ts +4 -0
  76. package/dist/state/gps-ar-pose-sampler.js +19 -0
  77. package/dist/state/{recording-coordinator.d.ts → gps-event-coordinator.d.ts} +1 -1
  78. package/dist/state/{recording-coordinator.js → gps-event-coordinator.js} +3 -4
  79. package/dist/state/index.d.ts +17 -17
  80. package/dist/state/index.js +10 -8
  81. package/dist/state/persistence-middleware.d.ts +2 -2
  82. package/dist/state/persistence-middleware.js +39 -11
  83. package/dist/state/recording-options.d.ts +1 -1
  84. package/dist/state/recording-replayer.d.ts +3 -2
  85. package/dist/state/recording-replayer.js +2 -2
  86. package/dist/state/recording-slice.d.ts +2 -0
  87. package/dist/state/{recorder-slice.js → recording-slice.js} +7 -15
  88. package/dist/state/replay-engine.d.ts +1 -1
  89. package/dist/state/replay-engine.js +3 -3
  90. package/dist/state/store-subscribers.d.ts +2 -2
  91. package/dist/state/store-subscribers.js +38 -20
  92. package/dist/state/subscribe-to-selector.d.ts +1 -1
  93. package/dist/state/tracking-quality.d.ts +2 -0
  94. package/dist/state/tracking-quality.js +723 -0
  95. package/dist/state/tracking-slice.d.ts +2 -0
  96. package/dist/state/tracking-slice.js +147 -0
  97. package/dist/storage/file-system-utils.d.ts +1 -1
  98. package/dist/storage/file-system.d.ts +2 -2
  99. package/dist/storage/file-system.js +154 -94
  100. package/dist/storage/index.d.ts +9 -11
  101. package/dist/storage/index.js +5 -7
  102. package/dist/storage/null-storage-backend.d.ts +1 -1
  103. package/dist/storage/null-storage-backend.js +7 -0
  104. package/dist/storage/opfs-storage-backend.d.ts +1 -1
  105. package/dist/storage/opfs-storage-backend.js +7 -1
  106. package/dist/storage/opfs-storage.d.ts +2 -2
  107. package/dist/storage/opfs-storage.js +69 -85
  108. package/dist/storage/storage-backend.d.ts +2 -2
  109. package/dist/storage/zip-export.d.ts +2 -2
  110. package/dist/storage/zip-export.js +55 -62
  111. package/dist/storage/zip-reader.d.ts +2 -2
  112. package/dist/storage/zip-reader.js +32 -5
  113. package/dist/storage-backend-CT0Il_AB.d.ts +28 -0
  114. package/dist/{store-subscribers-CuoIzB5T.d.ts → store-subscribers-Dce_CoJF.d.ts} +31 -7
  115. package/dist/{subscribe-to-selector-UTZ0KkAc.d.ts → subscribe-to-selector-DIHm3BRZ.d.ts} +1 -1
  116. package/dist/test-utils/browser-mocks.d.ts +1 -1
  117. package/dist/test-utils/zip-round-trip-helpers.d.ts +7 -1
  118. package/dist/test-utils/zip-round-trip-helpers.js +6 -5
  119. package/dist/tracking-slice-utCNND8t.d.ts +86 -0
  120. package/dist/types/ar-types.d.ts +1 -1
  121. package/dist/types/geo-types.d.ts +2 -2
  122. package/dist/types/index.d.ts +3 -3
  123. package/dist/utils/concurrency.d.ts +1 -1
  124. package/dist/utils/failure-tracker.d.ts +1 -1
  125. package/dist/utils/format-file-size.d.ts +1 -1
  126. package/dist/utils/fused-path.d.ts +1 -1
  127. package/dist/utils/index.d.ts +6 -6
  128. package/dist/utils/list-formatter.d.ts +1 -1
  129. package/dist/utils/logger.d.ts +1 -1
  130. package/dist/utils/logger.js +68 -8
  131. package/dist/visualization/accuracy-circles.d.ts +2 -0
  132. package/dist/visualization/accuracy-circles.js +57 -0
  133. package/dist/visualization/alignment-lerper.d.ts +1 -1
  134. package/dist/visualization/camera-follower.d.ts +1 -1
  135. package/dist/visualization/css3d-renderer-manager.d.ts +1 -1
  136. package/dist/visualization/frustum-visibility.d.ts +2 -0
  137. package/dist/visualization/frustum-visibility.js +139 -0
  138. package/dist/visualization/gps-anchor.d.ts +2 -0
  139. package/dist/visualization/gps-anchor.js +2 -0
  140. package/dist/visualization/gps-compass-cubes.d.ts +1 -1
  141. package/dist/visualization/gps-event-markers.d.ts +2 -2
  142. package/dist/visualization/gps-event-markers.js +69 -2
  143. package/dist/visualization/index.d.ts +17 -12
  144. package/dist/visualization/index.js +6 -2
  145. package/dist/visualization/leaflet-map-overlay.d.ts +1 -1
  146. package/dist/visualization/leaflet-map-overlay.js +56 -98
  147. package/dist/visualization/lerp-utils.d.ts +1 -1
  148. package/dist/visualization/map-data.d.ts +2 -0
  149. package/dist/visualization/map-data.js +33 -0
  150. package/dist/visualization/map-overlay-draw.d.ts +2 -0
  151. package/dist/visualization/map-overlay-draw.js +107 -0
  152. package/dist/visualization/map-overlay.d.ts +1 -1
  153. package/dist/visualization/three-dispose.d.ts +1 -1
  154. package/dist/visualization/vis-colors.d.ts +1 -1
  155. package/dist/{webxr-session-COlSrXdL.d.ts → webxr-session-DB5cIZnN.d.ts} +53 -12
  156. package/dist/{zip-export-Rtpi75JJ.d.ts → zip-export-QO4iLIi7.d.ts} +58 -7
  157. package/dist/{zip-reader-D7pkpX8K.d.ts → zip-reader-D1105idz.d.ts} +38 -1
  158. package/package.json +29 -18
  159. package/dist/ar/tracking-state.d.ts +0 -2
  160. package/dist/ar/tracking-state.js +0 -164
  161. package/dist/community-license-key-ChZLCJyO.d.ts +0 -21
  162. package/dist/file-system-BsQgYCU3.d.ts +0 -128
  163. package/dist/gps-event-markers-DpEy2qRd.d.ts +0 -67
  164. package/dist/licensing/community-license-key.d.ts +0 -2
  165. package/dist/licensing/community-license-key.js +0 -21
  166. package/dist/opfs-storage-backend-CVcmfJHN.d.ts +0 -11
  167. package/dist/persistence-middleware-DrptY02i.d.ts +0 -31
  168. package/dist/recorder-slice-fcwAXrsh.d.ts +0 -35
  169. package/dist/ref-point-importer-B_wn5ABk.d.ts +0 -62
  170. package/dist/ref-point-loader-Pt1smgHS.d.ts +0 -117
  171. package/dist/ref-points/h3-ref-point.d.ts +0 -2
  172. package/dist/ref-points/index.d.ts +0 -2
  173. package/dist/ref-points/index.js +0 -2
  174. package/dist/ref-points-slice-tC-Wd6XJ.d.ts +0 -48
  175. package/dist/reference-points-CFWy8_wj.d.ts +0 -68
  176. package/dist/routing-slice-BlHTM8eh.d.ts +0 -23
  177. package/dist/state/recorder-slice.d.ts +0 -2
  178. package/dist/state/ref-points-slice.d.ts +0 -2
  179. package/dist/state/ref-points-slice.js +0 -44
  180. package/dist/state/routing-slice.d.ts +0 -2
  181. package/dist/state/routing-slice.js +0 -13
  182. package/dist/state/store.d.ts +0 -9
  183. package/dist/state/store.js +0 -71
  184. package/dist/storage/ref-point-importer.d.ts +0 -2
  185. package/dist/storage/ref-point-importer.js +0 -163
  186. package/dist/storage/ref-point-loader.d.ts +0 -2
  187. package/dist/storage/ref-point-loader.js +0 -248
  188. package/dist/storage/ref-point-recovery.d.ts +0 -28
  189. package/dist/storage/ref-point-recovery.js +0 -159
  190. package/dist/storage-backend-BcEItnFr.d.ts +0 -20
  191. package/dist/store-C7PKOj8S.d.ts +0 -96
  192. package/dist/tracking-state-B9QfuZCg.d.ts +0 -101
  193. package/dist/visualization/reference-points.d.ts +0 -2
  194. package/dist/visualization/reference-points.js +0 -160
  195. /package/dist/{alignment-lerper-D9BeDwZX.d.ts → alignment-lerper-L1RbWHQ4.d.ts} +0 -0
  196. /package/dist/{ar-types-B-ORgk6Z.d.ts → ar-types-Ck8LEq8k.d.ts} +0 -0
  197. /package/dist/{camera-blit-capture-DHuyrrxr.d.ts → camera-blit-capture-C1V9I1Z6.d.ts} +0 -0
  198. /package/dist/{camera-follower-BMqPddxx.d.ts → camera-follower-1k8mnw5J.d.ts} +0 -0
  199. /package/dist/{capture-failure-tracker-2Q6oI2uQ.d.ts → capture-failure-tracker-JKb1ST8E.d.ts} +0 -0
  200. /package/dist/{chromium-camera-access-workaround-CU5zSKNr.d.ts → chromium-camera-access-workaround-NvChXsi8.d.ts} +0 -0
  201. /package/dist/{concurrency-p6gB30fT.d.ts → concurrency-CMw8pSaG.d.ts} +0 -0
  202. /package/dist/{css3d-renderer-manager-rgLMVAvd.d.ts → css3d-renderer-manager-CkjCb2HW.d.ts} +0 -0
  203. /package/dist/{failure-tracker-1sQ_Cgyw.d.ts → failure-tracker-DnHXOIRa.d.ts} +0 -0
  204. /package/dist/{file-system-utils-5cWO8Xyv.d.ts → file-system-utils-CtKiiw27.d.ts} +0 -0
  205. /package/dist/{format-file-size-onGq0k1Q.d.ts → format-file-size-BTS5H62-.d.ts} +0 -0
  206. /package/dist/{gps-compass-cubes-C-6JEnpR.d.ts → gps-compass-cubes-CsLtFv4B.d.ts} +0 -0
  207. /package/dist/{gps-error-handler-VPuNOuf8.d.ts → gps-error-handler-Dg3ziRVY.d.ts} +0 -0
  208. /package/dist/{lerp-utils-87NWjUOD.d.ts → lerp-utils-B8MM8zCR.d.ts} +0 -0
  209. /package/dist/{list-formatter-DKQQVLcl.d.ts → list-formatter-CNqi-E2j.d.ts} +0 -0
  210. /package/dist/{map-overlay-DuiZuCTx.d.ts → map-overlay-DAmreyA3.d.ts} +0 -0
  211. /package/dist/{recording-options-BYUFX8iU.d.ts → recording-options-Dq6IsKAv.d.ts} +0 -0
  212. /package/dist/{scene-node-names-DPsaE2z3.d.ts → scene-node-names-6LBjPmwz.d.ts} +0 -0
  213. /package/dist/{three-dispose-CCBHjE_a.d.ts → three-dispose-C2drkp6f.d.ts} +0 -0
  214. /package/dist/{vis-colors-Qi2vlRGt.d.ts → vis-colors-CHQhjal3.d.ts} +0 -0
  215. /package/dist/{xr-camera-texture-BbukNE79.d.ts → xr-camera-texture-BU5YvKAM.d.ts} +0 -0
  216. /package/dist/{xr-error-handler-DTHfmvrl.d.ts → xr-error-handler-CDY5QxGb.d.ts} +0 -0
@@ -0,0 +1,2 @@
1
+ import { H3_RESOLUTION, approxDistanceMetres, findNearbyGeoAnchor, gpsToH3, h3CellsMatch, isH3Index } from "./h3-proximity.js";
2
+ export { H3_RESOLUTION, approxDistanceMetres, findNearbyGeoAnchor, gpsToH3, h3CellsMatch, isH3Index };
@@ -18,6 +18,21 @@ interface GpsCoord {
18
18
  readonly lat: number;
19
19
  readonly lng: number;
20
20
  }
21
+ /**
22
+ * A raw GPS sample with optional accuracy (1σ horizontal radius in meters,
23
+ * mirroring the browser's `GeolocationCoordinates.accuracy`).
24
+ *
25
+ * Used for 2D map visualizations that draw a per-event accuracy circle
26
+ * alongside the GPS polyline so that low-quality fixes (large radius) are
27
+ * visually distinguishable from accurate fixes (small radius).
28
+ */
29
+ interface RawGpsSample extends GpsCoord {
30
+ /**
31
+ * Horizontal accuracy in meters (1σ / 68% confidence).
32
+ * `undefined` when the source GPS event lacked the field.
33
+ */
34
+ readonly accuracy?: number;
35
+ }
21
36
  /**
22
37
  * A reference point with GPS location and a human-readable name.
23
38
  * Extends GpsCoord so it is usable wherever GpsCoord is expected.
@@ -26,4 +41,4 @@ interface RefPointMarker extends GpsCoord {
26
41
  readonly name: string;
27
42
  }
28
43
  //#endregion
29
- export { RefPointMarker as n, GpsCoord as t };
44
+ export { RawGpsSample as n, RefPointMarker as r, GpsCoord as t };
@@ -17,7 +17,7 @@ interface GpsPosition {
17
17
  /**
18
18
  * Raw device orientation from the browser's DeviceOrientationEvent API.
19
19
  * Fields are nullable because sensors may be unavailable on some devices.
20
- * See also: DeviceOrientation in ar/tracking-state.ts (resolved, non-nullable).
20
+ * See also: DeviceOrientation in state/tracking-slice.ts (resolved, non-nullable).
21
21
  */
22
22
  interface RawDeviceOrientation {
23
23
  alpha: number | null;
@@ -0,0 +1,48 @@
1
+ import { i as LatLongAlt, r as LatLong } from "./index-gzWJMVpo.js";
2
+ import * as THREE from "three";
3
+
4
+ //#region ../src/visualization/gps-anchor.d.ts
5
+ type GpsAnchorMode = 'snap-when-offscreen' | 'snap-every-tick';
6
+ type GpsAnchorPhase = 'bootstrap' | 'anchored';
7
+ /**
8
+ * The minimum shape needed for the bootstrap median — a `LatLong` with
9
+ * optional altitude. Re-exported as a named alias so the sidecar and
10
+ * tests can refer to "the kind of point the anchor samples" without
11
+ * importing core types.
12
+ */
13
+ type GpsAnchorSamplePoint = LatLong | LatLongAlt;
14
+ interface GpsAnchorOptions {
15
+ readonly object3D: THREE.Object3D;
16
+ readonly arWorldGroup: THREE.Object3D;
17
+ readonly camera: THREE.Camera;
18
+ readonly gpsPoint: LatLong | LatLongAlt;
19
+ readonly skipBootstrap?: boolean;
20
+ readonly getAlignmentMatrix: () => readonly number[] | null;
21
+ readonly getGpsZeroRef: () => LatLong | null;
22
+ /** Returns the current GPS reading at "now", or null when no fix yet. */
23
+ readonly getCurrentGpsPoint: () => GpsAnchorSamplePoint | null;
24
+ readonly mode?: GpsAnchorMode;
25
+ readonly floorY?: () => number | null;
26
+ readonly distanceThreshold?: number;
27
+ readonly angleThresholdInDegrees?: number;
28
+ readonly targetPosRefreshRateInSec?: number;
29
+ /** Number of 1 Hz samples collected during bootstrap. Default 7. */
30
+ readonly secondsToAccumulateGpsPose?: number;
31
+ /** Wait window (seconds) at phase entry during which no samples are taken. Default 0. */
32
+ readonly settlingSeconds?: number;
33
+ readonly heightAboveGround?: number | null;
34
+ }
35
+ interface GpsAnchor {
36
+ readonly phase: GpsAnchorPhase;
37
+ readonly isFullyAnchored: boolean;
38
+ /** Current target GPS pose; during `bootstrap` this is the seed, post-bootstrap the median. */
39
+ readonly gpsPoint: LatLong | LatLongAlt;
40
+ markMovedExternally(): void;
41
+ setGpsPoint(point: LatLong | LatLongAlt): void;
42
+ dispose(): void;
43
+ /** @internal — testing seam; exposed in lieu of pumping `runFrameUpdates`. */
44
+ __tickForTests(dt: number, elapsed: number): void;
45
+ }
46
+ declare function createGpsAnchor(options: GpsAnchorOptions): GpsAnchor;
47
+ //#endregion
48
+ export { GpsAnchorSamplePoint as a, GpsAnchorPhase as i, GpsAnchorMode as n, createGpsAnchor as o, GpsAnchorOptions as r, GpsAnchor as t };
@@ -0,0 +1,257 @@
1
+ import { registerFrameUpdate } from "./ar/frame-loop.js";
2
+ import { isObjectInCameraFrustum } from "./visualization/frustum-visibility.js";
3
+ import { calcRelativeCoordsInMeters } from "./core/index.js";
4
+ import * as THREE from "three";
5
+ //#region ../src/visualization/frame-conversions.ts
6
+ /**
7
+ * `frame-conversions` — small, pure coordinate-frame helpers for the AR scene
8
+ * graph. Currently a single function, `nueToArLocal`, that converts a
9
+ * GPS-world NUE point into the AR-odometry local frame of `arWorldGroup`.
10
+ *
11
+ * Background: the scene root is GPS-world NUE; `arWorldGroup.matrix` IS the
12
+ * alignment matrix, which maps **AR-odometry NUE → GPS-world NUE**. So a
13
+ * direct child of `arWorldGroup` whose WORLD position must equal a GPS-world
14
+ * point `nue` has to store `alignment⁻¹ · nue` as its LOCAL position —
15
+ * writing raw `nue` double-applies the alignment. Getting this wrong was the
16
+ * alignment-frame bug
17
+ * (gps-plus-slam/GpsPlusSlamJs_Docs/docs/2026-05-31-gps-anchor-alignment-frame-bug.md);
18
+ * this helper centralises the conversion behind one tested, well-named seam.
19
+ *
20
+ * Design (see the plan
21
+ * gps-plus-slam/GpsPlusSlamJs_Docs/docs/2026-05-31-nue-to-ar-local-helper-plan.md):
22
+ * - **Takes the explicit alignment matrix array**, NOT `Object3D.worldToLocal`.
23
+ * `worldToLocal` would read the live, mid-lerp `matrixWorld` and break
24
+ * replay determinism; the explicit target matrix keeps the result a pure
25
+ * function of its inputs. Full rationale in the review doc
26
+ * (gps-plus-slam/GpsPlusSlamJs_Docs/docs/2026-05-31-worldtolocal-frame-helper-review.md).
27
+ * - **Positions only.** No pose/quaternion variant exists until a caller
28
+ * needs one (YAGNI). No caller currently sets a GPS-world rotation.
29
+ */
30
+ const scratchMatrix = new THREE.Matrix4();
31
+ /**
32
+ * Convert a GPS-world NUE point into the AR-local position to write into a
33
+ * direct child of `arWorldGroup`, given the current alignment matrix.
34
+ *
35
+ * Computes `alignment⁻¹ · nue`. When the result is later composed back by the
36
+ * group's matrix (the same `alignment`), the child's WORLD position equals
37
+ * `nue` exactly.
38
+ *
39
+ * @param alignment - The alignment matrix as a 16-element **column-major**
40
+ * array (AR-odometry NUE → GPS-world NUE), e.g. from `getAlignmentMatrix()`
41
+ * or `THREE.Matrix4.toArray()`. Not mutated.
42
+ * @param nue - The GPS-world NUE point `[north, up, east]` in metres. Not
43
+ * mutated.
44
+ * @param out - Optional target vector to write into and return (pass a reused
45
+ * scratch to avoid allocation on hot paths). Defaults to a fresh
46
+ * `THREE.Vector3`.
47
+ * @returns `out`, set to the AR-local position.
48
+ */
49
+ function nueToArLocal(alignment, nue, out = new THREE.Vector3()) {
50
+ scratchMatrix.fromArray(alignment).invert();
51
+ return out.set(nue[0], nue[1], nue[2]).applyMatrix4(scratchMatrix);
52
+ }
53
+ //#endregion
54
+ //#region ../src/visualization/gps-anchor.ts
55
+ /**
56
+ * `createGpsAnchor` — GPS-anchored placement of a single `THREE.Object3D`.
57
+ *
58
+ * See the colocated sidecar (`gps-anchor.ts.md`) and the port plan at
59
+ * `gps-plus-slam/GpsPlusSlamJs_Docs/docs/2026-05-13-gps-anchor-port-plan.md`
60
+ * for the full design, state machine, and test matrix.
61
+ *
62
+ * This file implements sub-steps 2 (bootstrap phase), 3 (steady-state
63
+ * `'snap-every-tick'` + distance-scaled threshold gate), and 4
64
+ * (`'snap-when-offscreen'` mode gate + alignment-matrix large-jump
65
+ * bypass). Floor-Y correction is sub-step 6 and remains deferred.
66
+ */
67
+ /**
68
+ * Module-level registry of objects currently owned by a `GpsAnchor`.
69
+ * Used to detect nested anchors (parent + child both anchored) which
70
+ * we explicitly forbid; mirrors the C# invariant.
71
+ */
72
+ const anchoredObjects = /* @__PURE__ */ new WeakSet();
73
+ function isObjectInAnchoredChain(object) {
74
+ let cursor = object;
75
+ while (cursor) {
76
+ if (anchoredObjects.has(cursor)) return true;
77
+ cursor = cursor.parent;
78
+ }
79
+ return false;
80
+ }
81
+ function median(values) {
82
+ const sorted = [...values].sort((a, b) => a - b);
83
+ const mid = Math.floor(sorted.length / 2);
84
+ if (sorted.length % 2 === 0) return (sorted[mid - 1] + sorted[mid]) / 2;
85
+ return sorted[mid];
86
+ }
87
+ function medianPoint(samples) {
88
+ const lat = median(samples.map((s) => s.lat));
89
+ const lon = median(samples.map((s) => s.lon));
90
+ const alts = samples.map((s) => "altitude" in s ? s.altitude : void 0).filter((a) => typeof a === "number");
91
+ if (alts.length > 0) return {
92
+ lat,
93
+ lon,
94
+ altitude: median(alts)
95
+ };
96
+ return {
97
+ lat,
98
+ lon
99
+ };
100
+ }
101
+ function createGpsAnchor(options) {
102
+ if (isObjectInAnchoredChain(options.object3D)) throw new Error("createGpsAnchor: nested GpsAnchors are not supported — the supplied object3D is already inside an anchored parent chain.");
103
+ anchoredObjects.add(options.object3D);
104
+ const sampleCount = options.secondsToAccumulateGpsPose ?? 7;
105
+ if (sampleCount < 1) {
106
+ anchoredObjects.delete(options.object3D);
107
+ throw new Error("createGpsAnchor: secondsToAccumulateGpsPose must be >= 1 — use skipBootstrap:true to bypass the accumulation phase.");
108
+ }
109
+ const settlingSeconds = options.settlingSeconds ?? 0;
110
+ const distanceThreshold = options.distanceThreshold ?? 2;
111
+ options.angleThresholdInDegrees;
112
+ const mode = options.mode ?? "snap-when-offscreen";
113
+ const LARGE_JUMP_TRANSLATION_M = 4;
114
+ const LARGE_JUMP_Y_M = 20;
115
+ const LARGE_JUMP_ROTATION_DEG = 2;
116
+ const scratchTarget = new THREE.Vector3();
117
+ const scratchCamWorld = new THREE.Vector3();
118
+ const scratchObjWorld = new THREE.Vector3();
119
+ const scratchPrevMatrix = new THREE.Matrix4();
120
+ const scratchCurrMatrix = new THREE.Matrix4();
121
+ const scratchPrevTrans = new THREE.Vector3();
122
+ const scratchCurrTrans = new THREE.Vector3();
123
+ const scratchPrevQuat = new THREE.Quaternion();
124
+ const scratchCurrQuat = new THREE.Quaternion();
125
+ let phase = options.skipBootstrap === true ? "anchored" : "bootstrap";
126
+ let isFullyAnchored = phase === "anchored";
127
+ let gpsPoint = options.gpsPoint;
128
+ let phaseEnteredAtElapsed = null;
129
+ let lastSampleAtElapsed = null;
130
+ const samples = [];
131
+ /**
132
+ * Snapshot of the previous tick's alignment matrix. `null` until the
133
+ * first steady-state tick in which `getAlignmentMatrix()` returned a
134
+ * non-null value. Used by the large-jump bypass to compare against
135
+ * the current tick's matrix.
136
+ */
137
+ let prevAlignmentMatrix = null;
138
+ /**
139
+ * Returns true iff the alignment matrix has jumped by more than the
140
+ * configured large-jump thresholds between `prev` and `curr`. A
141
+ * `null` `prev` (first tick) is treated as "no jump".
142
+ */
143
+ const detectLargeAlignmentJump = (prev, curr) => {
144
+ if (prev === null || curr === null) return false;
145
+ scratchPrevMatrix.fromArray(prev);
146
+ scratchCurrMatrix.fromArray(curr);
147
+ scratchPrevTrans.setFromMatrixPosition(scratchPrevMatrix);
148
+ scratchCurrTrans.setFromMatrixPosition(scratchCurrMatrix);
149
+ const dTrans = scratchPrevTrans.distanceTo(scratchCurrTrans);
150
+ const dY = Math.abs(scratchCurrTrans.y - scratchPrevTrans.y);
151
+ scratchPrevQuat.setFromRotationMatrix(scratchPrevMatrix);
152
+ scratchCurrQuat.setFromRotationMatrix(scratchCurrMatrix);
153
+ const dRotDeg = scratchPrevQuat.angleTo(scratchCurrQuat) * 180 / Math.PI;
154
+ return dTrans > LARGE_JUMP_TRANSLATION_M || dY > LARGE_JUMP_Y_M || dRotDeg > LARGE_JUMP_ROTATION_DEG;
155
+ };
156
+ const enterBootstrap = () => {
157
+ phase = "bootstrap";
158
+ isFullyAnchored = false;
159
+ phaseEnteredAtElapsed = null;
160
+ lastSampleAtElapsed = null;
161
+ samples.length = 0;
162
+ prevAlignmentMatrix = null;
163
+ };
164
+ const commitMedian = () => {
165
+ gpsPoint = medianPoint(samples);
166
+ phase = "anchored";
167
+ isFullyAnchored = true;
168
+ samples.length = 0;
169
+ };
170
+ /**
171
+ * Steady-state: compute the GPS-world NUE target from the stored
172
+ * `gpsPoint` and the current `zeroRef`, map it into `arWorldGroup`'s
173
+ * AR-local frame via the inverse alignment matrix, and commit it to
174
+ * `object3D.position` iff the (AR-local) position delta exceeds the
175
+ * distance-scaled threshold AND the mode gate (with optional large-jump
176
+ * bypass) allows it.
177
+ *
178
+ * Frame note: `arWorldGroup.matrix` IS the alignment matrix, which maps
179
+ * **AR-odometry NUE → GPS-world NUE**. `object3D.position` is a *local*
180
+ * transform in that AR-odometry frame. To make the object's *world*
181
+ * position equal the GPS-world `nue`, the local target must be
182
+ * `alignment⁻¹ · nue` — writing raw `nue` would double-apply the
183
+ * alignment (world = `alignment · nue`). See
184
+ * `gps-plus-slam/GpsPlusSlamJs_Docs/docs/2026-05-31-gps-anchor-alignment-frame-bug.md`.
185
+ *
186
+ * Returns silently on missing inputs (no `zeroRef`, or no alignment
187
+ * matrix yet — an AR-local object cannot be placed without knowing the
188
+ * AR↔NUE transform).
189
+ */
190
+ const maybeCommitSteadyState = () => {
191
+ const zero = options.getGpsZeroRef();
192
+ if (zero === null || zero === void 0) return;
193
+ const currentAlignment = options.getAlignmentMatrix();
194
+ const largeJump = detectLargeAlignmentJump(prevAlignmentMatrix, currentAlignment);
195
+ prevAlignmentMatrix = currentAlignment;
196
+ if (currentAlignment === null || currentAlignment === void 0) return;
197
+ const targetAlt = "altitude" in gpsPoint && typeof gpsPoint.altitude === "number" ? gpsPoint.altitude : 0;
198
+ const nue = calcRelativeCoordsInMeters(zero, gpsPoint, targetAlt, 0);
199
+ nueToArLocal(currentAlignment, [
200
+ nue[0],
201
+ nue[1],
202
+ nue[2]
203
+ ], scratchTarget);
204
+ options.camera.getWorldPosition(scratchCamWorld);
205
+ options.object3D.getWorldPosition(scratchObjWorld);
206
+ const scale = 1 + 10 * scratchCamWorld.distanceTo(scratchObjWorld) / 100;
207
+ if (options.object3D.position.distanceTo(scratchTarget) < distanceThreshold * scale) return;
208
+ if (mode === "snap-when-offscreen" && !largeJump) {
209
+ if (isObjectInCameraFrustum(options.camera, options.object3D)) return;
210
+ }
211
+ options.object3D.position.copy(scratchTarget);
212
+ };
213
+ const tick = (_dt, elapsed) => {
214
+ if (phase === "anchored") {
215
+ maybeCommitSteadyState();
216
+ return;
217
+ }
218
+ if (phaseEnteredAtElapsed === null) {
219
+ phaseEnteredAtElapsed = elapsed;
220
+ lastSampleAtElapsed = elapsed - 1;
221
+ }
222
+ if (elapsed - phaseEnteredAtElapsed < settlingSeconds) return;
223
+ if (lastSampleAtElapsed !== null && elapsed - lastSampleAtElapsed < 1) return;
224
+ const sample = options.getCurrentGpsPoint();
225
+ if (sample === null || sample === void 0) return;
226
+ samples.push(sample);
227
+ lastSampleAtElapsed = elapsed;
228
+ if (samples.length >= sampleCount) commitMedian();
229
+ };
230
+ const unregister = registerFrameUpdate(tick);
231
+ return {
232
+ get phase() {
233
+ return phase;
234
+ },
235
+ get isFullyAnchored() {
236
+ return isFullyAnchored;
237
+ },
238
+ get gpsPoint() {
239
+ return gpsPoint;
240
+ },
241
+ markMovedExternally() {
242
+ enterBootstrap();
243
+ },
244
+ setGpsPoint(point) {
245
+ gpsPoint = point;
246
+ },
247
+ dispose() {
248
+ unregister();
249
+ anchoredObjects.delete(options.object3D);
250
+ },
251
+ __tickForTests(dt, elapsed) {
252
+ tick(dt, elapsed);
253
+ }
254
+ };
255
+ }
256
+ //#endregion
257
+ export { nueToArLocal as n, createGpsAnchor as t };
@@ -0,0 +1,43 @@
1
+ import { t as ARPose } from "./ar-types-Ck8LEq8k.js";
2
+ import { Quaternion, Vector3 } from "gps-plus-slam-js";
3
+
4
+ //#region ../src/state/gps-ar-pose-sampler.d.ts
5
+ /**
6
+ * GPS coordinate captured at observation time. Loosely typed so the recorder
7
+ * can pass either a `RawGpsPoint` or a plain `{ latitude, longitude,
8
+ * altitude? }` shape from the fused-path computation.
9
+ */
10
+ interface GpsAnchorSampleGpsPoint {
11
+ readonly latitude: number;
12
+ readonly longitude: number;
13
+ readonly altitude?: number;
14
+ }
15
+ /**
16
+ * A snapshot pairing the raw GPS reading, the optional fused GPS reading
17
+ * (computed when an alignment matrix is available), and the AR pose at the
18
+ * moment of capture. The `odomPosition` and `odomRotation` are pre-extracted
19
+ * from the AR pose for convenience — callers persisting the snapshot
20
+ * usually need them in tuple form.
21
+ */
22
+ interface GpsAnchorSample {
23
+ readonly gpsPoint: GpsAnchorSampleGpsPoint;
24
+ readonly fusedGpsPoint?: GpsAnchorSampleGpsPoint;
25
+ readonly arPose: ARPose;
26
+ readonly odomPosition: Vector3;
27
+ readonly odomRotation: Quaternion;
28
+ readonly timestamp: number;
29
+ }
30
+ interface CaptureGpsAnchorSampleOptions {
31
+ /** Optional fused GPS point (computed elsewhere from the alignment matrix). */
32
+ readonly fusedGpsPoint?: GpsAnchorSampleGpsPoint;
33
+ /** Defaults to `Date.now()` at capture time. */
34
+ readonly timestamp?: number;
35
+ }
36
+ /**
37
+ * Capture a GPS-AR pose snapshot. Extracts `odomPosition`/`odomRotation`
38
+ * from the supplied `arPose`. The supplied `gpsPoint` and optional
39
+ * `fusedGpsPoint` are stored verbatim.
40
+ */
41
+ declare function captureGpsAnchorSample(arPose: ARPose, gpsPoint: GpsAnchorSampleGpsPoint, options?: CaptureGpsAnchorSampleOptions): GpsAnchorSample;
42
+ //#endregion
43
+ export { captureGpsAnchorSample as i, GpsAnchorSample as n, GpsAnchorSampleGpsPoint as r, CaptureGpsAnchorSampleOptions as t };
@@ -1,15 +1,16 @@
1
- import { t as ARPose } from "./ar-types-B-ORgk6Z.js";
2
- import { n as RawDeviceOrientation, t as GpsPosition } from "./gps-Ddzr2KlX.js";
3
- import { c as RecordGpsEventPayload, l as RecorderStore } from "./store-C7PKOj8S.js";
4
- import { Quaternion, RawGpsPoint, Vector3, eulerToQuaternion } from "gps-plus-slam-js";
1
+ import { t as ARPose } from "./ar-types-Ck8LEq8k.js";
2
+ import { n as RawDeviceOrientation, t as GpsPosition } from "./gps-DXoQb9Kf.js";
3
+ import { i as SlamAppStore } from "./create-slam-app-store-CNsZ3rcX.js";
4
+ import { Quaternion, RawGpsPoint, RecordGpsEventPayload, Vector3, eulerToQuaternion } from "gps-plus-slam-js";
5
+ import { ReducersMapObject } from "@reduxjs/toolkit";
5
6
 
6
- //#region ../src/state/recording-coordinator.d.ts
7
+ //#region ../src/state/gps-event-coordinator.d.ts
7
8
  /**
8
9
  * Configuration for the recording coordinator
9
10
  */
10
11
  interface RecordingCoordinatorConfig {
11
12
  /** Redux store to dispatch actions to */
12
- store: RecorderStore;
13
+ store: SlamAppStore<ReducersMapObject>;
13
14
  /** Function to get current AR pose (from WebXR module) */
14
15
  getArPose: () => ARPose | null;
15
16
  }
@@ -0,0 +1,103 @@
1
+ import { LatLong, Vector3 } from "gps-plus-slam-js";
2
+
3
+ //#region ../src/visualization/gps-event-markers.d.ts
4
+ /**
5
+ * Optional GPS-accuracy hints used by {@link GpsEventVisualizer.addGpsEvent}
6
+ * to render the raw-GPS marker as a non-uniform-scaled ellipsoid.
7
+ *
8
+ * Both fields are optional — missing or non-positive values fall back to the
9
+ * legacy fixed 8 cm sphere (same defensive policy as `preview-map.ts`).
10
+ */
11
+ interface GpsEventAccuracy {
12
+ /** Horizontal 1σ accuracy in metres (applied to both X and Z axes). */
13
+ horizontal?: number;
14
+ /** Vertical 1σ accuracy in metres (applied to the Y axis). */
15
+ vertical?: number;
16
+ }
17
+ /**
18
+ * Manager for GPS event visualization
19
+ */
20
+ declare class GpsEventVisualizer {
21
+ private rawGpsMarkers;
22
+ private fusedMarkers;
23
+ private snapshotMarkers;
24
+ private zeroRef;
25
+ private eventCounter;
26
+ private snapshotCounter;
27
+ /**
28
+ * Set the GPS zero reference (origin for coordinate conversion).
29
+ * Must be called before adding GPS events.
30
+ *
31
+ * @param zero - GPS coordinates of the origin
32
+ */
33
+ setZeroRef(zero: LatLong): void;
34
+ /**
35
+ * Get the current zero reference
36
+ */
37
+ getZeroRef(): LatLong | null;
38
+ /**
39
+ * Add a GPS event to the visualization.
40
+ * Creates both a raw GPS marker and a fused alignment marker.
41
+ *
42
+ * @param gpsCoords - GPS position as [x, y, z] meters from zero reference
43
+ * @param odomPosition - AR odometry position at the time of GPS reading
44
+ * @param accuracy - Optional 1σ GPS accuracy. When both fields are positive,
45
+ * the raw-GPS marker is rendered as a non-uniform-scaled ellipsoid:
46
+ * `mesh.scale.set(horizontal, vertical, horizontal)` over a unit sphere.
47
+ * When missing, zero, or negative, the marker falls back to the legacy
48
+ * fixed 8 cm sphere (same defensive policy as `preview-map.ts`).
49
+ * Cyan fused and red snapshot markers are never affected by this argument.
50
+ */
51
+ addGpsEvent(gpsCoords: Vector3, odomPosition: Vector3, accuracy?: GpsEventAccuracy): void;
52
+ /**
53
+ * Add an alignment snapshot marker at the given NUE position.
54
+ * Snapshots represent the system's best GPS estimate at each alignment update.
55
+ * Added to scene root (GPS world space), not arWorldGroup.
56
+ */
57
+ addAlignmentSnapshot(nuePosition: Vector3): void;
58
+ /**
59
+ * Get the NUE positions of all alignment snapshot markers.
60
+ * Used at session end to convert to GPS coordinates for the summary map.
61
+ */
62
+ getAlignmentSnapshotPositions(): Vector3[];
63
+ /**
64
+ * Create a sphere marker mesh with the given color and name.
65
+ */
66
+ private createMarkerMesh;
67
+ /**
68
+ * Clear all markers from the scene and reset state.
69
+ */
70
+ clearAll(): void;
71
+ /**
72
+ * Get count of visible markers.
73
+ */
74
+ getCounts(): {
75
+ raw: number;
76
+ fused: number;
77
+ snapshots: number;
78
+ };
79
+ /**
80
+ * Diagnostic accessor: returns the world-space size (`THREE.Box3.setFromObject`)
81
+ * of each raw-GPS marker, in insertion order.
82
+ *
83
+ * Used by the rec31 accuracy-ellipsoid Playwright spec (§3c) to verify the
84
+ * relative scaling of two events with different `latLongAccuracy`. This is
85
+ * the size of the rendered ellipsoid in scene units (= metres in replay
86
+ * mode), not the underlying geometry radius — so it correctly reflects the
87
+ * `mesh.scale.set(h, v, h)` applied for accuracy-aware markers.
88
+ *
89
+ * @returns array of `{ x, y, z }` sizes — empty array if there are no markers.
90
+ */
91
+ getRawMarkerWorldSizes(): Array<{
92
+ x: number;
93
+ y: number;
94
+ z: number;
95
+ }>;
96
+ }
97
+ /**
98
+ * Singleton instance for global use.
99
+ * Import this to visualize GPS events.
100
+ */
101
+ declare const gpsEventVisualizer: GpsEventVisualizer;
102
+ //#endregion
103
+ export { GpsEventVisualizer as n, gpsEventVisualizer as r, GpsEventAccuracy as t };
@@ -1,21 +1,25 @@
1
- //#region ../src/ref-points/h3-ref-point.d.ts
1
+ //#region ../src/geo/h3-proximity.d.ts
2
2
  /**
3
- * H3 Reference Point Matching
3
+ * H3 Geo-Anchor Proximity Matching
4
4
  *
5
- * Utility for matching GPS positions to known reference points using
6
- * H3 hexagonal grid indices. Uses gridDisk(cell, 1) to create a ~65m
5
+ * Generic utility for matching GPS positions to known geo-anchored points
6
+ * using H3 hexagonal grid indices. Uses gridDisk(cell, 1) to create a ~65m
7
7
  * safe zone that absorbs GPS jitter of 3-10m.
8
8
  *
9
+ * Renamed from `ref-points/h3-ref-point.ts` in Iter 4 to drop recorder-only
10
+ * naming. The math is unchanged; only the public type/function names were
11
+ * generalised so the framework can serve any geo-anchored consumer.
12
+ *
9
13
  * @see docs/2026-03-08-ref-point-naming-investigation.md §6
10
14
  */
11
15
  /** H3 resolution 11: ~25m edge, ~65m gridDisk safe zone */
12
16
  declare const H3_RESOLUTION = 11;
13
17
  /**
14
- * A known reference point with its H3 cell index and GPS coordinates,
18
+ * A known geo-anchored point with its H3 cell index and GPS coordinates,
15
19
  * used for proximity matching. GPS coordinates are needed to rank
16
20
  * multiple candidates by distance when their gridDisk zones overlap.
17
21
  */
18
- interface KnownRefPoint {
22
+ interface KnownGeoAnchor {
19
23
  readonly h3Index: string;
20
24
  readonly displayName?: string;
21
25
  readonly lat: number;
@@ -33,27 +37,27 @@ declare function gpsToH3(lat: number, lng: number): string;
33
37
  */
34
38
  declare function approxDistanceMetres(lat1: number, lon1: number, lat2: number, lon2: number): number;
35
39
  /**
36
- * Find the known reference point closest to the given GPS position,
40
+ * Find the known geo-anchor closest to the given GPS position,
37
41
  * provided it falls within the gridDisk safe zone.
38
42
  *
39
43
  * The safe zone is the center cell plus its 6 neighbors (~65m radius),
40
- * which absorbs GPS jitter of 3-10m while keeping ref points >130m apart
44
+ * which absorbs GPS jitter of 3-10m while keeping anchors >130m apart
41
45
  * distinguishable.
42
46
  *
43
- * When multiple ref points have overlapping safe zones (65–130 m apart),
47
+ * When multiple anchors have overlapping safe zones (65–130 m apart),
44
48
  * the closest one by equirectangular distance is returned.
45
49
  */
46
- declare function findNearbyRefPoint(lat: number, lng: number, knownRefPoints: readonly KnownRefPoint[]): KnownRefPoint | undefined;
50
+ declare function findNearbyGeoAnchor(lat: number, lng: number, knownAnchors: readonly KnownGeoAnchor[]): KnownGeoAnchor | undefined;
47
51
  /**
48
52
  * Check whether two H3 indices refer to the same physical location
49
53
  * (i.e., one falls within the other's gridDisk safe zone).
50
54
  * This is the canonical cross-session matching check.
51
55
  */
52
- declare function h3RefsMatch(h3a: string, h3b: string): boolean;
56
+ declare function h3CellsMatch(h3a: string, h3b: string): boolean;
53
57
  /**
54
58
  * Check whether a string looks like a valid H3 resolution-11 index.
55
59
  * Used to distinguish H3 IDs from legacy user-typed string IDs.
56
60
  */
57
61
  declare function isH3Index(id: string): boolean;
58
62
  //#endregion
59
- export { gpsToH3 as a, findNearbyRefPoint as i, KnownRefPoint as n, h3RefsMatch as o, approxDistanceMetres as r, isH3Index as s, H3_RESOLUTION as t };
63
+ export { gpsToH3 as a, findNearbyGeoAnchor as i, KnownGeoAnchor as n, h3CellsMatch as o, approxDistanceMetres as r, isH3Index as s, H3_RESOLUTION as t };
@@ -1,4 +1,4 @@
1
- import { a as WebXRQuaternion, o as WebXRVec3, t as ARPose } from "./ar-types-B-ORgk6Z.js";
1
+ import { a as WebXRQuaternion, o as WebXRVec3, t as ARPose } from "./ar-types-Ck8LEq8k.js";
2
2
 
3
3
  //#region ../src/ar/image-capture.d.ts
4
4
  /**
@@ -0,0 +1,2 @@
1
+ import { ArImageCapture as ArImageCapture$1, GpsPoint as GpsPoint$1, LatLong as LatLong$1, LatLongAlt, Matrix4 as Matrix4$1, Quaternion as Quaternion$1, RootState as LibraryRootState, RootState as RootState$1, Vector3 as Vector3$1, calcGpsCoords as calcGpsCoords$1, calcRelativeCoordsInMeters, createGpsSlamStore, isIdentityMatrix4, odometryTrackingRestarted, validateLicenseKey as validateLicenseKey$1, webxrToNUE } from "gps-plus-slam-js";
2
+ export { LibraryRootState as a, RootState$1 as c, calcRelativeCoordsInMeters as d, createGpsSlamStore as f, webxrToNUE as g, validateLicenseKey$1 as h, LatLongAlt as i, Vector3$1 as l, odometryTrackingRestarted as m, GpsPoint$1 as n, Matrix4$1 as o, isIdentityMatrix4 as p, LatLong$1 as r, Quaternion$1 as s, ArImageCapture$1 as t, calcGpsCoords$1 as u };
@@ -0,0 +1,24 @@
1
+ import * as THREE from "three";
2
+
3
+ //#region ../src/visualization/frame-conversions.d.ts
4
+ /**
5
+ * Convert a GPS-world NUE point into the AR-local position to write into a
6
+ * direct child of `arWorldGroup`, given the current alignment matrix.
7
+ *
8
+ * Computes `alignment⁻¹ · nue`. When the result is later composed back by the
9
+ * group's matrix (the same `alignment`), the child's WORLD position equals
10
+ * `nue` exactly.
11
+ *
12
+ * @param alignment - The alignment matrix as a 16-element **column-major**
13
+ * array (AR-odometry NUE → GPS-world NUE), e.g. from `getAlignmentMatrix()`
14
+ * or `THREE.Matrix4.toArray()`. Not mutated.
15
+ * @param nue - The GPS-world NUE point `[north, up, east]` in metres. Not
16
+ * mutated.
17
+ * @param out - Optional target vector to write into and return (pass a reused
18
+ * scratch to avoid allocation on hot paths). Defaults to a fresh
19
+ * `THREE.Vector3`.
20
+ * @returns `out`, set to the AR-local position.
21
+ */
22
+ declare function nueToArLocal(alignment: readonly number[], nue: readonly [number, number, number], out?: THREE.Vector3): THREE.Vector3;
23
+ //#endregion
24
+ export { nueToArLocal as t };