react-native-image-stitcher 0.3.0 → 0.4.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.
@@ -0,0 +1,478 @@
1
+ /**
2
+ * PanoramaSettings (v0.4) — engine-discriminated, hierarchical
3
+ * settings types.
4
+ *
5
+ * Background
6
+ * ──────────
7
+ *
8
+ * Pre-v0.4 the lib exported a single flat `PanoramaSettings`
9
+ * interface with 45+ fields covering three unrelated stitching
10
+ * engines (batch-keyframe, hybrid, slit-scan). The 2026-05-22
11
+ * audit (CHANGELOG entry for v0.3.0) traced every field's actual
12
+ * native consumer and proved:
13
+ *
14
+ * • batch-keyframe and the live engines (hybrid + slit-scan)
15
+ * share **zero** settings — they read disjoint subsets of
16
+ * the flat interface.
17
+ * • ~10 fields had no native consumer at all (dead surface).
18
+ * • The <Camera> public component hardcodes `engine:
19
+ * 'batch-keyframe'` and never reaches the slit-scan / hybrid
20
+ * branches.
21
+ *
22
+ * v0.4 splits the flat interface into three engine-specific types:
23
+ *
24
+ * • `PanoramaSettings` — what <Camera> consumes (batch-keyframe).
25
+ * • `SlitscanSettings` — for Layer 2 hosts using the slit-scan
26
+ * engine (incremental.start({ engine:
27
+ * 'slitscan-*', ... })).
28
+ * • `HybridSettings` — for the RetaiLens-specific hybrid live
29
+ * engine. Exported for completeness;
30
+ * most consumers won't touch it.
31
+ *
32
+ * Each type carries only the fields its target engine actually
33
+ * reads. Sub-objects (`stitcher`, `frameSelection`, `flow`,
34
+ * `painting`, `registration`, `plane`, `ncc2d`, `emaSmoothing`,
35
+ * `panAxisLock`) group related knobs so the modal can render
36
+ * collapsible sections that match the type tree.
37
+ *
38
+ * Migration
39
+ * ─────────
40
+ *
41
+ * No automated migration helper. v0.4 is a clean break; the
42
+ * v0.3 `PanoramaSettings` type is deleted. Consumers (notably
43
+ * `retailens-camera-sdk`) update their settings literals to match
44
+ * the new shape. See the v0.4.0 CHANGELOG entry for the field-
45
+ * by-field mapping.
46
+ */
47
+ export interface CaptureBaseSettings {
48
+ /**
49
+ * Which camera + tracking source feeds the engine:
50
+ *
51
+ * • `'ar'` — ARKit (iOS) / ARCore (Android) session. Rich
52
+ * pose with real translation. Required for
53
+ * plane-projected slit-scan; recommended for
54
+ * batch-keyframe whenever the device supports
55
+ * it (the auto-resolver's translation signal
56
+ * comes from AR pose).
57
+ * • `'non-ar'` — vision-camera fallback. Gyro-integrated yaw
58
+ * + pitch only; no translation from pose. The
59
+ * JS-side IMU translation gate fills in the
60
+ * translation signal. Required on devices
61
+ * without ARKit/ARCore support.
62
+ */
63
+ captureSource: 'ar' | 'non-ar';
64
+ /**
65
+ * Show the lib's built-in diagnostic overlay (memory pill,
66
+ * keyframe pill, orientation pill, stitch-stats toast, detailed
67
+ * metrics block). Default `false` so end-users don't see them.
68
+ * Hosts that compose their own debug surface can leave this off
69
+ * and mount the individual `Capture*Pill` components themselves.
70
+ */
71
+ debug: boolean;
72
+ }
73
+ /**
74
+ * Top-level settings for the standard panorama capture flow
75
+ * exposed by <Camera>. Engine is fixed to batch-keyframe internally;
76
+ * the only mode choice exposed at this level is `captureSource`
77
+ * (AR-backed vs vision-camera fallback) and the `stitcher` /
78
+ * `frameSelection` sub-trees.
79
+ */
80
+ export interface PanoramaSettings extends CaptureBaseSettings {
81
+ /** cv::Stitcher pipeline configuration (applied at finalize). */
82
+ stitcher: BatchStitcherSettings;
83
+ /** Per-frame keyframe-selection gate configuration. */
84
+ frameSelection: FrameSelectionSettings;
85
+ }
86
+ /**
87
+ * cv::Stitcher tuning — these knobs reach the C++ stitcher at
88
+ * `finalize()` time, after all keyframes are collected. They have
89
+ * no effect on per-frame selection.
90
+ */
91
+ export interface BatchStitcherSettings {
92
+ /**
93
+ * cv::Stitcher pipeline mode.
94
+ *
95
+ * • `'auto'` (default) — engine looks at the
96
+ * translation/rotation ratio between first + last accepted
97
+ * keyframe poses (and, in non-AR mode, the IMU translation
98
+ * accumulator) and picks `'panorama'` or `'scans'` at
99
+ * finalize.
100
+ * • `'panorama'` — rotation-only pipeline (ORB + BA-Ray +
101
+ * SphericalWarper). Best for "rotate phone in place" pans.
102
+ * Diverges on translation-heavy input.
103
+ * • `'scans'` — affine pipeline (Affine matcher + BA-Affine +
104
+ * PlaneWarper). Best for "walk past a shelf" captures.
105
+ * Slight quality drop on pure rotation, never diverges.
106
+ *
107
+ * Both platforms now honour this and the auto-resolver. Both
108
+ * also retry with the OPPOSITE mode if the configured mode
109
+ * produces degenerate camera params (warpRoi too large).
110
+ */
111
+ stitchMode: 'auto' | 'panorama' | 'scans';
112
+ /**
113
+ * Output projection. PANORAMA mode uses this directly; SCANS
114
+ * hard-wires PlaneWarper internally and ignores this field.
115
+ */
116
+ warperType: 'plane' | 'cylindrical' | 'spherical';
117
+ /**
118
+ * Pixel blender for the warped frames. `'multiband'` produces
119
+ * cleaner seams but holds all warped frames in memory; `'feather'`
120
+ * streams and uses less peak memory.
121
+ */
122
+ blenderType: 'multiband' | 'feather';
123
+ /**
124
+ * Seam-finder strategy. `'graphcut'` finds optimal seams before
125
+ * blending (pair with multiband); `'skip'` streams warp+feed (pair
126
+ * with feather for the lowest-memory configuration).
127
+ */
128
+ seamFinderType: 'graphcut' | 'skip';
129
+ /**
130
+ * Output crop strategy. `false` (default) crops to the bounding
131
+ * rectangle of non-black pixels. `true` runs the
132
+ * max-inscribed-rectangle + morph-close pipeline — cleaner output
133
+ * with no black corners, more CPU at finalize.
134
+ */
135
+ enableMaxInscribedRectCrop: boolean;
136
+ }
137
+ /**
138
+ * KeyframeGate tuning — these knobs control which incoming frames
139
+ * become keyframes. The mode selects the strategy (passthrough,
140
+ * plane-overlap, or sparse optical flow); the `flow` sub-tree is
141
+ * only consulted when `mode === 'flow-based'`.
142
+ */
143
+ export interface FrameSelectionSettings {
144
+ /**
145
+ * Frame selection strategy:
146
+ *
147
+ * • `'time-based'` — gate disabled. Every JS-driver / AR
148
+ * frame becomes a keyframe up to
149
+ * `maxKeyframes`. Useful for testing or
150
+ * when the host wants to do its own
151
+ * keyframe selection upstream.
152
+ * • `'pose-based'` — plane-overlap novelty (when a plane is
153
+ * latched) or angular-delta fallback (no
154
+ * plane). Cheap to evaluate but conservative
155
+ * about pure-rotation motion.
156
+ * • `'flow-based'` — sparse Shi-Tomasi corners + KLT tracking.
157
+ * More expensive (~3–5 ms per AR frame on
158
+ * a Galaxy A35) but accurate for translation.
159
+ * The default for v0.3+.
160
+ */
161
+ mode: 'time-based' | 'pose-based' | 'flow-based';
162
+ /**
163
+ * Hard cap on accepted keyframes per capture. Clamped to
164
+ * `[3, 10]` natively. Higher is rarely useful: cv::Stitcher
165
+ * convergence degrades past ~8-10 frames, and the per-keyframe
166
+ * disk + memory cost adds up fast at 4K+ resolutions.
167
+ */
168
+ maxKeyframes: number;
169
+ /**
170
+ * Required NEW-content fraction (0..1) for a candidate frame to
171
+ * be accepted. Default 0.20 = 20% novel content per accept.
172
+ * Lower = more frames accepted, larger panoramas. Higher = fewer
173
+ * frames, faster captures but more conservative about coverage.
174
+ * Clamped to `[0.10, 0.80]` natively
175
+ * (`IncrementalStitcher.swift:962`).
176
+ */
177
+ overlapThreshold: number;
178
+ /**
179
+ * Sparse-optical-flow strategy tunables. Consulted only when
180
+ * `mode === 'flow-based'`; safe to omit otherwise. Defaults
181
+ * track [DEFAULT_PANORAMA_SETTINGS.frameSelection.flow].
182
+ */
183
+ flow?: FlowGateSettings;
184
+ }
185
+ /**
186
+ * Sparse-flow KLT tuning for the gate. All ranges are enforced
187
+ * (clamped silently) at the native boundary.
188
+ */
189
+ export interface FlowGateSettings {
190
+ /**
191
+ * Percentile used to aggregate the per-feature absolute
192
+ * displacements into a single per-axis novelty estimate. Default
193
+ * 0.85 (V16 change from the pre-V16 median of 0.50). Higher
194
+ * percentile picks up leading-edge motion sooner; lower is more
195
+ * conservative. Clamped to `[0.50, 0.99]`.
196
+ */
197
+ noveltyPercentile: number;
198
+ /**
199
+ * Caller-side throttle: evaluate the Flow strategy every Nth
200
+ * frame instead of every frame. Default 5 (≈ 6 Hz at 30 Hz
201
+ * ARCore). Pure CPU savings; doesn't change WHICH frames are
202
+ * accepted. Clamped to `[1, 10]`.
203
+ */
204
+ evalEveryNFrames: number;
205
+ /**
206
+ * Translation budget in centimetres. When > 0, the gate
207
+ * force-accepts the next frame after the operator has translated
208
+ * more than this distance since the last accepted keyframe — even
209
+ * when novelty is below `overlapThreshold`. Bounds parallax
210
+ * between adjacent keyframes so the stitcher's matcher sees
211
+ * inputs it can handle. Default 50. `0` disables. Clamped
212
+ * to `[0, 100]`.
213
+ */
214
+ maxTranslationCm: number;
215
+ /**
216
+ * Shi-Tomasi corner count. Default 150; clamped to `[50, 300]`.
217
+ * Higher = more robust median, slower detect (~15-25 ms at 150
218
+ * on Galaxy A35).
219
+ */
220
+ maxCorners: number;
221
+ /**
222
+ * Shi-Tomasi quality level. Default 0.01; clamped to
223
+ * `[0.005, 0.05]`. Lower lets weaker corners in (more candidate
224
+ * points, more KLT noise); higher demands stronger corners.
225
+ */
226
+ qualityLevel: number;
227
+ /**
228
+ * Shi-Tomasi minimum distance between detected corners, in
229
+ * working-resolution pixels (the gate downscales the input
230
+ * internally to a 720-px-longest-side working frame). Default
231
+ * 10; clamped to `[1, 50]`. Higher = more spatially-spread
232
+ * features = more representative median.
233
+ */
234
+ minDistance: number;
235
+ }
236
+ /**
237
+ * Canonical FlowGateSettings defaults, exported as a standalone
238
+ * constant so consumers (the bridge, the modal, prop translators)
239
+ * can reach the values WITHOUT typing
240
+ * `DEFAULT_PANORAMA_SETTINGS.frameSelection.flow!.X` — the
241
+ * non-null-assertion form is brittle (will start crashing at
242
+ * runtime the moment someone "cleans up" the default tree and
243
+ * makes `flow` undefined in `DEFAULT_PANORAMA_SETTINGS`). Lifted
244
+ * out 2026-05-22 in the F10 Phase 2 review (NIT-4).
245
+ *
246
+ * Numerical values mirror the v0.3 defaults; they're verified
247
+ * against the native engine's compiled-in fallback values
248
+ * (`IncrementalStitcher.swift:1003-1029`, `IncrementalStitcher.kt:419-445`)
249
+ * — discrepancies are flagged in the v0.3.0 audit and resolved by
250
+ * the bridge always-emitting these on the wire (see
251
+ * `PanoramaSettingsBridge.ts:panoramaSettingsToNativeConfig`).
252
+ */
253
+ export declare const DEFAULT_FLOW_GATE_SETTINGS: FlowGateSettings;
254
+ export declare const DEFAULT_PANORAMA_SETTINGS: PanoramaSettings;
255
+ /**
256
+ * Settings for slit-scan stitching engines (`slitscan-rotate`,
257
+ * `slitscan-both`, `firstwins-rectilinear`). Reached via
258
+ * `incremental.start({ engine: '<variant>', config: { ... } })`,
259
+ * NOT via <Camera> (which always uses batch-keyframe). Each
260
+ * sub-tree corresponds to a section of the native `RLISStitcherConfig`
261
+ * the slit-scan engine reads at start.
262
+ *
263
+ * Field-by-field native consumer references are documented in
264
+ * `OpenCVSlitScanStitcher.mm` / `OpenCVIncrementalStitcher.h`.
265
+ */
266
+ export interface SlitscanSettings extends CaptureBaseSettings {
267
+ /**
268
+ * Which slit-scan variant the engine runs. All three share the
269
+ * same painting + registration + plane configuration; they differ
270
+ * in their internal motion model (rotation-only vs combined
271
+ * translation+rotation, and slit position).
272
+ *
273
+ * • `'slitscan-rotate'` — preferred name; rotation-only
274
+ * motion model.
275
+ * • `'slitscan-both'` — combined translation + rotation
276
+ * motion model.
277
+ * • `'firstwins-rectilinear'` — legacy alias of
278
+ * `'slitscan-rotate'` (V13.0a naming). Accepted natively
279
+ * but new code should prefer the canonical name.
280
+ */
281
+ variant: 'slitscan-rotate' | 'slitscan-both' | 'firstwins-rectilinear';
282
+ /** Where the per-accept slit is taken from + how it's blended. */
283
+ painting: SlitscanPaintingSettings;
284
+ /** Frame-to-frame registration (NCC + RANSAC + triangulation). */
285
+ registration: SlitscanRegistrationSettings;
286
+ /** Plane projection (ARKit-detected, virtual, or disabled). */
287
+ plane: PlaneProjectionSettings;
288
+ /**
289
+ * Advanced motion-tuning knobs that the v0.3 modal never exposed.
290
+ * Both are read by the native side
291
+ * (`IncrementalStitcher.swift:1074, 1077`) and have sensible
292
+ * defaults; most consumers can leave this field undefined.
293
+ */
294
+ advanced?: SlitscanAdvancedSettings;
295
+ }
296
+ export interface SlitscanAdvancedSettings {
297
+ /**
298
+ * Fraction of the pan-axis sensor extent used to compute the
299
+ * per-frame slit width. Range `[0.05, 0.90]`, default 0.70
300
+ * (engine internal). Higher = wider slits = fewer accepts per
301
+ * pan. Set this only if you know what the slit-scan motion
302
+ * model needs for your specific capture geometry.
303
+ * Native key: `kPanAxisFractionRect`.
304
+ */
305
+ panAxisFractionRect?: number;
306
+ /**
307
+ * Minimum pan-axis delta (in canvas pixels) between consecutive
308
+ * accepted strips. Acts as a hard floor below which subsequent
309
+ * frames are rejected regardless of NCC scores. Range
310
+ * `[0, 500]`, default 0 (no floor). Native key:
311
+ * `kMinAcceptDeltaPx`.
312
+ */
313
+ minAcceptDeltaPx?: number;
314
+ }
315
+ export interface SlitscanPaintingSettings {
316
+ /**
317
+ * How new strips are blended into already-painted canvas pixels.
318
+ *
319
+ * • `'FirstPaintedWins'` (default) — preserve the first frame's
320
+ * content at any pixel; later strips don't overwrite.
321
+ * • `'FeatherBlend'` — alpha-blend new strips into
322
+ * already-painted areas at slit boundaries. Smooths visible
323
+ * seams when many narrow slits stack.
324
+ */
325
+ paintMode: 'FirstPaintedWins' | 'FeatherBlend';
326
+ /**
327
+ * Where on the camera frame the per-accept slit is sampled from.
328
+ * For a typical landscape vertical pan tilting DOWN, the leading
329
+ * edge (new content) is at the BOTTOM of the camera frame; for
330
+ * upward tilt, it's at the TOP. `'Center'` is the V13.x default.
331
+ */
332
+ sliverPosition: 'Center' | 'Bottom' | 'Top';
333
+ /**
334
+ * When `true`, the very first frame's FULL frame is painted onto
335
+ * the canvas (not just the configured slit clip). Default
336
+ * `true` — gives the panorama a wider initial anchor that
337
+ * subsequent slits extend from. Set false if you want strict
338
+ * slit-only behaviour even on the first frame.
339
+ */
340
+ firstFrameFullFrame: boolean;
341
+ }
342
+ export interface SlitscanRegistrationSettings {
343
+ /**
344
+ * 3D triangulation step. Cross-references features across
345
+ * multiple frames to estimate scene depth. Default `false` (off);
346
+ * adds latency, useful for parallax-heavy captures.
347
+ */
348
+ enableTriangulation: boolean;
349
+ /**
350
+ * Triangulation accumulator — when `enableTriangulation` is on,
351
+ * keeps a running pose graph across the whole capture. Default
352
+ * `false` (off); needed for multi-shot fusion.
353
+ */
354
+ enableTriAccumulator: boolean;
355
+ /**
356
+ * RANSAC homography fit per pair. Adds robustness to feature
357
+ * matching at the cost of a few ms per frame. Default `false`.
358
+ */
359
+ enableRansacHomography: boolean;
360
+ /**
361
+ * 1D NCC strip alignment. Present iff enabled. Default
362
+ * undefined (disabled); engine uses pure feature matching.
363
+ */
364
+ ncc1d?: Ncc1dSettings;
365
+ /**
366
+ * 2D NCC strip alignment. Present iff enabled. More expensive
367
+ * than 1D NCC; needed for shelf-scan captures with vertical
368
+ * misalignment. Default undefined (disabled).
369
+ */
370
+ ncc2d?: Ncc2dSettings;
371
+ }
372
+ export interface Ncc1dSettings {
373
+ /**
374
+ * Search radius in working-resolution pixels (along the pan axis).
375
+ * Clamped to `[5, 60]`. Default 15 when the field is set.
376
+ */
377
+ searchRadius: number;
378
+ }
379
+ export interface Ncc2dSettings {
380
+ /**
381
+ * 2D search margin in pixels (rectangular region around the
382
+ * predicted strip position). Clamped to `[4, 60]`. Default 12.
383
+ */
384
+ searchMargin: number;
385
+ /**
386
+ * Minimum NCC score to accept a match. Below this the engine
387
+ * falls back to the predicted (pose-only) position. Clamped
388
+ * to `[0.30, 0.99]`. Default 0.99 (only accept very strong
389
+ * matches; the canvas falls back to pose-only quickly).
390
+ */
391
+ confidenceThreshold: number;
392
+ /**
393
+ * EMA smoothing of the NCC-derived offset across consecutive
394
+ * strips. Present iff enabled. Default undefined. Useful
395
+ * for jittery captures.
396
+ */
397
+ emaSmoothing?: {
398
+ alpha: number;
399
+ };
400
+ /**
401
+ * Pan-axis-lock — when enabled, the NCC offset is constrained
402
+ * to the dominant pan axis (cross-axis movement bounded by
403
+ * `crossAxisLockPx`). Useful when the operator's hand wobble
404
+ * introduces unwanted cross-axis motion. Present iff enabled.
405
+ */
406
+ panAxisLock?: {
407
+ crossAxisLockPx: number;
408
+ };
409
+ }
410
+ export interface PlaneProjectionSettings {
411
+ /**
412
+ * Where the plane the slit-scan projects onto comes from.
413
+ *
414
+ * • `'Disabled'` — no plane projection; engine runs
415
+ * its baseline slit-scan path.
416
+ * • `'ARKitDetected'` — use the first vertical plane that
417
+ * ARKit/ARCore finds AND whose normal
418
+ * aligns with the camera (filtered by
419
+ * `alignmentThreshold`). Requires
420
+ * `captureSource === 'ar'`.
421
+ * • `'Virtual'` — synthesise a plane at a fixed depth
422
+ * (`virtualDepthMeters`) in front of the
423
+ * camera at first-frame pose. No
424
+ * ARKit dependency.
425
+ */
426
+ source: 'Disabled' | 'ARKitDetected' | 'Virtual';
427
+ /**
428
+ * How frames are warped onto the plane. Only consulted when
429
+ * `source !== 'Disabled'`. Default `'Rectified'` for slit-scan.
430
+ */
431
+ projectionStyle?: 'Trapezoidal' | 'Rectified';
432
+ /**
433
+ * Depth in metres for `source === 'Virtual'`. Range `[0.3, 5.0]`,
434
+ * default 1.5. Set close to the actual shelf distance for the
435
+ * cleanest projection.
436
+ */
437
+ virtualDepthMeters?: number;
438
+ /**
439
+ * Minimum `|planeNormal · cameraForward|` for an ARKit-detected
440
+ * plane to be accepted (when `source === 'ARKitDetected'`).
441
+ * Range `[0, 1]`, default 0.6 (≈ 53° max off-axis). Higher =
442
+ * stricter, only accept very-on-axis planes.
443
+ */
444
+ alignmentThreshold?: number;
445
+ }
446
+ export declare const DEFAULT_SLITSCAN_SETTINGS: SlitscanSettings;
447
+ /**
448
+ * Settings for the hybrid live-compositing engine
449
+ * (`incremental.start({ engine: 'hybrid', ... })`). Most consumers
450
+ * won't touch this — the hybrid engine is RetaiLens-specific and
451
+ * the public lib's batch-keyframe pipeline is a better fit for
452
+ * general-purpose captures. Exported here for completeness.
453
+ *
454
+ * Important: the hybrid engine has internal preset paths
455
+ * (`OpenCVIncrementalStitcher.mm:139-180`) that hard-set
456
+ * `enableTriangulation`, `enable2dNcc`, `enableRansacHomography`,
457
+ * `planeSource = Disabled`, etc. Code-reviewer flagged that
458
+ * exposing those fields would be misleading — the engine clobbers
459
+ * any overrides. So this type is intentionally minimal: only
460
+ * `projection` is reliably operator-tunable. Hosts that need to
461
+ * reach deeper-level hybrid knobs can pass a raw config dict to
462
+ * `incremental.start()` directly (Layer 2 escape hatch).
463
+ */
464
+ export interface HybridSettings extends CaptureBaseSettings {
465
+ /**
466
+ * Internal projection during real-time compositing. Independent
467
+ * from the panorama-stitcher's warperType (which doesn't apply
468
+ * to the hybrid engine — its output is the live canvas directly).
469
+ *
470
+ * Note: only effective in the rotation-only preset path (hybrid
471
+ * preset 1). In the other hybrid presets the engine forces
472
+ * Planar internally regardless of this setting. Native source:
473
+ * `OpenCVIncrementalStitcher.mm:146,161,180`.
474
+ */
475
+ projection: 'Cylindrical' | 'Planar';
476
+ }
477
+ export declare const DEFAULT_HYBRID_SETTINGS: HybridSettings;
478
+ //# sourceMappingURL=PanoramaSettings.d.ts.map
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * PanoramaSettings (v0.4) — engine-discriminated, hierarchical
5
+ * settings types.
6
+ *
7
+ * Background
8
+ * ──────────
9
+ *
10
+ * Pre-v0.4 the lib exported a single flat `PanoramaSettings`
11
+ * interface with 45+ fields covering three unrelated stitching
12
+ * engines (batch-keyframe, hybrid, slit-scan). The 2026-05-22
13
+ * audit (CHANGELOG entry for v0.3.0) traced every field's actual
14
+ * native consumer and proved:
15
+ *
16
+ * • batch-keyframe and the live engines (hybrid + slit-scan)
17
+ * share **zero** settings — they read disjoint subsets of
18
+ * the flat interface.
19
+ * • ~10 fields had no native consumer at all (dead surface).
20
+ * • The <Camera> public component hardcodes `engine:
21
+ * 'batch-keyframe'` and never reaches the slit-scan / hybrid
22
+ * branches.
23
+ *
24
+ * v0.4 splits the flat interface into three engine-specific types:
25
+ *
26
+ * • `PanoramaSettings` — what <Camera> consumes (batch-keyframe).
27
+ * • `SlitscanSettings` — for Layer 2 hosts using the slit-scan
28
+ * engine (incremental.start({ engine:
29
+ * 'slitscan-*', ... })).
30
+ * • `HybridSettings` — for the RetaiLens-specific hybrid live
31
+ * engine. Exported for completeness;
32
+ * most consumers won't touch it.
33
+ *
34
+ * Each type carries only the fields its target engine actually
35
+ * reads. Sub-objects (`stitcher`, `frameSelection`, `flow`,
36
+ * `painting`, `registration`, `plane`, `ncc2d`, `emaSmoothing`,
37
+ * `panAxisLock`) group related knobs so the modal can render
38
+ * collapsible sections that match the type tree.
39
+ *
40
+ * Migration
41
+ * ─────────
42
+ *
43
+ * No automated migration helper. v0.4 is a clean break; the
44
+ * v0.3 `PanoramaSettings` type is deleted. Consumers (notably
45
+ * `retailens-camera-sdk`) update their settings literals to match
46
+ * the new shape. See the v0.4.0 CHANGELOG entry for the field-
47
+ * by-field mapping.
48
+ */
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.DEFAULT_HYBRID_SETTINGS = exports.DEFAULT_SLITSCAN_SETTINGS = exports.DEFAULT_PANORAMA_SETTINGS = exports.DEFAULT_FLOW_GATE_SETTINGS = void 0;
51
+ /**
52
+ * Canonical FlowGateSettings defaults, exported as a standalone
53
+ * constant so consumers (the bridge, the modal, prop translators)
54
+ * can reach the values WITHOUT typing
55
+ * `DEFAULT_PANORAMA_SETTINGS.frameSelection.flow!.X` — the
56
+ * non-null-assertion form is brittle (will start crashing at
57
+ * runtime the moment someone "cleans up" the default tree and
58
+ * makes `flow` undefined in `DEFAULT_PANORAMA_SETTINGS`). Lifted
59
+ * out 2026-05-22 in the F10 Phase 2 review (NIT-4).
60
+ *
61
+ * Numerical values mirror the v0.3 defaults; they're verified
62
+ * against the native engine's compiled-in fallback values
63
+ * (`IncrementalStitcher.swift:1003-1029`, `IncrementalStitcher.kt:419-445`)
64
+ * — discrepancies are flagged in the v0.3.0 audit and resolved by
65
+ * the bridge always-emitting these on the wire (see
66
+ * `PanoramaSettingsBridge.ts:panoramaSettingsToNativeConfig`).
67
+ */
68
+ exports.DEFAULT_FLOW_GATE_SETTINGS = {
69
+ noveltyPercentile: 0.85,
70
+ evalEveryNFrames: 5,
71
+ maxTranslationCm: 50,
72
+ maxCorners: 150,
73
+ qualityLevel: 0.01,
74
+ minDistance: 10,
75
+ };
76
+ exports.DEFAULT_PANORAMA_SETTINGS = {
77
+ captureSource: 'ar',
78
+ debug: false,
79
+ stitcher: {
80
+ stitchMode: 'auto',
81
+ warperType: 'plane',
82
+ blenderType: 'multiband',
83
+ seamFinderType: 'graphcut',
84
+ enableMaxInscribedRectCrop: false,
85
+ },
86
+ frameSelection: {
87
+ mode: 'flow-based',
88
+ maxKeyframes: 6,
89
+ overlapThreshold: 0.20,
90
+ flow: exports.DEFAULT_FLOW_GATE_SETTINGS,
91
+ },
92
+ };
93
+ exports.DEFAULT_SLITSCAN_SETTINGS = {
94
+ captureSource: 'ar',
95
+ debug: false,
96
+ variant: 'slitscan-rotate',
97
+ painting: {
98
+ paintMode: 'FirstPaintedWins',
99
+ sliverPosition: 'Bottom',
100
+ firstFrameFullFrame: true,
101
+ },
102
+ registration: {
103
+ enableTriangulation: false,
104
+ enableTriAccumulator: false,
105
+ enableRansacHomography: false,
106
+ // ncc1d / ncc2d omitted — both disabled by default.
107
+ },
108
+ plane: {
109
+ source: 'ARKitDetected',
110
+ projectionStyle: 'Rectified',
111
+ virtualDepthMeters: 1.5,
112
+ alignmentThreshold: 0.6,
113
+ },
114
+ };
115
+ exports.DEFAULT_HYBRID_SETTINGS = {
116
+ captureSource: 'ar',
117
+ debug: false,
118
+ projection: 'Planar',
119
+ };
120
+ //# sourceMappingURL=PanoramaSettings.js.map
@@ -0,0 +1,84 @@
1
+ /**
2
+ * PanoramaSettingsBridge — JS-side adapters that convert the v0.4
3
+ * typed `PanoramaSettings` / `SlitscanSettings` / `HybridSettings`
4
+ * shape into the flat `configOverrides` dictionary the native
5
+ * bridges read.
6
+ *
7
+ * Why this file exists
8
+ * ────────────────────
9
+ *
10
+ * The v0.4 types use hierarchical sub-trees (`stitcher`,
11
+ * `frameSelection.flow`, `painting`, `registration.ncc1d`,
12
+ * `registration.ncc2d.emaSmoothing`, `plane`, …) to give consumers
13
+ * a clean, ergonomic settings surface that mirrors the native
14
+ * engine's domain. But the native bridges (iOS Swift's
15
+ * `applyConfigOverrides`, Android Kotlin's `IncrementalStitcher.start`)
16
+ * read a FLAT dictionary of native-named keys (e.g. `nccSearchRadius1d`,
17
+ * `enable1dNcc`, `ncc2dEmaAlpha`, `flowMaxTranslationCm`).
18
+ *
19
+ * Two semantic gaps to bridge:
20
+ *
21
+ * 1. **Naming.** JS `registration.ncc1d.searchRadius` →
22
+ * native `nccSearchRadius1d`. JS `painting.paintMode` →
23
+ * native `paintMode` (same). Etc.
24
+ *
25
+ * 2. **Presence-as-enable.** The native side reads explicit
26
+ * `enable1dNcc`, `enable2dNcc`, `enableNcc2dEmaSmoothing`,
27
+ * `enableNcc2dPanAxisLock` booleans. JS models these as
28
+ * optional sub-objects (sub-object present ⇒ enabled). This
29
+ * adapter flattens the booleans for the wire.
30
+ *
31
+ * 3. **Skipped engine defaults.** Hybrid engine presets internally
32
+ * clobber most fields (see HybridSettings JSDoc), so we don't
33
+ * send overrides that would be ignored — just the small useful
34
+ * surface.
35
+ *
36
+ * The Camera component calls `panoramaSettingsToNativeConfig` once
37
+ * per capture start to produce the value passed as
38
+ * `incremental.start({ config: … })`. Layer 2 callers building
39
+ * SlitscanSettings or HybridSettings call the matching adapter
40
+ * before reaching `incremental.start()`.
41
+ */
42
+ import { type PanoramaSettings, type SlitscanSettings, type HybridSettings } from './PanoramaSettings';
43
+ /**
44
+ * Flat config dictionary type — what the native bridges expect.
45
+ * Indexed by the native-side key name; values are platform-
46
+ * marshallable (booleans / numbers / strings). Keep this type
47
+ * loose: native validates each key individually, and silently
48
+ * ignores keys it doesn't recognise.
49
+ */
50
+ export type NativeConfigDict = Record<string, boolean | number | string>;
51
+ /**
52
+ * Convert a v0.4 PanoramaSettings tree into the flat dict the
53
+ * batch-keyframe native side reads. Maps every consumed field
54
+ * exactly once and skips fields the engine doesn't reach.
55
+ *
56
+ * Verified against:
57
+ * - iOS `IncrementalStitcher.swift:810-960` (batch path)
58
+ * - Android `IncrementalStitcher.kt:280-430` (batch path)
59
+ */
60
+ export declare function panoramaSettingsToNativeConfig(s: PanoramaSettings): NativeConfigDict;
61
+ /**
62
+ * Convert a v0.4 SlitscanSettings tree into the flat dict the
63
+ * slit-scan / firstwins native engines read. Handles the
64
+ * "presence-as-enable" boolean expansion: a non-undefined
65
+ * `registration.ncc1d` means `enable1dNcc: true` on the wire,
66
+ * with the sub-object's `searchRadius` carried alongside.
67
+ *
68
+ * Verified against:
69
+ * - iOS `IncrementalStitcher.swift:1006-1100` (applyConfigOverrides)
70
+ * - iOS `OpenCVSlitScanStitcher.mm` (all numbered references in
71
+ * the audit ground-truth matrix)
72
+ */
73
+ export declare function slitscanSettingsToNativeConfig(s: SlitscanSettings): NativeConfigDict;
74
+ /**
75
+ * Convert a v0.4 HybridSettings tree into the flat dict the hybrid
76
+ * engine reads. Minimal surface — hybrid presets internally clobber
77
+ * almost everything; see HybridSettings JSDoc for context.
78
+ *
79
+ * Verified against:
80
+ * - iOS `OpenCVIncrementalStitcher.mm:139-180` (preset paths)
81
+ * - iOS `IncrementalStitcher.swift:1034-1040` (hybridProjection override)
82
+ */
83
+ export declare function hybridSettingsToNativeConfig(s: HybridSettings): NativeConfigDict;
84
+ //# sourceMappingURL=PanoramaSettingsBridge.d.ts.map