react-native-image-stitcher 0.14.2 → 0.15.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 (116) hide show
  1. package/CHANGELOG.md +131 -0
  2. package/README.md +35 -0
  3. package/RNImageStitcher.podspec +8 -7
  4. package/android/build.gradle +0 -16
  5. package/android/src/main/cpp/CMakeLists.txt +2 -63
  6. package/android/src/main/cpp/image_stitcher_jni.cpp +14 -0
  7. package/android/src/main/cpp/keyframe_gate_jni.cpp +13 -0
  8. package/android/src/main/java/io/imagestitcher/rn/BatchStitcher.kt +285 -3
  9. package/android/src/main/java/io/imagestitcher/rn/IncrementalStitcher.kt +180 -1162
  10. package/android/src/main/java/io/imagestitcher/rn/KeyframeGate.kt +29 -0
  11. package/android/src/main/java/io/imagestitcher/rn/RNImageStitcherPackage.kt +0 -4
  12. package/android/src/main/java/io/imagestitcher/rn/RNSARCameraView.kt +13 -64
  13. package/cpp/keyframe_gate.cpp +82 -23
  14. package/cpp/keyframe_gate.hpp +31 -2
  15. package/cpp/stitcher.cpp +208 -28
  16. package/cpp/tests/CMakeLists.txt +18 -12
  17. package/cpp/tests/keyframe_timebudget_test.cpp +65 -0
  18. package/cpp/tests/warp_guard_test.cpp +48 -0
  19. package/cpp/warp_guard.hpp +41 -0
  20. package/dist/camera/Camera.d.ts +31 -16
  21. package/dist/camera/Camera.js +10 -2
  22. package/dist/camera/CaptureStitchStatsToast.d.ts +15 -2
  23. package/dist/camera/CaptureStitchStatsToast.js +27 -7
  24. package/dist/camera/PanoramaSettings.d.ts +10 -223
  25. package/dist/camera/PanoramaSettings.js +6 -28
  26. package/dist/camera/PanoramaSettingsBridge.d.ts +1 -24
  27. package/dist/camera/PanoramaSettingsBridge.js +3 -102
  28. package/dist/camera/PanoramaSettingsModal.js +7 -1
  29. package/dist/camera/buildPanoramaInitialSettings.d.ts +11 -0
  30. package/dist/camera/buildPanoramaInitialSettings.js +4 -0
  31. package/dist/camera/cameraErrorMessages.d.ts +32 -0
  32. package/dist/camera/cameraErrorMessages.js +53 -0
  33. package/dist/camera/selectCaptureDevice.d.ts +5 -1
  34. package/dist/camera/selectCaptureDevice.js +22 -2
  35. package/dist/camera/useCapture.js +38 -0
  36. package/dist/index.d.ts +5 -8
  37. package/dist/index.js +11 -34
  38. package/dist/stitching/incremental.d.ts +1 -117
  39. package/dist/stitching/stitchVideo.d.ts +0 -35
  40. package/dist/types.d.ts +0 -87
  41. package/ios/Sources/RNImageStitcher/IncrementalStitcher.swift +96 -674
  42. package/ios/Sources/RNImageStitcher/IncrementalStitcherBridge.swift +9 -12
  43. package/ios/Sources/RNImageStitcher/KeyframeGate.swift +14 -0
  44. package/ios/Sources/RNImageStitcher/KeyframeGateBridge.h +7 -0
  45. package/ios/Sources/RNImageStitcher/KeyframeGateBridge.mm +6 -0
  46. package/ios/Sources/RNImageStitcher/OpenCVKeyframeCollector.h +2 -2
  47. package/ios/Sources/RNImageStitcher/OpenCVKeyframeCollector.mm +3 -3
  48. package/ios/Sources/RNImageStitcher/OpenCVStitcher.h +28 -60
  49. package/ios/Sources/RNImageStitcher/OpenCVStitcher.mm +180 -921
  50. package/ios/Sources/RNImageStitcher/RNSARSession.swift +10 -35
  51. package/ios/Sources/RNImageStitcher/Stitcher.swift +84 -35
  52. package/ios/Sources/RNImageStitcher/StitcherBridge.m +13 -0
  53. package/ios/Sources/RNImageStitcher/StitcherBridge.swift +132 -5
  54. package/package.json +3 -2
  55. package/src/camera/Camera.tsx +43 -22
  56. package/src/camera/CaptureStitchStatsToast.tsx +58 -14
  57. package/src/camera/PanoramaSettings.ts +16 -289
  58. package/src/camera/PanoramaSettingsBridge.ts +3 -114
  59. package/src/camera/PanoramaSettingsModal.tsx +14 -1
  60. package/src/camera/__tests__/PanoramaSettingsBridge.test.ts +3 -188
  61. package/src/camera/__tests__/buildPanoramaInitialSettings.test.ts +41 -0
  62. package/src/camera/__tests__/cameraErrorMessages.test.ts +76 -0
  63. package/src/camera/__tests__/selectCaptureDevice.test.ts +33 -0
  64. package/src/camera/buildPanoramaInitialSettings.ts +17 -0
  65. package/src/camera/cameraErrorMessages.ts +84 -0
  66. package/src/camera/selectCaptureDevice.ts +28 -3
  67. package/src/camera/useCapture.ts +44 -1
  68. package/src/index.ts +11 -40
  69. package/src/stitching/incremental.ts +3 -140
  70. package/src/stitching/stitchVideo.ts +0 -26
  71. package/src/types.ts +0 -95
  72. package/android/src/main/cpp/stitcher_jsi_install_jni.cpp +0 -227
  73. package/android/src/main/java/io/imagestitcher/rn/IncrementalFirstwinsEngine.kt +0 -1081
  74. package/android/src/main/java/io/imagestitcher/rn/StitcherJsiInstallerModule.kt +0 -103
  75. package/android/src/main/java/io/imagestitcher/rn/StitcherWorkletRuntime.kt +0 -256
  76. package/cpp/stitcher_frame_jsi.cpp +0 -214
  77. package/cpp/stitcher_frame_jsi.hpp +0 -108
  78. package/cpp/stitcher_proxy_jsi.cpp +0 -109
  79. package/cpp/stitcher_proxy_jsi.hpp +0 -46
  80. package/cpp/stitcher_worklet_dispatch.cpp +0 -103
  81. package/cpp/stitcher_worklet_dispatch.hpp +0 -71
  82. package/cpp/stitcher_worklet_registry.cpp +0 -91
  83. package/cpp/stitcher_worklet_registry.hpp +0 -146
  84. package/cpp/tests/stitcher_worklet_registry_test.cpp +0 -195
  85. package/dist/stitching/IncrementalStitcherView.d.ts +0 -41
  86. package/dist/stitching/IncrementalStitcherView.js +0 -157
  87. package/dist/stitching/StitcherWorkletRegistry.d.ts +0 -117
  88. package/dist/stitching/StitcherWorkletRegistry.js +0 -78
  89. package/dist/stitching/ensureStitcherProxyInstalled.d.ts +0 -8
  90. package/dist/stitching/ensureStitcherProxyInstalled.js +0 -81
  91. package/dist/stitching/useFrameProcessor.d.ts +0 -119
  92. package/dist/stitching/useFrameProcessor.js +0 -196
  93. package/dist/stitching/useFrameStream.d.ts +0 -34
  94. package/dist/stitching/useFrameStream.js +0 -234
  95. package/dist/stitching/useThrottledFrameProcessor.d.ts +0 -33
  96. package/dist/stitching/useThrottledFrameProcessor.js +0 -132
  97. package/ios/Sources/RNImageStitcher/OpenCVIncrementalStitcher.h +0 -474
  98. package/ios/Sources/RNImageStitcher/OpenCVIncrementalStitcher.mm +0 -1328
  99. package/ios/Sources/RNImageStitcher/OpenCVSlitScanStitcher.h +0 -103
  100. package/ios/Sources/RNImageStitcher/OpenCVSlitScanStitcher.mm +0 -3285
  101. package/ios/Sources/RNImageStitcher/RNSARWorkletRuntime.h +0 -128
  102. package/ios/Sources/RNImageStitcher/RNSARWorkletRuntime.mm +0 -313
  103. package/ios/Sources/RNImageStitcher/SaveFrameAsJpegPlugin.mm +0 -185
  104. package/ios/Sources/RNImageStitcher/StitcherFrameHostObject.h +0 -60
  105. package/ios/Sources/RNImageStitcher/StitcherFrameHostObject.mm +0 -214
  106. package/ios/Sources/RNImageStitcher/StitcherJsiInstaller.h +0 -42
  107. package/ios/Sources/RNImageStitcher/StitcherJsiInstaller.mm +0 -160
  108. package/src/stitching/IncrementalStitcherView.tsx +0 -198
  109. package/src/stitching/StitcherWorkletRegistry.ts +0 -156
  110. package/src/stitching/__tests__/StitcherWorkletRegistry.test.ts +0 -176
  111. package/src/stitching/__tests__/ensureStitcherProxyInstalled.test.ts +0 -94
  112. package/src/stitching/__tests__/useThrottledFrameProcessor.test.ts +0 -178
  113. package/src/stitching/ensureStitcherProxyInstalled.ts +0 -141
  114. package/src/stitching/useFrameProcessor.ts +0 -226
  115. package/src/stitching/useFrameStream.ts +0 -271
  116. package/src/stitching/useThrottledFrameProcessor.ts +0 -145
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Friendly, action-guiding copy for the *recoverable* stitch-failure
3
+ * `CameraErrorCode`s — the ones a user can fix by simply re-capturing.
4
+ * Hosts map these onto an Alert/toast instead of surfacing the raw
5
+ * cv::Stitcher diagnostic (e.g. "warpRoi too large (8171x12336) —
6
+ * estimator produced degenerate camera params").
7
+ *
8
+ * Returns `null` for every non-recoverable / non-stitch code (permission
9
+ * denied, device unavailable, generic finalize failure, unknown, ...):
10
+ * those have no single corrective action to suggest, so the host should
11
+ * fall back to its generic error display.
12
+ *
13
+ * Lives in the SDK (not per-host) so every consumer shows the same
14
+ * vetted guidance for the same failure — and so the mapping is
15
+ * unit-testable in isolation.
16
+ */
17
+ import type { CameraErrorCode } from './Camera';
18
+ export interface UserFacingStitchError {
19
+ /** Short, friendly alert title. */
20
+ title: string;
21
+ /** One-paragraph, plain-language corrective guidance. */
22
+ message: string;
23
+ }
24
+ /**
25
+ * Maps a `CameraErrorCode` to friendly, action-guiding alert copy.
26
+ *
27
+ * @returns the title+message for a recoverable stitch failure, or `null`
28
+ * if `code` has no single user-recoverable action (the host should
29
+ * then show its generic error UI).
30
+ */
31
+ export declare function userFacingStitchError(code: CameraErrorCode): UserFacingStitchError | null;
32
+ //# sourceMappingURL=cameraErrorMessages.d.ts.map
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.userFacingStitchError = userFacingStitchError;
4
+ /**
5
+ * The four recoverable stitch outcomes, each with copy tuned to its
6
+ * actual root cause. `Partial<Record<...>>` keeps the keys
7
+ * compile-checked against the `CameraErrorCode` union — a renamed or
8
+ * dropped code breaks the build here rather than silently going
9
+ * unhandled.
10
+ */
11
+ const RECOVERABLE_STITCH_GUIDANCE = {
12
+ // cv::Stitcher ERR_NEED_MORE_IMGS / the manual pipeline's "0 valid
13
+ // pairwise matches" — the frames simply don't overlap enough to chain.
14
+ STITCH_NEED_MORE_IMGS: {
15
+ title: 'Please pan more slowly',
16
+ message: "There wasn't enough overlap between the frames to stitch them "
17
+ + 'together — each frame needs to overlap the one before it.',
18
+ },
19
+ // Bundle adjuster produced degenerate camera params (the warp canvas
20
+ // blew past the size guard) — almost always real camera *translation*
21
+ // breaking PANORAMA mode's pure-rotation assumption, amplified hugely
22
+ // on the ultra-wide lens.
23
+ STITCH_CAMERA_PARAMS_FAIL: {
24
+ title: 'Please pan more slowly',
25
+ message: 'The view moved too much between frames to line them up — usually '
26
+ + 'because the phone moved through space rather than just turning. '
27
+ + 'The ultra-wide (0.5x) lens is especially sensitive to this, so '
28
+ + 'try 1x for wide scenes.',
29
+ },
30
+ // Pairwise homography estimation failed — frames couldn't be aligned.
31
+ STITCH_HOMOGRAPHY_FAIL: {
32
+ title: 'Please pan more slowly',
33
+ message: "The frames couldn't be aligned — keep the phone level and steady so "
34
+ + 'each frame overlaps the one before it.',
35
+ },
36
+ // Ran out of memory finishing the stitch — usually an over-long sweep.
37
+ STITCH_OOM: {
38
+ title: 'Try a shorter sweep',
39
+ message: 'This panorama needs more memory than the device can spare to finish '
40
+ + '— a shorter, narrower sweep (or 1x for wide scenes) will fit.',
41
+ },
42
+ };
43
+ /**
44
+ * Maps a `CameraErrorCode` to friendly, action-guiding alert copy.
45
+ *
46
+ * @returns the title+message for a recoverable stitch failure, or `null`
47
+ * if `code` has no single user-recoverable action (the host should
48
+ * then show its generic error UI).
49
+ */
50
+ function userFacingStitchError(code) {
51
+ return RECOVERABLE_STITCH_GUIDANCE[code] ?? null;
52
+ }
53
+ //# sourceMappingURL=cameraErrorMessages.js.map
@@ -43,7 +43,11 @@ export interface DeviceLike {
43
43
  export type CaptureDeviceMode =
44
44
  /** One multi-cam device spans wide + ultra-wide; switch lenses via zoom. */
45
45
  'multicam'
46
- /** Separate standalone wide + ultra-wide devices; switch by remounting. */
46
+ /**
47
+ * Ultra-wide reached by remounting a dedicated ultra-wide device on 0.5x
48
+ * (the 1x primary may be a multi-cam *or* a standalone wide). Used when
49
+ * no multi-cam device can reach the ultra-wide by zoom.
50
+ */
47
51
  | 'standalone-uw'
48
52
  /** No ultra-wide anywhere; wide-angle only (no 0.5× chip). */
49
53
  | 'wide-only';
@@ -29,6 +29,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports.selectCaptureDevice = selectCaptureDevice;
30
30
  exports.zoomForLens = zoomForLens;
31
31
  const hasLens = (d, lens) => d.physicalDevices.includes(lens);
32
+ /**
33
+ * Max `minZoom` a multi-cam device may report and still count as able to
34
+ * reach the ultra-wide *by zoom*. Real ultra-wides sit at ~0.5-0.65x, so a
35
+ * logical device whose zoom range genuinely extends to the ultra-wide reports
36
+ * `minZoom <= ~0.65`. A device that only *lists* the ultra-wide (a separate
37
+ * physical camera on Android/Camera2, not a zoom target) reports
38
+ * `minZoom = 1.0`. 0.7 cleanly separates the two.
39
+ */
40
+ const UW_ZOOM_REACH_MAX = 0.7;
32
41
  /**
33
42
  * Choose the back-camera device(s) for capture.
34
43
  *
@@ -59,7 +68,13 @@ function selectCaptureDevice(devices) {
59
68
  // (more lenses → more reach), as a stable tiebreak.
60
69
  const multicamCandidates = back.filter((d) => d.isMultiCam &&
61
70
  hasLens(d, 'wide-angle-camera') &&
62
- hasLens(d, 'ultra-wide-angle-camera'));
71
+ hasLens(d, 'ultra-wide-angle-camera') &&
72
+ // Must reach the ultra-wide by zoom. On iOS the virtual device's zoom
73
+ // range spans it (minZoom ~0.5); on Android a logical device often
74
+ // *lists* the ultra-wide while its zoom range starts at 1.0 (separate
75
+ // physical camera, not a zoom target). If it can't zoom there, it
76
+ // does NOT qualify -- we fall through to the device-swap path below.
77
+ d.minZoom <= UW_ZOOM_REACH_MAX);
63
78
  if (multicamCandidates.length > 0) {
64
79
  const device = multicamCandidates.reduce((best, d) => {
65
80
  // torch-bearing wins; then wider zoom span; then more lenses.
@@ -87,8 +102,13 @@ function selectCaptureDevice(devices) {
87
102
  //
88
103
  // Prefer a torch-bearing wide-angle device as the `1×`/primary mount.
89
104
  const wideDevices = back.filter((d) => hasLens(d, 'wide-angle-camera'));
105
+ // A *true* standalone ultra-wide (its own id, NOT a multi-cam grouping).
106
+ // We deliberately do NOT fall back to a multi-cam device: mounting a
107
+ // logical multi-cam yields its WIDE member, not the ultra-wide, so a
108
+ // "swap" to it would silently show the wrong FOV. If the only ultra-wide
109
+ // lives inside a non-zoomable multi-cam device, it is undeliverable and we
110
+ // hide the chooser (wide-only) below.
90
111
  const ultraWide = back.find((d) => !d.isMultiCam && hasLens(d, 'ultra-wide-angle-camera')) ??
91
- back.find((d) => hasLens(d, 'ultra-wide-angle-camera')) ??
92
112
  null;
93
113
  if (wideDevices.length > 0 && ultraWide != null) {
94
114
  // Prefer the simplest wide device (fewest extra lenses) with a torch
@@ -109,6 +109,44 @@ function useCapture(options = {}) {
109
109
  device = legacyDevice ?? legacyFallback;
110
110
  activeZoom = undefined;
111
111
  }
112
+ // v0.15 diagnostic (dev-only) — for the "0.5× pill shows but tapping
113
+ // doesn't switch the camera" report on Android (Samsung). Logs the
114
+ // resolved capture mode + the mounted device's zoom range so logcat
115
+ // reveals whether `minZoom` actually reaches the ultra-wide. On
116
+ // Camera2 the logical multi-camera's zoom range usually starts at 1.0
117
+ // (the ultra-wide is a separate physical id, not a zoom target), so a
118
+ // zoom-based 0.5× switch is a silent no-op.
119
+ (0, react_1.useEffect)(() => {
120
+ if (!__DEV__)
121
+ return;
122
+ const summarise = (d) => d
123
+ ? {
124
+ id: d.id,
125
+ physical: d.physicalDevices,
126
+ isMultiCam: d.isMultiCam,
127
+ minZoom: d.minZoom,
128
+ neutralZoom: d.neutralZoom,
129
+ maxZoom: d.maxZoom,
130
+ hasTorch: d.hasTorch,
131
+ }
132
+ : null;
133
+ const back = allDevices.filter((d) => d.position === 'back');
134
+ // eslint-disable-next-line no-console
135
+ console.log('[rnimagestitcher] lens-select ' +
136
+ JSON.stringify({
137
+ lens: lens ?? null,
138
+ mode: selection.mode,
139
+ has0_5x: selection.has0_5x,
140
+ activeZoom: activeZoom ?? null,
141
+ selected: summarise(selection.device),
142
+ ultraWide: summarise(selection.ultraWideDevice),
143
+ // Full back-camera enumeration — reveals whether a multicam
144
+ // device merely *lists* the ultra-wide while its zoom range
145
+ // can't reach it (minZoom ~1.0), and whether a STANDALONE
146
+ // ultra-wide device exists for the standalone-uw fallback.
147
+ allBack: back.map(summarise),
148
+ }));
149
+ }, [allDevices, selection, lens, activeZoom]);
112
150
  // Enumerate ALL physical lens types available on the chosen
113
151
  // position so the host can decide whether to render a switcher.
114
152
  // Vision-camera's `useCameraDevices()` returns CameraDevice[]; each
package/dist/index.d.ts CHANGED
@@ -21,6 +21,8 @@
21
21
  */
22
22
  export { Camera, CameraError } from './camera/Camera';
23
23
  export type { CameraProps, CameraCaptureResult, CameraErrorCode, CaptureSource, CaptureSourcesMode, CameraLens, StitchMode, Blender, SeamFinder, Warper, FramesDroppedInfo, } from './camera/Camera';
24
+ export { userFacingStitchError } from './camera/cameraErrorMessages';
25
+ export type { UserFacingStitchError } from './camera/cameraErrorMessages';
24
26
  export { useARSession, ARTrackingState } from './ar/useARSession';
25
27
  export type { UseARSessionReturn, FramePose, } from './ar/useARSession';
26
28
  export { useIMUTranslationGate } from './sensors/useIMUTranslationGate';
@@ -50,9 +52,9 @@ export type { CaptureThumbnailItem } from './camera/CaptureThumbnailStrip';
50
52
  export { PanoramaBandOverlay } from './camera/PanoramaBandOverlay';
51
53
  export { PanoramaSettingsModal } from './camera/PanoramaSettingsModal';
52
54
  export type { PanoramaSettingsModalProps } from './camera/PanoramaSettingsModal';
53
- export { DEFAULT_PANORAMA_SETTINGS, DEFAULT_FLOW_GATE_SETTINGS, DEFAULT_SLITSCAN_SETTINGS, DEFAULT_HYBRID_SETTINGS, } from './camera/PanoramaSettings';
54
- export type { CaptureBaseSettings, PanoramaSettings, BatchStitcherSettings, FrameSelectionSettings, FlowGateSettings, SlitscanSettings, SlitscanPaintingSettings, SlitscanRegistrationSettings, SlitscanAdvancedSettings, Ncc1dSettings, Ncc2dSettings, PlaneProjectionSettings, HybridSettings, } from './camera/PanoramaSettings';
55
- export { panoramaSettingsToNativeConfig, slitscanSettingsToNativeConfig, hybridSettingsToNativeConfig, } from './camera/PanoramaSettingsBridge';
55
+ export { DEFAULT_PANORAMA_SETTINGS, DEFAULT_FLOW_GATE_SETTINGS, } from './camera/PanoramaSettings';
56
+ export type { CaptureBaseSettings, PanoramaSettings, BatchStitcherSettings, FrameSelectionSettings, FlowGateSettings, } from './camera/PanoramaSettings';
57
+ export { panoramaSettingsToNativeConfig, } from './camera/PanoramaSettingsBridge';
56
58
  export type { NativeConfigDict } from './camera/PanoramaSettingsBridge';
57
59
  export { ViewportCropOverlay } from './camera/ViewportCropOverlay';
58
60
  export { useCapture } from './camera/useCapture';
@@ -69,11 +71,6 @@ export type { IncrementalState, AcceptedKeyframe } from './stitching/incremental
69
71
  export { useIncrementalStitcher } from './stitching/useIncrementalStitcher';
70
72
  export { useKeyframeStream } from './stitching/useKeyframeStream';
71
73
  export type { StitcherFrame, StitcherFrameProcessor, ARAnchor, } from './stitching/StitcherFrame';
72
- export { useFrameProcessor } from './stitching/useFrameProcessor';
73
- export { useThrottledFrameProcessor } from './stitching/useThrottledFrameProcessor';
74
- export type { ThrottledFrameProcessorOptions } from './types';
75
- export { useFrameStream } from './stitching/useFrameStream';
76
- export type { FrameStreamOptions, SampledFrame } from './types';
77
74
  export { useFrameProcessorDriver } from './stitching/useFrameProcessorDriver';
78
75
  export type { UseFrameProcessorDriverOptions, FrameProcessorDriverHandle, } from './stitching/useFrameProcessorDriver';
79
76
  export { useStitcherWorklet } from './stitching/useStitcherWorklet';
package/dist/index.js CHANGED
@@ -22,13 +22,18 @@
22
22
  * adds RetaiLens-specific features on top.
23
23
  */
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.stitchVideo = exports.useStitcherWorklet = exports.useFrameProcessorDriver = exports.useFrameStream = exports.useThrottledFrameProcessor = exports.useFrameProcessor = exports.useKeyframeStream = exports.useIncrementalStitcher = exports.cleanupOldKeyframes = exports.getIncrementalNativeModule = exports.subscribeIncrementalState = exports.incrementalStitcherIsAvailable = exports.IncrementalOutcome = exports.OrientationDriftModal = exports.useOrientationDrift = exports.useDeviceOrientation = exports.useVideoCapture = exports.useCapture = exports.ViewportCropOverlay = exports.hybridSettingsToNativeConfig = exports.slitscanSettingsToNativeConfig = exports.panoramaSettingsToNativeConfig = exports.DEFAULT_HYBRID_SETTINGS = exports.DEFAULT_SLITSCAN_SETTINGS = exports.DEFAULT_FLOW_GATE_SETTINGS = exports.DEFAULT_PANORAMA_SETTINGS = exports.PanoramaSettingsModal = exports.PanoramaBandOverlay = exports.CaptureThumbnailStrip = exports.useStitchStatsToast = exports.CaptureStitchStatsToast = exports.CaptureOrientationPill = exports.CaptureKeyframePill = exports.CaptureMemoryPill = exports.CaptureDebugOverlay = exports.CaptureStatusOverlay = exports.CapturePreview = exports.CaptureControlsBar = exports.CaptureHeader = exports.CameraView = exports.ARCameraView = exports.useIMUTranslationGate = exports.ARTrackingState = exports.useARSession = exports.CameraError = exports.Camera = void 0;
25
+ exports.stitchVideo = exports.useStitcherWorklet = exports.useFrameProcessorDriver = exports.useKeyframeStream = exports.useIncrementalStitcher = exports.cleanupOldKeyframes = exports.getIncrementalNativeModule = exports.subscribeIncrementalState = exports.incrementalStitcherIsAvailable = exports.IncrementalOutcome = exports.OrientationDriftModal = exports.useOrientationDrift = exports.useDeviceOrientation = exports.useVideoCapture = exports.useCapture = exports.ViewportCropOverlay = exports.panoramaSettingsToNativeConfig = exports.DEFAULT_FLOW_GATE_SETTINGS = exports.DEFAULT_PANORAMA_SETTINGS = exports.PanoramaSettingsModal = exports.PanoramaBandOverlay = exports.CaptureThumbnailStrip = exports.useStitchStatsToast = exports.CaptureStitchStatsToast = exports.CaptureOrientationPill = exports.CaptureKeyframePill = exports.CaptureMemoryPill = exports.CaptureDebugOverlay = exports.CaptureStatusOverlay = exports.CapturePreview = exports.CaptureControlsBar = exports.CaptureHeader = exports.CameraView = exports.ARCameraView = exports.useIMUTranslationGate = exports.ARTrackingState = exports.useARSession = exports.userFacingStitchError = exports.CameraError = exports.Camera = void 0;
26
26
  // ─────────────────────────────────────────────────────────────────────
27
27
  // Layer 1 — the high-level <Camera> component
28
28
  // ─────────────────────────────────────────────────────────────────────
29
29
  var Camera_1 = require("./camera/Camera");
30
30
  Object.defineProperty(exports, "Camera", { enumerable: true, get: function () { return Camera_1.Camera; } });
31
31
  Object.defineProperty(exports, "CameraError", { enumerable: true, get: function () { return Camera_1.CameraError; } });
32
+ // Recoverable-stitch-failure → friendly Alert copy. Hosts call this in
33
+ // their onError handler to surface actionable guidance ("pan more slowly",
34
+ // "pivot in place") instead of the raw cv::Stitcher diagnostic.
35
+ var cameraErrorMessages_1 = require("./camera/cameraErrorMessages");
36
+ Object.defineProperty(exports, "userFacingStitchError", { enumerable: true, get: function () { return cameraErrorMessages_1.userFacingStitchError; } });
32
37
  // ─────────────────────────────────────────────────────────────────────
33
38
  // AR foundation (public since 0.1.0)
34
39
  // ─────────────────────────────────────────────────────────────────────
@@ -106,16 +111,12 @@ Object.defineProperty(exports, "PanoramaSettingsModal", { enumerable: true, get:
106
111
  var PanoramaSettings_1 = require("./camera/PanoramaSettings");
107
112
  Object.defineProperty(exports, "DEFAULT_PANORAMA_SETTINGS", { enumerable: true, get: function () { return PanoramaSettings_1.DEFAULT_PANORAMA_SETTINGS; } });
108
113
  Object.defineProperty(exports, "DEFAULT_FLOW_GATE_SETTINGS", { enumerable: true, get: function () { return PanoramaSettings_1.DEFAULT_FLOW_GATE_SETTINGS; } });
109
- Object.defineProperty(exports, "DEFAULT_SLITSCAN_SETTINGS", { enumerable: true, get: function () { return PanoramaSettings_1.DEFAULT_SLITSCAN_SETTINGS; } });
110
- Object.defineProperty(exports, "DEFAULT_HYBRID_SETTINGS", { enumerable: true, get: function () { return PanoramaSettings_1.DEFAULT_HYBRID_SETTINGS; } });
111
114
  // Settings → native config adapters. Layer 2 hosts building their
112
115
  // own capture flow on top of `incremental.start()` should always
113
116
  // pass the result of the matching adapter as `config`; the bridge is
114
117
  // the single source of truth for the JS↔native wire format.
115
118
  var PanoramaSettingsBridge_1 = require("./camera/PanoramaSettingsBridge");
116
119
  Object.defineProperty(exports, "panoramaSettingsToNativeConfig", { enumerable: true, get: function () { return PanoramaSettingsBridge_1.panoramaSettingsToNativeConfig; } });
117
- Object.defineProperty(exports, "slitscanSettingsToNativeConfig", { enumerable: true, get: function () { return PanoramaSettingsBridge_1.slitscanSettingsToNativeConfig; } });
118
- Object.defineProperty(exports, "hybridSettingsToNativeConfig", { enumerable: true, get: function () { return PanoramaSettingsBridge_1.hybridSettingsToNativeConfig; } });
119
120
  var ViewportCropOverlay_1 = require("./camera/ViewportCropOverlay");
120
121
  Object.defineProperty(exports, "ViewportCropOverlay", { enumerable: true, get: function () { return ViewportCropOverlay_1.ViewportCropOverlay; } });
121
122
  // ── Capture hooks ─────────────────────────────────────────────────────
@@ -157,35 +158,11 @@ Object.defineProperty(exports, "useIncrementalStitcher", { enumerable: true, get
157
158
  // keyframe, packet detection, server-side analysis, etc.).
158
159
  var useKeyframeStream_1 = require("./stitching/useKeyframeStream");
159
160
  Object.defineProperty(exports, "useKeyframeStream", { enumerable: true, get: function () { return useKeyframeStream_1.useKeyframeStream; } });
160
- // v0.8.0 Phase 4a — public host-worklet hook. Hosts that want a
161
- // per-frame callback (OCR overlay, packet detection, ML inference)
162
- // use this to attach a `'worklet'`-prefixed function that fires
163
- // on the camera producer thread. Non-AR mode is fully wired
164
- // today via vision-camera passthrough; AR-mode dispatch is
165
- // API-stable but registration-only until Phase 4b lands the
166
- // cross-runtime handoff (the AR runtime iterating the registry).
167
- // See the hook's docstring + StitcherFrame.ts for the contract.
168
- var useFrameProcessor_1 = require("./stitching/useFrameProcessor");
169
- Object.defineProperty(exports, "useFrameProcessor", { enumerable: true, get: function () { return useFrameProcessor_1.useFrameProcessor; } });
170
- // v0.9.0 Layer 2 — `useThrottledFrameProcessor`. Throttle gate over
171
- // `useFrameProcessor` for sub-frame-rate worklet-native processing
172
- // (native OCR via Vision.framework / ML Kit, TFLite ML detection,
173
- // LiDAR depth). The worklet runtime has direct access to
174
- // `frame.toArrayBuffer()` / `frame.arDepth`; bridge small payloads
175
- // (bboxes, depth-derived metrics) to JS via `runOnJS`. For JS-thread
176
- // JPEG consumers (file-path OCR libs, cloud upload, thumbnail UI),
177
- // prefer `useFrameStream` (Layer 3, ships in the same release).
178
- var useThrottledFrameProcessor_1 = require("./stitching/useThrottledFrameProcessor");
179
- Object.defineProperty(exports, "useThrottledFrameProcessor", { enumerable: true, get: function () { return useThrottledFrameProcessor_1.useThrottledFrameProcessor; } });
180
- // v0.9.0 Layer 3 — `useFrameStream`. JS-thread sampled-frame
181
- // stream over Layer 1 (`save_frame_as_jpeg` vc plugin) + Layer 2
182
- // (`useThrottledFrameProcessor`). Use for JS-thread consumers:
183
- // file-path OCR libs (RN modules), cloud upload, thumbnail UI.
184
- // For worklet-native processing (Vision/ML Kit as vc plugins,
185
- // TFLite ML, LiDAR depth), prefer `useThrottledFrameProcessor`
186
- // (Layer 2) — lower latency, no JPEG roundtrip.
187
- var useFrameStream_1 = require("./stitching/useFrameStream");
188
- Object.defineProperty(exports, "useFrameStream", { enumerable: true, get: function () { return useFrameStream_1.useFrameStream; } });
161
+ // NOTE: the host-worklet / frame-stream hooks `useFrameProcessor`,
162
+ // `useThrottledFrameProcessor` and `useFrameStream` (v0.8–v0.9) were
163
+ // archived in the batch-keyframe cleanup they drove the third-party
164
+ // `__stitcherProxy` observer API, not batch-keyframe capture. Source is
165
+ // preserved under archive/src/stitching/ to build on later.
189
166
  // vision-camera Frame Processor driver for non-AR captures. As
190
167
  // of v0.6 the only non-AR driver exported (the legacy
191
168
  // `useIncrementalJSDriver` was removed; was deprecated in v0.5).
@@ -403,7 +403,7 @@ export interface IncrementalStartOptions {
403
403
  * 'slitscan' fall back to 'slitscan-both' with a deprecation warning
404
404
  * in the native log.
405
405
  */
406
- engine?: 'hybrid' | 'slitscan-rotate' | 'slitscan-both' | 'batch-keyframe' | 'firstwins' | 'firstwins-zoomed' | 'firstwins-rectilinear' | 'slitscan';
406
+ engine?: 'batch-keyframe';
407
407
  /**
408
408
  * V15 — per-stage correction config overrides. Mode-driven defaults
409
409
  * are applied first (see RLISStitcherConfig +configForMode:); fields
@@ -418,122 +418,6 @@ export interface IncrementalStartOptions {
418
418
  * fields optional (omit to accept the engine-mode default).
419
419
  */
420
420
  export interface StitcherConfig {
421
- /** Fraction of pan-axis the rectilinear slit retains per frame.
422
- * Range 0.10 – 0.70, default 0.30 in V15 slit-scan modes. */
423
- kPanAxisFractionRect: number;
424
- /** Minimum pan-axis advance (px) before a frame is accepted.
425
- * 0 = accept on every consumeFrame (Apple-dense slit-scan, V15
426
- * default). 50 = V13.0g default. */
427
- kMinAcceptDeltaPx: number;
428
- /** V13.0e+ ORB triangulation + median-Z parallax correction. */
429
- enableTriangulation: boolean;
430
- /** V13.0g per-accept incremental Δt accumulator on top of triangulation. */
431
- enableTriAccumulator: boolean;
432
- /** V15 1D NCC perpendicular-axis wobble correction (slitscan-rotate
433
- * default). Independent of the other correction stages. */
434
- enable1dNcc: boolean;
435
- /** 1D NCC search radius in pixels (5 – 60). */
436
- nccSearchRadius1d: number;
437
- /** V13.0g 2D NCC fine-alignment after triangulation. */
438
- enable2dNcc: boolean;
439
- /** V14.0a RANSAC homography per slit + cv::warpPerspective. When
440
- * enabled and successful, supersedes the rectangular paste path. */
441
- enableRansacHomography: boolean;
442
- /** 'FirstPaintedWins' protects already-painted pixels (V13.0e+
443
- * default). 'FeatherBlend' alpha-blends new content into already-
444
- * painted overlap pixels (V13.0d-style; V15 slitscan-both default). */
445
- paintMode: 'FirstPaintedWins' | 'FeatherBlend';
446
- /** 'Cylindrical' (V12.x – V14.0a behaviour) or 'Planar' (V15 default;
447
- * cv::detail::PlaneWarper). Planar is well-behaved for pans <60°. */
448
- hybridProjection: 'Cylindrical' | 'Planar';
449
- /** V15.0c — where on the camera frame the per-accept sliver is taken.
450
- * 'Center' (V13.x default), 'Bottom' (leading edge for top-to-bottom
451
- * pan), or 'Top' (leading edge for bottom-to-top pan). */
452
- sliverPosition: 'Center' | 'Bottom' | 'Top';
453
- /** V15.0c — when true, the FIRST accepted frame paints the entire
454
- * camera frame at canvas (0, 0); subsequent frames still use the
455
- * configured sliver clip. Default false; set true when sliverPosition
456
- * is Bottom/Top so the canvas is anchored with full-frame content. */
457
- firstFrameFullFrame: boolean;
458
- /** **DEPRECATED in V15.0d** — use `planeSource` instead.
459
- *
460
- * V15.0b boolean toggle for the plane-projected stitch path.
461
- * Kept for backward compat: when `planeSource` is left at its
462
- * default (Disabled), `useDetectedPlane = true` upgrades it to
463
- * ARKitDetected. New callers should set `planeSource` directly. */
464
- useDetectedPlane: boolean;
465
- /** V15.0d — source of the plane used by the V15.0b plane-projected
466
- * stitch path.
467
- *
468
- * - 'Disabled' (default): no plane projection; slit-scan path runs.
469
- * - 'ARKitDetected': use ARKit's first vertical plane that aligns
470
- * with the camera's view direction (filter threshold:
471
- * `arkitPlaneAlignmentThreshold`). Falls back to slit-scan
472
- * silently when no aligned plane is found.
473
- * - 'Virtual': synthesize a plane at first frame: origin =
474
- * camera_pos + `virtualPlaneDepthMeters` × camera_forward;
475
- * normal = -camera_forward. Always works; no ARKit dependency.
476
- *
477
- * Field testing showed ARKit plane detection often picks the WRONG
478
- * surface (side wall, doorframe) — Virtual mode is the safer
479
- * default for arbitrary scenes. ARKitDetected wins when ARKit
480
- * finds the correct fixture face. */
481
- planeSource: 'Disabled' | 'ARKitDetected' | 'Virtual';
482
- /** V15.0d — depth (metres) at which the synthetic plane is placed
483
- * in front of the camera when `planeSource = Virtual`. Set to
484
- * the user's typical scan distance. Range 0.3 – 5.0 m. Default
485
- * 1.5 m. */
486
- virtualPlaneDepthMeters: number;
487
- /** V15.0d — minimum dot product between an ARKit-detected plane's
488
- * surface normal and the camera's facing direction for the plane
489
- * to be accepted (when `planeSource = ARKitDetected`). 1.0 =
490
- * plane perfectly facing camera; 0.0 = plane edge-on; negative
491
- * = facing away. Range 0.0 – 1.0. Default 0.6 (≈53° max angle
492
- * off-camera). */
493
- arkitPlaneAlignmentThreshold: number;
494
- /** V15.0g — how the plane-projection helper renders each frame onto
495
- * the canvas. Affects ARKitDetected and Virtual modes; ignored
496
- * when planeSource = Disabled.
497
- *
498
- * - 'Trapezoidal' (V15.0b legacy): geometrically-correct 3D
499
- * raycast. Each camera pixel maps to its plane intersection.
500
- * Result is a trapezoid that grows distorted with tilt
501
- * (cooler-bottom-2.3×-wider-than-top problem).
502
- * - 'Rectified' (V15.0g default): camera frame pasted as a clean
503
- * rectangle around its plane-projected anchor. Eliminates the
504
- * tilt-induced trapezoidal distortion at the cost of strict 3D-
505
- * correctness — the camera's per-pixel perspective stays inside
506
- * the rectangle but doesn't reconcile across tilts. */
507
- planeProjectionStyle: 'Trapezoidal' | 'Rectified';
508
- /** V15.0d — 2D NCC search half-window in pixels. Was hardcoded
509
- * ±12 in V15.0c.4. Smaller = less wandering on repetitive
510
- * textures (peg holes, slatted panels), but easier to miss the
511
- * true overlap when pose noise is high. Range 4 – 30. Default
512
- * 12. */
513
- nccSearchMargin2d: number;
514
- /** V15.0d — 2D NCC confidence threshold below which the correction
515
- * is rejected. Was hardcoded 0.75 in V15.0c.4. Higher = stricter,
516
- * fewer false matches on repetitive textures, but more frames
517
- * where NCC silently doesn't fire. Range 0.30 – 0.99. Default
518
- * 0.75. */
519
- nccConfidenceThreshold2d: number;
520
- /** V15.0d (1B) — exponential-moving-average smoothing on 2D NCC
521
- * corrections. When enabled, the applied correction is
522
- * `α × current + (1−α) × prev` instead of just `current`. Damps
523
- * single-frame snaps to spurious peaks. Default false. */
524
- enableNcc2dEmaSmoothing: boolean;
525
- /** V15.0d — EMA weight on the CURRENT-frame NCC correction
526
- * (1 − α weight on the previous correction). Range 0.05 – 0.95.
527
- * Default 0.4 (60% prev / 40% current — heavy damping). */
528
- ncc2dEmaAlpha: number;
529
- /** V15.0d (1C) — pan-axis-aware 2D NCC. When enabled, the cross-
530
- * axis (perpendicular to pan) NCC correction is clamped tighter
531
- * than the pan-axis (since 1D NCC + pose already handle cross-
532
- * axis wobble). Default false. */
533
- enableNcc2dPanAxisLock: boolean;
534
- /** V15.0d — cross-axis clamp (pixels) for the pan-axis-aware mode.
535
- * Range 0 – 30. Default 5. */
536
- ncc2dCrossAxisLockPx: number;
537
421
  /** V16 — how the engine decides which ARFrames to ingest.
538
422
  *
539
423
  * - 'time-based' (default): every frame the AR delegate delivers
@@ -69,41 +69,6 @@ export interface StitchVideoOptions {
69
69
  * Right choice on low-RAM devices or with `feather`.
70
70
  */
71
71
  seamFinderType?: 'graphcut' | 'skip';
72
- /**
73
- * Phase 5: pose-driven stitching. When present and non-empty,
74
- * the native stitcher skips features → matching → BundleAdjuster
75
- * and builds cv::detail::CameraParams directly from each pose's
76
- * intrinsics + quaternion. Each entry has the shape returned
77
- * by `NativeModules.RNSARSession.snapshotPoseLog()`:
78
- *
79
- * { tx, ty, tz, qx, qy, qz, qw,
80
- * fx, fy, cx, cy,
81
- * imageWidth, imageHeight,
82
- * timestampMs, trackingState }
83
- *
84
- * Frames whose closest pose is beyond a 100 ms tolerance are
85
- * dropped before stitching; if fewer than 2 remain the call
86
- * rejects with `opencv-failed-1032` so the host can fall back
87
- * to the feature-matched path (re-call `stitchVideo` without
88
- * `poses`).
89
- */
90
- poses?: Array<{
91
- tx: number;
92
- ty: number;
93
- tz: number;
94
- qx: number;
95
- qy: number;
96
- qz: number;
97
- qw: number;
98
- fx: number;
99
- fy: number;
100
- cx: number;
101
- cy: number;
102
- imageWidth: number;
103
- imageHeight: number;
104
- timestampMs: number;
105
- trackingState: number;
106
- }>;
107
72
  }
108
73
  /**
109
74
  * Stitch a recorded video file into a single panoramic JPEG.
package/dist/types.d.ts CHANGED
@@ -35,93 +35,6 @@ export interface DeviceMetadata {
35
35
  cameraId: string;
36
36
  flashEnabled: boolean;
37
37
  }
38
- /**
39
- * v0.9.0 Layer 3 — one sampled frame delivered by `useFrameStream`
40
- * to the JS-thread handler.
41
- *
42
- * The JPEG file at `jpegPath` is the stream's own copy. Hosts that
43
- * need long-term retention MUST copy the file synchronously inside
44
- * the handler — the same path may be overwritten by a subsequent
45
- * sample (slot reuse — see the hook's docstring for the rotation
46
- * policy).
47
- */
48
- export interface SampledFrame {
49
- /** Absolute filesystem path to the JPEG. No `file://` prefix. */
50
- jpegPath: string;
51
- /**
52
- * Pose at sample time. `translation` is `undefined` in non-AR
53
- * mode (gyro provides rotation only; no spatial anchor).
54
- */
55
- pose: {
56
- rotation: [number, number, number, number];
57
- translation?: [number, number, number];
58
- };
59
- /** Frame timestamp (ms; per the v0.8.0 StitcherFrame contract). */
60
- timestamp: number;
61
- /** JPEG width / height in pixels. */
62
- width: number;
63
- height: number;
64
- }
65
- /**
66
- * v0.9.0 Layer 3 — options for `useFrameStream`.
67
- *
68
- * For worklet-native processing without JPEG roundtrip (OCR via
69
- * Vision/ML Kit, TFLite ML, LiDAR depth), use
70
- * `useThrottledFrameProcessor` (Layer 2) instead.
71
- */
72
- export interface FrameStreamOptions {
73
- /**
74
- * Target sampling rate in Hertz. Clamped to `[0.5, 10]`. The
75
- * Layer 2 throttle gate enforces the rate inside the worklet;
76
- * ticks too close together are dropped silently.
77
- *
78
- * Clamp upper bound (10 Hz) is intentionally lower than Layer 2's
79
- * (30 Hz) — beyond 10 Hz the per-frame JPEG encode + JS-bridge
80
- * cost dominates the wall-clock budget. Hosts that need higher
81
- * rates should be on Layer 2 with their own JPEG encoder call
82
- * (or no JPEG at all).
83
- */
84
- sampleHz: number;
85
- /**
86
- * JPEG quality (0-100). Default 75. Clamped silently to
87
- * `[1, 100]` by the underlying `save_frame_as_jpeg` native plugin.
88
- */
89
- quality?: number;
90
- /**
91
- * Directory to write JPEG files into. Defaults to a per-app
92
- * `<cache>/rnis-frame-stream/` subdirectory. The directory is
93
- * `mkdir -p`'d on first use; hosts that supply an existing
94
- * absolute path are responsible for its lifecycle.
95
- */
96
- outputDir?: string;
97
- }
98
- /**
99
- * v0.9.0 Layer 2 — options for `useThrottledFrameProcessor`.
100
- *
101
- * Wraps v0.8.0's `useFrameProcessor` with a monotonic-time throttle
102
- * gate so the supplied worklet fires at most `sampleHz` times per
103
- * second. Use for sub-frame-rate worklet-native processing — native
104
- * OCR (Vision.framework / ML Kit), TFLite ML detection, LiDAR depth
105
- * processing — where the bbox / depth payloads are small enough to
106
- * bridge to JS via `runOnJS`.
107
- *
108
- * For JS-thread JPEG consumers (file-path OCR libraries, cloud
109
- * upload, thumbnail UI), use `useFrameStream` (Layer 3) instead.
110
- */
111
- export interface ThrottledFrameProcessorOptions {
112
- /**
113
- * Target sampling rate in Hertz. Clamped to `[0.5, 30]`. Inside
114
- * the worklet a monotonic-time gate enforces the rate; ticks too
115
- * close together are silently dropped.
116
- *
117
- * The clamp upper bound (30 Hz) sits at typical AR rates on
118
- * mid-range Android devices — beyond that, the host should just
119
- * use `useFrameProcessor` directly (no throttle). The clamp
120
- * lower bound (0.5 Hz) prevents accidentally-zero-divide values
121
- * + matches `useFrameStream`'s convention.
122
- */
123
- sampleHz: number;
124
- }
125
38
  export interface CaptureResult {
126
39
  /** Unique device-generated UUID */
127
40
  deviceUuid: string;