react-native-expo-cropper 1.2.36 → 1.2.37
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/babel.config.js +6 -0
- package/dist/CustomCamera.js +343 -245
- package/dist/ImageCropper.js +1620 -513
- package/dist/ImageCropperStyles.js +217 -217
- package/dist/ImageMaskProcessor.js +177 -0
- package/dist/ImageProcessor.js +40 -52
- package/package.json +5 -5
- package/src/CustomCamera.js +93 -18
- package/src/ImageCropper.js +861 -58
package/dist/ImageCropper.js
CHANGED
|
@@ -1,514 +1,1621 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
4
|
-
Object.defineProperty(exports, "__esModule", {
|
|
5
|
-
value: true
|
|
6
|
-
});
|
|
7
|
-
exports["default"] = void 0;
|
|
8
|
-
var _ImageCropperStyles = _interopRequireDefault(require("./ImageCropperStyles"));
|
|
9
|
-
var _react = _interopRequireWildcard(require("react"));
|
|
10
|
-
var _reactNative = require("react-native");
|
|
11
|
-
var _reactNativeSvg = _interopRequireWildcard(require("react-native-svg"));
|
|
12
|
-
var _reactNativeViewShot = require("react-native-view-shot");
|
|
13
|
-
var _CustomCamera = _interopRequireDefault(require("./CustomCamera"));
|
|
14
|
-
var
|
|
15
|
-
var
|
|
16
|
-
var _vectorIcons = require("@expo/vector-icons");
|
|
17
|
-
var _reactNativeSafeAreaContext = require("react-native-safe-area-context");
|
|
18
|
-
|
|
19
|
-
function
|
|
20
|
-
function
|
|
21
|
-
function
|
|
22
|
-
function
|
|
23
|
-
function
|
|
24
|
-
function
|
|
25
|
-
function
|
|
26
|
-
function
|
|
27
|
-
function
|
|
28
|
-
function
|
|
29
|
-
function
|
|
30
|
-
function
|
|
31
|
-
function
|
|
32
|
-
function
|
|
33
|
-
function
|
|
34
|
-
function
|
|
35
|
-
function
|
|
36
|
-
function
|
|
37
|
-
function
|
|
38
|
-
function
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
var
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
var
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
var
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
var
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports["default"] = void 0;
|
|
8
|
+
var _ImageCropperStyles = _interopRequireDefault(require("./ImageCropperStyles"));
|
|
9
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
10
|
+
var _reactNative = require("react-native");
|
|
11
|
+
var _reactNativeSvg = _interopRequireWildcard(require("react-native-svg"));
|
|
12
|
+
var _reactNativeViewShot = require("react-native-view-shot");
|
|
13
|
+
var _CustomCamera = _interopRequireDefault(require("./CustomCamera"));
|
|
14
|
+
var ImageManipulator = _interopRequireWildcard(require("expo-image-manipulator"));
|
|
15
|
+
var FileSystem = _interopRequireWildcard(require("expo-file-system"));
|
|
16
|
+
var _vectorIcons = require("@expo/vector-icons");
|
|
17
|
+
var _reactNativeSafeAreaContext = require("react-native-safe-area-context");
|
|
18
|
+
var _ImageMaskProcessor = require("./ImageMaskProcessor");
|
|
19
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t3 in e) "default" !== _t3 && {}.hasOwnProperty.call(e, _t3) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t3)) && (i.get || i.set) ? o(f, _t3, i) : f[_t3] = e[_t3]); return f; })(e, t); }
|
|
20
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
|
|
21
|
+
function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
|
|
22
|
+
function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
|
|
23
|
+
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
24
|
+
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
25
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
26
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
27
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
28
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
29
|
+
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
30
|
+
function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
|
|
31
|
+
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
32
|
+
function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
|
|
33
|
+
function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
|
|
34
|
+
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
35
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
36
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
37
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
38
|
+
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
39
|
+
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
40
|
+
var ImageCropper = function ImageCropper(_ref) {
|
|
41
|
+
var onConfirm = _ref.onConfirm,
|
|
42
|
+
openCameraFirst = _ref.openCameraFirst,
|
|
43
|
+
initialImage = _ref.initialImage,
|
|
44
|
+
addheight = _ref.addheight;
|
|
45
|
+
var _useState = (0, _react.useState)(null),
|
|
46
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
47
|
+
image = _useState2[0],
|
|
48
|
+
setImage = _useState2[1];
|
|
49
|
+
var _useState3 = (0, _react.useState)([]),
|
|
50
|
+
_useState4 = _slicedToArray(_useState3, 2),
|
|
51
|
+
points = _useState4[0],
|
|
52
|
+
setPoints = _useState4[1];
|
|
53
|
+
var _useState5 = (0, _react.useState)(false),
|
|
54
|
+
_useState6 = _slicedToArray(_useState5, 2),
|
|
55
|
+
showResult = _useState6[0],
|
|
56
|
+
setShowResult = _useState6[1];
|
|
57
|
+
var _useState7 = (0, _react.useState)(false),
|
|
58
|
+
_useState8 = _slicedToArray(_useState7, 2),
|
|
59
|
+
showCustomCamera = _useState8[0],
|
|
60
|
+
setShowCustomCamera = _useState8[1];
|
|
61
|
+
var viewRef = (0, _react.useRef)(null);
|
|
62
|
+
var maskViewRef = (0, _react.useRef)(null); // Ref pour la vue de masque (invisible)
|
|
63
|
+
var sourceImageUri = (0, _react.useRef)(null); // keep original image URI (full-res) for upload
|
|
64
|
+
var cameraFrameData = (0, _react.useRef)(null); // ✅ Store green frame coordinates from camera
|
|
65
|
+
|
|
66
|
+
// ✅ REFACTORISATION : Séparation claire entre dimensions originales et affichage
|
|
67
|
+
// Dimensions réelles de l'image originale (pixels)
|
|
68
|
+
var originalImageDimensions = (0, _react.useRef)({
|
|
69
|
+
width: 0,
|
|
70
|
+
height: 0
|
|
71
|
+
});
|
|
72
|
+
// Dimensions et position d'affichage à l'écran (pour le calcul des points de crop)
|
|
73
|
+
var displayedImageLayout = (0, _react.useRef)({
|
|
74
|
+
x: 0,
|
|
75
|
+
y: 0,
|
|
76
|
+
width: 0,
|
|
77
|
+
height: 0
|
|
78
|
+
});
|
|
79
|
+
// Conserver imageMeasure pour compatibilité avec le code existant (utilisé pour SVG overlay)
|
|
80
|
+
var imageMeasure = (0, _react.useRef)({
|
|
81
|
+
x: 0,
|
|
82
|
+
y: 0,
|
|
83
|
+
width: 0,
|
|
84
|
+
height: 0
|
|
85
|
+
});
|
|
86
|
+
// Rectangle réel de l'image affichée (quand resizeMode='contain') à l'intérieur du conteneur
|
|
87
|
+
// Sert à rendre la conversion coordonnées écran -> pixels image réellement pixel-perfect.
|
|
88
|
+
var displayedContentRect = (0, _react.useRef)({
|
|
89
|
+
x: 0,
|
|
90
|
+
y: 0,
|
|
91
|
+
width: 0,
|
|
92
|
+
height: 0
|
|
93
|
+
});
|
|
94
|
+
var updateDisplayedContentRect = function updateDisplayedContentRect(layoutWidth, layoutHeight) {
|
|
95
|
+
var iw = originalImageDimensions.current.width;
|
|
96
|
+
var ih = originalImageDimensions.current.height;
|
|
97
|
+
console.log("🔄 updateDisplayedContentRect called:", {
|
|
98
|
+
originalDimensions: {
|
|
99
|
+
width: iw,
|
|
100
|
+
height: ih
|
|
101
|
+
},
|
|
102
|
+
layoutDimensions: {
|
|
103
|
+
width: layoutWidth,
|
|
104
|
+
height: layoutHeight
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
if (iw > 0 && ih > 0 && layoutWidth > 0 && layoutHeight > 0) {
|
|
108
|
+
var scale = Math.min(layoutWidth / iw, layoutHeight / ih);
|
|
109
|
+
var contentW = iw * scale;
|
|
110
|
+
var contentH = ih * scale;
|
|
111
|
+
var offsetX = (layoutWidth - contentW) / 2;
|
|
112
|
+
var offsetY = (layoutHeight - contentH) / 2;
|
|
113
|
+
displayedContentRect.current = {
|
|
114
|
+
x: offsetX,
|
|
115
|
+
y: offsetY,
|
|
116
|
+
width: contentW,
|
|
117
|
+
height: contentH
|
|
118
|
+
};
|
|
119
|
+
console.log("✅ Displayed content rect (contain) calculated:", displayedContentRect.current);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ✅ FALLBACK: If original dimensions not available yet, use layout as temporary measure
|
|
124
|
+
if (layoutWidth > 0 && layoutHeight > 0) {
|
|
125
|
+
displayedContentRect.current = {
|
|
126
|
+
x: 0,
|
|
127
|
+
y: 0,
|
|
128
|
+
width: layoutWidth,
|
|
129
|
+
height: layoutHeight
|
|
130
|
+
};
|
|
131
|
+
console.log("⚠️ Using layout dimensions as fallback (original dimensions not available yet):", displayedContentRect.current);
|
|
132
|
+
} else {
|
|
133
|
+
displayedContentRect.current = {
|
|
134
|
+
x: 0,
|
|
135
|
+
y: 0,
|
|
136
|
+
width: 0,
|
|
137
|
+
height: 0
|
|
138
|
+
};
|
|
139
|
+
console.warn("❌ Cannot calculate displayedContentRect: missing dimensions");
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
var selectedPointIndex = (0, _react.useRef)(null);
|
|
143
|
+
var lastTap = (0, _react.useRef)(null);
|
|
144
|
+
|
|
145
|
+
// ✅ FREE DRAG: Store initial touch position and point position for delta-based movement
|
|
146
|
+
var initialTouchPosition = (0, _react.useRef)(null); // { x, y } - initial touch position when drag starts
|
|
147
|
+
var initialPointPosition = (0, _react.useRef)(null); // { x, y } - initial point position when drag starts
|
|
148
|
+
var lastTouchPosition = (0, _react.useRef)(null); // { x, y } - last touch position for incremental delta calculation
|
|
149
|
+
|
|
150
|
+
// Angle de rotation accumulé (pour éviter les rotations multiples)
|
|
151
|
+
var rotationAngle = (0, _react.useRef)(0);
|
|
152
|
+
|
|
153
|
+
// États pour la vue de masque temporaire
|
|
154
|
+
var _useState9 = (0, _react.useState)(null),
|
|
155
|
+
_useState0 = _slicedToArray(_useState9, 2),
|
|
156
|
+
maskImageUri = _useState0[0],
|
|
157
|
+
setMaskImageUri = _useState0[1];
|
|
158
|
+
var _useState1 = (0, _react.useState)([]),
|
|
159
|
+
_useState10 = _slicedToArray(_useState1, 2),
|
|
160
|
+
maskPoints = _useState10[0],
|
|
161
|
+
setMaskPoints = _useState10[1];
|
|
162
|
+
var _useState11 = (0, _react.useState)({
|
|
163
|
+
width: 0,
|
|
164
|
+
height: 0
|
|
165
|
+
}),
|
|
166
|
+
_useState12 = _slicedToArray(_useState11, 2),
|
|
167
|
+
maskDimensions = _useState12[0],
|
|
168
|
+
setMaskDimensions = _useState12[1];
|
|
169
|
+
var _useState13 = (0, _react.useState)(false),
|
|
170
|
+
_useState14 = _slicedToArray(_useState13, 2),
|
|
171
|
+
showMaskView = _useState14[0],
|
|
172
|
+
setShowMaskView = _useState14[1];
|
|
173
|
+
var _useState15 = (0, _react.useState)(false),
|
|
174
|
+
_useState16 = _slicedToArray(_useState15, 2),
|
|
175
|
+
isLoading = _useState16[0],
|
|
176
|
+
setIsLoading = _useState16[1];
|
|
177
|
+
var _useState17 = (0, _react.useState)(false),
|
|
178
|
+
_useState18 = _slicedToArray(_useState17, 2),
|
|
179
|
+
showFullScreenCapture = _useState18[0],
|
|
180
|
+
setShowFullScreenCapture = _useState18[1];
|
|
181
|
+
var _useState19 = (0, _react.useState)(false),
|
|
182
|
+
_useState20 = _slicedToArray(_useState19, 2),
|
|
183
|
+
isRotating = _useState20[0],
|
|
184
|
+
setIsRotating = _useState20[1];
|
|
185
|
+
var lastValidPosition = (0, _react.useRef)(null);
|
|
186
|
+
var insets = (0, _reactNativeSafeAreaContext.useSafeAreaInsets)();
|
|
187
|
+
|
|
188
|
+
// ✅ NEW ARCH: mobile does NOT export the final crop.
|
|
189
|
+
// We only compute crop metadata (bbox + polygon) and upload the ORIGINAL image to backend Python.
|
|
190
|
+
// No view-shot / captureRef / bitmap masking on device.
|
|
191
|
+
var enableMask = false;
|
|
192
|
+
var enableRotation = false; // rotation would require careful coord transforms; keep off for pixel-perfect pipeline.
|
|
193
|
+
|
|
194
|
+
(0, _react.useEffect)(function () {
|
|
195
|
+
if (openCameraFirst) {
|
|
196
|
+
setShowCustomCamera(true);
|
|
197
|
+
} else if (initialImage) {
|
|
198
|
+
setImage(initialImage);
|
|
199
|
+
sourceImageUri.current = initialImage;
|
|
200
|
+
// ✅ CRITICAL: Reset points when loading a new image from gallery
|
|
201
|
+
// This ensures the crop box will be automatically initialized
|
|
202
|
+
setPoints([]);
|
|
203
|
+
rotationAngle.current = 0;
|
|
204
|
+
// Clear camera frame data for gallery images
|
|
205
|
+
cameraFrameData.current = null;
|
|
206
|
+
}
|
|
207
|
+
}, [openCameraFirst, initialImage]);
|
|
208
|
+
|
|
209
|
+
// ✅ REFACTORISATION : Stocker uniquement les dimensions originales (pas de calcul théorique)
|
|
210
|
+
// ✅ CRITICAL FIX: Get dimensions and account for potential scale factor differences
|
|
211
|
+
// On Android, Image.getSize() returns physical pixels, but the image sent to backend
|
|
212
|
+
// might be at a different scale. We'll send original dimensions to backend for adjustment.
|
|
213
|
+
(0, _react.useEffect)(function () {
|
|
214
|
+
if (!image) {
|
|
215
|
+
originalImageDimensions.current = {
|
|
216
|
+
width: 0,
|
|
217
|
+
height: 0
|
|
218
|
+
};
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (!sourceImageUri.current) {
|
|
222
|
+
sourceImageUri.current = image;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ✅ CRITICAL FIX: If we have capturedImageSize from camera, use it as source of truth
|
|
226
|
+
// takePictureAsync returns physical dimensions, while Image.getSize() may return EXIF-oriented dimensions
|
|
227
|
+
if (cameraFrameData.current && cameraFrameData.current.capturedImageSize) {
|
|
228
|
+
var _cameraFrameData$curr = cameraFrameData.current.capturedImageSize,
|
|
229
|
+
capturedWidth = _cameraFrameData$curr.width,
|
|
230
|
+
capturedHeight = _cameraFrameData$curr.height;
|
|
231
|
+
originalImageDimensions.current = {
|
|
232
|
+
width: capturedWidth,
|
|
233
|
+
height: capturedHeight
|
|
234
|
+
};
|
|
235
|
+
console.log("✅ Using captured image dimensions from takePictureAsync:", {
|
|
236
|
+
width: capturedWidth,
|
|
237
|
+
height: capturedHeight,
|
|
238
|
+
source: 'takePictureAsync'
|
|
239
|
+
});
|
|
240
|
+
// ✅ CRITICAL: Use displayedImageLayout dimensions if available, otherwise wait for onImageLayout
|
|
241
|
+
var lw = displayedImageLayout.current.width;
|
|
242
|
+
var lh = displayedImageLayout.current.height;
|
|
243
|
+
if (lw > 0 && lh > 0) {
|
|
244
|
+
updateDisplayedContentRect(lw, lh);
|
|
245
|
+
// ✅ CRITICAL: Initialize crop box when we have camera frame data and image dimensions
|
|
246
|
+
if (points.length === 0) {
|
|
247
|
+
initializeCropBox();
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// ✅ FALLBACK: Use Image.getSize() if no captured dimensions available (e.g., from gallery)
|
|
254
|
+
_reactNative.Image.getSize(image, function (imgWidth, imgHeight) {
|
|
255
|
+
originalImageDimensions.current = {
|
|
256
|
+
width: imgWidth,
|
|
257
|
+
height: imgHeight
|
|
258
|
+
};
|
|
259
|
+
console.log("✅ Image dimensions from Image.getSize():", {
|
|
260
|
+
width: imgWidth,
|
|
261
|
+
height: imgHeight,
|
|
262
|
+
platform: _reactNative.Platform.OS,
|
|
263
|
+
pixelRatio: _reactNative.PixelRatio.get(),
|
|
264
|
+
uri: image,
|
|
265
|
+
source: 'Image.getSize()'
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// ✅ IMPORTANT: onImageLayout peut se déclencher avant Image.getSize (race condition).
|
|
269
|
+
// Recalculer le contentRect dès qu'on connaît la taille originale, sinon les coords seront décalées.
|
|
270
|
+
var lw = displayedImageLayout.current.width;
|
|
271
|
+
var lh = displayedImageLayout.current.height;
|
|
272
|
+
if (lw > 0 && lh > 0) {
|
|
273
|
+
updateDisplayedContentRect(lw, lh);
|
|
274
|
+
// ✅ CRITICAL: Initialize crop box when we have image dimensions
|
|
275
|
+
// - If we have camera frame data, use it to match green frame exactly
|
|
276
|
+
// - If no camera frame data (gallery image), initialize with 70% default box
|
|
277
|
+
if (points.length === 0) {
|
|
278
|
+
initializeCropBox();
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}, function (error) {
|
|
282
|
+
console.error("Error getting image size:", error);
|
|
283
|
+
});
|
|
284
|
+
}, [image]);
|
|
285
|
+
|
|
286
|
+
// ✅ CRITICAL FIX: Convert green frame coordinates (camera preview) to captured image coordinates
|
|
287
|
+
var convertGreenFrameToImageCoords = function convertGreenFrameToImageCoords(greenFrame, capturedImageSize, displayedImageRect) {
|
|
288
|
+
if (!greenFrame || !capturedImageSize) {
|
|
289
|
+
console.warn("Cannot convert green frame: missing data");
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
var frameX = greenFrame.x,
|
|
293
|
+
frameY = greenFrame.y,
|
|
294
|
+
frameWidth = greenFrame.width,
|
|
295
|
+
frameHeight = greenFrame.height,
|
|
296
|
+
wrapperWidth = greenFrame.wrapperWidth,
|
|
297
|
+
wrapperHeight = greenFrame.wrapperHeight;
|
|
298
|
+
var imgWidth = capturedImageSize.width,
|
|
299
|
+
imgHeight = capturedImageSize.height;
|
|
300
|
+
var displayX = displayedImageRect.x,
|
|
301
|
+
displayY = displayedImageRect.y,
|
|
302
|
+
displayWidth = displayedImageRect.width,
|
|
303
|
+
displayHeight = displayedImageRect.height;
|
|
304
|
+
console.log("🔄 Converting green frame:", {
|
|
305
|
+
greenFrame: {
|
|
306
|
+
frameX: frameX,
|
|
307
|
+
frameY: frameY,
|
|
308
|
+
frameWidth: frameWidth,
|
|
309
|
+
frameHeight: frameHeight,
|
|
310
|
+
wrapperWidth: wrapperWidth,
|
|
311
|
+
wrapperHeight: wrapperHeight
|
|
312
|
+
},
|
|
313
|
+
capturedImageSize: {
|
|
314
|
+
imgWidth: imgWidth,
|
|
315
|
+
imgHeight: imgHeight
|
|
316
|
+
},
|
|
317
|
+
displayedImageRect: {
|
|
318
|
+
displayX: displayX,
|
|
319
|
+
displayY: displayY,
|
|
320
|
+
displayWidth: displayWidth,
|
|
321
|
+
displayHeight: displayHeight
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
// ✅ SIMPLIFIED APPROACH: Assume CameraView fills the wrapper completely (no letterboxing in preview)
|
|
326
|
+
// The green frame is drawn as a percentage of the wrapper (95% width, 80% height)
|
|
327
|
+
// We need to map this directly to the captured image, accounting for aspect ratio differences
|
|
328
|
+
|
|
329
|
+
var previewAspect = wrapperWidth / wrapperHeight;
|
|
330
|
+
var capturedAspect = imgWidth / imgHeight;
|
|
331
|
+
console.log("📐 Aspect ratios:", {
|
|
332
|
+
previewAspect: previewAspect.toFixed(3),
|
|
333
|
+
capturedAspect: capturedAspect.toFixed(3),
|
|
334
|
+
wrapperSize: {
|
|
335
|
+
wrapperWidth: wrapperWidth,
|
|
336
|
+
wrapperHeight: wrapperHeight
|
|
337
|
+
},
|
|
338
|
+
capturedSize: {
|
|
339
|
+
imgWidth: imgWidth,
|
|
340
|
+
imgHeight: imgHeight
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// ✅ KEY INSIGHT: The green frame is 95% of wrapper width and 80% of wrapper height
|
|
345
|
+
// If the captured image has a different aspect ratio, we need to map proportionally
|
|
346
|
+
// The green frame represents a region in the preview, which should map to the same region in the image
|
|
347
|
+
|
|
348
|
+
// Calculate green frame as percentage of wrapper
|
|
349
|
+
var greenFramePercentX = frameX / wrapperWidth;
|
|
350
|
+
var greenFramePercentY = frameY / wrapperHeight;
|
|
351
|
+
var greenFramePercentWidth = frameWidth / wrapperWidth;
|
|
352
|
+
var greenFramePercentHeight = frameHeight / wrapperHeight;
|
|
353
|
+
console.log("📊 Green frame as percentage of wrapper:", {
|
|
354
|
+
x: (greenFramePercentX * 100).toFixed(2) + '%',
|
|
355
|
+
y: (greenFramePercentY * 100).toFixed(2) + '%',
|
|
356
|
+
width: (greenFramePercentWidth * 100).toFixed(2) + '%',
|
|
357
|
+
height: (greenFramePercentHeight * 100).toFixed(2) + '%'
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// ✅ DIRECT MAPPING: Map green frame percentage directly to captured image
|
|
361
|
+
// The green frame covers a certain percentage of the preview, which should map to the same percentage of the image
|
|
362
|
+
// However, we need to account for aspect ratio differences
|
|
363
|
+
|
|
364
|
+
// If preview and captured have same aspect ratio, direct mapping works
|
|
365
|
+
// If different, we need to account for letterboxing in the preview
|
|
366
|
+
|
|
367
|
+
// Calculate how the captured image would be displayed in the preview (aspect-fit)
|
|
368
|
+
var previewContentWidth, previewContentHeight, previewOffsetX, previewOffsetY;
|
|
369
|
+
if (Math.abs(capturedAspect - previewAspect) < 0.01) {
|
|
370
|
+
// Same aspect ratio → no letterboxing, direct mapping
|
|
371
|
+
previewContentWidth = wrapperWidth;
|
|
372
|
+
previewContentHeight = wrapperHeight;
|
|
373
|
+
previewOffsetX = 0;
|
|
374
|
+
previewOffsetY = 0;
|
|
375
|
+
} else if (capturedAspect > previewAspect) {
|
|
376
|
+
// Image is wider → fills width, letterboxing on top/bottom
|
|
377
|
+
previewContentWidth = wrapperWidth;
|
|
378
|
+
previewContentHeight = wrapperWidth / capturedAspect;
|
|
379
|
+
previewOffsetX = 0;
|
|
380
|
+
previewOffsetY = (wrapperHeight - previewContentHeight) / 2;
|
|
381
|
+
} else {
|
|
382
|
+
// Image is taller → fills height, letterboxing on left/right
|
|
383
|
+
previewContentHeight = wrapperHeight;
|
|
384
|
+
previewContentWidth = wrapperHeight * capturedAspect;
|
|
385
|
+
previewOffsetX = (wrapperWidth - previewContentWidth) / 2;
|
|
386
|
+
previewOffsetY = 0;
|
|
387
|
+
}
|
|
388
|
+
console.log("📐 Preview content area (actual image area in preview):", {
|
|
389
|
+
previewContentWidth: previewContentWidth.toFixed(2),
|
|
390
|
+
previewContentHeight: previewContentHeight.toFixed(2),
|
|
391
|
+
previewOffsetX: previewOffsetX.toFixed(2),
|
|
392
|
+
previewOffsetY: previewOffsetY.toFixed(2)
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// Step 3: Convert green frame coordinates from wrapper space to preview content space
|
|
396
|
+
// ✅ CRITICAL FIX: The green frame is drawn on the wrapper, but we need to map it to the actual image area
|
|
397
|
+
// If the green frame overlaps letterboxing areas, we need to clip it to the actual image content area
|
|
398
|
+
|
|
399
|
+
// Calculate green frame bounds in wrapper coordinates
|
|
400
|
+
var frameLeft = frameX;
|
|
401
|
+
var frameTop = frameY;
|
|
402
|
+
var frameRight = frameX + frameWidth;
|
|
403
|
+
var frameBottom = frameY + frameHeight;
|
|
404
|
+
|
|
405
|
+
// Calculate preview content bounds in wrapper coordinates
|
|
406
|
+
var contentLeft = previewOffsetX;
|
|
407
|
+
var contentTop = previewOffsetY;
|
|
408
|
+
var contentRight = previewOffsetX + previewContentWidth;
|
|
409
|
+
var contentBottom = previewOffsetY + previewContentHeight;
|
|
410
|
+
|
|
411
|
+
// ✅ KEY INSIGHT: The green frame should map to the same percentage of the image content area
|
|
412
|
+
// But we need to account for letterboxing - the green frame might extend into letterboxing areas
|
|
413
|
+
|
|
414
|
+
// ✅ Clip green frame to preview content area (intersection)
|
|
415
|
+
var clippedLeft = Math.max(frameLeft, contentLeft);
|
|
416
|
+
var clippedTop = Math.max(frameTop, contentTop);
|
|
417
|
+
var clippedRight = Math.min(frameRight, contentRight);
|
|
418
|
+
var clippedBottom = Math.min(frameBottom, contentBottom);
|
|
419
|
+
|
|
420
|
+
// Calculate clipped green frame dimensions
|
|
421
|
+
var clippedWidth = Math.max(0, clippedRight - clippedLeft);
|
|
422
|
+
var clippedHeight = Math.max(0, clippedBottom - clippedTop);
|
|
423
|
+
|
|
424
|
+
// If green frame is completely outside content area, return null
|
|
425
|
+
if (clippedWidth <= 0 || clippedHeight <= 0) {
|
|
426
|
+
console.error("❌ Green frame is completely outside preview content area!");
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// ✅ ALTERNATIVE APPROACH: Map green frame as percentage of image content area
|
|
431
|
+
// The green frame covers a certain percentage of the wrapper, but we want it to cover
|
|
432
|
+
// the same visual percentage of the image content area
|
|
433
|
+
|
|
434
|
+
// Calculate green frame center and size as percentage of wrapper
|
|
435
|
+
var greenFrameCenterX = (frameLeft + frameRight) / 2;
|
|
436
|
+
var greenFrameCenterY = (frameTop + frameBottom) / 2;
|
|
437
|
+
var greenFrameCenterPercentX = greenFrameCenterX / wrapperWidth;
|
|
438
|
+
var greenFrameCenterPercentY = greenFrameCenterY / wrapperHeight;
|
|
439
|
+
|
|
440
|
+
// Map center to image content area
|
|
441
|
+
var imageContentCenterX = previewOffsetX + previewContentWidth * greenFrameCenterPercentX;
|
|
442
|
+
var imageContentCenterY = previewOffsetY + previewContentHeight * greenFrameCenterPercentY;
|
|
443
|
+
|
|
444
|
+
// Calculate green frame size as percentage of image content area (not wrapper)
|
|
445
|
+
// The green frame should cover the same visual percentage of the image as it does of the wrapper
|
|
446
|
+
var imageContentFrameWidth = previewContentWidth * greenFramePercentWidth;
|
|
447
|
+
var imageContentFrameHeight = previewContentHeight * greenFramePercentHeight;
|
|
448
|
+
|
|
449
|
+
// Calculate final green frame in image content coordinates
|
|
450
|
+
var finalFrameX = imageContentCenterX - imageContentFrameWidth / 2;
|
|
451
|
+
var finalFrameY = imageContentCenterY - imageContentFrameHeight / 2;
|
|
452
|
+
var finalFrameWidth = imageContentFrameWidth;
|
|
453
|
+
var finalFrameHeight = imageContentFrameHeight;
|
|
454
|
+
|
|
455
|
+
// Clamp to image content bounds
|
|
456
|
+
var clampedFinalX = Math.max(previewOffsetX, Math.min(finalFrameX, previewOffsetX + previewContentWidth - finalFrameWidth));
|
|
457
|
+
var clampedFinalY = Math.max(previewOffsetY, Math.min(finalFrameY, previewOffsetY + previewContentHeight - finalFrameHeight));
|
|
458
|
+
var clampedFinalWidth = Math.min(finalFrameWidth, previewOffsetX + previewContentWidth - clampedFinalX);
|
|
459
|
+
var clampedFinalHeight = Math.min(finalFrameHeight, previewOffsetY + previewContentHeight - clampedFinalY);
|
|
460
|
+
|
|
461
|
+
// Convert to relative coordinates within preview content area
|
|
462
|
+
var relativeX = clampedFinalX - previewOffsetX;
|
|
463
|
+
var relativeY = clampedFinalY - previewOffsetY;
|
|
464
|
+
|
|
465
|
+
// Normalize to 0-1 range within the preview content area (actual image area)
|
|
466
|
+
var normalizedX = relativeX / previewContentWidth;
|
|
467
|
+
var normalizedY = relativeY / previewContentHeight;
|
|
468
|
+
var normalizedWidth = clampedFinalWidth / previewContentWidth;
|
|
469
|
+
var normalizedHeight = clampedFinalHeight / previewContentHeight;
|
|
470
|
+
console.log("✂️ Green frame mapping (percentage-based):", {
|
|
471
|
+
originalFrame: {
|
|
472
|
+
frameX: frameX,
|
|
473
|
+
frameY: frameY,
|
|
474
|
+
frameWidth: frameWidth,
|
|
475
|
+
frameHeight: frameHeight
|
|
476
|
+
},
|
|
477
|
+
greenFramePercentages: {
|
|
478
|
+
centerX: (greenFrameCenterPercentX * 100).toFixed(2) + '%',
|
|
479
|
+
centerY: (greenFrameCenterPercentY * 100).toFixed(2) + '%',
|
|
480
|
+
width: (greenFramePercentWidth * 100).toFixed(2) + '%',
|
|
481
|
+
height: (greenFramePercentHeight * 100).toFixed(2) + '%'
|
|
482
|
+
},
|
|
483
|
+
previewContent: {
|
|
484
|
+
previewOffsetX: previewOffsetX,
|
|
485
|
+
previewOffsetY: previewOffsetY,
|
|
486
|
+
previewContentWidth: previewContentWidth,
|
|
487
|
+
previewContentHeight: previewContentHeight
|
|
488
|
+
},
|
|
489
|
+
mappedFrame: {
|
|
490
|
+
finalX: finalFrameX.toFixed(2),
|
|
491
|
+
finalY: finalFrameY.toFixed(2),
|
|
492
|
+
finalWidth: finalFrameWidth.toFixed(2),
|
|
493
|
+
finalHeight: finalFrameHeight.toFixed(2)
|
|
494
|
+
},
|
|
495
|
+
clampedFrame: {
|
|
496
|
+
x: clampedFinalX.toFixed(2),
|
|
497
|
+
y: clampedFinalY.toFixed(2),
|
|
498
|
+
width: clampedFinalWidth.toFixed(2),
|
|
499
|
+
height: clampedFinalHeight.toFixed(2)
|
|
500
|
+
},
|
|
501
|
+
normalized: {
|
|
502
|
+
normalizedX: normalizedX.toFixed(4),
|
|
503
|
+
normalizedY: normalizedY.toFixed(4),
|
|
504
|
+
normalizedWidth: normalizedWidth.toFixed(4),
|
|
505
|
+
normalizedHeight: normalizedHeight.toFixed(4)
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
console.log("📊 Normalized coordinates:", {
|
|
509
|
+
relativeX: relativeX,
|
|
510
|
+
relativeY: relativeY,
|
|
511
|
+
normalizedX: normalizedX,
|
|
512
|
+
normalizedY: normalizedY,
|
|
513
|
+
normalizedWidth: normalizedWidth,
|
|
514
|
+
normalizedHeight: normalizedHeight
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
// Step 4: Convert normalized coordinates to captured image pixel coordinates
|
|
518
|
+
var imageX = normalizedX * imgWidth;
|
|
519
|
+
var imageY = normalizedY * imgHeight;
|
|
520
|
+
var imageWidth = normalizedWidth * imgWidth;
|
|
521
|
+
var imageHeight = normalizedHeight * imgHeight;
|
|
522
|
+
console.log("🖼️ Image pixel coordinates:", {
|
|
523
|
+
imageX: imageX,
|
|
524
|
+
imageY: imageY,
|
|
525
|
+
imageWidth: imageWidth,
|
|
526
|
+
imageHeight: imageHeight,
|
|
527
|
+
imgWidth: imgWidth,
|
|
528
|
+
imgHeight: imgHeight
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
// Step 5: Convert to displayed image coordinates (for white bounding box)
|
|
532
|
+
// The captured image is displayed with resizeMode='contain' in ImageCropper
|
|
533
|
+
var displayAspect = displayWidth / displayHeight;
|
|
534
|
+
var displayContentWidth, displayContentHeight, displayOffsetX, displayOffsetY;
|
|
535
|
+
if (capturedAspect > displayAspect) {
|
|
536
|
+
// Image is wider than display → letterboxing on top/bottom
|
|
537
|
+
displayContentWidth = displayWidth;
|
|
538
|
+
displayContentHeight = displayWidth / capturedAspect;
|
|
539
|
+
displayOffsetX = displayX;
|
|
540
|
+
displayOffsetY = displayY + (displayHeight - displayContentHeight) / 2;
|
|
541
|
+
} else {
|
|
542
|
+
// Image is taller than display → letterboxing on left/right
|
|
543
|
+
displayContentHeight = displayHeight;
|
|
544
|
+
displayContentWidth = displayHeight * capturedAspect;
|
|
545
|
+
displayOffsetX = displayX + (displayWidth - displayContentWidth) / 2;
|
|
546
|
+
displayOffsetY = displayY;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Convert image pixel coordinates to display coordinates
|
|
550
|
+
var scaleX = displayContentWidth / imgWidth;
|
|
551
|
+
var scaleY = displayContentHeight / imgHeight;
|
|
552
|
+
var displayBoxX = displayOffsetX + imageX * scaleX;
|
|
553
|
+
var displayBoxY = displayOffsetY + imageY * scaleY;
|
|
554
|
+
var displayBoxWidth = imageWidth * scaleX;
|
|
555
|
+
var displayBoxHeight = imageHeight * scaleY;
|
|
556
|
+
|
|
557
|
+
// Clamp to displayed image bounds
|
|
558
|
+
var clampedX = Math.max(displayOffsetX, Math.min(displayBoxX, displayOffsetX + displayContentWidth - displayBoxWidth));
|
|
559
|
+
var clampedY = Math.max(displayOffsetY, Math.min(displayBoxY, displayOffsetY + displayContentHeight - displayBoxHeight));
|
|
560
|
+
var clampedWidth = Math.min(displayBoxWidth, displayOffsetX + displayContentWidth - clampedX);
|
|
561
|
+
var clampedHeight = Math.min(displayBoxHeight, displayOffsetY + displayContentHeight - clampedY);
|
|
562
|
+
var result = {
|
|
563
|
+
// Display coordinates (for white bounding box)
|
|
564
|
+
displayCoords: {
|
|
565
|
+
x: clampedX,
|
|
566
|
+
y: clampedY,
|
|
567
|
+
width: clampedWidth,
|
|
568
|
+
height: clampedHeight
|
|
569
|
+
},
|
|
570
|
+
// Image pixel coordinates (for backend crop)
|
|
571
|
+
imageCoords: {
|
|
572
|
+
x: Math.max(0, Math.min(imageX, imgWidth - imageWidth)),
|
|
573
|
+
y: Math.max(0, Math.min(imageY, imgHeight - imageHeight)),
|
|
574
|
+
width: Math.max(0, Math.min(imageWidth, imgWidth)),
|
|
575
|
+
height: Math.max(0, Math.min(imageHeight, imgHeight))
|
|
576
|
+
},
|
|
577
|
+
debug: {
|
|
578
|
+
previewAspect: previewAspect,
|
|
579
|
+
capturedAspect: capturedAspect,
|
|
580
|
+
displayAspect: displayAspect,
|
|
581
|
+
previewContentWidth: previewContentWidth,
|
|
582
|
+
previewContentHeight: previewContentHeight,
|
|
583
|
+
previewOffsetX: previewOffsetX,
|
|
584
|
+
previewOffsetY: previewOffsetY,
|
|
585
|
+
displayContentWidth: displayContentWidth,
|
|
586
|
+
displayContentHeight: displayContentHeight,
|
|
587
|
+
displayOffsetX: displayOffsetX,
|
|
588
|
+
displayOffsetY: displayOffsetY,
|
|
589
|
+
scaleX: scaleX,
|
|
590
|
+
scaleY: scaleY
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
console.log("✅ Green frame converted to image coordinates:", JSON.stringify(result, null, 2));
|
|
594
|
+
|
|
595
|
+
// ✅ VALIDATION: Ensure the white bounding box matches the green frame visually
|
|
596
|
+
// Calculate what percentage of the image the green frame covers
|
|
597
|
+
var greenFramePercentOfImage = {
|
|
598
|
+
width: imageWidth / imgWidth * 100,
|
|
599
|
+
height: imageHeight / imgHeight * 100
|
|
600
|
+
};
|
|
601
|
+
var greenFramePercentOfPreview = {
|
|
602
|
+
width: frameWidth / wrapperWidth * 100,
|
|
603
|
+
height: frameHeight / wrapperHeight * 100
|
|
604
|
+
};
|
|
605
|
+
console.log("📏 Green frame coverage:", {
|
|
606
|
+
percentOfImage: greenFramePercentOfImage,
|
|
607
|
+
percentOfPreview: greenFramePercentOfPreview,
|
|
608
|
+
shouldMatch: "Green frame should cover same % of image as it does of preview"
|
|
609
|
+
});
|
|
610
|
+
return result;
|
|
611
|
+
};
|
|
612
|
+
|
|
613
|
+
// ✅ REFACTORISATION : Initialiser le crop box avec les dimensions d'affichage réelles
|
|
614
|
+
// ✅ CRITICAL FIX: If camera frame data exists, use it to match green frame exactly
|
|
615
|
+
var initializeCropBox = function initializeCropBox() {
|
|
616
|
+
// ✅ CRITICAL: Ensure displayedContentRect is available
|
|
617
|
+
var contentRect = displayedContentRect.current;
|
|
618
|
+
var layout = displayedImageLayout.current;
|
|
619
|
+
|
|
620
|
+
// Recalculate if not available
|
|
621
|
+
if (contentRect.width === 0 || contentRect.height === 0) {
|
|
622
|
+
if (layout.width > 0 && layout.height > 0) {
|
|
623
|
+
updateDisplayedContentRect(layout.width, layout.height);
|
|
624
|
+
contentRect = displayedContentRect.current;
|
|
625
|
+
}
|
|
626
|
+
// If still not available, use layout as fallback
|
|
627
|
+
if (contentRect.width === 0 || contentRect.height === 0) {
|
|
628
|
+
if (layout.width > 0 && layout.height > 0) {
|
|
629
|
+
contentRect = {
|
|
630
|
+
x: layout.x,
|
|
631
|
+
y: layout.y,
|
|
632
|
+
width: layout.width,
|
|
633
|
+
height: layout.height
|
|
634
|
+
};
|
|
635
|
+
} else {
|
|
636
|
+
console.warn("Cannot initialize crop box: displayed dimensions are zero");
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
var _contentRect = contentRect,
|
|
642
|
+
x = _contentRect.x,
|
|
643
|
+
y = _contentRect.y,
|
|
644
|
+
width = _contentRect.width,
|
|
645
|
+
height = _contentRect.height;
|
|
646
|
+
|
|
647
|
+
// ✅ PRIORITY: If we have green frame data from camera, use it to match exactly
|
|
648
|
+
if (cameraFrameData.current && cameraFrameData.current.greenFrame && originalImageDimensions.current.width > 0) {
|
|
649
|
+
var converted = convertGreenFrameToImageCoords(cameraFrameData.current.greenFrame, cameraFrameData.current.capturedImageSize || originalImageDimensions.current, contentRect);
|
|
650
|
+
if (converted && converted.displayCoords) {
|
|
651
|
+
var _converted$displayCoo = converted.displayCoords,
|
|
652
|
+
_boxX = _converted$displayCoo.x,
|
|
653
|
+
_boxY = _converted$displayCoo.y,
|
|
654
|
+
_boxWidth = _converted$displayCoo.width,
|
|
655
|
+
_boxHeight = _converted$displayCoo.height;
|
|
656
|
+
var _contentRect2 = contentRect,
|
|
657
|
+
contentX = _contentRect2.x,
|
|
658
|
+
contentY = _contentRect2.y,
|
|
659
|
+
contentWidth = _contentRect2.width,
|
|
660
|
+
contentHeight = _contentRect2.height;
|
|
661
|
+
|
|
662
|
+
// ✅ CRITICAL: Clamp points to displayed image bounds (double-check)
|
|
663
|
+
// Ensure the bounding box stays within contentRect, but preserve aspect ratio and percentage
|
|
664
|
+
// First, clamp position
|
|
665
|
+
var clampedBoxX = Math.max(contentX, Math.min(_boxX, contentX + contentWidth - _boxWidth));
|
|
666
|
+
var clampedBoxY = Math.max(contentY, Math.min(_boxY, contentY + contentHeight - _boxHeight));
|
|
667
|
+
|
|
668
|
+
// ✅ CRITICAL: Preserve the width and height from conversion (they should already be correct)
|
|
669
|
+
// Only adjust if they would exceed bounds, but try to maintain the 80% coverage
|
|
670
|
+
var clampedBoxWidth = _boxWidth;
|
|
671
|
+
var clampedBoxHeight = _boxHeight;
|
|
672
|
+
|
|
673
|
+
// Ensure the box fits within contentRect
|
|
674
|
+
if (clampedBoxX + clampedBoxWidth > contentX + contentWidth) {
|
|
675
|
+
clampedBoxWidth = contentX + contentWidth - clampedBoxX;
|
|
676
|
+
}
|
|
677
|
+
if (clampedBoxY + clampedBoxHeight > contentY + contentHeight) {
|
|
678
|
+
clampedBoxHeight = contentY + contentHeight - clampedBoxY;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// ✅ CRITICAL: If clamping reduced dimensions, adjust position to center the box
|
|
682
|
+
// This ensures the white bounding box maintains the same visual percentage as the green frame
|
|
683
|
+
if (clampedBoxWidth < _boxWidth || clampedBoxHeight < _boxHeight) {
|
|
684
|
+
// Re-center if possible
|
|
685
|
+
var idealX = contentX + (contentWidth - clampedBoxWidth) / 2;
|
|
686
|
+
var idealY = contentY + (contentHeight - clampedBoxHeight) / 2;
|
|
687
|
+
clampedBoxX = Math.max(contentX, Math.min(idealX, contentX + contentWidth - clampedBoxWidth));
|
|
688
|
+
clampedBoxY = Math.max(contentY, Math.min(idealY, contentY + contentHeight - clampedBoxHeight));
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// ✅ CRITICAL: Ensure points are within contentRect bounds but not exactly at the edges
|
|
692
|
+
// This allows free movement in all directions
|
|
693
|
+
var minX = contentX;
|
|
694
|
+
var maxX = contentX + contentWidth;
|
|
695
|
+
var minY = contentY;
|
|
696
|
+
var maxY = contentY + contentHeight;
|
|
697
|
+
|
|
698
|
+
// Create points from the clamped green frame coordinates
|
|
699
|
+
// Clamp each point individually to ensure they're within bounds
|
|
700
|
+
var _newPoints = [{
|
|
701
|
+
x: Math.max(minX, Math.min(clampedBoxX, maxX)),
|
|
702
|
+
y: Math.max(minY, Math.min(clampedBoxY, maxY))
|
|
703
|
+
}, {
|
|
704
|
+
x: Math.max(minX, Math.min(clampedBoxX + clampedBoxWidth, maxX)),
|
|
705
|
+
y: Math.max(minY, Math.min(clampedBoxY, maxY))
|
|
706
|
+
}, {
|
|
707
|
+
x: Math.max(minX, Math.min(clampedBoxX + clampedBoxWidth, maxX)),
|
|
708
|
+
y: Math.max(minY, Math.min(clampedBoxY + clampedBoxHeight, maxY))
|
|
709
|
+
}, {
|
|
710
|
+
x: Math.max(minX, Math.min(clampedBoxX, maxX)),
|
|
711
|
+
y: Math.max(minY, Math.min(clampedBoxY + clampedBoxHeight, maxY))
|
|
712
|
+
}];
|
|
713
|
+
|
|
714
|
+
// ✅ VALIDATION: Verify the white bounding box matches green frame percentage (80%)
|
|
715
|
+
var whiteBoxPercentOfDisplay = {
|
|
716
|
+
width: clampedBoxWidth / contentRect.width * 100,
|
|
717
|
+
height: clampedBoxHeight / contentRect.height * 100
|
|
718
|
+
};
|
|
719
|
+
var greenFramePercentOfWrapper = {
|
|
720
|
+
width: cameraFrameData.current.greenFrame.width / cameraFrameData.current.greenFrame.wrapperWidth * 100,
|
|
721
|
+
height: cameraFrameData.current.greenFrame.height / cameraFrameData.current.greenFrame.wrapperHeight * 100
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
// ✅ DEBUG: Log if percentages don't match (should both be ~80%)
|
|
725
|
+
if (Math.abs(whiteBoxPercentOfDisplay.width - greenFramePercentOfWrapper.width) > 5 || Math.abs(whiteBoxPercentOfDisplay.height - greenFramePercentOfWrapper.height) > 5) {
|
|
726
|
+
console.warn("⚠️ White box percentage doesn't match green frame:", {
|
|
727
|
+
whiteBox: whiteBoxPercentOfDisplay,
|
|
728
|
+
greenFrame: greenFramePercentOfWrapper,
|
|
729
|
+
difference: {
|
|
730
|
+
width: Math.abs(whiteBoxPercentOfDisplay.width - greenFramePercentOfWrapper.width),
|
|
731
|
+
height: Math.abs(whiteBoxPercentOfDisplay.height - greenFramePercentOfWrapper.height)
|
|
732
|
+
}
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
console.log("✅ Initializing crop box from green frame (clamped):", {
|
|
736
|
+
greenFrame: cameraFrameData.current.greenFrame,
|
|
737
|
+
converted: converted,
|
|
738
|
+
clamped: {
|
|
739
|
+
x: clampedBoxX,
|
|
740
|
+
y: clampedBoxY,
|
|
741
|
+
width: clampedBoxWidth,
|
|
742
|
+
height: clampedBoxHeight
|
|
743
|
+
},
|
|
744
|
+
contentRect: contentRect,
|
|
745
|
+
points: _newPoints,
|
|
746
|
+
validation: {
|
|
747
|
+
whiteBoxPercentOfDisplay: whiteBoxPercentOfDisplay,
|
|
748
|
+
greenFramePercentOfWrapper: greenFramePercentOfWrapper,
|
|
749
|
+
shouldMatch: "White box % should match green frame % (80% - accounting for aspect ratio)"
|
|
750
|
+
}
|
|
751
|
+
});
|
|
752
|
+
setPoints(_newPoints);
|
|
753
|
+
// Clear camera frame data after use to avoid reusing it
|
|
754
|
+
cameraFrameData.current = null;
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// ✅ CRITICAL: Default crop box (70% of displayed area - centered)
|
|
760
|
+
var boxWidth = width * 0.70; // 70% width
|
|
761
|
+
var boxHeight = height * 0.70; // 70% height
|
|
762
|
+
var boxX = x + (width - boxWidth) / 2; // Centered horizontally
|
|
763
|
+
var boxY = y + (height - boxHeight) / 2; // Centered vertically
|
|
764
|
+
var newPoints = [{
|
|
765
|
+
x: boxX,
|
|
766
|
+
y: boxY
|
|
767
|
+
},
|
|
768
|
+
// Top-left
|
|
769
|
+
{
|
|
770
|
+
x: boxX + boxWidth,
|
|
771
|
+
y: boxY
|
|
772
|
+
},
|
|
773
|
+
// Top-right
|
|
774
|
+
{
|
|
775
|
+
x: boxX + boxWidth,
|
|
776
|
+
y: boxY + boxHeight
|
|
777
|
+
},
|
|
778
|
+
// Bottom-right
|
|
779
|
+
{
|
|
780
|
+
x: boxX,
|
|
781
|
+
y: boxY + boxHeight
|
|
782
|
+
} // Bottom-left
|
|
783
|
+
];
|
|
784
|
+
console.log("Initializing crop box (default - 70% centered):", {
|
|
785
|
+
displayedWidth: width,
|
|
786
|
+
displayedHeight: height,
|
|
787
|
+
boxWidth: boxWidth,
|
|
788
|
+
boxHeight: boxHeight,
|
|
789
|
+
boxX: boxX,
|
|
790
|
+
boxY: boxY,
|
|
791
|
+
points: newPoints
|
|
792
|
+
});
|
|
793
|
+
setPoints(newPoints);
|
|
794
|
+
};
|
|
795
|
+
|
|
796
|
+
// ✅ REFACTORISATION : Mettre à jour les dimensions d'affichage et les dimensions pour SVG
|
|
797
|
+
var onImageLayout = function onImageLayout(e) {
|
|
798
|
+
var layout = e.nativeEvent.layout;
|
|
799
|
+
|
|
800
|
+
// Stocker les dimensions d'affichage réelles (pour conversion de coordonnées)
|
|
801
|
+
displayedImageLayout.current = {
|
|
802
|
+
x: layout.x,
|
|
803
|
+
y: layout.y,
|
|
804
|
+
width: layout.width,
|
|
805
|
+
height: layout.height
|
|
806
|
+
};
|
|
807
|
+
|
|
808
|
+
// Conserver aussi dans imageMeasure pour compatibilité avec SVG overlay
|
|
809
|
+
imageMeasure.current = {
|
|
810
|
+
x: layout.x,
|
|
811
|
+
y: layout.y,
|
|
812
|
+
width: layout.width,
|
|
813
|
+
height: layout.height
|
|
814
|
+
};
|
|
815
|
+
|
|
816
|
+
// ✅ CORRECTION: recalculer contentRect (contain). Si les dimensions originales ne sont pas encore connues,
|
|
817
|
+
// updateDisplayedContentRect va fallback et sera recalculé quand Image.getSize arrivera.
|
|
818
|
+
updateDisplayedContentRect(layout.width, layout.height);
|
|
819
|
+
console.log("Displayed image layout updated:", {
|
|
820
|
+
width: layout.width,
|
|
821
|
+
height: layout.height,
|
|
822
|
+
x: layout.x,
|
|
823
|
+
y: layout.y
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
// ✅ CRITICAL FIX: Only initialize crop box if:
|
|
827
|
+
// 1. Layout dimensions are available
|
|
828
|
+
// 2. We have no points yet (first initialization)
|
|
829
|
+
// 3. We have original image dimensions (either from camera or Image.getSize)
|
|
830
|
+
// This prevents initializing with wrong dimensions for subsequent images
|
|
831
|
+
if (layout.width > 0 && layout.height > 0 && points.length === 0) {
|
|
832
|
+
// ✅ CRITICAL: Wait for original dimensions before initializing
|
|
833
|
+
// If dimensions not available yet, initializeCropBox will be called from useEffect when Image.getSize completes
|
|
834
|
+
if (originalImageDimensions.current.width > 0 && originalImageDimensions.current.height > 0) {
|
|
835
|
+
initializeCropBox();
|
|
836
|
+
} else if (cameraFrameData.current && cameraFrameData.current.capturedImageSize) {
|
|
837
|
+
// ✅ If we have camera dimensions, use them immediately
|
|
838
|
+
originalImageDimensions.current = {
|
|
839
|
+
width: cameraFrameData.current.capturedImageSize.width,
|
|
840
|
+
height: cameraFrameData.current.capturedImageSize.height
|
|
841
|
+
};
|
|
842
|
+
initializeCropBox();
|
|
843
|
+
} else {
|
|
844
|
+
// ✅ For gallery images, we can initialize with layout dimensions as fallback
|
|
845
|
+
// The crop box will be recalculated when Image.getSize completes
|
|
846
|
+
// But we initialize now so the user sees a border immediately
|
|
847
|
+
initializeCropBox();
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
};
|
|
851
|
+
var createPath = function createPath() {
|
|
852
|
+
if (points.length < 1) return '';
|
|
853
|
+
var path = "M ".concat(points[0].x, " ").concat(points[0].y, " ");
|
|
854
|
+
points.forEach(function (point) {
|
|
855
|
+
return path += "L ".concat(point.x, " ").concat(point.y, " ");
|
|
856
|
+
});
|
|
857
|
+
return path + 'Z';
|
|
858
|
+
};
|
|
859
|
+
var handleTap = function handleTap(e) {
|
|
860
|
+
if (!image || showResult) return;
|
|
861
|
+
var now = Date.now();
|
|
862
|
+
var _e$nativeEvent = e.nativeEvent,
|
|
863
|
+
tapX = _e$nativeEvent.locationX,
|
|
864
|
+
tapY = _e$nativeEvent.locationY;
|
|
865
|
+
|
|
866
|
+
// ✅ CRITICAL: Ensure displayedContentRect is available
|
|
867
|
+
var contentRect = displayedContentRect.current;
|
|
868
|
+
var layout = displayedImageLayout.current;
|
|
869
|
+
|
|
870
|
+
// Recalculate if not available
|
|
871
|
+
if (contentRect.width === 0 || contentRect.height === 0) {
|
|
872
|
+
if (layout.width > 0 && layout.height > 0) {
|
|
873
|
+
updateDisplayedContentRect(layout.width, layout.height);
|
|
874
|
+
contentRect = displayedContentRect.current;
|
|
875
|
+
}
|
|
876
|
+
// If still not available, use layout as fallback
|
|
877
|
+
if (contentRect.width === 0 || contentRect.height === 0) {
|
|
878
|
+
if (layout.width > 0 && layout.height > 0) {
|
|
879
|
+
contentRect = {
|
|
880
|
+
x: layout.x,
|
|
881
|
+
y: layout.y,
|
|
882
|
+
width: layout.width,
|
|
883
|
+
height: layout.height
|
|
884
|
+
};
|
|
885
|
+
} else {
|
|
886
|
+
console.warn("⚠️ Cannot handle tap: no layout dimensions available");
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// ✅ Clamp to real displayed image content (avoid points outside image due to letterboxing)
|
|
893
|
+
var _contentRect3 = contentRect,
|
|
894
|
+
cx = _contentRect3.x,
|
|
895
|
+
cy = _contentRect3.y,
|
|
896
|
+
cw = _contentRect3.width,
|
|
897
|
+
ch = _contentRect3.height;
|
|
898
|
+
var boundedTapX = Math.max(cx, Math.min(tapX, cx + cw));
|
|
899
|
+
var boundedTapY = Math.max(cy, Math.min(tapY, cy + ch));
|
|
900
|
+
var selectRadius = 28; // easier to grab points
|
|
901
|
+
if (lastTap.current && now - lastTap.current < 300) {
|
|
902
|
+
var exists = points.some(function (p) {
|
|
903
|
+
return Math.abs(p.x - boundedTapX) < selectRadius && Math.abs(p.y - boundedTapY) < selectRadius;
|
|
904
|
+
});
|
|
905
|
+
if (!exists) setPoints([].concat(_toConsumableArray(points), [{
|
|
906
|
+
x: boundedTapX,
|
|
907
|
+
y: boundedTapY
|
|
908
|
+
}]));
|
|
909
|
+
lastTap.current = null;
|
|
910
|
+
} else {
|
|
911
|
+
var index = points.findIndex(function (p) {
|
|
912
|
+
return Math.abs(p.x - boundedTapX) < selectRadius && Math.abs(p.y - boundedTapY) < selectRadius;
|
|
913
|
+
});
|
|
914
|
+
if (index !== -1) {
|
|
915
|
+
selectedPointIndex.current = index;
|
|
916
|
+
// ✅ FREE DRAG: Store initial positions for delta-based movement
|
|
917
|
+
initialTouchPosition.current = {
|
|
918
|
+
x: tapX,
|
|
919
|
+
y: tapY
|
|
920
|
+
};
|
|
921
|
+
lastTouchPosition.current = {
|
|
922
|
+
x: tapX,
|
|
923
|
+
y: tapY
|
|
924
|
+
}; // Store last touch for incremental delta
|
|
925
|
+
initialPointPosition.current = _objectSpread({}, points[index]);
|
|
926
|
+
lastValidPosition.current = _objectSpread({}, points[index]); // store original position before move
|
|
927
|
+
|
|
928
|
+
// ✅ CRITICAL: Disable parent ScrollView scrolling when dragging a point
|
|
929
|
+
// This prevents ScrollView from intercepting vertical movement
|
|
930
|
+
try {
|
|
931
|
+
// Find and disable parent ScrollView if it exists
|
|
932
|
+
var _findScrollView = function findScrollView(node) {
|
|
933
|
+
if (!node) return null;
|
|
934
|
+
if (node._component && node._component.setNativeProps) {
|
|
935
|
+
// Try to disable scrolling
|
|
936
|
+
node._component.setNativeProps({
|
|
937
|
+
scrollEnabled: false
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
return _findScrollView(node._owner || node._parent);
|
|
941
|
+
};
|
|
942
|
+
// Note: This is a workaround - ideally we'd pass a ref to disable scroll
|
|
943
|
+
} catch (e) {
|
|
944
|
+
// Ignore errors
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
lastTap.current = now;
|
|
948
|
+
}
|
|
949
|
+
};
|
|
950
|
+
var handleMove = function handleMove(e) {
|
|
951
|
+
if (showResult || selectedPointIndex.current === null) return;
|
|
952
|
+
|
|
953
|
+
// ✅ FREE DRAG: Use delta-based movement for smooth, unconstrained dragging
|
|
954
|
+
// ✅ CRITICAL FIX: Use incremental delta calculation for more reliable vertical movement
|
|
955
|
+
// Instead of calculating delta from initial position, calculate from last position
|
|
956
|
+
// This works better when ScrollView intercepts some events
|
|
957
|
+
var nativeEvent = e.nativeEvent;
|
|
958
|
+
var currentX = nativeEvent.locationX;
|
|
959
|
+
var currentY = nativeEvent.locationY;
|
|
960
|
+
|
|
961
|
+
// ✅ Validate coordinates
|
|
962
|
+
if (currentX === undefined || currentY === undefined || isNaN(currentX) || isNaN(currentY)) {
|
|
963
|
+
console.warn("⚠️ Cannot get touch coordinates", {
|
|
964
|
+
locationX: nativeEvent.locationX,
|
|
965
|
+
locationY: nativeEvent.locationY
|
|
966
|
+
});
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
// ✅ CRITICAL: Use incremental delta (from last position) instead of absolute delta
|
|
971
|
+
// This is more reliable when ScrollView affects coordinate updates
|
|
972
|
+
var deltaX, deltaY;
|
|
973
|
+
if (lastTouchPosition.current) {
|
|
974
|
+
// Calculate incremental delta from last touch position
|
|
975
|
+
deltaX = currentX - lastTouchPosition.current.x;
|
|
976
|
+
deltaY = currentY - lastTouchPosition.current.y;
|
|
977
|
+
} else if (initialTouchPosition.current) {
|
|
978
|
+
// Fallback to absolute delta if lastTouchPosition not set
|
|
979
|
+
deltaX = currentX - initialTouchPosition.current.x;
|
|
980
|
+
deltaY = currentY - initialTouchPosition.current.y;
|
|
981
|
+
} else {
|
|
982
|
+
console.warn("⚠️ No touch position reference available");
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
// ✅ CRITICAL: Don't update lastTouchPosition here - update it AFTER clamping
|
|
987
|
+
// This ensures that if the point was clamped, lastTouchPosition reflects the actual
|
|
988
|
+
// touch position, allowing the next delta to be calculated correctly
|
|
989
|
+
|
|
990
|
+
// ✅ CRITICAL: Ensure displayedContentRect is available
|
|
991
|
+
var contentRect = displayedContentRect.current;
|
|
992
|
+
var layout = displayedImageLayout.current;
|
|
993
|
+
|
|
994
|
+
// Recalculate if not available
|
|
995
|
+
if (contentRect.width === 0 || contentRect.height === 0) {
|
|
996
|
+
if (layout.width > 0 && layout.height > 0) {
|
|
997
|
+
updateDisplayedContentRect(layout.width, layout.height);
|
|
998
|
+
contentRect = displayedContentRect.current;
|
|
999
|
+
}
|
|
1000
|
+
// If still not available, use layout as fallback
|
|
1001
|
+
if (contentRect.width === 0 || contentRect.height === 0) {
|
|
1002
|
+
if (layout.width > 0 && layout.height > 0) {
|
|
1003
|
+
contentRect = {
|
|
1004
|
+
x: layout.x,
|
|
1005
|
+
y: layout.y,
|
|
1006
|
+
width: layout.width,
|
|
1007
|
+
height: layout.height
|
|
1008
|
+
};
|
|
1009
|
+
} else {
|
|
1010
|
+
console.warn("⚠️ Cannot move point: no layout dimensions available");
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// ✅ FREE DRAG: Ensure initial positions are set
|
|
1017
|
+
if (!initialPointPosition.current) {
|
|
1018
|
+
var currentPoint = points[selectedPointIndex.current];
|
|
1019
|
+
if (currentPoint) {
|
|
1020
|
+
initialPointPosition.current = _objectSpread({}, currentPoint);
|
|
1021
|
+
} else {
|
|
1022
|
+
console.warn("⚠️ No point found for selected index");
|
|
1023
|
+
return;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
// ✅ CRITICAL: deltaX and deltaY are already calculated above using incremental approach
|
|
1028
|
+
// This ensures smooth movement even when ScrollView affects coordinate updates
|
|
1029
|
+
|
|
1030
|
+
// ✅ CRITICAL FIX: Use CURRENT point position (after previous clamping) instead of initial position
|
|
1031
|
+
// This allows the point to move even if it was previously clamped to a limit
|
|
1032
|
+
// If lastValidPosition exists (from previous clamping), use it; otherwise use initial position
|
|
1033
|
+
var basePoint = lastValidPosition.current || initialPointPosition.current;
|
|
1034
|
+
|
|
1035
|
+
// ✅ DEBUG: Log movement to identify vertical movement issues
|
|
1036
|
+
if (Math.abs(deltaY) > 10) {
|
|
1037
|
+
var _initialTouchPosition;
|
|
1038
|
+
console.log("🔄 Movement detected:", {
|
|
1039
|
+
deltaX: deltaX.toFixed(2),
|
|
1040
|
+
deltaY: deltaY.toFixed(2),
|
|
1041
|
+
currentY: currentY.toFixed(2),
|
|
1042
|
+
initialY: (_initialTouchPosition = initialTouchPosition.current) === null || _initialTouchPosition === void 0 || (_initialTouchPosition = _initialTouchPosition.y) === null || _initialTouchPosition === void 0 ? void 0 : _initialTouchPosition.toFixed(2),
|
|
1043
|
+
pointInitialY: initialPointPosition.current.y.toFixed(2),
|
|
1044
|
+
basePointY: basePoint.y.toFixed(2),
|
|
1045
|
+
usingLastValid: !!lastValidPosition.current
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
// ✅ FREE DRAG: Apply delta to CURRENT point position (not initial)
|
|
1050
|
+
// - This allows movement even if point was previously clamped
|
|
1051
|
+
// - No constraints on distance, angle, or edge length
|
|
1052
|
+
// - No forced horizontal/vertical alignment
|
|
1053
|
+
// - No angle locking
|
|
1054
|
+
// - Direct mapping of gesture delta (dx, dy) - both X and Y treated equally
|
|
1055
|
+
var newX = basePoint.x + deltaX;
|
|
1056
|
+
var newY = basePoint.y + deltaY;
|
|
1057
|
+
|
|
1058
|
+
// ✅ ONLY CONSTRAINT: Clamp to image bounds to keep points inside the image
|
|
1059
|
+
// x ∈ [cx, cx + cw], y ∈ [cy, cy + ch]
|
|
1060
|
+
// This is the ONLY constraint - no other geometry normalization
|
|
1061
|
+
var _contentRect4 = contentRect,
|
|
1062
|
+
cx = _contentRect4.x,
|
|
1063
|
+
cy = _contentRect4.y,
|
|
1064
|
+
cw = _contentRect4.width,
|
|
1065
|
+
ch = _contentRect4.height;
|
|
1066
|
+
// ✅ CRITICAL: Calculate bounds correctly - maxX = cx + cw, maxY = cy + ch
|
|
1067
|
+
var maxX = cx + cw;
|
|
1068
|
+
var maxY = cy + ch;
|
|
1069
|
+
var boundedX = Math.max(cx, Math.min(newX, maxX));
|
|
1070
|
+
var boundedY = Math.max(cy, Math.min(newY, maxY));
|
|
1071
|
+
|
|
1072
|
+
// ✅ DEBUG: Log bounds calculation to verify clamping is correct
|
|
1073
|
+
if (Math.abs(newY - boundedY) > 1) {
|
|
1074
|
+
console.log("🔍 Y coordinate clamping:", {
|
|
1075
|
+
newY: newY.toFixed(2),
|
|
1076
|
+
boundedY: boundedY.toFixed(2),
|
|
1077
|
+
cy: cy.toFixed(2),
|
|
1078
|
+
maxY: maxY.toFixed(2),
|
|
1079
|
+
ch: ch.toFixed(2),
|
|
1080
|
+
deltaY: deltaY.toFixed(2),
|
|
1081
|
+
isAtMaxLimit: Math.abs(boundedY - maxY) < 0.01,
|
|
1082
|
+
isAtMinLimit: Math.abs(boundedY - cy) < 0.01
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
// ✅ DEBUG: Log if clamping is limiting movement
|
|
1087
|
+
if (Math.abs(newY - boundedY) > 1 || Math.abs(newX - boundedX) > 1) {
|
|
1088
|
+
console.log("⚠️ Movement clamped:", {
|
|
1089
|
+
requested: {
|
|
1090
|
+
x: newX.toFixed(2),
|
|
1091
|
+
y: newY.toFixed(2)
|
|
1092
|
+
},
|
|
1093
|
+
clamped: {
|
|
1094
|
+
x: boundedX.toFixed(2),
|
|
1095
|
+
y: boundedY.toFixed(2)
|
|
1096
|
+
},
|
|
1097
|
+
contentRect: {
|
|
1098
|
+
x: cx.toFixed(2),
|
|
1099
|
+
y: cy.toFixed(2),
|
|
1100
|
+
width: cw.toFixed(2),
|
|
1101
|
+
height: ch.toFixed(2)
|
|
1102
|
+
}
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
// ✅ FREE DRAG: Update point directly
|
|
1107
|
+
// - No polygon simplification
|
|
1108
|
+
// - No reordering of points
|
|
1109
|
+
// - No geometry normalization during drag
|
|
1110
|
+
var updatedPoint = {
|
|
1111
|
+
x: boundedX,
|
|
1112
|
+
y: boundedY
|
|
1113
|
+
};
|
|
1114
|
+
// ✅ CRITICAL: Always update lastValidPosition with the clamped position
|
|
1115
|
+
// This ensures that the next movement calculation uses the current (clamped) position
|
|
1116
|
+
// instead of the initial position, allowing movement even after clamping
|
|
1117
|
+
lastValidPosition.current = updatedPoint;
|
|
1118
|
+
|
|
1119
|
+
// ✅ CRITICAL: Update lastTouchPosition AFTER clamping
|
|
1120
|
+
// This ensures that the next delta calculation is relative to the current touch position
|
|
1121
|
+
// If the point was clamped, this allows movement in the opposite direction on the next move
|
|
1122
|
+
lastTouchPosition.current = {
|
|
1123
|
+
x: currentX,
|
|
1124
|
+
y: currentY
|
|
1125
|
+
};
|
|
1126
|
+
setPoints(function (prev) {
|
|
1127
|
+
return prev.map(function (p, i) {
|
|
1128
|
+
return i === selectedPointIndex.current ? updatedPoint : p;
|
|
1129
|
+
});
|
|
1130
|
+
});
|
|
1131
|
+
};
|
|
1132
|
+
var handleRelease = function handleRelease() {
|
|
1133
|
+
// ✅ FREE DRAG: Clear initial positions when drag ends
|
|
1134
|
+
initialTouchPosition.current = null;
|
|
1135
|
+
initialPointPosition.current = null;
|
|
1136
|
+
var wasDragging = selectedPointIndex.current !== null;
|
|
1137
|
+
selectedPointIndex.current = null;
|
|
1138
|
+
|
|
1139
|
+
// ✅ CRITICAL: Re-enable parent ScrollView scrolling when drag ends
|
|
1140
|
+
if (wasDragging) {
|
|
1141
|
+
try {
|
|
1142
|
+
// Re-enable scrolling after a short delay to avoid conflicts
|
|
1143
|
+
setTimeout(function () {
|
|
1144
|
+
// ScrollView will be re-enabled automatically when responder is released
|
|
1145
|
+
}, 100);
|
|
1146
|
+
} catch (e) {
|
|
1147
|
+
// Ignore errors
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
};
|
|
1151
|
+
var handleReset = function handleReset() {
|
|
1152
|
+
// setPoints([]);
|
|
1153
|
+
initializeCropBox();
|
|
1154
|
+
};
|
|
1155
|
+
|
|
1156
|
+
// ✅ REFACTORISATION : Stocker l'angle de rotation au lieu de modifier l'image immédiatement
|
|
1157
|
+
// La rotation sera appliquée uniquement lors du crop final pour éviter les interpolations multiples
|
|
1158
|
+
var rotatePreviewImage = /*#__PURE__*/function () {
|
|
1159
|
+
var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(degrees) {
|
|
1160
|
+
var rotated, _t;
|
|
1161
|
+
return _regenerator().w(function (_context) {
|
|
1162
|
+
while (1) switch (_context.p = _context.n) {
|
|
1163
|
+
case 0:
|
|
1164
|
+
if (!(!image || isRotating)) {
|
|
1165
|
+
_context.n = 1;
|
|
1166
|
+
break;
|
|
1167
|
+
}
|
|
1168
|
+
return _context.a(2);
|
|
1169
|
+
case 1:
|
|
1170
|
+
setIsRotating(true);
|
|
1171
|
+
_context.p = 2;
|
|
1172
|
+
// ✅ CORRECTION : appliquer la rotation de façon incrémentale sur le fichier (pas cumulée sur un fichier déjà roté).
|
|
1173
|
+
// L'ancienne version rotait l'image déjà rotée par l'angle total, ce qui donnait des rotations incorrectes (90 + 180 => 270).
|
|
1174
|
+
rotationAngle.current = (rotationAngle.current + degrees) % 360;
|
|
1175
|
+
_context.n = 3;
|
|
1176
|
+
return ImageManipulator.manipulateAsync(image, [{
|
|
1177
|
+
rotate: degrees
|
|
1178
|
+
}], {
|
|
1179
|
+
compress: 1,
|
|
1180
|
+
// Qualité maximale (pas de compression)
|
|
1181
|
+
format: ImageManipulator.SaveFormat.PNG // Format sans perte
|
|
1182
|
+
});
|
|
1183
|
+
case 3:
|
|
1184
|
+
rotated = _context.v;
|
|
1185
|
+
// Update image pour l'aperçu - onImageLayout will call initializeCropBox automatically
|
|
1186
|
+
setImage(rotated.uri);
|
|
1187
|
+
console.log("Rotation applied (preview increment):", degrees, "degrees; accumulated:", rotationAngle.current);
|
|
1188
|
+
_context.n = 5;
|
|
1189
|
+
break;
|
|
1190
|
+
case 4:
|
|
1191
|
+
_context.p = 4;
|
|
1192
|
+
_t = _context.v;
|
|
1193
|
+
console.error("Error rotating image:", _t);
|
|
1194
|
+
alert("Error rotating image");
|
|
1195
|
+
case 5:
|
|
1196
|
+
_context.p = 5;
|
|
1197
|
+
setIsRotating(false);
|
|
1198
|
+
return _context.f(5);
|
|
1199
|
+
case 6:
|
|
1200
|
+
return _context.a(2);
|
|
1201
|
+
}
|
|
1202
|
+
}, _callee, null, [[2, 4, 5, 6]]);
|
|
1203
|
+
}));
|
|
1204
|
+
return function rotatePreviewImage(_x) {
|
|
1205
|
+
return _ref2.apply(this, arguments);
|
|
1206
|
+
};
|
|
1207
|
+
}();
|
|
1208
|
+
|
|
1209
|
+
// Helper function to wait for multiple render cycles (works on iOS)
|
|
1210
|
+
var waitForRender = function waitForRender() {
|
|
1211
|
+
var cycles = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 5;
|
|
1212
|
+
return new Promise(function (resolve) {
|
|
1213
|
+
var count = 0;
|
|
1214
|
+
var _tick = function tick() {
|
|
1215
|
+
count++;
|
|
1216
|
+
if (count >= cycles) {
|
|
1217
|
+
resolve();
|
|
1218
|
+
} else {
|
|
1219
|
+
setImmediate(_tick);
|
|
1220
|
+
}
|
|
1221
|
+
};
|
|
1222
|
+
setImmediate(_tick);
|
|
1223
|
+
});
|
|
1224
|
+
};
|
|
1225
|
+
return /*#__PURE__*/_react["default"].createElement(_reactNative.View, {
|
|
1226
|
+
style: _ImageCropperStyles["default"].container
|
|
1227
|
+
}, showCustomCamera ? /*#__PURE__*/_react["default"].createElement(_CustomCamera["default"], {
|
|
1228
|
+
onPhotoCaptured: function onPhotoCaptured(uri, frameData) {
|
|
1229
|
+
// ✅ CRITICAL FIX: Store green frame coordinates for coordinate conversion
|
|
1230
|
+
if (frameData && frameData.greenFrame) {
|
|
1231
|
+
cameraFrameData.current = {
|
|
1232
|
+
greenFrame: frameData.greenFrame,
|
|
1233
|
+
capturedImageSize: frameData.capturedImageSize
|
|
1234
|
+
};
|
|
1235
|
+
console.log("✅ Camera frame data received:", cameraFrameData.current);
|
|
1236
|
+
}
|
|
1237
|
+
setImage(uri);
|
|
1238
|
+
setShowCustomCamera(false);
|
|
1239
|
+
// ✅ CORRECTION : Réinitialiser les points et l'angle de rotation quand une nouvelle photo est capturée
|
|
1240
|
+
setPoints([]);
|
|
1241
|
+
rotationAngle.current = 0;
|
|
1242
|
+
// ✅ CRITICAL: initializeCropBox will be called automatically when image layout is ready
|
|
1243
|
+
// The green frame coordinates are stored in cameraFrameData.current and will be used
|
|
1244
|
+
},
|
|
1245
|
+
onCancel: function onCancel() {
|
|
1246
|
+
return setShowCustomCamera(false);
|
|
1247
|
+
}
|
|
1248
|
+
}) : /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, !showResult && /*#__PURE__*/_react["default"].createElement(_reactNative.View, {
|
|
1249
|
+
style: image ? _ImageCropperStyles["default"].buttonContainer : _ImageCropperStyles["default"].centerButtonsContainer
|
|
1250
|
+
}, image && _reactNative.Platform.OS === 'android' && /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, /*#__PURE__*/_react["default"].createElement(_reactNative.TouchableOpacity, {
|
|
1251
|
+
style: _ImageCropperStyles["default"].iconButton,
|
|
1252
|
+
onPress: function onPress() {
|
|
1253
|
+
return enableRotation && rotatePreviewImage(90);
|
|
1254
|
+
},
|
|
1255
|
+
disabled: isRotating
|
|
1256
|
+
}, /*#__PURE__*/_react["default"].createElement(_vectorIcons.Ionicons, {
|
|
1257
|
+
name: "sync",
|
|
1258
|
+
size: 24,
|
|
1259
|
+
color: "white"
|
|
1260
|
+
}))), image && /*#__PURE__*/_react["default"].createElement(_reactNative.TouchableOpacity, {
|
|
1261
|
+
style: _ImageCropperStyles["default"].button,
|
|
1262
|
+
onPress: handleReset
|
|
1263
|
+
}, /*#__PURE__*/_react["default"].createElement(_reactNative.Text, {
|
|
1264
|
+
style: _ImageCropperStyles["default"].buttonText
|
|
1265
|
+
}, "Reset")), image && /*#__PURE__*/_react["default"].createElement(_reactNative.TouchableOpacity, {
|
|
1266
|
+
style: _ImageCropperStyles["default"].button,
|
|
1267
|
+
onPress: /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
|
|
1268
|
+
var actualImageWidth, actualImageHeight, layout, contentRect, displayedWidth, displayedHeight, scaleX, scaleY, scale, originalUri, cropMeta, imagePoints, minX, minY, maxX, maxY, cropX, cropY, cropEndX, cropEndY, cropWidth, cropHeight, bbox, polygon, name, _t2;
|
|
1269
|
+
return _regenerator().w(function (_context2) {
|
|
1270
|
+
while (1) switch (_context2.p = _context2.n) {
|
|
1271
|
+
case 0:
|
|
1272
|
+
setIsLoading(true);
|
|
1273
|
+
_context2.p = 1;
|
|
1274
|
+
console.log("=== Starting pixel-perfect metadata export (no bitmap crop on mobile) ===");
|
|
1275
|
+
|
|
1276
|
+
// ✅ REFACTORISATION : Utiliser les dimensions stockées (plus efficace)
|
|
1277
|
+
actualImageWidth = originalImageDimensions.current.width;
|
|
1278
|
+
actualImageHeight = originalImageDimensions.current.height; // Vérifier que les dimensions sont valides
|
|
1279
|
+
if (!(actualImageWidth === 0 || actualImageHeight === 0)) {
|
|
1280
|
+
_context2.n = 2;
|
|
1281
|
+
break;
|
|
1282
|
+
}
|
|
1283
|
+
throw new Error("Original image dimensions not available. Please wait for image to load.");
|
|
1284
|
+
case 2:
|
|
1285
|
+
console.log("Original image dimensions:", {
|
|
1286
|
+
width: actualImageWidth,
|
|
1287
|
+
height: actualImageHeight
|
|
1288
|
+
});
|
|
1289
|
+
|
|
1290
|
+
// ✅ CORRECTION : Utiliser le rectangle de contenu réel (contain)
|
|
1291
|
+
// ✅ CRITICAL: Recalculate displayedContentRect if not available
|
|
1292
|
+
layout = displayedImageLayout.current;
|
|
1293
|
+
console.log("🔍 Checking displayedContentRect before crop:", {
|
|
1294
|
+
displayedContentRect: displayedContentRect.current,
|
|
1295
|
+
displayedImageLayout: layout,
|
|
1296
|
+
originalImageDimensions: originalImageDimensions.current
|
|
1297
|
+
});
|
|
1298
|
+
if (layout.width > 0 && layout.height > 0) {
|
|
1299
|
+
updateDisplayedContentRect(layout.width, layout.height);
|
|
1300
|
+
}
|
|
1301
|
+
contentRect = displayedContentRect.current;
|
|
1302
|
+
displayedWidth = contentRect.width;
|
|
1303
|
+
displayedHeight = contentRect.height; // Vérifier que les dimensions d'affichage sont valides
|
|
1304
|
+
if (!(displayedWidth === 0 || displayedHeight === 0)) {
|
|
1305
|
+
_context2.n = 4;
|
|
1306
|
+
break;
|
|
1307
|
+
}
|
|
1308
|
+
if (!(layout.width > 0 && layout.height > 0)) {
|
|
1309
|
+
_context2.n = 3;
|
|
1310
|
+
break;
|
|
1311
|
+
}
|
|
1312
|
+
console.warn("⚠️ displayedContentRect not available, using displayedImageLayout as fallback");
|
|
1313
|
+
// Use layout dimensions as fallback (assuming no letterboxing)
|
|
1314
|
+
contentRect = {
|
|
1315
|
+
x: layout.x,
|
|
1316
|
+
y: layout.y,
|
|
1317
|
+
width: layout.width,
|
|
1318
|
+
height: layout.height
|
|
1319
|
+
};
|
|
1320
|
+
displayedWidth = contentRect.width;
|
|
1321
|
+
displayedHeight = contentRect.height;
|
|
1322
|
+
// Update the ref for consistency
|
|
1323
|
+
displayedContentRect.current = contentRect;
|
|
1324
|
+
_context2.n = 4;
|
|
1325
|
+
break;
|
|
1326
|
+
case 3:
|
|
1327
|
+
throw new Error("Displayed image dimensions not available. Image may not be laid out yet. Please wait a moment and try again.");
|
|
1328
|
+
case 4:
|
|
1329
|
+
console.log("✅ Using contentRect for crop:", contentRect);
|
|
1330
|
+
console.log("Displayed image dimensions:", {
|
|
1331
|
+
width: displayedWidth,
|
|
1332
|
+
height: displayedHeight
|
|
1333
|
+
});
|
|
1334
|
+
|
|
1335
|
+
// ✅ CORRECTION : avec resizeMode='contain', l'échelle est UNIFORME + offsets.
|
|
1336
|
+
// Vérifier que le scale est cohérent (même ratio pour width et height)
|
|
1337
|
+
scaleX = actualImageWidth / displayedWidth;
|
|
1338
|
+
scaleY = actualImageHeight / displayedHeight; // Pour resizeMode='contain', scaleX et scaleY doivent être égaux
|
|
1339
|
+
// Si ce n'est pas le cas, il y a un problème de calcul
|
|
1340
|
+
if (Math.abs(scaleX - scaleY) > 0.01) {
|
|
1341
|
+
console.warn("Scale mismatch detected! This may cause incorrect crop coordinates.", {
|
|
1342
|
+
scaleX: scaleX,
|
|
1343
|
+
scaleY: scaleY,
|
|
1344
|
+
actualImageWidth: actualImageWidth,
|
|
1345
|
+
actualImageHeight: actualImageHeight,
|
|
1346
|
+
displayedWidth: displayedWidth,
|
|
1347
|
+
displayedHeight: displayedHeight
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1350
|
+
scale = scaleX; // Utiliser scaleX (ou scaleY, ils devraient être égaux)
|
|
1351
|
+
console.log("Scale factor (contain, uniform):", {
|
|
1352
|
+
scale: scale,
|
|
1353
|
+
scaleX: scaleX,
|
|
1354
|
+
scaleY: scaleY,
|
|
1355
|
+
contentRect: contentRect,
|
|
1356
|
+
actualImageSize: {
|
|
1357
|
+
width: actualImageWidth,
|
|
1358
|
+
height: actualImageHeight
|
|
1359
|
+
},
|
|
1360
|
+
displayedSize: {
|
|
1361
|
+
width: displayedWidth,
|
|
1362
|
+
height: displayedHeight
|
|
1363
|
+
}
|
|
1364
|
+
});
|
|
1365
|
+
originalUri = sourceImageUri.current || image;
|
|
1366
|
+
cropMeta = null;
|
|
1367
|
+
if (points.length > 0) {
|
|
1368
|
+
try {
|
|
1369
|
+
console.log("Calculating crop boundaries from points...");
|
|
1370
|
+
console.log("Points (display coordinates):", points);
|
|
1371
|
+
console.log("Content rect (offsets):", contentRect);
|
|
1372
|
+
|
|
1373
|
+
// ✅ CORRECTION : conversion display -> image px (contain) avec offsets
|
|
1374
|
+
// S'assurer que les points sont bien dans les limites de l'image affichée
|
|
1375
|
+
imagePoints = points.map(function (point) {
|
|
1376
|
+
// Clamp les coordonnées d'affichage à la zone réelle de l'image
|
|
1377
|
+
var clampedX = Math.max(contentRect.x, Math.min(point.x, contentRect.x + contentRect.width));
|
|
1378
|
+
var clampedY = Math.max(contentRect.y, Math.min(point.y, contentRect.y + contentRect.height));
|
|
1379
|
+
|
|
1380
|
+
// Convertir en coordonnées de l'image originale
|
|
1381
|
+
var origX = (clampedX - contentRect.x) * scale;
|
|
1382
|
+
var origY = (clampedY - contentRect.y) * scale;
|
|
1383
|
+
|
|
1384
|
+
// Clamp aux dimensions de l'image originale
|
|
1385
|
+
var finalX = Math.max(0, Math.min(origX, actualImageWidth));
|
|
1386
|
+
var finalY = Math.max(0, Math.min(origY, actualImageHeight));
|
|
1387
|
+
return {
|
|
1388
|
+
x: finalX,
|
|
1389
|
+
y: finalY
|
|
1390
|
+
};
|
|
1391
|
+
});
|
|
1392
|
+
console.log("Converted image points (original coordinates):", imagePoints);
|
|
1393
|
+
|
|
1394
|
+
// Calculer la bounding box : min X, min Y, max X, max Y
|
|
1395
|
+
minX = Math.min.apply(Math, _toConsumableArray(imagePoints.map(function (p) {
|
|
1396
|
+
return p.x;
|
|
1397
|
+
})));
|
|
1398
|
+
minY = Math.min.apply(Math, _toConsumableArray(imagePoints.map(function (p) {
|
|
1399
|
+
return p.y;
|
|
1400
|
+
})));
|
|
1401
|
+
maxX = Math.max.apply(Math, _toConsumableArray(imagePoints.map(function (p) {
|
|
1402
|
+
return p.x;
|
|
1403
|
+
})));
|
|
1404
|
+
maxY = Math.max.apply(Math, _toConsumableArray(imagePoints.map(function (p) {
|
|
1405
|
+
return p.y;
|
|
1406
|
+
}))); // ✅ CORRECTION : arrondi "conservateur" (floor origin + ceil end)
|
|
1407
|
+
// évite de rogner des pixels et réduit le risque de crop plus petit (perte de détails).
|
|
1408
|
+
cropX = Math.max(0, Math.floor(minX));
|
|
1409
|
+
cropY = Math.max(0, Math.floor(minY));
|
|
1410
|
+
cropEndX = Math.min(actualImageWidth, Math.ceil(maxX));
|
|
1411
|
+
cropEndY = Math.min(actualImageHeight, Math.ceil(maxY));
|
|
1412
|
+
cropWidth = Math.max(0, cropEndX - cropX);
|
|
1413
|
+
cropHeight = Math.max(0, cropEndY - cropY);
|
|
1414
|
+
console.log("Crop parameters (pixel-perfect):", {
|
|
1415
|
+
x: cropX,
|
|
1416
|
+
y: cropY,
|
|
1417
|
+
width: cropWidth,
|
|
1418
|
+
height: cropHeight,
|
|
1419
|
+
imageWidth: actualImageWidth,
|
|
1420
|
+
imageHeight: actualImageHeight,
|
|
1421
|
+
boundingBox: {
|
|
1422
|
+
minX: minX,
|
|
1423
|
+
minY: minY,
|
|
1424
|
+
maxX: maxX,
|
|
1425
|
+
maxY: maxY
|
|
1426
|
+
}
|
|
1427
|
+
});
|
|
1428
|
+
if (cropWidth > 0 && cropHeight > 0) {
|
|
1429
|
+
// 1) bbox in ORIGINAL image pixel coords
|
|
1430
|
+
bbox = {
|
|
1431
|
+
x: cropX,
|
|
1432
|
+
y: cropY,
|
|
1433
|
+
width: cropWidth,
|
|
1434
|
+
height: cropHeight
|
|
1435
|
+
}; // 2) polygon points relative to bbox (still in ORIGINAL pixel grid)
|
|
1436
|
+
polygon = imagePoints.map(function (point) {
|
|
1437
|
+
return {
|
|
1438
|
+
x: point.x - cropX,
|
|
1439
|
+
y: point.y - cropY
|
|
1440
|
+
};
|
|
1441
|
+
});
|
|
1442
|
+
cropMeta = {
|
|
1443
|
+
bbox: bbox,
|
|
1444
|
+
polygon: polygon,
|
|
1445
|
+
rotation: 0,
|
|
1446
|
+
imageSize: {
|
|
1447
|
+
width: actualImageWidth,
|
|
1448
|
+
height: actualImageHeight
|
|
1449
|
+
}
|
|
1450
|
+
};
|
|
1451
|
+
console.log("Crop meta ready:", cropMeta);
|
|
1452
|
+
} else {
|
|
1453
|
+
console.warn("Invalid crop dimensions, cannot export crop meta");
|
|
1454
|
+
}
|
|
1455
|
+
} catch (cropError) {
|
|
1456
|
+
console.error("Error computing crop meta:", cropError);
|
|
1457
|
+
}
|
|
1458
|
+
} else {
|
|
1459
|
+
console.log("No crop points defined, using original image");
|
|
1460
|
+
}
|
|
1461
|
+
name = "IMAGE XTK".concat(Date.now());
|
|
1462
|
+
if (onConfirm) {
|
|
1463
|
+
console.log("Calling onConfirm with:", originalUri, name, cropMeta);
|
|
1464
|
+
onConfirm(originalUri, name, cropMeta);
|
|
1465
|
+
}
|
|
1466
|
+
_context2.n = 6;
|
|
1467
|
+
break;
|
|
1468
|
+
case 5:
|
|
1469
|
+
_context2.p = 5;
|
|
1470
|
+
_t2 = _context2.v;
|
|
1471
|
+
console.error("Erreur lors du crop :", _t2);
|
|
1472
|
+
alert("Erreur lors du crop ! " + _t2.message);
|
|
1473
|
+
case 6:
|
|
1474
|
+
_context2.p = 6;
|
|
1475
|
+
setShowResult(false);
|
|
1476
|
+
setIsLoading(false);
|
|
1477
|
+
setShowFullScreenCapture(false);
|
|
1478
|
+
return _context2.f(6);
|
|
1479
|
+
case 7:
|
|
1480
|
+
return _context2.a(2);
|
|
1481
|
+
}
|
|
1482
|
+
}, _callee2, null, [[1, 5, 6, 7]]);
|
|
1483
|
+
}))
|
|
1484
|
+
}, /*#__PURE__*/_react["default"].createElement(_reactNative.Text, {
|
|
1485
|
+
style: _ImageCropperStyles["default"].buttonText
|
|
1486
|
+
}, "Confirm"))), image && /*#__PURE__*/_react["default"].createElement(_reactNative.View, {
|
|
1487
|
+
ref: viewRef,
|
|
1488
|
+
collapsable: false,
|
|
1489
|
+
style: showFullScreenCapture ? _ImageCropperStyles["default"].fullscreenImageContainer : _ImageCropperStyles["default"].imageContainer,
|
|
1490
|
+
onStartShouldSetResponder: function onStartShouldSetResponder() {
|
|
1491
|
+
return true;
|
|
1492
|
+
},
|
|
1493
|
+
onMoveShouldSetResponder: function onMoveShouldSetResponder(evt, gestureState) {
|
|
1494
|
+
// ✅ CRITICAL: Always capture movement when a point is selected
|
|
1495
|
+
// This ensures vertical movement is captured correctly
|
|
1496
|
+
if (selectedPointIndex.current !== null) {
|
|
1497
|
+
return true;
|
|
1498
|
+
}
|
|
1499
|
+
// ✅ CRITICAL: Capture ANY movement immediately (even 0px) to prevent ScrollView interception
|
|
1500
|
+
// This is especially important for vertical movement which ScrollView tries to intercept
|
|
1501
|
+
// We return true for ANY movement to ensure we capture it before ScrollView
|
|
1502
|
+
var hasMovement = Math.abs(gestureState.dx) > 0 || Math.abs(gestureState.dy) > 0;
|
|
1503
|
+
if (hasMovement && Math.abs(gestureState.dy) > 5) {
|
|
1504
|
+
console.log("🔄 Vertical movement detected in responder:", {
|
|
1505
|
+
dx: gestureState.dx.toFixed(2),
|
|
1506
|
+
dy: gestureState.dy.toFixed(2),
|
|
1507
|
+
selectedPoint: selectedPointIndex.current
|
|
1508
|
+
});
|
|
1509
|
+
}
|
|
1510
|
+
return true;
|
|
1511
|
+
},
|
|
1512
|
+
onResponderGrant: function onResponderGrant(e) {
|
|
1513
|
+
// ✅ CRITICAL: Grant responder immediately to prevent ScrollView from intercepting
|
|
1514
|
+
// This ensures we capture all movement, especially vertical
|
|
1515
|
+
// Handle tap to select point if needed
|
|
1516
|
+
if (selectedPointIndex.current === null) {
|
|
1517
|
+
handleTap(e);
|
|
1518
|
+
}
|
|
1519
|
+
},
|
|
1520
|
+
onResponderStart: handleTap,
|
|
1521
|
+
onResponderMove: function onResponderMove(e) {
|
|
1522
|
+
// ✅ CRITICAL: Always handle move events to ensure smooth movement in all directions
|
|
1523
|
+
// This is called for every move event, ensuring vertical movement is captured
|
|
1524
|
+
// handleMove now uses incremental delta calculation which is more reliable
|
|
1525
|
+
handleMove(e);
|
|
1526
|
+
},
|
|
1527
|
+
onResponderRelease: handleRelease,
|
|
1528
|
+
onResponderTerminationRequest: function onResponderTerminationRequest() {
|
|
1529
|
+
// ✅ CRITICAL: Never allow termination when dragging a point
|
|
1530
|
+
// This prevents ScrollView from stealing the responder during vertical movement
|
|
1531
|
+
return selectedPointIndex.current === null;
|
|
1532
|
+
}
|
|
1533
|
+
// ✅ CRITICAL: Prevent parent ScrollView from intercepting touches
|
|
1534
|
+
// Capture responder BEFORE parent ScrollView can intercept
|
|
1535
|
+
,
|
|
1536
|
+
onStartShouldSetResponderCapture: function onStartShouldSetResponderCapture() {
|
|
1537
|
+
// Always capture start events
|
|
1538
|
+
return true;
|
|
1539
|
+
},
|
|
1540
|
+
onMoveShouldSetResponderCapture: function onMoveShouldSetResponderCapture(evt, gestureState) {
|
|
1541
|
+
// ✅ CRITICAL: Always capture movement events before parent ScrollView
|
|
1542
|
+
// This is essential for vertical movement which ScrollView tries to intercept
|
|
1543
|
+
// Especially important when a point is selected or when there's any movement
|
|
1544
|
+
if (selectedPointIndex.current !== null) {
|
|
1545
|
+
return true;
|
|
1546
|
+
}
|
|
1547
|
+
// Also capture if there's any movement to prevent ScrollView from intercepting
|
|
1548
|
+
var hasMovement = Math.abs(gestureState.dx) > 0 || Math.abs(gestureState.dy) > 0;
|
|
1549
|
+
if (hasMovement && Math.abs(gestureState.dy) > 2) {
|
|
1550
|
+
console.log("🔄 Capturing vertical movement before ScrollView:", {
|
|
1551
|
+
dy: gestureState.dy.toFixed(2)
|
|
1552
|
+
});
|
|
1553
|
+
}
|
|
1554
|
+
return hasMovement;
|
|
1555
|
+
}
|
|
1556
|
+
// ✅ CRITICAL: Prevent ScrollView from scrolling by stopping propagation
|
|
1557
|
+
,
|
|
1558
|
+
onResponderReject: function onResponderReject() {
|
|
1559
|
+
console.warn("⚠️ Responder rejected - ScrollView may intercept");
|
|
1560
|
+
}
|
|
1561
|
+
}, /*#__PURE__*/_react["default"].createElement(_reactNative.Image, {
|
|
1562
|
+
source: {
|
|
1563
|
+
uri: image
|
|
1564
|
+
},
|
|
1565
|
+
style: _ImageCropperStyles["default"].image,
|
|
1566
|
+
onLayout: onImageLayout
|
|
1567
|
+
}), /*#__PURE__*/_react["default"].createElement(_reactNativeSvg["default"], {
|
|
1568
|
+
style: _ImageCropperStyles["default"].overlay,
|
|
1569
|
+
pointerEvents: "none"
|
|
1570
|
+
}, /*#__PURE__*/_react["default"].createElement(_reactNativeSvg.Path, {
|
|
1571
|
+
d: "M 0 0 H ".concat(imageMeasure.current.width, " V ").concat(imageMeasure.current.height, " H 0 Z ").concat(createPath()),
|
|
1572
|
+
fill: showResult ? 'white' : 'rgba(0, 0, 0, 0.8)',
|
|
1573
|
+
fillRule: "evenodd"
|
|
1574
|
+
}), !showResult && points.length > 0 && /*#__PURE__*/_react["default"].createElement(_reactNativeSvg.Path, {
|
|
1575
|
+
d: createPath(),
|
|
1576
|
+
fill: "transparent",
|
|
1577
|
+
stroke: "white",
|
|
1578
|
+
strokeWidth: 2
|
|
1579
|
+
}), !showResult && points.map(function (point, index) {
|
|
1580
|
+
return /*#__PURE__*/_react["default"].createElement(_reactNativeSvg.Circle, {
|
|
1581
|
+
key: index,
|
|
1582
|
+
cx: point.x,
|
|
1583
|
+
cy: point.y,
|
|
1584
|
+
r: 10,
|
|
1585
|
+
fill: "white"
|
|
1586
|
+
});
|
|
1587
|
+
})))), showMaskView && maskImageUri && maskPoints.length > 0 && /*#__PURE__*/_react["default"].createElement(_reactNative.View, {
|
|
1588
|
+
style: {
|
|
1589
|
+
position: 'absolute',
|
|
1590
|
+
left: -maskDimensions.width - 100,
|
|
1591
|
+
// Hors écran mais pas trop loin
|
|
1592
|
+
top: -maskDimensions.height - 100,
|
|
1593
|
+
width: maskDimensions.width,
|
|
1594
|
+
height: maskDimensions.height,
|
|
1595
|
+
opacity: 1,
|
|
1596
|
+
// Opacité normale pour la capture
|
|
1597
|
+
pointerEvents: 'none',
|
|
1598
|
+
zIndex: 9999,
|
|
1599
|
+
// Z-index élevé pour s'assurer qu'elle est au-dessus
|
|
1600
|
+
overflow: 'hidden'
|
|
1601
|
+
},
|
|
1602
|
+
collapsable: false
|
|
1603
|
+
}, /*#__PURE__*/_react["default"].createElement(_ImageMaskProcessor.MaskView, {
|
|
1604
|
+
ref: maskViewRef,
|
|
1605
|
+
imageUri: maskImageUri,
|
|
1606
|
+
points: maskPoints,
|
|
1607
|
+
width: maskDimensions.width,
|
|
1608
|
+
height: maskDimensions.height
|
|
1609
|
+
})), /*#__PURE__*/_react["default"].createElement(_reactNative.Modal, {
|
|
1610
|
+
visible: isLoading,
|
|
1611
|
+
transparent: true,
|
|
1612
|
+
animationType: "fade"
|
|
1613
|
+
}, /*#__PURE__*/_react["default"].createElement(_reactNative.View, {
|
|
1614
|
+
style: _ImageCropperStyles["default"].loadingOverlay
|
|
1615
|
+
}, /*#__PURE__*/_react["default"].createElement(_reactNative.Image, {
|
|
1616
|
+
source: require('../src/assets/loadingCamera.gif'),
|
|
1617
|
+
style: _ImageCropperStyles["default"].loadingGif,
|
|
1618
|
+
resizeMode: "contain"
|
|
1619
|
+
}))));
|
|
1620
|
+
};
|
|
514
1621
|
var _default = exports["default"] = ImageCropper;
|