You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have read and followed every applicable step in the Nitro Modules Troubleshooting guide.
I have read the VisionCamera Troubleshooting guide.
I have searched existing issues and found nothing matching (no hits for convertCameraPointToFramePoint).
I am on the latest version, or have a specific reason I cannot upgrade — see "VisionCamera version" below.
Reproduction
#4113 — adds one failing Harness test, maps a square Frame region onto a square View region, to apps/simple-camera/__tests__/visioncamera.coordinates.harness.tsx.
Every transform in between — sensor scale, orientation counter-rotation, the preview's aspect-preserving crop — preserves aspect. Camera space itself is allowed to be anisotropic (iOS normalises per-axis to [0, 1]), but converting out of it should undo the same per-axis scaling it applied.
What actually happened?
On iOS the square comes back as a wide, short strip. Measured on iPhone 15 Pro Max, frame 3840×2160, orientation: "left", using a square guide cell of identical ±19.7 dp in both view axes:
383.6 × 121.4 px — a ratio of 3.159, where a correct mapping measures 1.000. Pixel 8 (1280×720) composes correctly at 1.000.
convertViewPointToCameraPoint looks correct on its own: for the square patch it returns Δx/Δy = 0.5625 = exactly 9/16, consistent with per-axis normalisation over a 16:9 image.
Suspected cause.ios/Utils/FrameCoordinateSystemConverter.getFrameToCameraMatrix rotates within [0, 1]² in step 1, then normalises in step 3 with matrix.scaledBy(x: 1 / width, y: 1 / height). scaledBy post-concatenates, so the point is normalised by the buffer's own extents before the rotation swaps the axes — and the extents never swap with it. The numbers match that reading exactly: 0.0562 × 2160 = 121.4, 0.0999 × 3840 = 383.6. Predicted anisotropy for 16:9 at orientation: left is (16/9)² = 3.1605 against the measured 3.159.
The existing tests can't catch it: the Frame→Camera→Frame round-trip is self-inverse (getCameraToFrameMatrix is getFrameToCameraMatrix().inverted()), and the end-to-end center test uses the frame center, which maps to camera (0.5, 0.5) under both a correct and an anisotropic matrix. Only an off-center region exposes it. Full analysis in the PR.
Affected platforms
iOS (device)
Device(s) affected
iPhone 15 Pro Max (iOS 26.5.2) — reproduces. Pixel 8 — does not reproduce (exact Android build not recorded at measurement time).
VisionCamera version
5.0.11. Not on the latest (5.2.0) because 5.2.0 is nitrogen-0.36.3-generated and our app is still on react-native-nitro-modules 0.35.x — that upgrade is scheduled separately.
This is very unlikely to matter here: ios/Utils/FrameCoordinateSystemConverter.swift is byte-identical across v5.0.11, v5.1.0, v5.1.1 and v5.2.0 (md5 7e406d9d979e0490f06ca05f7e52ea70, 74 lines each), and no 5.x release note touches it. Happy to re-measure on 5.2.0 if you'd like it confirmed on the current release.
React Native version
0.86.0 at measurement time (since upgraded to 0.86.2).
React Native architecture
New Architecture (Fabric / bridgeless)
Context
Found while mapping a Rubik's cube scan guide onto sampled frames in a shipping app. Our workaround validates the composed mapping with an isotropy check and falls back to deriving the geometry from the frame dimensions when it fails — no platform branch, it's measured at runtime. Android accepts the library transform; iOS rejects it at 3.159 and falls back. Glad to test a fix on device.
Prerequisites
convertCameraPointToFramePoint).Reproduction
#4113 — adds one failing Harness test,
maps a square Frame region onto a square View region, toapps/simple-camera/__tests__/visioncamera.coordinates.harness.tsx.Steps to reproduce
bun run test:harness:ios -- --testPathPatterns=coordinates.maps a square Frame region onto a square View region.What did you expect to happen?
Composing the two public coordinate conversions should preserve aspect. A square region in Frame space should map to a square region in View space:
Every transform in between — sensor scale, orientation counter-rotation, the preview's aspect-preserving crop — preserves aspect. Camera space itself is allowed to be anisotropic (iOS normalises per-axis to
[0, 1]), but converting out of it should undo the same per-axis scaling it applied.What actually happened?
On iOS the square comes back as a wide, short strip. Measured on iPhone 15 Pro Max, frame 3840×2160,
orientation: "left", using a square guide cell of identical ±19.7 dp in both view axes:383.6 × 121.4 px — a ratio of 3.159, where a correct mapping measures 1.000. Pixel 8 (1280×720) composes correctly at 1.000.
convertViewPointToCameraPointlooks correct on its own: for the square patch it returns Δx/Δy = 0.5625 = exactly 9/16, consistent with per-axis normalisation over a 16:9 image.Suspected cause.
ios/Utils/FrameCoordinateSystemConverter.getFrameToCameraMatrixrotates within[0, 1]²in step 1, then normalises in step 3 withmatrix.scaledBy(x: 1 / width, y: 1 / height).scaledBypost-concatenates, so the point is normalised by the buffer's own extents before the rotation swaps the axes — and the extents never swap with it. The numbers match that reading exactly:0.0562 × 2160 = 121.4,0.0999 × 3840 = 383.6. Predicted anisotropy for 16:9 atorientation: leftis (16/9)² = 3.1605 against the measured 3.159.The existing tests can't catch it: the Frame→Camera→Frame round-trip is self-inverse (
getCameraToFrameMatrixisgetFrameToCameraMatrix().inverted()), and the end-to-end center test uses the frame center, which maps to camera(0.5, 0.5)under both a correct and an anisotropic matrix. Only an off-center region exposes it. Full analysis in the PR.Affected platforms
iOS (device)
Device(s) affected
iPhone 15 Pro Max (iOS 26.5.2) — reproduces. Pixel 8 — does not reproduce (exact Android build not recorded at measurement time).
VisionCamera version
5.0.11. Not on the latest (5.2.0) because 5.2.0 is nitrogen-0.36.3-generated and our app is still onreact-native-nitro-modules0.35.x — that upgrade is scheduled separately.This is very unlikely to matter here:
ios/Utils/FrameCoordinateSystemConverter.swiftis byte-identical across v5.0.11, v5.1.0, v5.1.1 and v5.2.0 (md57e406d9d979e0490f06ca05f7e52ea70, 74 lines each), and no 5.x release note touches it. Happy to re-measure on 5.2.0 if you'd like it confirmed on the current release.React Native version
0.86.0at measurement time (since upgraded to 0.86.2).React Native architecture
New Architecture (Fabric / bridgeless)
Context
Found while mapping a Rubik's cube scan guide onto sampled frames in a shipping app. Our workaround validates the composed mapping with an isotropy check and falls back to deriving the geometry from the frame dimensions when it fails — no platform branch, it's measured at runtime. Android accepts the library transform; iOS rejects it at 3.159 and falls back. Glad to test a fix on device.