react-native-image-stitcher 0.18.0 → 0.20.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 +62 -0
- package/android/src/main/java/io/imagestitcher/rn/ARFrameContext.kt +89 -0
- package/android/src/main/java/io/imagestitcher/rn/ARFramePlugin.kt +57 -0
- package/android/src/main/java/io/imagestitcher/rn/AROverlayRenderer.kt +406 -0
- package/android/src/main/java/io/imagestitcher/rn/AROverlayStore.kt +441 -0
- package/android/src/main/java/io/imagestitcher/rn/RNSARCameraView.kt +472 -13
- package/android/src/main/java/io/imagestitcher/rn/RNSARCameraViewManager.kt +30 -5
- package/android/src/main/java/io/imagestitcher/rn/RNSARPluginRegistry.kt +177 -0
- package/android/src/main/java/io/imagestitcher/rn/RNSARSession.kt +127 -0
- package/dist/camera/ARCameraView.d.ts +55 -2
- package/dist/camera/ARCameraView.js +68 -2
- package/dist/camera/Camera.d.ts +65 -2
- package/dist/camera/Camera.js +24 -6
- package/dist/camera/arOverlayController.d.ts +52 -0
- package/dist/camera/arOverlayController.js +132 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +5 -2
- package/dist/stitching/ARFrameMeta.d.ts +49 -0
- package/dist/stitching/AROverlay.d.ts +97 -0
- package/dist/stitching/AROverlay.js +4 -0
- package/ios/Sources/RNImageStitcher/ARCameraViewManager.m +15 -8
- package/ios/Sources/RNImageStitcher/ARCameraViewManager.swift +22 -0
- package/ios/Sources/RNImageStitcher/ARSessionBridge.m +14 -0
- package/ios/Sources/RNImageStitcher/ARSessionBridge.swift +117 -1
- package/ios/Sources/RNImageStitcher/CameraFrameHostObject.h +25 -0
- package/ios/Sources/RNImageStitcher/CameraFrameHostObject.mm +66 -54
- package/ios/Sources/RNImageStitcher/RNISARFramePlugin.swift +284 -0
- package/ios/Sources/RNImageStitcher/RNISAROverlay.swift +409 -0
- package/ios/Sources/RNImageStitcher/RNSARCameraView.swift +281 -3
- package/ios/Sources/RNImageStitcher/RNSARSession.swift +127 -1
- package/package.json +1 -1
- package/src/camera/ARCameraView.tsx +139 -3
- package/src/camera/Camera.tsx +94 -3
- package/src/camera/arOverlayController.ts +184 -0
- package/src/index.ts +21 -1
- package/src/stitching/ARFrameMeta.ts +50 -0
- package/src/stitching/AROverlay.ts +105 -0
package/dist/camera/Camera.js
CHANGED
|
@@ -74,8 +74,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
74
74
|
};
|
|
75
75
|
})();
|
|
76
76
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
77
|
-
exports._cameraShouldUnmountForTests = exports._isSideEdgeForTests = exports._homeIndicatorEdgeForTests = exports.CameraError = void 0;
|
|
78
|
-
exports.Camera = Camera;
|
|
77
|
+
exports._cameraShouldUnmountForTests = exports._isSideEdgeForTests = exports._homeIndicatorEdgeForTests = exports.Camera = exports.CameraError = void 0;
|
|
79
78
|
const react_1 = __importStar(require("react"));
|
|
80
79
|
const react_native_1 = require("react-native");
|
|
81
80
|
const react_native_safe_area_context_1 = require("react-native-safe-area-context");
|
|
@@ -308,9 +307,13 @@ function extractPanoramaOverrides(props) {
|
|
|
308
307
|
// `<Image>` requires the scheme; iOS is lenient).
|
|
309
308
|
/**
|
|
310
309
|
* The public `<Camera>` component.
|
|
310
|
+
*
|
|
311
|
+
* v0.20.0 — now a `forwardRef`. The ref exposes {@link CameraHandle} (the AR
|
|
312
|
+
* overlay methods); existing callers that don't pass a ref are unaffected
|
|
313
|
+
* (`forwardRef` makes the ref optional).
|
|
311
314
|
*/
|
|
312
|
-
function Camera(props) {
|
|
313
|
-
const { defaultCaptureSource = 'non-ar', defaultLens = '1x', captureSources = 'both', enablePhotoMode = true, enablePanoramaMode = true, showSettingsButton = false, style, outputDir, onCapture, onCaptureSourceChange, onLensChange, onFramesDropped, onError, onCaptureAbandoned, flash: controlledFlash, onFlashChange, showFlashButton = true, headerTitle, onHeaderBack, headerBackLabel, headerGuidance, headerColors, thumbnails, thumbnailsMin, thumbnailsMax, onThumbnailPress, capturePreview, capturePreviewActions, onCapturePreviewClose, frameProcessor: hostFrameProcessor, arFrameProcessor, enableDepth, enableAnchors, enableMesh, planeDetection, onArFrame, arFrameMetaInterval, engine = 'batch-keyframe',
|
|
315
|
+
exports.Camera = (0, react_1.forwardRef)(function Camera(props, ref) {
|
|
316
|
+
const { defaultCaptureSource = 'non-ar', defaultLens = '1x', captureSources = 'both', enablePhotoMode = true, enablePanoramaMode = true, showSettingsButton = false, style, outputDir, onCapture, onCaptureSourceChange, onLensChange, onFramesDropped, onError, onCaptureAbandoned, flash: controlledFlash, onFlashChange, showFlashButton = true, headerTitle, onHeaderBack, headerBackLabel, headerGuidance, headerColors, thumbnails, thumbnailsMin, thumbnailsMax, onThumbnailPress, capturePreview, capturePreviewActions, onCapturePreviewClose, frameProcessor: hostFrameProcessor, arFrameProcessor, enableDepth, enableAnchors, enableMesh, planeDetection, onArFrame, arFrameMetaInterval, onArPluginResult, overlays, engine = 'batch-keyframe',
|
|
314
317
|
// ── Panorama GUIDANCE (feature/pano-ux-guidance) ──────────────
|
|
315
318
|
panMode = 'vertical', panGuidance = true, maxPanDurationMs = 0, panTooFastThreshold, lateralBudgetCm = 4, rectCrop = false, showPreview = false, guidanceCopy, } = props;
|
|
316
319
|
// Derived guidance state. The landscape-only gate decision itself is
|
|
@@ -513,6 +516,21 @@ function Camera(props) {
|
|
|
513
516
|
const incremental = (0, useIncrementalStitcher_1.useIncrementalStitcher)();
|
|
514
517
|
const visionCameraRef = (0, react_1.useRef)(null);
|
|
515
518
|
const arViewRef = (0, react_1.useRef)(null);
|
|
519
|
+
// v0.20.0 — AR overlay imperative handle. `<Camera>` itself renders no
|
|
520
|
+
// overlay layer; the overlay methods forward to the mounted
|
|
521
|
+
// `<ARCameraView>`'s handle (which owns the controller + native dispatch).
|
|
522
|
+
// No-op when AR mode isn't mounted (`arViewRef.current === null`), matching
|
|
523
|
+
// the CameraHandle docstring — the declarative `overlays` prop is the path
|
|
524
|
+
// that survives AR↔non-AR transitions. The `overlays` prop is also threaded
|
|
525
|
+
// straight to `<ARCameraView>` below, so a host can use either API.
|
|
526
|
+
(0, react_1.useImperativeHandle)(ref, () => ({
|
|
527
|
+
setOverlays: (o) => arViewRef.current?.setOverlays(o),
|
|
528
|
+
addOverlay: (o) => arViewRef.current?.addOverlay(o),
|
|
529
|
+
updateOverlay: (id, patch) => arViewRef.current?.updateOverlay(id, patch),
|
|
530
|
+
removeOverlay: (id) => arViewRef.current?.removeOverlay(id),
|
|
531
|
+
clearOverlays: () => arViewRef.current?.clearOverlays(),
|
|
532
|
+
raycast: () => arViewRef.current?.raycast() ?? Promise.resolve(null),
|
|
533
|
+
}), []);
|
|
516
534
|
// Effect that does the async transition work whenever the settled
|
|
517
535
|
// refs disagree with the current isAR/lens. Order matters:
|
|
518
536
|
// 1. Set the cameraTransitioning state so the gate stays closed
|
|
@@ -1425,7 +1443,7 @@ function Camera(props) {
|
|
|
1425
1443
|
// (V12.14.8 OOM fix). The CaptureStatusOverlay renders the
|
|
1426
1444
|
// "Stitching…" state on top, so no placeholder label is needed
|
|
1427
1445
|
// in that case — only for the camera-switch transition.
|
|
1428
|
-
react_1.default.createElement(react_native_1.View, { style: [react_native_1.StyleSheet.absoluteFill, styles.transitionPlaceholder] }, statusPhase === 'stitching' ? null : (react_1.default.createElement(react_native_1.Text, { style: styles.transitionLabel }, "Switching camera\u2026")))) : isAR ? (react_1.default.createElement(ARCameraView_1.ARCameraView, { ref: arViewRef, style: react_native_1.StyleSheet.absoluteFill, arFrameProcessor: arFrameProcessor, enableDepth: enableDepth, enableAnchors: enableAnchors, enableMesh: enableMesh, planeDetection: planeDetection, onArFrame: onArFrame, arFrameMetaInterval: arFrameMetaInterval })) : (react_1.default.createElement(CameraView_1.CameraView, { ref: visionCameraRef, device: capture.device, isActive: true,
|
|
1446
|
+
react_1.default.createElement(react_native_1.View, { style: [react_native_1.StyleSheet.absoluteFill, styles.transitionPlaceholder] }, statusPhase === 'stitching' ? null : (react_1.default.createElement(react_native_1.Text, { style: styles.transitionLabel }, "Switching camera\u2026")))) : isAR ? (react_1.default.createElement(ARCameraView_1.ARCameraView, { ref: arViewRef, style: react_native_1.StyleSheet.absoluteFill, arFrameProcessor: arFrameProcessor, enableDepth: enableDepth, enableAnchors: enableAnchors, enableMesh: enableMesh, planeDetection: planeDetection, onArFrame: onArFrame, arFrameMetaInterval: arFrameMetaInterval, onArPluginResult: onArPluginResult, overlays: overlays })) : (react_1.default.createElement(CameraView_1.CameraView, { ref: visionCameraRef, device: capture.device, isActive: true,
|
|
1429
1447
|
// `video={true}` is REQUIRED for takeSnapshot to work on iOS.
|
|
1430
1448
|
// vision-camera v4's iOS implementation of takeSnapshot waits
|
|
1431
1449
|
// for a frame on the video pipeline; with video disabled, the
|
|
@@ -1590,7 +1608,7 @@ function Camera(props) {
|
|
|
1590
1608
|
setCropPending(null);
|
|
1591
1609
|
}
|
|
1592
1610
|
} })));
|
|
1593
|
-
}
|
|
1611
|
+
});
|
|
1594
1612
|
function noop() {
|
|
1595
1613
|
/* no-op handler used when panorama mode is disabled */
|
|
1596
1614
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { AROverlay } from '../stitching/AROverlay';
|
|
2
|
+
/**
|
|
3
|
+
* The imperative overlay methods exposed on both `<ARCameraView>` and
|
|
4
|
+
* `<Camera>` refs. Identical shape on both so a host can swap components
|
|
5
|
+
* without rewriting overlay code.
|
|
6
|
+
*/
|
|
7
|
+
export interface AROverlayMethods {
|
|
8
|
+
/** Replace the entire JS-set overlay collection. */
|
|
9
|
+
setOverlays: (overlays: AROverlay[]) => void;
|
|
10
|
+
/** Add one overlay (replaces any existing overlay with the same `id`). */
|
|
11
|
+
addOverlay: (overlay: AROverlay) => void;
|
|
12
|
+
/**
|
|
13
|
+
* Shallow-merge a patch into the overlay with `id`. No-op if no overlay
|
|
14
|
+
* with that `id` is currently set.
|
|
15
|
+
*/
|
|
16
|
+
updateOverlay: (id: string, patch: Partial<AROverlay>) => void;
|
|
17
|
+
/** Remove the overlay with `id` (no-op if absent). */
|
|
18
|
+
removeOverlay: (id: string) => void;
|
|
19
|
+
/** Remove all JS-set overlays. */
|
|
20
|
+
clearOverlays: () => void;
|
|
21
|
+
/**
|
|
22
|
+
* Raycast from the screen centre (the crosshair) to the first real-world
|
|
23
|
+
* surface and resolve its world position `[x, y, z]` in metres (ARKit/ARCore
|
|
24
|
+
* world frame), or `null` when nothing is hit (e.g. a featureless wall before
|
|
25
|
+
* any plane is detected). Use it to place an overlay ON the aimed surface at
|
|
26
|
+
* the real distance — pass the result as a `worldPosition` to
|
|
27
|
+
* {@link setOverlays} / {@link addOverlay} — instead of guessing a distance.
|
|
28
|
+
* Resolves `null` (never throws) when the native module / method is absent.
|
|
29
|
+
*/
|
|
30
|
+
raycast: () => Promise<[number, number, number] | null>;
|
|
31
|
+
}
|
|
32
|
+
/** The `RNSARSession` native-module method name overlays dispatch through. */
|
|
33
|
+
export declare const AR_OVERLAY_SET_METHOD: "setOverlays";
|
|
34
|
+
/**
|
|
35
|
+
* The agreed UIManager view-command name for native sides that drive overlays
|
|
36
|
+
* via per-view command dispatch instead of the module method. The JS layer
|
|
37
|
+
* dispatches through the module method (there's only one AR view), but the
|
|
38
|
+
* name is pinned here so the native side can match if it chooses commands.
|
|
39
|
+
*/
|
|
40
|
+
export declare const AR_OVERLAY_VIEW_COMMAND: "RNSARCameraViewOverlays";
|
|
41
|
+
/**
|
|
42
|
+
* Build an overlay controller backed by an in-memory ordered set keyed by
|
|
43
|
+
* `id`. Every mutating call resolves the new full array and pushes it to
|
|
44
|
+
* native via `RNSARSession.setOverlays`. The controller is the single source
|
|
45
|
+
* of truth for BOTH the imperative ref methods and the declarative `overlays`
|
|
46
|
+
* prop (the prop's effect calls `setOverlays` with the prop value).
|
|
47
|
+
*/
|
|
48
|
+
export declare function createAROverlayController(): AROverlayMethods & {
|
|
49
|
+
/** Current JS-set overlays in insertion order (used by tests / diffing). */
|
|
50
|
+
getOverlays: () => AROverlay[];
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=arOverlayController.d.ts.map
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.AR_OVERLAY_VIEW_COMMAND = exports.AR_OVERLAY_SET_METHOD = void 0;
|
|
5
|
+
exports.createAROverlayController = createAROverlayController;
|
|
6
|
+
/**
|
|
7
|
+
* v0.20.0 — shared JS→native plumbing for the AR overlay renderer.
|
|
8
|
+
*
|
|
9
|
+
* `<ARCameraView>` and `<Camera>` expose an IDENTICAL imperative overlay API
|
|
10
|
+
* (`setOverlays` / `addOverlay` / `updateOverlay` / `removeOverlay` /
|
|
11
|
+
* `clearOverlays`) plus a declarative `overlays` prop. Rather than duplicate
|
|
12
|
+
* the diff + native-dispatch logic in each component, both build their handle
|
|
13
|
+
* from {@link createAROverlayController} — DRY, single source of truth for the
|
|
14
|
+
* wire format and the merge-by-id semantics.
|
|
15
|
+
*
|
|
16
|
+
* ## Native mechanism (agreed cross-platform contract)
|
|
17
|
+
*
|
|
18
|
+
* Every AR-session setting in this SDK already flows through the
|
|
19
|
+
* `RNSARSession` native-module singleton (`setPlaneDetection`,
|
|
20
|
+
* `setArFrameMetaEnabled`, `setSceneReconstructionEnabled`) because
|
|
21
|
+
* `RNSARSession.shared` drives the single mounted `RNSARCameraView`. Overlays
|
|
22
|
+
* follow the same pattern: a single `setOverlays(overlays)` method on
|
|
23
|
+
* `RNSARSession` carries the FULL current JS-set overlay array each time it
|
|
24
|
+
* changes. Native replaces its JS-set overlay collection wholesale (the merge
|
|
25
|
+
* with the namespaced native-plugin set happens on the native side) and the
|
|
26
|
+
* overlay layer redraws every AR frame.
|
|
27
|
+
*
|
|
28
|
+
* The declarative `overlays` prop and the imperative methods both ultimately
|
|
29
|
+
* call this same `setOverlays` with the resolved array, so the two APIs are
|
|
30
|
+
* interchangeable and can't diverge.
|
|
31
|
+
*
|
|
32
|
+
* Why a module method (not a UIManager view command)? It matches every other
|
|
33
|
+
* AR setting in this codebase and there is only ever ONE `RNSARCameraView`
|
|
34
|
+
* mounted (ARKit/ARCore can't share the camera), so there's nothing to key by
|
|
35
|
+
* view tag. The equivalent UIManager view-command name, for native sides that
|
|
36
|
+
* prefer per-view dispatch, is documented as `RNSARCameraViewOverlays`.
|
|
37
|
+
*/
|
|
38
|
+
const react_native_1 = require("react-native");
|
|
39
|
+
/** The `RNSARSession` native-module method name overlays dispatch through. */
|
|
40
|
+
exports.AR_OVERLAY_SET_METHOD = 'setOverlays';
|
|
41
|
+
/**
|
|
42
|
+
* The agreed UIManager view-command name for native sides that drive overlays
|
|
43
|
+
* via per-view command dispatch instead of the module method. The JS layer
|
|
44
|
+
* dispatches through the module method (there's only one AR view), but the
|
|
45
|
+
* name is pinned here so the native side can match if it chooses commands.
|
|
46
|
+
*/
|
|
47
|
+
exports.AR_OVERLAY_VIEW_COMMAND = 'RNSARCameraViewOverlays';
|
|
48
|
+
/**
|
|
49
|
+
* Build an overlay controller backed by an in-memory ordered set keyed by
|
|
50
|
+
* `id`. Every mutating call resolves the new full array and pushes it to
|
|
51
|
+
* native via `RNSARSession.setOverlays`. The controller is the single source
|
|
52
|
+
* of truth for BOTH the imperative ref methods and the declarative `overlays`
|
|
53
|
+
* prop (the prop's effect calls `setOverlays` with the prop value).
|
|
54
|
+
*/
|
|
55
|
+
function createAROverlayController() {
|
|
56
|
+
// Insertion-ordered map: preserves the order overlays were added so the
|
|
57
|
+
// native render order is stable + predictable.
|
|
58
|
+
const overlaysById = new Map();
|
|
59
|
+
const flush = () => {
|
|
60
|
+
const native = react_native_1.NativeModules
|
|
61
|
+
.RNSARSession;
|
|
62
|
+
// Native module / method unavailable (web, or a native build predating the
|
|
63
|
+
// overlay channel): no-op, no crash — mirrors the other AR setters.
|
|
64
|
+
const ret = native?.setOverlays?.(Array.from(overlaysById.values()));
|
|
65
|
+
// iOS returns a Promise (the native method is Promise-typed); swallow any
|
|
66
|
+
// rejection so a transient native error never surfaces as an unhandled
|
|
67
|
+
// rejection. Android returns void — the optional chain skips the catch.
|
|
68
|
+
ret?.catch?.(() => undefined);
|
|
69
|
+
};
|
|
70
|
+
return {
|
|
71
|
+
getOverlays: () => Array.from(overlaysById.values()),
|
|
72
|
+
setOverlays: (overlays) => {
|
|
73
|
+
overlaysById.clear();
|
|
74
|
+
for (const o of overlays) {
|
|
75
|
+
// Last-writer-wins on duplicate ids in the incoming array.
|
|
76
|
+
overlaysById.set(o.id, o);
|
|
77
|
+
}
|
|
78
|
+
flush();
|
|
79
|
+
},
|
|
80
|
+
addOverlay: (overlay) => {
|
|
81
|
+
// Re-set to move an existing id to the end? No — preserve original slot
|
|
82
|
+
// by deleting first only when absent. Map#set keeps the existing slot
|
|
83
|
+
// when the key already exists, so a plain set is the right "replace in
|
|
84
|
+
// place" behaviour.
|
|
85
|
+
overlaysById.set(overlay.id, overlay);
|
|
86
|
+
flush();
|
|
87
|
+
},
|
|
88
|
+
updateOverlay: (id, patch) => {
|
|
89
|
+
const existing = overlaysById.get(id);
|
|
90
|
+
if (existing == null) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
// Shallow-merge; `id` is preserved from the existing overlay regardless
|
|
94
|
+
// of what the patch carries (the map key must stay consistent).
|
|
95
|
+
overlaysById.set(id, { ...existing, ...patch, id });
|
|
96
|
+
flush();
|
|
97
|
+
},
|
|
98
|
+
removeOverlay: (id) => {
|
|
99
|
+
if (overlaysById.delete(id)) {
|
|
100
|
+
flush();
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
clearOverlays: () => {
|
|
104
|
+
if (overlaysById.size > 0) {
|
|
105
|
+
overlaysById.clear();
|
|
106
|
+
flush();
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
raycast: async () => {
|
|
110
|
+
const native = react_native_1.NativeModules
|
|
111
|
+
.RNSARSession;
|
|
112
|
+
const fn = native?.raycast;
|
|
113
|
+
// Native module / method unavailable (web, or a native build predating
|
|
114
|
+
// the raycast channel): resolve null — the caller falls back.
|
|
115
|
+
if (typeof fn !== 'function') {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
const res = await fn();
|
|
120
|
+
const wp = res?.worldPosition;
|
|
121
|
+
if (Array.isArray(wp) && wp.length >= 3) {
|
|
122
|
+
return [Number(wp[0]), Number(wp[1]), Number(wp[2])];
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=arOverlayController.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* adds RetaiLens-specific features on top.
|
|
21
21
|
*/
|
|
22
22
|
export { Camera, CameraError } from './camera/Camera';
|
|
23
|
-
export type { CameraProps, CameraCaptureResult, PanoramaCaptureResult, CameraErrorCode, CaptureSource, CaptureSourcesMode, CameraLens, StitchMode, Blender, SeamFinder, Warper, FramesDroppedInfo, } from './camera/Camera';
|
|
23
|
+
export type { CameraProps, CameraHandle, CameraCaptureResult, PanoramaCaptureResult, CameraErrorCode, CaptureSource, CaptureSourcesMode, CameraLens, StitchMode, Blender, SeamFinder, Warper, FramesDroppedInfo, } from './camera/Camera';
|
|
24
24
|
export type { CaptureWarning, CaptureWarningCode, CaptureWarningCopy, } from './camera/captureWarnings';
|
|
25
25
|
export { DEFAULT_CAPTURE_WARNING_COPY } from './camera/captureWarnings';
|
|
26
26
|
export { userFacingStitchError, RECOVERABLE_STITCH_GUIDANCE, RECOVERABLE_STITCH_CODES, } from './camera/cameraErrorMessages';
|
|
@@ -93,6 +93,10 @@ export { useIncrementalStitcher } from './stitching/useIncrementalStitcher';
|
|
|
93
93
|
export { useKeyframeStream } from './stitching/useKeyframeStream';
|
|
94
94
|
export type { CameraFrame, CameraFrameProcessor, ARAnchor, } from './stitching/CameraFrame';
|
|
95
95
|
export type { ARFrameMeta } from './stitching/ARFrameMeta';
|
|
96
|
+
export type { ARPluginResult } from './stitching/ARFrameMeta';
|
|
97
|
+
export type { AROverlay } from './stitching/AROverlay';
|
|
98
|
+
export type { AROverlayMethods } from './camera/arOverlayController';
|
|
99
|
+
export { AR_OVERLAY_SET_METHOD, AR_OVERLAY_VIEW_COMMAND, } from './camera/arOverlayController';
|
|
96
100
|
export { useFrameProcessorDriver } from './stitching/useFrameProcessorDriver';
|
|
97
101
|
export type { UseFrameProcessorDriverOptions, FrameProcessorDriverHandle, } from './stitching/useFrameProcessorDriver';
|
|
98
102
|
export { useStitcherWorklet } from './stitching/useStitcherWorklet';
|
package/dist/index.js
CHANGED
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
* adds RetaiLens-specific features on top.
|
|
23
23
|
*/
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
exports.
|
|
26
|
-
exports.stitchVideo = exports.useStitcherWorklet = void 0;
|
|
25
|
+
exports.AR_OVERLAY_SET_METHOD = exports.useKeyframeStream = exports.useIncrementalStitcher = exports.cleanupOldKeyframes = exports.getIncrementalNativeModule = exports.subscribeIncrementalState = exports.incrementalStitcherIsAvailable = exports.IncrementalOutcome = exports.cropQuad = exports.RectCropPreview = exports.LateralMotionModal = exports.CaptureFrameCounterOverlay = exports.CaptureCountdownOverlay = exports.PanHowToOverlay = exports.RotateToLandscapePrompt = exports.usePanMotion = exports.DEFAULT_GUIDANCE_COPY = 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.RECOVERABLE_STITCH_CODES = exports.RECOVERABLE_STITCH_GUIDANCE = exports.userFacingStitchError = exports.DEFAULT_CAPTURE_WARNING_COPY = exports.CameraError = exports.Camera = void 0;
|
|
26
|
+
exports.stitchVideo = exports.useStitcherWorklet = exports.useFrameProcessorDriver = exports.AR_OVERLAY_VIEW_COMMAND = void 0;
|
|
27
27
|
// ─────────────────────────────────────────────────────────────────────
|
|
28
28
|
// Layer 1 — the high-level <Camera> component
|
|
29
29
|
// ─────────────────────────────────────────────────────────────────────
|
|
@@ -189,6 +189,9 @@ Object.defineProperty(exports, "useIncrementalStitcher", { enumerable: true, get
|
|
|
189
189
|
// keyframe, packet detection, server-side analysis, etc.).
|
|
190
190
|
var useKeyframeStream_1 = require("./stitching/useKeyframeStream");
|
|
191
191
|
Object.defineProperty(exports, "useKeyframeStream", { enumerable: true, get: function () { return useKeyframeStream_1.useKeyframeStream; } });
|
|
192
|
+
var arOverlayController_1 = require("./camera/arOverlayController");
|
|
193
|
+
Object.defineProperty(exports, "AR_OVERLAY_SET_METHOD", { enumerable: true, get: function () { return arOverlayController_1.AR_OVERLAY_SET_METHOD; } });
|
|
194
|
+
Object.defineProperty(exports, "AR_OVERLAY_VIEW_COMMAND", { enumerable: true, get: function () { return arOverlayController_1.AR_OVERLAY_VIEW_COMMAND; } });
|
|
192
195
|
// NOTE: the host-worklet / frame-stream hooks `useFrameProcessor`,
|
|
193
196
|
// `useThrottledFrameProcessor` and `useFrameStream` (v0.8–v0.9) were
|
|
194
197
|
// archived in the batch-keyframe cleanup — they drove the third-party
|
|
@@ -96,5 +96,54 @@ export interface ARFrameMeta {
|
|
|
96
96
|
vertexCount: number;
|
|
97
97
|
faceCount: number;
|
|
98
98
|
} | null;
|
|
99
|
+
/**
|
|
100
|
+
* v0.19.0 — SYNCHRONOUS results from host-registered AR frame plugins
|
|
101
|
+
* (the AR plugin framework). Keyed by each plugin's `name()`; the value
|
|
102
|
+
* is the light JSON result the plugin's `process(ctx)` returned on the AR
|
|
103
|
+
* thread (`nil`/`null`-returning plugins are omitted). Only present when
|
|
104
|
+
* the native plugin registry is non-empty AND at least one plugin returned
|
|
105
|
+
* a sync result for this frame; otherwise omitted entirely (zero-plugin
|
|
106
|
+
* apps pay nothing — native skips building the context).
|
|
107
|
+
*
|
|
108
|
+
* The SDK ships ONLY the generic framework — there are no built-in
|
|
109
|
+
* plugins. Hosts register native plugins via `RNISARPluginRegistry`
|
|
110
|
+
* (iOS) / `RNSARPluginRegistry` (Android) at startup; each plugin is
|
|
111
|
+
* called once per AR frame while the registry is non-empty. Result
|
|
112
|
+
* values are `unknown` because each plugin defines its own shape — cast
|
|
113
|
+
* after reading the entry you care about (e.g.
|
|
114
|
+
* `meta.plugins?.brightness as number`).
|
|
115
|
+
*
|
|
116
|
+
* ## Sync vs async results
|
|
117
|
+
*
|
|
118
|
+
* This field carries only the LIGHT, in-band SYNC results (computed fast
|
|
119
|
+
* enough to ride the throttled `onArFrame` event). Plugins that offload
|
|
120
|
+
* heavy work to their own queue deliver results out-of-band via
|
|
121
|
+
* `registry.emit(name, result)`, which surfaces through the separate
|
|
122
|
+
* `onArPluginResult` callback (the `RNImageStitcherARPluginResult`
|
|
123
|
+
* event) — NOT here.
|
|
124
|
+
*/
|
|
125
|
+
plugins?: {
|
|
126
|
+
[name: string]: unknown;
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* v0.19.0 — an ASYNCHRONOUS result from a host-registered AR frame plugin,
|
|
131
|
+
* delivered via the `onArPluginResult` callback.
|
|
132
|
+
*
|
|
133
|
+
* Unlike the in-band SYNC results carried on {@link ARFrameMeta.plugins}
|
|
134
|
+
* (which ride the throttled `onArFrame` event), a plugin produces an async
|
|
135
|
+
* result by offloading heavy work to its own queue and later calling
|
|
136
|
+
* `registry.emit(name, result)` on the native side. The SDK routes that to
|
|
137
|
+
* JS as a `RNImageStitcherARPluginResult` device event; `<ARCameraView>`
|
|
138
|
+
* subscribes and invokes `onArPluginResult` on the JS MAIN thread.
|
|
139
|
+
*
|
|
140
|
+
* `result` is `unknown` because each plugin defines its own result shape —
|
|
141
|
+
* branch on `plugin` (the emitting plugin's `name()`) and cast accordingly.
|
|
142
|
+
*/
|
|
143
|
+
export interface ARPluginResult {
|
|
144
|
+
/** The `name()` of the plugin that emitted this result. */
|
|
145
|
+
plugin: string;
|
|
146
|
+
/** The plugin-defined result payload (cast after branching on `plugin`). */
|
|
147
|
+
result: unknown;
|
|
99
148
|
}
|
|
100
149
|
//# sourceMappingURL=ARFrameMeta.d.ts.map
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v0.20.0 — the AR OVERLAY / ANNOTATION renderer's data model.
|
|
3
|
+
*
|
|
4
|
+
* An {@link AROverlay} describes a 2D shape (a billboard marker/box or a
|
|
5
|
+
* world-anchored quad) that the native overlay layer draws ON TOP of the AR
|
|
6
|
+
* camera preview (`RNSARCameraView`). Each overlay is anchored to WORLD
|
|
7
|
+
* positions and REPROJECTED to screen on EVERY AR frame from the current
|
|
8
|
+
* camera pose + intrinsics — so it tracks the scene at display rate with no
|
|
9
|
+
* 3D-engine dependency.
|
|
10
|
+
*
|
|
11
|
+
* ## Two ways to anchor an overlay
|
|
12
|
+
*
|
|
13
|
+
* 1. **A single world point** (`worldPosition`) — drawn as a billboard
|
|
14
|
+
* marker/box facing the camera, sized by `sizeMeters` (default a small
|
|
15
|
+
* marker). Use this for a pin on a detected plane anchor, a label on a
|
|
16
|
+
* point of interest, etc.
|
|
17
|
+
* 2. **Explicit world corners** (`worldQuad`, 3–4 points) — drawn as the
|
|
18
|
+
* outline/box connecting the projected corners. Use this for a detected
|
|
19
|
+
* quad (a shelf face, a packet, a door) whose real-world shape you
|
|
20
|
+
* already know.
|
|
21
|
+
*
|
|
22
|
+
* Provide ONE of the two anchor forms. If both are present `worldQuad` wins
|
|
23
|
+
* (it's the more specific description); the native renderers read `worldQuad`
|
|
24
|
+
* first and fall back to `worldPosition` + `sizeMeters`.
|
|
25
|
+
*
|
|
26
|
+
* ## Rendering / reprojection (native)
|
|
27
|
+
*
|
|
28
|
+
* The native side reprojects each overlay's world point(s) to screen with the
|
|
29
|
+
* AR framework's BUILT-IN, correct projection — iOS
|
|
30
|
+
* `ARFrame.camera.projectPoint(_:orientation:viewportSize:)`, Android
|
|
31
|
+
* `viewMatrix · projectionMatrix` → clip → NDC → screen. Points behind the
|
|
32
|
+
* camera or off-screen are hidden. The layer redraws every frame so the
|
|
33
|
+
* outline/box + label stay pinned to the world as the camera moves.
|
|
34
|
+
*
|
|
35
|
+
* ## Where overlays come from
|
|
36
|
+
*
|
|
37
|
+
* Overlays reach the native renderer through two INDEPENDENT, merged sets:
|
|
38
|
+
*
|
|
39
|
+
* - **JS-set** — the declarative `overlays` prop or the imperative ref
|
|
40
|
+
* methods (`setOverlays` / `addOverlay` / `updateOverlay` / `removeOverlay`
|
|
41
|
+
* / `clearOverlays`) on `<Camera>` and `<ARCameraView>`.
|
|
42
|
+
* - **Native-plugin-set** — a registered AR plugin places overlays directly
|
|
43
|
+
* via the 0.19 registry (`RNISARPluginRegistry.setOverlays(...)` on iOS /
|
|
44
|
+
* `RNSARPluginRegistry.setOverlays(...)` on Android), with zero JS latency.
|
|
45
|
+
*
|
|
46
|
+
* The native renderer draws the UNION of both sets; the plugin set is
|
|
47
|
+
* namespaced so a JS `setOverlays(...)` never clobbers plugin overlays.
|
|
48
|
+
*/
|
|
49
|
+
export interface AROverlay {
|
|
50
|
+
/**
|
|
51
|
+
* Stable identifier. The declarative `overlays` prop diffs the incoming
|
|
52
|
+
* array against the current set BY `id` (add / update / remove); the
|
|
53
|
+
* imperative `updateOverlay` / `removeOverlay` methods key off it too. Must
|
|
54
|
+
* be unique within a set.
|
|
55
|
+
*/
|
|
56
|
+
id: string;
|
|
57
|
+
/**
|
|
58
|
+
* Anchor form 1 — a single world point in METRES (world space `[x, y, z]`).
|
|
59
|
+
* Drawn as a billboard marker/box of `sizeMeters` extent facing the camera.
|
|
60
|
+
* Ignored when `worldQuad` is provided.
|
|
61
|
+
*/
|
|
62
|
+
worldPosition?: [number, number, number];
|
|
63
|
+
/**
|
|
64
|
+
* Box extent in METRES `[width, height]` at `worldPosition`. Only meaningful
|
|
65
|
+
* with `worldPosition`. Defaults to a small marker on the native side when
|
|
66
|
+
* omitted.
|
|
67
|
+
*/
|
|
68
|
+
sizeMeters?: [number, number];
|
|
69
|
+
/**
|
|
70
|
+
* Anchor form 2 — 3 or 4 explicit world corners in METRES (e.g. a detected
|
|
71
|
+
* quad). Each corner is `[x, y, z]` in world space. Drawn as the
|
|
72
|
+
* outline/box connecting the projected corners. Takes precedence over
|
|
73
|
+
* `worldPosition` + `sizeMeters` when both are present.
|
|
74
|
+
*/
|
|
75
|
+
worldQuad?: Array<[number, number, number]>;
|
|
76
|
+
/**
|
|
77
|
+
* Draw style. Default `'outline'` (stroked edges). `'box'` is a filled /
|
|
78
|
+
* boxed marker. Both render in 2D this release.
|
|
79
|
+
*/
|
|
80
|
+
shape?: 'box' | 'outline';
|
|
81
|
+
/** Optional text label drawn at the overlay's anchor point. */
|
|
82
|
+
label?: string;
|
|
83
|
+
/**
|
|
84
|
+
* Stroke / fill colour as a hex string (e.g. `'#00E5FF'`). Defaults to a
|
|
85
|
+
* theme colour on the native side when omitted.
|
|
86
|
+
*/
|
|
87
|
+
color?: string;
|
|
88
|
+
/**
|
|
89
|
+
* Render mode. Default `'2d'` — a flat shape reprojected to screen.
|
|
90
|
+
* `'3d'` is SCAFFOLD ONLY this release: the data-model field exists and the
|
|
91
|
+
* native renderers leave a marked hook for a future SceneKit (iOS) / Android
|
|
92
|
+
* 3D renderer, but v1 treats `'3d'` as `'2d'` (with a one-time native log
|
|
93
|
+
* warning). Document-only forward compatibility.
|
|
94
|
+
*/
|
|
95
|
+
mode?: '2d' | '3d';
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=AROverlay.d.ts.map
|
|
@@ -21,13 +21,20 @@
|
|
|
21
21
|
|
|
22
22
|
@interface RCT_EXTERN_MODULE(RNSARCameraViewManager, RCTViewManager)
|
|
23
23
|
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
24
|
+
// v0.20.0 — declarative AR overlay set. React-state-driven array of
|
|
25
|
+
// overlay dictionaries (the JS `AROverlay[]` shape). RN sets this via KVC
|
|
26
|
+
// on the VIEW (`RNSARCameraView.overlays`); the view's `@objc` setter
|
|
27
|
+
// forwards the array to the JS namespace of `RNISAROverlayStore.shared`,
|
|
28
|
+
// which the per-frame draw view reprojects + strokes. (We forward through
|
|
29
|
+
// the view rather than store per-view state because the overlay set is
|
|
30
|
+
// global to the single AR session.)
|
|
31
|
+
//
|
|
32
|
+
// The IMPERATIVE overlay API (setOverlays / addOverlay / updateOverlay /
|
|
33
|
+
// removeOverlay / clearOverlays on the ref) is NOT a view command — per
|
|
34
|
+
// the shared contract (src/camera/arOverlayController.ts) it dispatches
|
|
35
|
+
// through the `RNSARSession.setOverlays(_:)` native MODULE method (see
|
|
36
|
+
// ARSessionBridge.{swift,m}), matching every other AR setting. Only the
|
|
37
|
+
// declarative prop lives on the view manager.
|
|
38
|
+
RCT_EXPORT_VIEW_PROPERTY(overlays, NSArray)
|
|
32
39
|
|
|
33
40
|
@end
|
|
@@ -36,5 +36,27 @@ public final class RNSARCameraViewManager: RCTViewManager {
|
|
|
36
36
|
public override class func requiresMainQueueSetup() -> Bool {
|
|
37
37
|
return true
|
|
38
38
|
}
|
|
39
|
+
|
|
40
|
+
// MARK: - v0.20.0 — AR overlays
|
|
41
|
+
//
|
|
42
|
+
// OVERLAY WIRE PATH (shared cross-platform contract, see
|
|
43
|
+
// `src/camera/arOverlayController.ts`): the JS imperative methods
|
|
44
|
+
// (setOverlays / addOverlay / updateOverlay / removeOverlay /
|
|
45
|
+
// clearOverlays) AND the declarative `overlays` prop both resolve, in
|
|
46
|
+
// JS, to the FULL current overlay array and dispatch it through the
|
|
47
|
+
// `RNSARSession.setOverlays(_:)` native MODULE method (see
|
|
48
|
+
// `ARSessionBridge.swift`). Native replaces its JS-overlay namespace
|
|
49
|
+
// in `RNISAROverlayStore` wholesale and merges with the SEPARATE
|
|
50
|
+
// plugin-overlay namespace (`RNISARPluginRegistry.setOverlays`) — the
|
|
51
|
+
// draw view renders the UNION every ARFrame.
|
|
52
|
+
//
|
|
53
|
+
// The module method is the chosen mechanism (matching every other AR
|
|
54
|
+
// setting: setPlaneDetection / setArFrameMetaEnabled /
|
|
55
|
+
// setSceneReconstructionEnabled) because there is exactly ONE
|
|
56
|
+
// `RNSARCameraView` mounted (ARKit can't share the camera), so there's
|
|
57
|
+
// nothing to key by view tag. We ALSO honor the declarative
|
|
58
|
+
// `overlays` view prop here on the view (`RNSARCameraView.overlays`
|
|
59
|
+
// KVC setter) so a host can drive overlays purely declaratively
|
|
60
|
+
// without the module — both land in the same store namespace.
|
|
39
61
|
}
|
|
40
62
|
#endif
|
|
@@ -56,6 +56,20 @@ RCT_EXTERN_METHOD(setArFrameMetaEnabled:(nonnull NSNumber *)enabled
|
|
|
56
56
|
resolver:(RCTPromiseResolveBlock)resolver
|
|
57
57
|
rejecter:(RCTPromiseRejectBlock)rejecter)
|
|
58
58
|
|
|
59
|
+
// v0.20.0 — AR overlay renderer. Replace the entire JS-set overlay
|
|
60
|
+
// collection (the shared arOverlayController sends the full array every
|
|
61
|
+
// mutation). Native merges with the namespaced plugin-overlay set + the
|
|
62
|
+
// RNSARCameraView draw view reprojects them each ARFrame.
|
|
63
|
+
RCT_EXTERN_METHOD(setOverlays:(nonnull NSArray *)overlays
|
|
64
|
+
resolver:(RCTPromiseResolveBlock)resolver
|
|
65
|
+
rejecter:(RCTPromiseRejectBlock)rejecter)
|
|
66
|
+
|
|
67
|
+
// v0.20.0 — raycast from the crosshair (screen centre) to the first real
|
|
68
|
+
// surface hit → { worldPosition: [x,y,z] } or null. Used to place an
|
|
69
|
+
// overlay ON the aimed surface (then anchor it), vs a guessed distance ahead.
|
|
70
|
+
RCT_EXTERN_METHOD(raycast:(RCTPromiseResolveBlock)resolver
|
|
71
|
+
rejecter:(RCTPromiseRejectBlock)rejecter)
|
|
72
|
+
|
|
59
73
|
RCT_EXTERN_METHOD(snapshotPoseLog:(RCTPromiseResolveBlock)resolver
|
|
60
74
|
rejecter:(RCTPromiseRejectBlock)rejecter)
|
|
61
75
|
|