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.
Files changed (247) hide show
  1. package/dist/absolute-orientation-CAEh840t.d.ts +54 -0
  2. package/dist/{app-selectors-B8VQoMDY.d.ts → app-selectors-ikTUQZpe.d.ts} +1 -1
  3. package/dist/ar/bresenham3d.d.ts +1 -1
  4. package/dist/ar/camera-blit-capture.d.ts +2 -2
  5. package/dist/ar/camera-blit-capture.js +74 -9
  6. package/dist/ar/capability-checker.d.ts +1 -1
  7. package/dist/ar/capture-failure-tracker.d.ts +1 -1
  8. package/dist/ar/chromium-camera-access-workaround.d.ts +1 -1
  9. package/dist/ar/depth-grid-lookup.d.ts +2 -0
  10. package/dist/ar/depth-grid-lookup.js +43 -0
  11. package/dist/ar/depth-sampler.d.ts +2 -2
  12. package/dist/ar/depth-sampler.js +2 -4
  13. package/dist/ar/depth-unprojection.d.ts +1 -1
  14. package/dist/ar/enable-gps-ar.d.ts +1 -1
  15. package/dist/ar/enable-gps-ar.js +26 -1
  16. package/dist/ar/frame-loop.d.ts +1 -1
  17. package/dist/ar/image-capture.d.ts +2 -2
  18. package/dist/ar/image-capture.js +1 -154
  19. package/dist/ar/image-quality.d.ts +2 -0
  20. package/dist/ar/image-quality.js +168 -0
  21. package/dist/ar/index.d.ts +31 -19
  22. package/dist/ar/index.js +17 -5
  23. package/dist/ar/occupancy-grid.d.ts +1 -1
  24. package/dist/ar/occupancy-grid.js +29 -2
  25. package/dist/ar/planar-pnp.d.ts +2 -0
  26. package/dist/ar/planar-pnp.js +425 -0
  27. package/dist/ar/qr-debug-view.d.ts +2 -0
  28. package/dist/ar/qr-debug-view.js +89 -0
  29. package/dist/ar/qr-derived-pose.d.ts +2 -0
  30. package/dist/ar/qr-derived-pose.js +114 -0
  31. package/dist/ar/qr-detection-controller.d.ts +2 -0
  32. package/dist/ar/qr-detection-controller.js +2 -0
  33. package/dist/ar/qr-frontend.d.ts +2 -0
  34. package/dist/ar/qr-frontend.js +60 -0
  35. package/dist/ar/qr-pose.d.ts +2 -0
  36. package/dist/ar/qr-pose.js +267 -0
  37. package/dist/ar/qr-size-depth-context.d.ts +3 -0
  38. package/dist/ar/qr-size-depth-context.js +42 -0
  39. package/dist/ar/qr-size-measurer.d.ts +2 -0
  40. package/dist/ar/qr-size-measurer.js +2 -0
  41. package/dist/ar/replay-scene.d.ts +1 -1
  42. package/dist/ar/replay-scene.js +1 -1
  43. package/dist/ar/scene-node-names.d.ts +1 -1
  44. package/dist/ar/webxr-nue-basis.d.ts +1 -1
  45. package/dist/ar/webxr-session.d.ts +4 -4
  46. package/dist/ar/webxr-session.js +2 -2
  47. package/dist/ar/xr-camera-texture.d.ts +1 -1
  48. package/dist/ar/xr-error-handler.d.ts +1 -1
  49. package/dist/ar/xr-frame-loop.d.ts +1 -1
  50. package/dist/ar-MFpFlV7j.js +382 -0
  51. package/dist/{ar-world-group-alignment-C9lwwIyI.d.ts → ar-world-group-alignment-69fgnB39.d.ts} +1 -1
  52. package/dist/{camera-blit-capture-CnxUDbKx.d.ts → camera-blit-capture-NKT9H5-e.d.ts} +57 -4
  53. package/dist/capture-motion-gate-0Yl2NpQG.d.ts +24 -0
  54. package/dist/capture-motion-gate-4EscNZCw.js +94 -0
  55. package/dist/concurrency-By0oOTZt.d.ts +50 -0
  56. package/dist/core/index.d.ts +2 -2
  57. package/dist/core/index.js +2 -2
  58. package/dist/create-slam-app-store-DkCHOpvb.js +149 -0
  59. package/dist/{create-slam-app-store-BljusOua.d.ts → create-slam-app-store-n_VF63D5.d.ts} +50 -40
  60. package/dist/depth-grid-lookup-DQ-IYhO3.d.ts +21 -0
  61. package/dist/{depth-sampler-Bhanfwla.d.ts → depth-sampler-asc8exHt.d.ts} +1 -1
  62. package/dist/{depth-unprojection-Y6i_mJuT.d.ts → depth-unprojection-DjsbJW9L.d.ts} +1 -1
  63. package/dist/{enable-gps-ar-BfUJOgpW.d.ts → enable-gps-ar-DkNhFv4Y.d.ts} +4 -4
  64. package/dist/{frame-conversions-BfsafGWR.d.ts → frame-conversions-DObhj8Wf.d.ts} +1 -1
  65. package/dist/{fused-path-DJoVu98b.d.ts → fused-path-v6eOmLl3.d.ts} +1 -1
  66. package/dist/geo/h3-proximity.d.ts +2 -2
  67. package/dist/geo/h3-proximity.js +61 -2
  68. package/dist/geo/index.d.ts +2 -2
  69. package/dist/geo/index.js +2 -2
  70. package/dist/geodesic-angle-BTldfFgs.js +39 -0
  71. package/dist/{gps-anchor-dYeCvvp5.d.ts → gps-anchor-BeFO2RYa.d.ts} +1 -1
  72. package/dist/{gps-ar-pose-sampler-DVb96iOy.d.ts → gps-ar-pose-sampler-DHMZmYAc.d.ts} +1 -1
  73. package/dist/{gps-event-coordinator-CWIDOk4U.d.ts → gps-event-coordinator-CjWW636x.d.ts} +21 -11
  74. package/dist/{gps-event-markers-Dc9FT-q1.d.ts → gps-event-markers-Cz9RMA2k.d.ts} +18 -0
  75. package/dist/{h3-proximity-I1msGPgE.d.ts → h3-proximity-C-j0pw1j.d.ts} +39 -1
  76. package/dist/image-capture-B2-b6tKK.js +374 -0
  77. package/dist/{image-capture-qAZ5Fh5X.d.ts → image-capture-CNZDrQXG.d.ts} +103 -3
  78. package/dist/image-quality-smCBoFj1.d.ts +158 -0
  79. package/dist/index-BAPokxMb.d.ts +22 -0
  80. package/dist/{index-BYYUdf9v.d.ts → index-BODDUTvD.d.ts} +1 -1
  81. package/dist/index-BneSj4_5.d.ts +399 -0
  82. package/dist/index-_Rhgoqkh.d.ts +2 -0
  83. package/dist/index.d.ts +80 -67
  84. package/dist/index.js +24 -12
  85. package/dist/{leaflet-map-overlay-6CJC-E1l.d.ts → leaflet-map-overlay-CmIyiE8n.d.ts} +1 -1
  86. package/dist/{map-data-CkloBxIl.d.ts → map-data-B4oEehUh.d.ts} +1 -1
  87. package/dist/{map-overlay-draw-krOKwDAq.d.ts → map-overlay-draw-ymz1s7fG.d.ts} +1 -1
  88. package/dist/{null-storage-backend-uKQE8tl7.d.ts → null-storage-backend-B9rtyDfG.d.ts} +2 -2
  89. package/dist/{occupancy-grid-BRNRaPyT.d.ts → occupancy-grid-DnOc-6PU.d.ts} +12 -2
  90. package/dist/{opfs-storage-D5zAn_lx.d.ts → opfs-storage-3VZh-qH6.d.ts} +23 -4
  91. package/dist/{opfs-storage-backend-6aYMUZlJ.d.ts → opfs-storage-backend-JJiUJo-3.d.ts} +3 -3
  92. package/dist/{persistence-middleware-B9ei4Nhx.d.ts → persistence-middleware-DAPTTwB2.d.ts} +1 -1
  93. package/dist/planar-pnp-my9bqULS.d.ts +67 -0
  94. package/dist/qr-debug-view-Brl73HGO.d.ts +29 -0
  95. package/dist/qr-derived-pose-CmxTP2m4.d.ts +122 -0
  96. package/dist/qr-detected-slice-VQssZmQJ.d.ts +166 -0
  97. package/dist/qr-detection-controller-B-tLVd-S.js +119 -0
  98. package/dist/qr-detection-controller-CDurHv8E.d.ts +48 -0
  99. package/dist/qr-frontend--fFX25mV.d.ts +58 -0
  100. package/dist/qr-pose-D2rfu5DA.d.ts +138 -0
  101. package/dist/qr-pose-aggregation-B7tWJq7j.d.ts +90 -0
  102. package/dist/qr-pose-aggregation-DBiaLW0O.js +191 -0
  103. package/dist/qr-size-depth-context-CeAG53fy.d.ts +13 -0
  104. package/dist/qr-size-measurer-DLb33geH.js +596 -0
  105. package/dist/qr-size-measurer-DQdAsid1.d.ts +165 -0
  106. package/dist/recording-options-BPCpMHLK.d.ts +514 -0
  107. package/dist/{recording-replayer-r0DZ9ALp.d.ts → recording-replayer-DQKpAGyQ.d.ts} +2 -2
  108. package/dist/recording-slice-DaxHZvNu.d.ts +55 -0
  109. package/dist/{replay-engine-XpDa0dy1.d.ts → replay-engine-CmKh_4wf.d.ts} +7 -4
  110. package/dist/{replay-scene-Bw58VL6p.d.ts → replay-scene-DBlDrV_x.d.ts} +3 -3
  111. package/dist/sensors/absolute-orientation.d.ts +2 -0
  112. package/dist/sensors/absolute-orientation.js +145 -0
  113. package/dist/sensors/gps-error-handler.d.ts +1 -1
  114. package/dist/sensors/gps.d.ts +1 -1
  115. package/dist/sensors/index.d.ts +5 -4
  116. package/dist/sensors/index.js +2 -1
  117. package/dist/sensors/permission-checker.d.ts +1 -1
  118. package/dist/sensors/permission-checker.js +1 -1
  119. package/dist/state/app-selectors.d.ts +1 -1
  120. package/dist/state/combined-root-state.d.ts +1 -1
  121. package/dist/state/create-slam-app-store.d.ts +1 -1
  122. package/dist/state/create-slam-app-store.js +1 -74
  123. package/dist/state/gps-ar-pose-sampler.d.ts +3 -3
  124. package/dist/state/gps-event-coordinator.d.ts +2 -2
  125. package/dist/state/gps-event-coordinator.js +27 -17
  126. package/dist/state/index.d.ts +20 -17
  127. package/dist/state/index.js +5 -4
  128. package/dist/state/persistence-middleware.d.ts +1 -1
  129. package/dist/state/persistence-middleware.js +30 -19
  130. package/dist/state/qr-detected-slice.d.ts +4 -0
  131. package/dist/state/qr-detected-slice.js +311 -0
  132. package/dist/state/recording-options.d.ts +2 -2
  133. package/dist/state/recording-options.js +273 -7
  134. package/dist/state/recording-replayer.d.ts +2 -2
  135. package/dist/state/recording-replayer.js +1 -1
  136. package/dist/state/recording-slice.d.ts +1 -1
  137. package/dist/state/recording-slice.js +5 -1
  138. package/dist/state/replay-engine.d.ts +1 -1
  139. package/dist/state/replay-engine.js +7 -3
  140. package/dist/state/store-subscribers.d.ts +2 -2
  141. package/dist/state/subscribe-to-selector.d.ts +1 -1
  142. package/dist/state/tracking-quality.d.ts +2 -2
  143. package/dist/state/tracking-quality.js +40 -128
  144. package/dist/state/tracking-slice.d.ts +1 -1
  145. package/dist/{state-BkIfpiyA.js → state-E51QT_-j.js} +2 -1
  146. package/dist/storage/file-system-utils.d.ts +1 -1
  147. package/dist/storage/index.d.ts +9 -9
  148. package/dist/storage/index.js +4 -4
  149. package/dist/storage/null-storage-backend.d.ts +1 -1
  150. package/dist/storage/opfs-storage-backend.d.ts +1 -1
  151. package/dist/storage/opfs-storage-backend.js +3 -3
  152. package/dist/storage/opfs-storage.d.ts +1 -1
  153. package/dist/storage/opfs-storage.js +5 -4
  154. package/dist/storage/storage-backend.d.ts +1 -1
  155. package/dist/storage/zip-coverage-embed.d.ts +2 -0
  156. package/dist/storage/zip-coverage-embed.js +88 -0
  157. package/dist/storage/zip-export.d.ts +2 -2
  158. package/dist/storage/zip-export.js +37 -59
  159. package/dist/storage/zip-reader.d.ts +1 -1
  160. package/dist/{storage-backend-Do6YDpNo.d.ts → storage-backend-BdVLD0dU.d.ts} +1 -1
  161. package/dist/{store-subscribers-0YWyOLvV.d.ts → store-subscribers-CA6AJH_8.d.ts} +2 -2
  162. package/dist/{subscribe-to-selector-CZ6k1NFP.d.ts → subscribe-to-selector-B6EUmQ3I.d.ts} +1 -1
  163. package/dist/test-utils/browser-mocks.d.ts +7 -1
  164. package/dist/test-utils/browser-mocks.js +6 -0
  165. package/dist/test-utils/zip-round-trip-helpers.d.ts +9 -1
  166. package/dist/test-utils/zip-round-trip-helpers.js +5 -1
  167. package/dist/{tracking-slice-CdK8c-8P.d.ts → tracking-slice-BuAZxXZ5.d.ts} +1 -1
  168. package/dist/types/ar-types.d.ts +1 -1
  169. package/dist/types/geo-types.d.ts +1 -1
  170. package/dist/types/index.d.ts +2 -2
  171. package/dist/utils/concurrency.d.ts +2 -2
  172. package/dist/utils/concurrency.js +66 -2
  173. package/dist/utils/failure-tracker.d.ts +1 -1
  174. package/dist/utils/format-file-size.d.ts +1 -1
  175. package/dist/utils/fused-path.d.ts +1 -1
  176. package/dist/utils/index.d.ts +8 -7
  177. package/dist/utils/index.js +2 -1
  178. package/dist/utils/list-formatter.d.ts +1 -1
  179. package/dist/utils/logger.d.ts +1 -1
  180. package/dist/visualization/accuracy-circles.d.ts +1 -1
  181. package/dist/visualization/alignment-lerper.d.ts +1 -1
  182. package/dist/visualization/ar-world-group-alignment.d.ts +1 -1
  183. package/dist/visualization/ar-world-group-alignment.js +1 -1
  184. package/dist/visualization/camera-follower.d.ts +1 -1
  185. package/dist/visualization/css3d-renderer-manager.d.ts +1 -1
  186. package/dist/visualization/frame-conversions.d.ts +1 -1
  187. package/dist/visualization/frustum-visibility.d.ts +1 -1
  188. package/dist/visualization/gps-anchor.d.ts +1 -1
  189. package/dist/visualization/gps-compass-cubes.d.ts +1 -1
  190. package/dist/visualization/gps-event-markers.d.ts +1 -1
  191. package/dist/visualization/gps-event-markers.js +40 -5
  192. package/dist/visualization/hit-test-reticle.d.ts +1 -1
  193. package/dist/visualization/hit-test-reticle.js +1 -0
  194. package/dist/visualization/index.d.ts +18 -18
  195. package/dist/visualization/leaflet-map-overlay.d.ts +1 -1
  196. package/dist/visualization/leaflet-map-overlay.js +15 -3
  197. package/dist/visualization/lerp-utils.d.ts +1 -1
  198. package/dist/visualization/map-data.d.ts +1 -1
  199. package/dist/visualization/map-overlay-draw.d.ts +1 -1
  200. package/dist/visualization/map-overlay.d.ts +1 -1
  201. package/dist/visualization/three-dispose.d.ts +1 -1
  202. package/dist/visualization/vis-colors.d.ts +1 -1
  203. package/dist/{webxr-session-TIaD883f.js → webxr-session-DG-eNfny.js} +244 -11
  204. package/dist/{webxr-session-GjZ2BIpS.d.ts → webxr-session-Do5L6G3B.d.ts} +86 -8
  205. package/dist/zip-coverage-embed-DCAREVmw.d.ts +41 -0
  206. package/dist/{zip-export-G14hkSPG.d.ts → zip-export-ol0S_Ofd.d.ts} +27 -23
  207. package/package.json +2 -2
  208. package/dist/concurrency-CO1FQvs3.d.ts +0 -24
  209. package/dist/file-system-CwXTNHmB.d.ts +0 -114
  210. package/dist/index-CF05QV2m.d.ts +0 -2
  211. package/dist/recording-options-DJBXuMxV.d.ts +0 -182
  212. package/dist/recording-slice-89O07ebs.d.ts +0 -36
  213. package/dist/storage/file-system.d.ts +0 -2
  214. package/dist/storage/file-system.js +0 -289
  215. /package/dist/{accuracy-circles-BpwWAs8G.d.ts → accuracy-circles-BtqOP_v1.d.ts} +0 -0
  216. /package/dist/{alignment-lerper-Dm9G7JJO.d.ts → alignment-lerper-Bil6vbVw.d.ts} +0 -0
  217. /package/dist/{ar-types-Dan1MVTi.d.ts → ar-types-BFX2r1wk.d.ts} +0 -0
  218. /package/dist/{bresenham3d-CFfd7n6B.d.ts → bresenham3d-CU1Gv2W4.d.ts} +0 -0
  219. /package/dist/{camera-follower-C2mwEn7c.d.ts → camera-follower-CqJJ7zRj.d.ts} +0 -0
  220. /package/dist/{capability-checker-CQXJHxXg.d.ts → capability-checker-DUQLg6gq.d.ts} +0 -0
  221. /package/dist/{capture-failure-tracker-CUeU3L_M.d.ts → capture-failure-tracker-gsm8q7yi.d.ts} +0 -0
  222. /package/dist/{chromium-camera-access-workaround-Jqbn0apH.d.ts → chromium-camera-access-workaround-D4K2uwwQ.d.ts} +0 -0
  223. /package/dist/{css3d-renderer-manager-ptu_t3B3.d.ts → css3d-renderer-manager-B3kGNtcn.d.ts} +0 -0
  224. /package/dist/{failure-tracker-Cn8Fo9D4.d.ts → failure-tracker-BO7M7jF_.d.ts} +0 -0
  225. /package/dist/{file-system-utils-CXIoL-DM.d.ts → file-system-utils-DOajnMm-.d.ts} +0 -0
  226. /package/dist/{format-file-size-CLCZz3lC.d.ts → format-file-size-Dc98ulzB.d.ts} +0 -0
  227. /package/dist/{frame-loop-CMewS8cY.d.ts → frame-loop-CpVjQ1dH.d.ts} +0 -0
  228. /package/dist/{frustum-visibility-CmI96ESS.d.ts → frustum-visibility-CeoCZchO.d.ts} +0 -0
  229. /package/dist/{geo-types-SaPnIEUM.d.ts → geo-types-3Ip1NzVR.d.ts} +0 -0
  230. /package/dist/{gps-VuezJWK3.d.ts → gps-BBO3hKfT.d.ts} +0 -0
  231. /package/dist/{gps-compass-cubes-BM1uGiYV.d.ts → gps-compass-cubes-DSN87L8p.d.ts} +0 -0
  232. /package/dist/{gps-error-handler-CJl_2MVm.d.ts → gps-error-handler-BbPsfw-_.d.ts} +0 -0
  233. /package/dist/{hit-test-reticle-BEzEybxZ.d.ts → hit-test-reticle-DV-2TpB9.d.ts} +0 -0
  234. /package/dist/{lerp-utils-BpV-0Dtk.d.ts → lerp-utils-BLmHhkVe.d.ts} +0 -0
  235. /package/dist/{list-formatter-RRcBe8Vg.d.ts → list-formatter-KNMS0ay2.d.ts} +0 -0
  236. /package/dist/{logger-BEHXsnnq.d.ts → logger-CLnPrHiD.d.ts} +0 -0
  237. /package/dist/{map-overlay-bcogY5fK.d.ts → map-overlay-Dh2B97MP.d.ts} +0 -0
  238. /package/dist/{permission-checker-GpAiSCwd.d.ts → permission-checker-CDuI8BwC.d.ts} +0 -0
  239. /package/dist/{scene-node-names-Bu0S66H1.d.ts → scene-node-names-BiShOQDB.d.ts} +0 -0
  240. /package/dist/{session-disposers-M-oashRH.js → session-disposers-zv9OGXPB.js} +0 -0
  241. /package/dist/{three-dispose-D950t71B.d.ts → three-dispose-Bt1k7WKh.d.ts} +0 -0
  242. /package/dist/{vis-colors-D6omC64K.d.ts → vis-colors-Obw2pLqT.d.ts} +0 -0
  243. /package/dist/{webxr-nue-basis-CqKQmZzT.d.ts → webxr-nue-basis-3nfjDJJp.d.ts} +0 -0
  244. /package/dist/{xr-camera-texture-DBwCaKTd.d.ts → xr-camera-texture-Cva1q3Qj.d.ts} +0 -0
  245. /package/dist/{xr-error-handler-C0AQRmxs.d.ts → xr-error-handler-CtvHpjfm.d.ts} +0 -0
  246. /package/dist/{xr-frame-loop-BtYytZJg.d.ts → xr-frame-loop-DIYtRcUa.d.ts} +0 -0
  247. /package/dist/{zip-reader-DSEXt7K6.d.ts → zip-reader-D9iPQwdQ.d.ts} +0 -0
