react-native-rectangle-doc-scanner 0.19.0 → 0.21.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.
@@ -43,26 +43,40 @@ const react_native_fast_opencv_1 = require("react-native-fast-opencv");
43
43
  const overlay_1 = require("./utils/overlay");
44
44
  const stability_1 = require("./utils/stability");
45
45
  const isConvexQuadrilateral = (points) => {
46
- if (points.length !== 4) {
47
- return false;
48
- }
49
- let previous = 0;
50
- for (let i = 0; i < 4; i++) {
51
- const p0 = points[i];
52
- const p1 = points[(i + 1) % 4];
53
- const p2 = points[(i + 2) % 4];
54
- const cross = (p1.x - p0.x) * (p2.y - p1.y) - (p1.y - p0.y) * (p2.x - p1.x);
55
- if (Math.abs(cross) < 1e-3) {
46
+ 'worklet';
47
+ try {
48
+ if (points.length !== 4) {
56
49
  return false;
57
50
  }
58
- if (i === 0) {
59
- previous = cross;
51
+ // Validate all points have valid x and y
52
+ for (const p of points) {
53
+ if (typeof p.x !== 'number' || typeof p.y !== 'number' ||
54
+ !isFinite(p.x) || !isFinite(p.y)) {
55
+ return false;
56
+ }
60
57
  }
61
- else if (previous * cross < 0) {
62
- return false;
58
+ let previous = 0;
59
+ for (let i = 0; i < 4; i++) {
60
+ const p0 = points[i];
61
+ const p1 = points[(i + 1) % 4];
62
+ const p2 = points[(i + 2) % 4];
63
+ const cross = (p1.x - p0.x) * (p2.y - p1.y) - (p1.y - p0.y) * (p2.x - p1.x);
64
+ // Relax the collinearity check - allow very small cross products
65
+ if (Math.abs(cross) < 0.1) {
66
+ return false;
67
+ }
68
+ if (i === 0) {
69
+ previous = cross;
70
+ }
71
+ else if (previous * cross < 0) {
72
+ return false;
73
+ }
63
74
  }
75
+ return true;
76
+ }
77
+ catch (err) {
78
+ return false;
64
79
  }
65
- return true;
66
80
  };
67
81
  const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, minStableFrames = 8, cameraProps, children, }) => {
68
82
  const device = (0, react_native_vision_camera_1.useCameraDevice)('back');
@@ -77,11 +91,28 @@ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, m
77
91
  (0, react_1.useEffect)(() => {
78
92
  requestPermission();
79
93
  }, [requestPermission]);
94
+ const lastQuadRef = (0, react_1.useRef)(null);
95
+ const noQuadFramesRef = (0, react_1.useRef)(0);
80
96
  const updateQuad = (0, react_native_worklets_core_1.useRunOnJS)((value) => {
81
97
  if (__DEV__) {
82
98
  console.log('[DocScanner] quad', value);
83
99
  }
84
- setQuad(value);
100
+ if (value) {
101
+ // Found a quad, reset counter and update
102
+ noQuadFramesRef.current = 0;
103
+ lastQuadRef.current = value;
104
+ setQuad(value);
105
+ }
106
+ else {
107
+ // No quad found, keep the last one for a few frames
108
+ noQuadFramesRef.current += 1;
109
+ // Keep the last quad for up to 3 frames
110
+ if (noQuadFramesRef.current > 3) {
111
+ lastQuadRef.current = null;
112
+ setQuad(null);
113
+ }
114
+ // Otherwise, keep showing the last quad
115
+ }
85
116
  }, []);
86
117
  const reportError = (0, react_native_worklets_core_1.useRunOnJS)((step, error) => {
87
118
  const message = error instanceof Error ? error.message : `${error}`;
@@ -39,8 +39,14 @@ const react_native_skia_1 = require("@shopify/react-native-skia");
39
39
  const Overlay = ({ quad, color = '#e7a649' }) => {
40
40
  const path = (0, react_1.useMemo)(() => {
41
41
  if (!quad) {
42
+ if (__DEV__) {
43
+ console.log('[Overlay] no quad');
44
+ }
42
45
  return null;
43
46
  }
47
+ if (__DEV__) {
48
+ console.log('[Overlay] drawing quad:', quad);
49
+ }
44
50
  const skPath = react_native_skia_1.Skia.Path.Make();
45
51
  skPath.moveTo(quad[0].x, quad[0].y);
46
52
  quad.slice(1).forEach((p) => skPath.lineTo(p.x, p.y));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-rectangle-doc-scanner",
3
- "version": "0.19.0",
3
+ "version": "0.21.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "repository": {
@@ -17,30 +17,44 @@ import { checkStability } from './utils/stability';
17
17
  import type { Point } from './types';
18
18
 
19
19
  const isConvexQuadrilateral = (points: Point[]) => {
20
- if (points.length !== 4) {
21
- return false;
22
- }
20
+ 'worklet';
21
+ try {
22
+ if (points.length !== 4) {
23
+ return false;
24
+ }
23
25
 
24
- let previous = 0;
26
+ // Validate all points have valid x and y
27
+ for (const p of points) {
28
+ if (typeof p.x !== 'number' || typeof p.y !== 'number' ||
29
+ !isFinite(p.x) || !isFinite(p.y)) {
30
+ return false;
31
+ }
32
+ }
25
33
 
26
- for (let i = 0; i < 4; i++) {
27
- const p0 = points[i];
28
- const p1 = points[(i + 1) % 4];
29
- const p2 = points[(i + 2) % 4];
30
- const cross = (p1.x - p0.x) * (p2.y - p1.y) - (p1.y - p0.y) * (p2.x - p1.x);
34
+ let previous = 0;
31
35
 
32
- if (Math.abs(cross) < 1e-3) {
33
- return false;
34
- }
36
+ for (let i = 0; i < 4; i++) {
37
+ const p0 = points[i];
38
+ const p1 = points[(i + 1) % 4];
39
+ const p2 = points[(i + 2) % 4];
40
+ const cross = (p1.x - p0.x) * (p2.y - p1.y) - (p1.y - p0.y) * (p2.x - p1.x);
35
41
 
36
- if (i === 0) {
37
- previous = cross;
38
- } else if (previous * cross < 0) {
39
- return false;
42
+ // Relax the collinearity check - allow very small cross products
43
+ if (Math.abs(cross) < 0.1) {
44
+ return false;
45
+ }
46
+
47
+ if (i === 0) {
48
+ previous = cross;
49
+ } else if (previous * cross < 0) {
50
+ return false;
51
+ }
40
52
  }
41
- }
42
53
 
43
- return true;
54
+ return true;
55
+ } catch (err) {
56
+ return false;
57
+ }
44
58
  };
45
59
 
46
60
  type CameraRef = {
@@ -82,11 +96,30 @@ export const DocScanner: React.FC<Props> = ({
82
96
  requestPermission();
83
97
  }, [requestPermission]);
84
98
 
99
+ const lastQuadRef = useRef<Point[] | null>(null);
100
+ const noQuadFramesRef = useRef(0);
101
+
85
102
  const updateQuad = useRunOnJS((value: Point[] | null) => {
86
103
  if (__DEV__) {
87
104
  console.log('[DocScanner] quad', value);
88
105
  }
89
- setQuad(value);
106
+
107
+ if (value) {
108
+ // Found a quad, reset counter and update
109
+ noQuadFramesRef.current = 0;
110
+ lastQuadRef.current = value;
111
+ setQuad(value);
112
+ } else {
113
+ // No quad found, keep the last one for a few frames
114
+ noQuadFramesRef.current += 1;
115
+
116
+ // Keep the last quad for up to 3 frames
117
+ if (noQuadFramesRef.current > 3) {
118
+ lastQuadRef.current = null;
119
+ setQuad(null);
120
+ }
121
+ // Otherwise, keep showing the last quad
122
+ }
90
123
  }, []);
91
124
 
92
125
  const reportError = useRunOnJS((step: string, error: unknown) => {
@@ -10,9 +10,16 @@ type OverlayProps = {
10
10
  export const Overlay: React.FC<OverlayProps> = ({ quad, color = '#e7a649' }) => {
11
11
  const path = useMemo(() => {
12
12
  if (!quad) {
13
+ if (__DEV__) {
14
+ console.log('[Overlay] no quad');
15
+ }
13
16
  return null;
14
17
  }
15
18
 
19
+ if (__DEV__) {
20
+ console.log('[Overlay] drawing quad:', quad);
21
+ }
22
+
16
23
  const skPath = Skia.Path.Make();
17
24
  skPath.moveTo(quad[0].x, quad[0].y);
18
25
  quad.slice(1).forEach((p) => skPath.lineTo(p.x, p.y));