react-native-image-stitcher 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -16,6 +16,102 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
16
16
 
17
17
  ## [Unreleased]
18
18
 
19
+ ## [0.11.0] — 2026-05-28
20
+
21
+ ### Added — `useStitcherWorklet` for non-AR composition
22
+
23
+ Closes the v0.8.0 Phase 5 either-or constraint: hosts that want to
24
+ write their OWN `useFrameProcessor` worklet body can now COMPOSE
25
+ first-party stitching back in with a single `stitcher.call(frame)`
26
+ call, instead of having to choose between their worklet and the
27
+ lib's stitching.
28
+
29
+ ```tsx
30
+ import {
31
+ Camera, useFrameProcessor, useStitcherWorklet,
32
+ type StitcherFrame,
33
+ } from 'react-native-image-stitcher';
34
+
35
+ function MyScreen() {
36
+ const stitcher = useStitcherWorklet();
37
+ const fp = useFrameProcessor((frame: StitcherFrame) => {
38
+ 'worklet';
39
+ hostPreLogic(frame);
40
+ stitcher.call(frame); // ← first-party stitching
41
+ hostPostLogic(frame);
42
+ }, [stitcher.call]);
43
+ return <Camera frameProcessor={fp} ... />;
44
+ }
45
+ ```
46
+
47
+ Migrating from v0.10.x is a one-line diff:
48
+
49
+ ```diff
50
+ + const stitcher = useStitcherWorklet();
51
+ const fp = useFrameProcessor((frame: StitcherFrame) => {
52
+ 'worklet';
53
+ + stitcher.call(frame); // ← first-party stitching back in
54
+ hostLogic(frame);
55
+ - }, [hostLogic]);
56
+ + }, [stitcher.call, hostLogic]);
57
+ ```
58
+
59
+ ### Changed
60
+
61
+ - `useFrameProcessorDriver` is now a thin wrapper around
62
+ `useStitcherWorklet`. Public API (`start` / `stop` /
63
+ `isRunning` / `frameProcessor`) is unchanged. Pose-reset
64
+ semantics preserved via the new `stitcher.reset()` method which
65
+ the driver calls internally from `start()` and `stop()`.
66
+ - The gyro subscription that powers pose tracking now lives in
67
+ `useStitcherWorklet` and runs for the lifetime of the hook
68
+ (mount → unmount) rather than being tied to the driver's
69
+ `start()` / `stop()`. In practice this matches all observed
70
+ host integrations (capture screens mount `<Camera>` for the
71
+ duration of capture; idle screens don't). Battery delta is
72
+ small (≪1% CPU at 33 ms gyro sampling).
73
+ - `<Camera frameProcessor>` JSDoc rewritten: the "Non-AR mode
74
+ tradeoff (HONEST)" section is replaced by a "Non-AR mode
75
+ composition" section that shows the v0.11.0 composition
76
+ pattern. The runtime `console.info` text is softened from
77
+ "your worklet REPLACES first-party stitching, panorama capture
78
+ will not produce stitched output" to "if you want first-party
79
+ stitching alongside, call `useStitcherWorklet()` from your
80
+ worklet body".
81
+ - Example app (`example/App.tsx`) now demonstrates the
82
+ composition pattern end-to-end: one
83
+ `useFrameProcessor` body that calls both `stitcher.call(frame)`
84
+ and the existing 1 Hz host tick log. `<Camera>` mounts with
85
+ `frameProcessor={exampleFrameProcessor}` (previously left
86
+ unwired with an "intentionally unused" comment block).
87
+ - `docs/frame-access-tiers.md` adds a `useStitcherWorklet`
88
+ reference section + 1-line migration diff. Softens the
89
+ "either-or" language in the Tier 3 + AR-vs-non-AR sections.
90
+
91
+ ### Files changed
92
+ - NEW: `src/stitching/useStitcherWorklet.ts`
93
+ - `src/stitching/useFrameProcessorDriver.ts` (refactored thin wrapper)
94
+ - `src/index.ts` (export new hook + types)
95
+ - `src/camera/Camera.tsx` (docstring + console.info softened)
96
+ - `example/App.tsx` (composition demo)
97
+ - `docs/frame-access-tiers.md` (new section + softened wording)
98
+ - `docs/v0.11.0-manual-verification-checklist.md` (Phase 4 human-loop checklist)
99
+
100
+ ### Not touched
101
+ - All native code (`ios/Sources/`, `android/src/main/cpp/`,
102
+ `android/src/main/java/io/imagestitcher/rn/`) — pure TS refactor.
103
+ - AR-mode dispatch path — already composes natively.
104
+ - `useFrameProcessor` (v0.8.0 public hook) — unchanged.
105
+
106
+ ### Verified
107
+ - JS Jest: **69 / 69 pass**
108
+ - C++ Gtest: **17 / 17 pass**
109
+ - Android JUnit: **6 / 6 pass**
110
+ - iOS build (Debug, generic iOS device): clean
111
+ - Android `:app:assembleDebug`: clean
112
+ - Real-device panorama capture verification deferred to the
113
+ human-in-the-loop checklist (`docs/v0.11.0-manual-verification-checklist.md`).
114
+
19
115
  ## [0.10.0] — 2026-05-28
20
116
 
21
117
  ### Added — v0.10.0 PR A: host-side test infrastructure (`#9A` + `#11A`)
@@ -221,26 +221,42 @@ export interface CameraProps {
221
221
  * }
222
222
  * ```
223
223
  *
224
- * ## Non-AR mode tradeoff (HONEST)
224
+ * ## Non-AR mode composition (v0.11.0+)
225
225
  *
226
226
  * vision-camera's `<Camera>` accepts ONLY ONE frame processor.
227
227
  * The lib's internal `useFrameProcessorDriver` produces the
228
228
  * processor that drives first-party panorama stitching in non-AR
229
- * mode. If you supply your own via this prop, **the lib's
230
- * first-party stitching is replaced**panorama capture in
231
- * non-AR mode will not produce stitched output until you remove
232
- * the prop or fork the SDK to compose both worklets manually.
229
+ * mode. If you supply your own via this prop, the lib's
230
+ * default processor is REPLACEDbut as of v0.11.0 you can
231
+ * COMPOSE first-party stitching back into your worklet body
232
+ * using `useStitcherWorklet`:
233
233
  *
234
- * For the common case (host wants worklet + lib wants stitching
235
- * concurrently), prefer AR mode: the AR-mode path natively fans
236
- * out to both the lib's first-party stitching AND every
237
- * registered host worklet on every frame, with per-worklet
238
- * failure isolation.
234
+ * ```tsx
235
+ * import {
236
+ * Camera, useFrameProcessor, useStitcherWorklet,
237
+ * type StitcherFrame,
238
+ * } from 'react-native-image-stitcher';
239
+ *
240
+ * function MyScreen() {
241
+ * const stitcher = useStitcherWorklet();
242
+ * const fp = useFrameProcessor((frame: StitcherFrame) => {
243
+ * 'worklet';
244
+ * hostPreLogic(frame);
245
+ * stitcher.call(frame); // ← first-party stitching
246
+ * hostPostLogic(frame);
247
+ * }, [stitcher.call]);
248
+ * return <Camera frameProcessor={fp} ... />;
249
+ * }
250
+ * ```
239
251
  *
240
- * Composition for non-AR mode (lib stitching + host worklet on
241
- * the same vc processor) is tracked as a v0.9+ follow-up;
242
- * needs the lib's first-party logic exposed as a vc Frame
243
- * Processor plugin the host's worklet can call.
252
+ * Hosts that DON'T call `useStitcherWorklet` from their worklet
253
+ * body replace first-party stitching for non-AR captures (a
254
+ * one-shot console.info documents this when the prop is first
255
+ * supplied). AR mode is unaffected either way — the AR-mode
256
+ * dispatch path (v0.8.0 Phase 4b.i / 4b.iii) natively fans out
257
+ * to both the lib's first-party stitching AND every registered
258
+ * host worklet on every frame, with per-worklet failure
259
+ * isolation.
244
260
  *
245
261
  * ## AR mode behaviour
246
262
  *
@@ -435,36 +435,36 @@ function Camera(props) {
435
435
  // Safety: stop the driver if the component unmounts mid-recording.
436
436
  // eslint-disable-next-line react-hooks/exhaustive-deps
437
437
  (0, react_1.useEffect)(() => () => { fpDriver.stop(); }, []);
438
- // v0.8.0 Phase 5 — frameProcessor prop semantics:
438
+ // v0.8.0 Phase 5 / v0.11.0 — frameProcessor prop semantics:
439
439
  //
440
- // - Host supplied? → use host's processor; lib's first-party
441
- // stitching is DISABLED in non-AR mode (vc accepts only one
442
- // processor). One-shot console.info documents the tradeoff
443
- // so the host isn't surprised by "panorama capture stopped
444
- // producing output" in non-AR mode. AR-mode capture is
445
- // unaffected the AR-session dispatch path fans out to BOTH
446
- // first-party and host worklets independently.
440
+ // - Host supplied? → use host's processor. The host's worklet
441
+ // body controls whether first-party stitching also fires:
442
+ // call `stitcher.call(frame)` (from `useStitcherWorklet`)
443
+ // inside the body to compose; omit to replace. One-shot
444
+ // console.info documents the choice so the host can spot a
445
+ // missing `useStitcherWorklet` call before they go hunting
446
+ // for "why is non-AR panorama capture not producing output".
447
+ // AR-mode capture is unaffected either way — the AR-session
448
+ // dispatch path fans out to BOTH first-party stitching AND
449
+ // every host worklet independently.
447
450
  //
448
451
  // - No host processor? → use `fpDriver.frameProcessor` which is
449
452
  // the lib's internal worklet driving first-party stitching
450
453
  // via `useFrameProcessorDriver`. Default behaviour for the
451
454
  // common "I just want panorama capture" case.
452
- //
453
- // The pre-v0.8.0 behaviour (host's prop silently ignored with
454
- // a warning) is gone — Phase 5 plumbs the prop through. The
455
- // tradeoff is honestly documented in the CameraProps docstring.
456
455
  const hostFrameProcessorAcceptedWarnedRef = (0, react_1.useRef)(false);
457
456
  if (hostFrameProcessor != null
458
457
  && !hostFrameProcessorAcceptedWarnedRef.current) {
459
458
  hostFrameProcessorAcceptedWarnedRef.current = true;
460
459
  // eslint-disable-next-line no-console
461
460
  console.info('[react-native-image-stitcher] Host frameProcessor supplied — '
462
- + 'non-AR mode will run YOUR worklet instead of the lib\'s '
463
- + 'first-party stitching plugin (vc accepts only one frame '
464
- + 'processor). Non-AR panorama capture will not produce '
465
- + 'stitched output until this prop is removed. AR-mode '
466
- + 'capture is unaffected (AR-session dispatch fans out to '
467
- + 'both first-party and host worklets independently).');
461
+ + 'non-AR mode will run YOUR composed worklet. If you want '
462
+ + 'first-party panorama stitching alongside your own logic, '
463
+ + 'call `useStitcherWorklet()` and invoke `stitcher.call(frame)` '
464
+ + 'from your worklet body (see `<Camera>` `frameProcessor` '
465
+ + 'JSDoc for the composition pattern). AR-mode capture is '
466
+ + 'unaffected (AR-session dispatch fans out to both '
467
+ + 'first-party and host worklets independently).');
468
468
  }
469
469
  // The Frame Processor worklet bound to vision-camera's Camera.
470
470
  // Host's wins if supplied; lib's internal driver otherwise.
package/dist/index.d.ts CHANGED
@@ -73,5 +73,7 @@ export { useFrameStream } from './stitching/useFrameStream';
73
73
  export type { FrameStreamOptions, SampledFrame } from './types';
74
74
  export { useFrameProcessorDriver } from './stitching/useFrameProcessorDriver';
75
75
  export type { UseFrameProcessorDriverOptions, FrameProcessorDriverHandle, } from './stitching/useFrameProcessorDriver';
76
+ export { useStitcherWorklet } from './stitching/useStitcherWorklet';
77
+ export type { UseStitcherWorkletOptions, StitcherWorkletHandle, StitcherWorkletInput, } from './stitching/useStitcherWorklet';
76
78
  export { stitchVideo } from './stitching/stitchVideo';
77
79
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -22,7 +22,7 @@
22
22
  * adds RetaiLens-specific features on top.
23
23
  */
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.stitchVideo = exports.useFrameProcessorDriver = exports.useFrameStream = exports.useThrottledFrameProcessor = exports.useFrameProcessor = exports.useKeyframeStream = exports.useIncrementalStitcher = exports.cleanupOldKeyframes = exports.getIncrementalNativeModule = exports.subscribeIncrementalState = exports.incrementalStitcherIsAvailable = exports.IncrementalOutcome = 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.PanoramaGuidance = exports.PanoramaBandOverlay = exports.IncrementalPanGuide = 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.useFrameStream = exports.useThrottledFrameProcessor = exports.useFrameProcessor = exports.useKeyframeStream = exports.useIncrementalStitcher = exports.cleanupOldKeyframes = exports.getIncrementalNativeModule = exports.subscribeIncrementalState = exports.incrementalStitcherIsAvailable = exports.IncrementalOutcome = 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.PanoramaGuidance = exports.PanoramaBandOverlay = exports.IncrementalPanGuide = 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;
26
26
  // ─────────────────────────────────────────────────────────────────────
27
27
  // Layer 1 — the high-level <Camera> component
28
28
  // ─────────────────────────────────────────────────────────────────────
@@ -179,6 +179,14 @@ Object.defineProperty(exports, "useFrameStream", { enumerable: true, get: functi
179
179
  // `useIncrementalJSDriver` was removed; was deprecated in v0.5).
180
180
  var useFrameProcessorDriver_1 = require("./stitching/useFrameProcessorDriver");
181
181
  Object.defineProperty(exports, "useFrameProcessorDriver", { enumerable: true, get: function () { return useFrameProcessorDriver_1.useFrameProcessorDriver; } });
182
+ // v0.11.0 — composable first-party stitching as a worklet function.
183
+ // Hosts that want to COMPOSE their own per-frame logic with the
184
+ // lib's stitching (instead of REPLACING it via the <Camera>
185
+ // `frameProcessor` prop) call this hook + invoke `stitcher.call`
186
+ // inside their own `useFrameProcessor` body. See
187
+ // `docs/host-app-integration.md` § Tier 3 for the full pattern.
188
+ var useStitcherWorklet_1 = require("./stitching/useStitcherWorklet");
189
+ Object.defineProperty(exports, "useStitcherWorklet", { enumerable: true, get: function () { return useStitcherWorklet_1.useStitcherWorklet; } });
182
190
  // ── Batch stitching ───────────────────────────────────────────────────
183
191
  // Feed a video file straight to OpenCV's cv::Stitcher, bypassing the
184
192
  // incremental pipeline. Useful when you have content captured
@@ -4,99 +4,54 @@
4
4
  * from v0.6 onward (replaced the deprecated `useIncrementalJSDriver`
5
5
  * hook, which was removed in v0.6).
6
6
  *
7
- * Why this exists (vs the pre-v0.6 JS-driver predecessor)
8
- *
9
- * The old JS driver took a JPEG snapshot every ~250 ms and fed the
10
- * path to `IncrementalStitcher.processFrameAtPath` (both removed in
11
- * v0.6). That path had three costs:
12
- *
13
- * 1. JPEG encode (`takeSnapshot` ≈ 30–80 ms on iPhone 16 Pro)
14
- * 2. Disk write of the JPEG
15
- * 3. JPEG decode + cv::Mat alloc inside the engine
16
- *
17
- * Per-frame round-trip ~80 ms means ~4 Hz max throughput, and
18
- * ~80 ms latency between "this is the moment to accept" and "this
19
- * frame is in the engine". Both numbers caused operator-felt lag
20
- * on long shelf pans.
21
- *
22
- * This hook uses vision-camera's Frame Processor instead. The
23
- * worklet runs on the camera producer thread at the native frame
24
- * rate (30 fps on iOS). Each frame goes through a JSI plugin
25
- * (`cv_flow_gate_process_frame`) directly into
26
- * `IncrementalStitcher.consumeFrame` — the SAME entry point AR
27
- * mode uses, with the engine's existing KeyframeGate making the
28
- * accept/reject decision. Rejected frames cost ~3–8 ms; accepted
29
- * frames take the same deep-copy + workQueue path AR mode takes.
30
- *
31
- * Net wins: no JPEG round-trip on rejected frames, no disk thrash
32
- * during recording, lower latency to accept, full 30 fps gate
33
- * evaluation budget.
34
- *
35
- * Pose synthesis
36
- *
37
- * Non-AR mode has no ARKit pose. We integrate the gyroscope on
38
- * the JS thread (`react-native-sensors`), accumulate yaw + pitch,
39
- * and publish them via Reanimated `useSharedValue` so the worklet
40
- * can read them WITHOUT a thread hop. Translation is reported as
41
- * zero (no IMU translation; this is a known limitation we share
42
- * with the legacy driver drift ~1–2°/min over a 30 s capture is
43
- * below the gate's overlap threshold and rarely matters).
44
- *
45
- * Quaternion synthesis (q = q_yaw * q_pitch * q_roll, Tait-Bryan
46
- * YPR order to match the legacy driver's body-frame intent):
47
- * q_yaw = (0, sin(yaw/2), 0, cos(yaw/2))
48
- * q_pitch = (sin(pitch/2), 0, 0, cos(pitch/2))
49
- * q_roll = (0, 0, sin(roll/2), cos(roll/2))
50
- *
51
- * Expanded (cy, sy = cos/sin(yaw/2); analogous for cp/sp, cr/sr):
52
- * qx = cy*sp*cr + sy*cp*sr
53
- * qy = sy*cp*cr - cy*sp*sr
54
- * qz = cy*cp*sr - sy*sp*cr
55
- * qw = cy*cp*cr + sy*sp*sr
56
- *
57
- * When roll=0 this collapses to the 2-axis form
58
- * `(cy*sp, sy*cp, -sy*sp, cy*cp)` the legacy driver used, so
59
- * captures held perfectly level produce identical poses to the
60
- * pre-roll behaviour.
61
- *
62
- * Intrinsics are synthesised from the actual frame dimensions
63
- * (`frame.width`, `frame.height`) plus the host-provided
64
- * horizontal/vertical FoV defaults. The stitcher derives its FoV-
65
- * overlap window from these, so the assumed FoV matters for the
66
- * gate's overlap math but not for the panorama itself (the
67
- * stitcher feature-matches + RANSACs the final alignment).
68
- *
69
- * Throttling
70
- *
71
- * `evalEveryNFrames` controls how often the worklet calls the
72
- * plugin. Default 1 (every frame). Set higher to amortise the
73
- * plugin call + consumeFrame's gate evaluation across multiple
74
- * producer-thread frames on lower-end devices. Independent of —
75
- * and stacks on top of — the stitcher's own internal
76
- * `flowEvalEveryNFrames` (see `KeyframeGate.swift`); both
77
- * throttles can be active simultaneously and the effective cadence
78
- * is `evalEveryNFrames * flowEvalEveryNFrames`.
79
- *
80
- * Lifecycle
81
- *
82
- * `start()` subscribes to the gyro and resets pose accumulators.
83
- * `stop()` unsubscribes and resets. The returned `frameProcessor`
84
- * is meant to be passed to `<Camera frameProcessor={...} />` —
85
- * it's stable as long as the plugin reference and the FoV props
86
- * haven't changed. Returns `null` when the plugin isn't loaded
87
- * yet; pass `null`-or-fallback to the Camera in that case.
88
- *
89
- * Pairing with `IncrementalStitcher.start({frameSourceMode})`
90
- *
91
- * The plugin's per-frame call into `consumeFrameFromPlugin` is
92
- * gated by `IncrementalStitcher.frameProcessorIngestEnabled`,
93
- * which is TRUE only when the stitcher was started with
94
- * `frameSourceMode === 'frameProcessor'`. Hosts MUST call
95
- * `incrementalStitcher.start({ frameSourceMode: 'frameProcessor',
96
- * ... })` to actually get frames into the engine — otherwise the
97
- * worklet runs to completion but the wrapper drops the call.
98
- * `Camera.tsx` does this wiring automatically when the host opts
99
- * into this driver.
7
+ * v0.11.0 refactored to a thin wrapper around `useStitcherWorklet`.
8
+ * The plugin acquisition + shared-value declarations + gyro
9
+ * subscription + worklet body all live in `useStitcherWorklet` now;
10
+ * this hook just binds the returned worklet via vision-camera's
11
+ * `useFrameProcessor` and exposes the legacy `start` / `stop` /
12
+ * `isRunning` API for backwards compatibility with v0.10.x.
13
+ *
14
+ * ## Why the v0.11.0 split
15
+ *
16
+ * vision-camera v4 allows ONE frame processor per `<Camera>` mount.
17
+ * Pre-v0.11.0, hosts that wanted to compose their own worklet with
18
+ * the lib's first-party stitching couldn't passing a host
19
+ * `frameProcessor` REPLACED the lib's processor. v0.11.0 closes
20
+ * this gap by exposing the worklet body via `useStitcherWorklet`
21
+ * so hosts can write:
22
+ *
23
+ * const stitcher = useStitcherWorklet();
24
+ * const fp = useFrameProcessor((frame) => {
25
+ * 'worklet';
26
+ * hostPreLogic(frame);
27
+ * stitcher.call(frame); // first-party stitching
28
+ * hostPostLogic(frame);
29
+ * }, [stitcher.call]);
30
+ *
31
+ * `useFrameProcessorDriver` keeps the legacy default-integration
32
+ * shape (start / stop / isRunning) for the `<Camera>` component's
33
+ * built-in non-AR path and for any host still using the v0.10.x API
34
+ * directly. No behavioural change for those callers.
35
+ *
36
+ * ## start / stop behaviour
37
+ *
38
+ * - `start()` calls `stitcher.reset()` to zero the accumulated
39
+ * pose (preserves the pre-v0.11.0 "each capture starts with
40
+ * pose = (0, 0, 0)" contract).
41
+ * - `stop()` also resets the pose (idempotent; matches the
42
+ * pre-v0.11.0 stop() side effect of zeroing yaw / pitch / roll).
43
+ * - The gyro subscription itself is owned by `useStitcherWorklet`
44
+ * and runs for the lifetime of the hook. In the default
45
+ * `<Camera>` integration this means gyro is on while the camera
46
+ * screen is mounted same practical scope as pre-v0.11.0 in
47
+ * all observed host integrations (capture screens mount
48
+ * `<Camera>` for the duration of capture; idle screens don't).
49
+ *
50
+ * ## Pose synthesis / intrinsics / throttling
51
+ *
52
+ * Owned by `useStitcherWorklet`. See that file's header for the
53
+ * quaternion math, FoV-to-intrinsics derivation, throttle gate, and
54
+ * pairing-with-IncrementalStitcher.start docs.
100
55
  */
101
56
  import type { ReadonlyFrameProcessor } from 'react-native-vision-camera';
102
57
  export interface UseFrameProcessorDriverOptions {
@@ -131,9 +86,9 @@ export interface UseFrameProcessorDriverOptions {
131
86
  evalEveryNFrames?: number;
132
87
  }
133
88
  export interface FrameProcessorDriverHandle {
134
- /** Subscribe to the gyro + reset pose accumulators. Idempotent. */
89
+ /** Reset pose accumulators + mark the driver as running. Idempotent. */
135
90
  start: () => void;
136
- /** Unsubscribe + reset pose. */
91
+ /** Reset pose + mark the driver as stopped. Idempotent. */
137
92
  stop: () => void;
138
93
  /**
139
94
  * Pass this to `<Camera frameProcessor={...} />`. `null` until