@@ -0,0 +1,425 @@
1
+ //#region ../src/ar/planar-pnp.ts
2
+ const EPS = 1e-12;
3
+ /**
4
+ * Solve the dense square linear system `A·x = b` by Gaussian elimination with
5
+ * partial pivoting. `A` is `n×n` ROW-MAJOR (length n²), `b` is length n. Returns
6
+ * the solution, or `null` when the matrix is singular (no unique solution).
7
+ */
8
+ function solveLinear(A, b) {
9
+ const n = b.length;
10
+ if (A.length !== n * n || n === 0) return null;
11
+ const m = A.slice();
12
+ const y = b.slice();
13
+ for (let col = 0; col < n; col++) {
14
+ let pivot = col;
15
+ let best = Math.abs(m[col * n + col]);
16
+ for (let row = col + 1; row < n; row++) {
17
+ const v = Math.abs(m[row * n + col]);
18
+ if (v > best) {
19
+ best = v;
20
+ pivot = row;
21
+ }
22
+ }
23
+ if (best < EPS) return null;
24
+ if (pivot !== col) {
25
+ for (let k = 0; k < n; k++) {
26
+ const tmp = m[col * n + k];
27
+ m[col * n + k] = m[pivot * n + k];
28
+ m[pivot * n + k] = tmp;
29
+ }
30
+ const tmp = y[col];
31
+ y[col] = y[pivot];
32
+ y[pivot] = tmp;
33
+ }
34
+ const diag = m[col * n + col];
35
+ for (let row = col + 1; row < n; row++) {
36
+ const factor = m[row * n + col] / diag;
37
+ if (factor === 0) continue;
38
+ for (let k = col; k < n; k++) m[row * n + k] = m[row * n + k] - factor * m[col * n + k];
39
+ y[row] = y[row] - factor * y[col];
40
+ }
41
+ }
42
+ const x = new Array(n).fill(0);
43
+ for (let row = n - 1; row >= 0; row--) {
44
+ let sum = y[row];
45
+ for (let k = row + 1; k < n; k++) sum -= m[row * n + k] * x[k];
46
+ x[row] = sum / m[row * n + row];
47
+ }
48
+ return x.every(Number.isFinite) ? x : null;
49
+ }
50
+ /**
51
+ * Estimate the exact planar homography H (row-major 3×3, h₃₃ = 1) mapping object
52
+ * plane coords `(X,Y,1)` to NORMALIZED image coords `(x,y,1)` from 4 (or more)
53
+ * correspondences via a DLT. Returns `null` when the configuration is degenerate
54
+ * (collinear points → singular system). `objectXY`/`imageXY` are parallel arrays
55
+ * of `[X,Y]` / `[x,y]`.
56
+ */
57
+ function homographyFromCorrespondences(objectXY, imageXY) {
58
+ const n = objectXY.length;
59
+ if (n < 4 || imageXY.length !== n) return null;
60
+ const A = [];
61
+ const b = [];
62
+ for (let i = 0; i < 4; i++) {
63
+ const o = objectXY[i];
64
+ const im = imageXY[i];
65
+ const X = o[0];
66
+ const Y = o[1];
67
+ const x = im[0];
68
+ const y = im[1];
69
+ A.push(X, Y, 1, 0, 0, 0, -x * X, -x * Y);
70
+ b.push(x);
71
+ A.push(0, 0, 0, X, Y, 1, -y * X, -y * Y);
72
+ b.push(y);
73
+ }
74
+ const h = solveLinear(A, b);
75
+ if (!h) return null;
76
+ const H = [
77
+ h[0],
78
+ h[1],
79
+ h[2],
80
+ h[3],
81
+ h[4],
82
+ h[5],
83
+ h[6],
84
+ h[7],
85
+ 1
86
+ ];
87
+ return H.every(Number.isFinite) ? H : null;
88
+ }
89
+ /** Multiply two row-major 3×3 matrices: returns A·B. */
90
+ function mul3(a, b) {
91
+ const out = new Array(9).fill(0);
92
+ for (let r = 0; r < 3; r++) for (let c = 0; c < 3; c++) out[r * 3 + c] = a[r * 3] * b[c] + a[r * 3 + 1] * b[3 + c] + a[r * 3 + 2] * b[6 + c];
93
+ return out;
94
+ }
95
+ /** Transpose a row-major 3×3 matrix. */
96
+ function transpose3(m) {
97
+ return [
98
+ m[0],
99
+ m[3],
100
+ m[6],
101
+ m[1],
102
+ m[4],
103
+ m[7],
104
+ m[2],
105
+ m[5],
106
+ m[8]
107
+ ];
108
+ }
109
+ /** Determinant of a row-major 3×3 matrix. */
110
+ function det3(m) {
111
+ return m[0] * (m[4] * m[8] - m[5] * m[7]) - m[1] * (m[3] * m[8] - m[5] * m[6]) + m[2] * (m[3] * m[7] - m[4] * m[6]);
112
+ }
113
+ /** Inverse-transpose of a row-major 3×3 matrix, or `null` if singular. */
114
+ function invTranspose3(m) {
115
+ const d = det3(m);
116
+ if (Math.abs(d) < EPS) return null;
117
+ const inv = 1 / d;
118
+ const c00 = m[4] * m[8] - m[5] * m[7];
119
+ const c01 = -(m[3] * m[8] - m[5] * m[6]);
120
+ const c02 = m[3] * m[7] - m[4] * m[6];
121
+ const c10 = -(m[1] * m[8] - m[2] * m[7]);
122
+ const c11 = m[0] * m[8] - m[2] * m[6];
123
+ const c12 = -(m[0] * m[7] - m[1] * m[6]);
124
+ const c20 = m[1] * m[5] - m[2] * m[4];
125
+ const c21 = -(m[0] * m[5] - m[2] * m[3]);
126
+ const c22 = m[0] * m[4] - m[1] * m[3];
127
+ return [
128
+ c00 * inv,
129
+ c01 * inv,
130
+ c02 * inv,
131
+ c10 * inv,
132
+ c11 * inv,
133
+ c12 * inv,
134
+ c20 * inv,
135
+ c21 * inv,
136
+ c22 * inv
137
+ ];
138
+ }
139
+ /**
140
+ * Project a near-orthogonal 3×3 matrix onto the nearest rotation (SO(3)) by
141
+ * iterative polar decomposition (Higham: `R_{k+1} = ½(R_k + R_k⁻ᵀ)`). Converges
142
+ * quadratically for an already near-orthonormal input; needs no SVD/eigensolver
143
+ * (gl-matrix has neither). Forces `det = +1` by flipping the least-aligned column
144
+ * if the input was left-handed. Returns the input unchanged if it is singular.
145
+ */
146
+ function nearestRotation3x3(input) {
147
+ let R = input;
148
+ for (let i = 0; i < 12; i++) {
149
+ const invT = invTranspose3(R);
150
+ if (!invT) break;
151
+ const next = [
152
+ .5 * (R[0] + invT[0]),
153
+ .5 * (R[1] + invT[1]),
154
+ .5 * (R[2] + invT[2]),
155
+ .5 * (R[3] + invT[3]),
156
+ .5 * (R[4] + invT[4]),
157
+ .5 * (R[5] + invT[5]),
158
+ .5 * (R[6] + invT[6]),
159
+ .5 * (R[7] + invT[7]),
160
+ .5 * (R[8] + invT[8])
161
+ ];
162
+ let delta = 0;
163
+ for (let k = 0; k < 9; k++) delta += Math.abs(next[k] - R[k]);
164
+ R = next;
165
+ if (delta < 1e-15) break;
166
+ }
167
+ if (det3(R) < 0) R = [
168
+ R[0],
169
+ R[1],
170
+ -R[2],
171
+ R[3],
172
+ R[4],
173
+ -R[5],
174
+ R[6],
175
+ R[7],
176
+ -R[8]
177
+ ];
178
+ return R;
179
+ }
180
+ /** Cross product a × b. */
181
+ function cross(a, b) {
182
+ return [
183
+ a[1] * b[2] - a[2] * b[1],
184
+ a[2] * b[0] - a[0] * b[2],
185
+ a[0] * b[1] - a[1] * b[0]
186
+ ];
187
+ }
188
+ /**
189
+ * Generate the planar-pose candidates from a homography via true IPPE
190
+ * (Collins & Bartoli 2014). Returns up to 4 chirality-valid candidates (the two
191
+ * tilt-flip roots × the z-sign of the in-plane axes); the caller picks the
192
+ * lowest reprojection error. Returns `[]` when the homography is degenerate.
193
+ *
194
+ * Derivation (see planar-pnp.ts.md): with `[r1 r2]` the first two columns of R,
195
+ * the local first-order map at the model origin gives `M·[r1 r2] = t_z·J` where
196
+ * `M = [[1,0,-p],[0,1,-q]]`, `(p,q)` is the origin's normalized image and `J` the
197
+ * 2×2 Jacobian of the homography there. Aligning the origin's ray to +z (rotation
198
+ * `Rv`) reduces this to a 2×2 block `B = τ·g` whose unknown bottom row plus the
199
+ * orthonormality of `[r1 r2]` yields a biquadratic in `τ = t_z`:
200
+ * `D²·τ⁴ − S·τ² + 1 = 0`, with `S = ‖g‖_F²`, `D = det(g)`. Its two roots are the
201
+ * two planar poses (they coincide fronto-parallel — no flip there).
202
+ */
203
+ function ippePoseCandidates(H) {
204
+ const p = H[2];
205
+ const q = H[5];
206
+ const Jxx = H[0] - p * H[6];
207
+ const Jxy = H[1] - p * H[7];
208
+ const Jyx = H[3] - q * H[6];
209
+ const Jyy = H[4] - q * H[7];
210
+ const t = Math.hypot(p, q);
211
+ const s = Math.hypot(p, q, 1);
212
+ let Rv;
213
+ if (t < EPS) Rv = [
214
+ 1,
215
+ 0,
216
+ 0,
217
+ 0,
218
+ 1,
219
+ 0,
220
+ 0,
221
+ 0,
222
+ 1
223
+ ];
224
+ else {
225
+ const ax = q / t;
226
+ const ay = -p / t;
227
+ const c = 1 / s;
228
+ const sn = t / s;
229
+ const omc = 1 - c;
230
+ Rv = [
231
+ c + ax * ax * omc,
232
+ ax * ay * omc,
233
+ ay * sn,
234
+ ax * ay * omc,
235
+ c + ay * ay * omc,
236
+ -ax * sn,
237
+ -ay * sn,
238
+ ax * sn,
239
+ c
240
+ ];
241
+ }
242
+ const RvT = transpose3(Rv);
243
+ const w = Rv[6] * p + Rv[7] * q + Rv[8];
244
+ if (Math.abs(w) < EPS) return [];
245
+ const g00 = (Rv[0] * Jxx + Rv[1] * Jyx) / w;
246
+ const g01 = (Rv[0] * Jxy + Rv[1] * Jyy) / w;
247
+ const g10 = (Rv[3] * Jxx + Rv[4] * Jyx) / w;
248
+ const g11 = (Rv[3] * Jxy + Rv[4] * Jyy) / w;
249
+ const G1 = g00 * g00 + g10 * g10;
250
+ const G2 = g01 * g01 + g11 * g11;
251
+ const G12 = g00 * g01 + g10 * g11;
252
+ const S = G1 + G2;
253
+ const D = g00 * g11 - g01 * g10;
254
+ const tauSquared = [];
255
+ if (Math.abs(D) < EPS) {
256
+ if (S > EPS) tauSquared.push(1 / S);
257
+ } else {
258
+ const disc = Math.max(0, S * S - 4 * D * D);
259
+ const root = Math.sqrt(disc);
260
+ const denom = 2 * D * D;
261
+ const t1 = (S + root) / denom;
262
+ const t2 = (S - root) / denom;
263
+ if (t1 > EPS) tauSquared.push(t1);
264
+ if (t2 > EPS && Math.abs(t2 - t1) > 1e-9 * t1) tauSquared.push(t2);
265
+ }
266
+ const candidates = [];
267
+ for (const tau2 of tauSquared) {
268
+ const tau = Math.sqrt(tau2);
269
+ const a2 = Math.max(0, 1 - tau2 * G1);
270
+ const b2 = Math.max(0, 1 - tau2 * G2);
271
+ const aMag = Math.sqrt(a2);
272
+ const bMag = Math.sqrt(b2);
273
+ const wantNeg = -tau2 * G12 < 0;
274
+ for (const signA of [1, -1]) {
275
+ const a = signA * aMag;
276
+ let b = bMag;
277
+ if (wantNeg) b = -b;
278
+ b *= signA;
279
+ const cand = buildCandidate(g00, g10, g01, g11, a, b, tau, Rv, RvT);
280
+ if (cand) candidates.push(cand);
281
+ }
282
+ }
283
+ return candidates;
284
+ }
285
+ /** Assemble a pose candidate from the aligned-frame 2×2 block + z-components. */
286
+ function buildCandidate(g00, g10, g01, g11, a, b, tau, Rv, RvT) {
287
+ const r1 = [
288
+ tau * g00,
289
+ tau * g10,
290
+ a
291
+ ];
292
+ const r2 = [
293
+ tau * g01,
294
+ tau * g11,
295
+ b
296
+ ];
297
+ const r3 = cross(r1, r2);
298
+ const R = nearestRotation3x3(mul3(RvT, [
299
+ r1[0],
300
+ r2[0],
301
+ r3[0],
302
+ r1[1],
303
+ r2[1],
304
+ r3[1],
305
+ r1[2],
306
+ r2[2],
307
+ r3[2]
308
+ ]));
309
+ const tt = [
310
+ tau * Rv[6],
311
+ tau * Rv[7],
312
+ tau * Rv[8]
313
+ ];
314
+ if (!R.every(Number.isFinite) || !tt.every(Number.isFinite)) return null;
315
+ return {
316
+ R,
317
+ t: tt
318
+ };
319
+ }
320
+ /**
321
+ * Convert a row-major rotation matrix to a Rodrigues vector (axis · angle).
322
+ * Double-precision, no gl-matrix Float32 round-trip.
323
+ */
324
+ function rotationToRodrigues(R) {
325
+ let cosTheta = (R[0] + R[4] + R[8] - 1) / 2;
326
+ cosTheta = Math.min(1, Math.max(-1, cosTheta));
327
+ const theta = Math.acos(cosTheta);
328
+ if (theta < 1e-9) return [
329
+ 0,
330
+ 0,
331
+ 0
332
+ ];
333
+ if (Math.PI - theta < 1e-6) {
334
+ const xx = (R[0] + 1) / 2;
335
+ const yy = (R[4] + 1) / 2;
336
+ const zz = (R[8] + 1) / 2;
337
+ let ax = Math.sqrt(Math.max(0, xx));
338
+ let ay = Math.sqrt(Math.max(0, yy));
339
+ let az = Math.sqrt(Math.max(0, zz));
340
+ if (ax >= ay && ax >= az) {
341
+ ay = Math.sign(R[1] + R[3]) * ay;
342
+ az = Math.sign(R[2] + R[6]) * az;
343
+ } else if (ay >= az) {
344
+ ax = Math.sign(R[1] + R[3]) * ax;
345
+ az = Math.sign(R[5] + R[7]) * az;
346
+ } else {
347
+ ax = Math.sign(R[2] + R[6]) * ax;
348
+ ay = Math.sign(R[5] + R[7]) * ay;
349
+ }
350
+ const norm = Math.hypot(ax, ay, az) || 1;
351
+ return [
352
+ theta * ax / norm,
353
+ theta * ay / norm,
354
+ theta * az / norm
355
+ ];
356
+ }
357
+ const k = theta / (2 * Math.sin(theta));
358
+ return [
359
+ k * (R[7] - R[5]),
360
+ k * (R[2] - R[6]),
361
+ k * (R[3] - R[1])
362
+ ];
363
+ }
364
+ /** RMS reprojection error (px) of a candidate against the detected corners. */
365
+ function reprojectionError(cand, objectPoints, imagePoints, intr) {
366
+ const { R, t } = cand;
367
+ let sumSq = 0;
368
+ for (let i = 0; i < objectPoints.length; i++) {
369
+ const o = objectPoints[i];
370
+ const img = imagePoints[i];
371
+ const cx = R[0] * o[0] + R[1] * o[1] + R[2] * o[2] + t[0];
372
+ const cy = R[3] * o[0] + R[4] * o[1] + R[5] * o[2] + t[1];
373
+ const cz = R[6] * o[0] + R[7] * o[1] + R[8] * o[2] + t[2];
374
+ if (!(cz > 0)) return Infinity;
375
+ const px = intr.cx + intr.fx * cx / cz;
376
+ const py = intr.cy + intr.fy * cy / cz;
377
+ const dx = px - img.x;
378
+ const dy = py - img.y;
379
+ sumSq += dx * dx + dy * dy;
380
+ }
381
+ return Math.sqrt(sumSq / objectPoints.length);
382
+ }
383
+ /**
384
+ * `SolvePnpSquare` for a centered planar square via pure-JS IPPE. Stateless and
385
+ * synchronous — construct once and reuse, or construct per call; either is fine.
386
+ */
387
+ var PlanarPnpSquare = class {
388
+ solve(objectPoints, imagePoints, intrinsics) {
389
+ if (objectPoints.length !== imagePoints.length || objectPoints.length < 4) return null;
390
+ const { fx, fy, cx, cy } = intrinsics;
391
+ if (!(fx > 0) || !(fy > 0) || !Number.isFinite(cx) || !Number.isFinite(cy)) return null;
392
+ const objectXY = [];
393
+ const imageXY = [];
394
+ for (let i = 0; i < objectPoints.length; i++) {
395
+ const o = objectPoints[i];
396
+ const im = imagePoints[i];
397
+ if (!Number.isFinite(im.x) || !Number.isFinite(im.y)) return null;
398
+ objectXY.push([o[0], o[1]]);
399
+ imageXY.push([(im.x - cx) / fx, (im.y - cy) / fy]);
400
+ }
401
+ const H = homographyFromCorrespondences(objectXY, imageXY);
402
+ if (!H) return null;
403
+ const candidates = ippePoseCandidates(H);
404
+ if (candidates.length === 0) return null;
405
+ let best = null;
406
+ let bestErr = Infinity;
407
+ for (const cand of candidates) {
408
+ const err = reprojectionError(cand, objectPoints, imagePoints, intrinsics);
409
+ if (err < bestErr) {
410
+ bestErr = err;
411
+ best = cand;
412
+ }
413
+ }
414
+ if (!best || !Number.isFinite(bestErr)) return null;
415
+ const rvec = rotationToRodrigues(best.R);
416
+ const tvec = best.t;
417
+ if (![...rvec, ...tvec].every(Number.isFinite) || !(tvec[2] > 0)) return null;
418
+ return {
419
+ rvec,
420
+ tvec
421
+ };
422
+ }
423
+ };
424
+ //#endregion
425
+ export { PlanarPnpSquare, homographyFromCorrespondences, ippePoseCandidates, nearestRotation3x3, rotationToRodrigues, solveLinear };
@@ -0,0 +1,2 @@
1
+ import { n as createQrDebugView, t as QrDebugView } from "../qr-debug-view-Brl73HGO.js";
2
+ export { QrDebugView, createQrDebugView };
@@ -0,0 +1,89 @@
1
+ import { WEBXR_TO_NUE } from "./webxr-nue-basis.js";
2
+ import { AxesHelper, BoxGeometry, Group, Mesh, MeshBasicMaterial } from "three";
3
+ //#region ../src/ar/qr-debug-view.ts
4
+ /**
5
+ * QR debug view — the shared 3D "is it glued to the code?" overlay: a 3D axis at
6
+ * the solved QR pose plus a semi-transparent cube sized to the QR so its front
7
+ * face lands on the printed corners. Both are parented under `arWorldGroup` so
8
+ * they ride the alignment / transform chain exactly like real content.
9
+ *
10
+ * Promoted to the framework (recorder live-QR WS-5) as the **shared consumer**
11
+ * view: both the QR-tracking demo and the GPS Recorder render this same overlay
12
+ * off the derived pose (`qr-derived-pose.ts` / `selectSolvedQrPose`). The view is
13
+ * pure three.js — it knows nothing about who solved the pose, so it runs
14
+ * identically live and on replay.
15
+ *
16
+ * Persistence across detection misses: `update()` is called on every lock;
17
+ * `clear()` is NOT called on a miss, so the objects keep their last pose and
18
+ * never flicker between throttled detections. They stay hidden until the first
19
+ * `update()`.
20
+ *
21
+ * Coordinate space (IMPORTANT): the QR pose is in **raw WebXR** space (the
22
+ * corners are depth-unprojected with the raw WebXR camera pose), but the
23
+ * injected `parent` (`arWorldGroup`) local space is **NUE**. So the objects must
24
+ * ride the SAME {@link WEBXR_TO_NUE} basis the camera does (`arWorldGroup →
25
+ * basisChangeNode → arpose → camera`). We mirror that by hanging them off an
26
+ * internal basis node carrying `WEBXR_TO_NUE`; parenting them directly under
27
+ * `arWorldGroup` would leave them East/North axis-swapped and they would NOT
28
+ * line up with the camera/QR on a real device (the recurring scene-frame bug —
29
+ * see the frame-tile / occupancy-cube / hit-test-reticle precedents). We never
30
+ * touch the camera; WebXR owns its pose.
31
+ *
32
+ * @see qr-derived-pose.ts — derives the `Pose` this view renders (size + PnP).
33
+ * @see webxr-nue-basis.ts — the `WEBXR_TO_NUE` basis the objects ride.
34
+ */
35
+ /** A thin slab so the cube's front face sits on the printed code (1 cm deep). */
36
+ const CUBE_DEPTH_M = .01;
37
+ /**
38
+ * Create the debug axis + cube under `parent` (the `arWorldGroup`). Objects
39
+ * start hidden; the first `update()` reveals and positions them.
40
+ */
41
+ function createQrDebugView(parent) {
42
+ const basis = new Group();
43
+ basis.name = "qr-debug-basis";
44
+ basis.matrix.copy(WEBXR_TO_NUE);
45
+ basis.matrixAutoUpdate = false;
46
+ parent.add(basis);
47
+ const axes = new AxesHelper(.15);
48
+ axes.visible = false;
49
+ const cubeMaterial = new MeshBasicMaterial({
50
+ color: 3399167,
51
+ transparent: true,
52
+ opacity: .35,
53
+ depthWrite: false
54
+ });
55
+ const cube = new Mesh(new BoxGeometry(1, 1, 1), cubeMaterial);
56
+ cube.visible = false;
57
+ basis.add(axes);
58
+ basis.add(cube);
59
+ function applyPose(object, pose) {
60
+ object.position.set(pose.position[0], pose.position[1], pose.position[2]);
61
+ object.quaternion.set(pose.rotation[0], pose.rotation[1], pose.rotation[2], pose.rotation[3]);
62
+ }
63
+ return {
64
+ update(pose, sizeM) {
65
+ applyPose(axes, pose);
66
+ axes.visible = true;
67
+ if (sizeM === null) {
68
+ cube.visible = false;
69
+ return;
70
+ }
71
+ applyPose(cube, pose);
72
+ cube.scale.set(sizeM, sizeM, CUBE_DEPTH_M);
73
+ cube.translateZ(-CUBE_DEPTH_M / 2);
74
+ cube.visible = true;
75
+ },
76
+ clear() {
77
+ axes.visible = false;
78
+ cube.visible = false;
79
+ },
80
+ dispose() {
81
+ parent.remove(basis);
82
+ axes.dispose();
83
+ cube.geometry.dispose();
84
+ cubeMaterial.dispose();
85
+ }
86
+ };
87
+ }
88
+ //#endregion
89
+ export { createQrDebugView };
@@ -0,0 +1,2 @@
1
+ import { a as createIncrementalQrPlacement, c as deriveSolvedQrPose, i as RawQrObservation, l as solveQrPoseFromObservation, n as DerivedQrPlacement, o as deriveQrPlacement, r as IncrementalQrPlacement, s as deriveQrSizeM, t as DeriveQrPoseDeps } from "../qr-derived-pose-CmxTP2m4.js";
2
+ export { DeriveQrPoseDeps, DerivedQrPlacement, IncrementalQrPlacement, RawQrObservation, createIncrementalQrPlacement, deriveQrPlacement, deriveQrSizeM, deriveSolvedQrPose, solveQrPoseFromObservation };
@@ -0,0 +1,114 @@
1
+ import { t as createQrSizeMeasurer } from "../qr-size-measurer-DLb33geH.js";
2
+ import { intrinsicsFromProjection, solveQrPose } from "./qr-pose.js";
3
+ //#region ../src/ar/qr-derived-pose.ts
4
+ /**
5
+ * Replay a marker's observation history through a fresh size measurer (each
6
+ * observation sized against its own as-of depth context) and return the running
7
+ * median size in meters, or `null` if no observation could be sized yet.
8
+ *
9
+ * Re-runs the whole history per call — O(history), bounded by the slice ring
10
+ * buffer. Cheap for v1; memoize if the debug-viz cadence makes it hot.
11
+ */
12
+ function deriveQrSizeM(text, observations, resolveDepthAt, sizeOptions) {
13
+ if (observations.length === 0) return null;
14
+ const measurer = createQrSizeMeasurer(sizeOptions);
15
+ for (const o of observations) {
16
+ const ctx = resolveDepthAt(o.timestamp);
17
+ if (!ctx) continue;
18
+ measurer.measure(text, o.corners, {
19
+ width: o.imageWidth,
20
+ height: o.imageHeight
21
+ }, ctx);
22
+ }
23
+ return measurer.current(text).estimateM;
24
+ }
25
+ /**
26
+ * Solve the world pose of ONE raw observation given a known metric size. Pure
27
+ * (no depth): intrinsics come from the observation's projection + frame size,
28
+ * the corners + camera pose are recorded. Returns `null` when PnP rejects the
29
+ * quad or exceeds the reprojection gate.
30
+ */
31
+ function solveQrPoseFromObservation(observation, sizeM, solver, maxReprojectionErrorPx) {
32
+ const intrinsics = intrinsicsFromProjection(observation.projectionMatrix, observation.imageWidth, observation.imageHeight);
33
+ const solution = solveQrPose({
34
+ imagePoints: observation.corners,
35
+ sizeM,
36
+ intrinsics,
37
+ cameraPose: observation.cameraPose,
38
+ solver,
39
+ ...maxReprojectionErrorPx !== void 0 ? { maxReprojectionErrorPx } : {}
40
+ });
41
+ return solution ? solution.qrPoseWorld : null;
42
+ }
43
+ /**
44
+ * Derive the best-effort {@link DerivedQrPlacement} for a marker: accumulate the
45
+ * size across the whole observation history (each against its as-of depth), then
46
+ * solve the LATEST observation with that size — returning BOTH the pose and the
47
+ * size (the debug cube needs the size; deriving once avoids two history passes).
48
+ * Returns `null` when there is no history, no observation can be sized, or PnP
49
+ * rejects the latest quad — callers render nothing (best-effort viz) rather than
50
+ * throwing.
51
+ */
52
+ function deriveQrPlacement(text, observations, deps) {
53
+ if (observations.length === 0) return null;
54
+ const sizeM = deriveQrSizeM(text, observations, deps.resolveDepthAt, deps.sizeOptions);
55
+ if (sizeM === null) return null;
56
+ const latest = observations[observations.length - 1];
57
+ const pose = solveQrPoseFromObservation(latest, sizeM, deps.solver, deps.maxReprojectionErrorPx);
58
+ return pose ? {
59
+ pose,
60
+ sizeM
61
+ } : null;
62
+ }
63
+ /**
64
+ * Derive only the best-effort solved WORLD pose for a marker (the size is an
65
+ * internal of {@link deriveQrPlacement}). Returns `null` under the same
66
+ * conditions. Kept for callers that need the pose alone.
67
+ */
68
+ function deriveSolvedQrPose(text, observations, deps) {
69
+ return deriveQrPlacement(text, observations, deps)?.pose ?? null;
70
+ }
71
+ function createIncrementalQrPlacement(deps) {
72
+ const measurer = createQrSizeMeasurer(deps.sizeOptions);
73
+ const lastFoldedTs = /* @__PURE__ */ new Map();
74
+ const lastPlacement = /* @__PURE__ */ new Map();
75
+ return {
76
+ update(text, observations) {
77
+ if (observations.length === 0) return null;
78
+ const newestTs = observations[observations.length - 1].timestamp;
79
+ const foldedTs = lastFoldedTs.get(text) ?? -Infinity;
80
+ if (newestTs <= foldedTs) return lastPlacement.get(text) ?? null;
81
+ let maxFoldedTs = foldedTs;
82
+ for (const o of observations) {
83
+ if (o.timestamp <= foldedTs) continue;
84
+ const ctx = deps.resolveDepthAt(o.timestamp);
85
+ if (!ctx) continue;
86
+ measurer.measure(text, o.corners, {
87
+ width: o.imageWidth,
88
+ height: o.imageHeight
89
+ }, ctx);
90
+ maxFoldedTs = o.timestamp;
91
+ }
92
+ lastFoldedTs.set(text, maxFoldedTs);
93
+ const sizeM = measurer.current(text).estimateM;
94
+ let placement = null;
95
+ if (sizeM !== null) {
96
+ const latest = observations[observations.length - 1];
97
+ const pose = solveQrPoseFromObservation(latest, sizeM, deps.solver, deps.maxReprojectionErrorPx);
98
+ placement = pose ? {
99
+ pose,
100
+ sizeM
101
+ } : null;
102
+ }
103
+ lastPlacement.set(text, placement);
104
+ return placement;
105
+ },
106
+ reset(text) {
107
+ lastFoldedTs.delete(text);
108
+ lastPlacement.delete(text);
109
+ measurer.reset(text);
110
+ }
111
+ };
112
+ }
113
+ //#endregion
114
+ export { createIncrementalQrPlacement, deriveQrPlacement, deriveQrSizeM, deriveSolvedQrPose, solveQrPoseFromObservation };
@@ -0,0 +1,2 @@
1
+ import { a as createQrDetectionController, i as RawObservationSink, n as QrDetectionControllerDeps, r as QrScanStatus, t as QrDetectionController } from "../qr-detection-controller-CDurHv8E.js";
2
+ export { QrDetectionController, QrDetectionControllerDeps, QrScanStatus, RawObservationSink, createQrDetectionController };
@@ -0,0 +1,2 @@
1
+ import { t as createQrDetectionController } from "../qr-detection-controller-B-tLVd-S.js";
2
+ export { createQrDetectionController };
@@ -0,0 +1,2 @@
1
+ import { a as QrFrontEnd, c as createBarcodeDetectorFrontEnd, i as QrDetection, n as BarcodeDetectorLike, o as RgbaImage, r as DetectedBarcodeLike, s as ToImageBitmapSource, t as BarcodeDetectorFrontEnd } from "../qr-frontend--fFX25mV.js";
2
+ export { BarcodeDetectorFrontEnd, BarcodeDetectorLike, DetectedBarcodeLike, QrDetection, QrFrontEnd, RgbaImage, ToImageBitmapSource, createBarcodeDetectorFrontEnd };
@@ -0,0 +1,60 @@
1
+ //#region ../src/ar/qr-frontend.ts
2
+ const defaultToImageData = (image) => new ImageData(new Uint8ClampedArray(image.data), image.width, image.height);
3
+ var BarcodeDetectorFrontEnd = class {
4
+ kind = "barcode-detector";
5
+ detector;
6
+ toSource;
7
+ constructor(detector, toSource = defaultToImageData) {
8
+ this.detector = detector;
9
+ this.toSource = toSource;
10
+ }
11
+ async detect(image) {
12
+ const results = await this.detector.detect(this.toSource(image));
13
+ for (const r of results) {
14
+ const corners = toQuad(r.cornerPoints);
15
+ if (corners && typeof r.rawValue === "string" && r.rawValue.length > 0) return {
16
+ corners,
17
+ text: r.rawValue
18
+ };
19
+ }
20
+ return null;
21
+ }
22
+ };
23
+ /**
24
+ * Build a {@link BarcodeDetectorFrontEnd} if the runtime exposes a
25
+ * `BarcodeDetector` constructor; otherwise `null` (→ the caller must handle the
26
+ * unsupported-browser case; there is no OpenCV fallback). `ctor` is injectable
27
+ * for tests.
28
+ */
29
+ function createBarcodeDetectorFrontEnd(ctor) {
30
+ const Ctor = ctor ?? globalThis.BarcodeDetector;
31
+ if (!Ctor) return null;
32
+ return new BarcodeDetectorFrontEnd(new Ctor({ formats: ["qr_code"] }));
33
+ }
34
+ function toQuad(points) {
35
+ if (points.length !== 4) return null;
36
+ const [a, b, c, d] = points;
37
+ if (!a || !b || !c || !d) return null;
38
+ const out = [
39
+ {
40
+ x: a.x,
41
+ y: a.y
42
+ },
43
+ {
44
+ x: b.x,
45
+ y: b.y
46
+ },
47
+ {
48
+ x: c.x,
49
+ y: c.y
50
+ },
51
+ {
52
+ x: d.x,
53
+ y: d.y
54
+ }
55
+ ];
56
+ if (out.some((p) => !Number.isFinite(p.x) || !Number.isFinite(p.y))) return null;
57
+ return out;
58
+ }
59
+ //#endregion
60
+ export { BarcodeDetectorFrontEnd, createBarcodeDetectorFrontEnd };