fix: retour arriere leger zoom du au 85% crop
This commit is contained in:
@@ -28,6 +28,11 @@ import 'widgets/grouping_stats.dart';
|
||||
|
||||
class AnalysisScreen extends StatelessWidget {
|
||||
final String imagePath;
|
||||
|
||||
/// Image originale (jamais recadrée), transmise pour pouvoir revenir au
|
||||
/// centrage sans recadrer un résultat déjà recadré.
|
||||
final String? originalImagePath;
|
||||
|
||||
final TargetType targetType;
|
||||
final double? initialCenterX;
|
||||
final double? initialCenterY;
|
||||
@@ -38,6 +43,7 @@ class AnalysisScreen extends StatelessWidget {
|
||||
const AnalysisScreen({
|
||||
super.key,
|
||||
required this.imagePath,
|
||||
this.originalImagePath,
|
||||
required this.targetType,
|
||||
this.initialCenterX,
|
||||
this.initialCenterY,
|
||||
@@ -75,6 +81,7 @@ class AnalysisScreen extends StatelessWidget {
|
||||
return p;
|
||||
},
|
||||
child: _AnalysisScreenContent(
|
||||
originalImagePath: originalImagePath ?? imagePath,
|
||||
cropScale: cropScale,
|
||||
cropOffset: cropOffset,
|
||||
cropRotation: cropRotation, // Envoyé à la structure d'affichage
|
||||
@@ -84,11 +91,13 @@ class AnalysisScreen extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _AnalysisScreenContent extends StatefulWidget {
|
||||
final String originalImagePath;
|
||||
final double? cropScale;
|
||||
final Offset? cropOffset;
|
||||
final double? cropRotation;
|
||||
|
||||
const _AnalysisScreenContent({
|
||||
required this.originalImagePath,
|
||||
this.cropScale,
|
||||
this.cropOffset,
|
||||
this.cropRotation,
|
||||
@@ -151,6 +160,22 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Revient au Centrage en repartant de l'image ORIGINALE.
|
||||
/// On ne restaure que la rotation (jamais le zoom) → pas de cumul.
|
||||
void _goBackToCrop(AnalysisProvider provider) {
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => CropScreen(
|
||||
imagePath: widget.originalImagePath,
|
||||
originalImagePath: widget.originalImagePath,
|
||||
targetType: provider.targetType!,
|
||||
initialRotation: provider.cropRotation,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final provider = context.watch<AnalysisProvider>();
|
||||
@@ -172,17 +197,7 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
|
||||
onPressed: () {
|
||||
if (_isCalibrating) {
|
||||
final provider = context.read<AnalysisProvider>();
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => CropScreen(
|
||||
imagePath: provider.imagePath!,
|
||||
targetType: provider.targetType!,
|
||||
initialScale: widget.cropScale,
|
||||
initialOffset: widget.cropOffset,
|
||||
),
|
||||
),
|
||||
);
|
||||
_goBackToCrop(provider);
|
||||
} else {
|
||||
setState(() => _isCalibrating = true);
|
||||
}
|
||||
@@ -698,17 +713,18 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
final path = provider.imagePath!;
|
||||
final type = provider.targetType!;
|
||||
|
||||
// Retour au centrage : on repart de l'ORIGINAL (pas de re-crop),
|
||||
// en conservant uniquement la rotation.
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => CropScreen(
|
||||
imagePath: path,
|
||||
imagePath: widget.originalImagePath,
|
||||
originalImagePath: widget.originalImagePath,
|
||||
targetType: type,
|
||||
initialScale: widget.cropScale,
|
||||
initialOffset: widget.cropOffset,
|
||||
initialRotation: provider.cropRotation,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -9,17 +9,28 @@ import '../analysis/analysis_screen.dart';
|
||||
import 'widgets/crop_overlay.dart';
|
||||
|
||||
class CropScreen extends StatefulWidget {
|
||||
/// Image affichée/recadrée dans cet écran. Au tout premier passage, c'est
|
||||
/// aussi l'image originale. Lors des allers-retours, on recharge toujours
|
||||
/// l'ORIGINAL ici (jamais une image déjà recadrée) pour éviter le zoom
|
||||
/// cumulatif.
|
||||
final String imagePath;
|
||||
|
||||
/// Chemin de l'image ORIGINALE (jamais recadrée). Si null, [imagePath] est
|
||||
/// considéré comme l'original (premier passage depuis la caméra/galerie).
|
||||
final String? originalImagePath;
|
||||
|
||||
final TargetType targetType;
|
||||
final double? initialScale;
|
||||
final Offset? initialOffset;
|
||||
|
||||
/// Rotation à restaurer au retour (en degrés). Le zoom n'est volontairement
|
||||
/// PAS restauré : on repart toujours de l'image entière.
|
||||
final double? initialRotation;
|
||||
|
||||
const CropScreen({
|
||||
super.key,
|
||||
required this.imagePath,
|
||||
this.originalImagePath,
|
||||
required this.targetType,
|
||||
this.initialScale,
|
||||
this.initialOffset,
|
||||
this.initialRotation,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -45,14 +56,20 @@ class _CropScreenState extends State<CropScreen> {
|
||||
late Size _viewportSize;
|
||||
late double _cropSize;
|
||||
|
||||
/// L'image effectivement travaillée par cet écran : toujours l'originale.
|
||||
String get _workingImagePath =>
|
||||
widget.originalImagePath ?? widget.imagePath;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// On restaure uniquement la rotation (pas le zoom).
|
||||
_rotation = widget.initialRotation ?? 0.0;
|
||||
_loadImageInfo();
|
||||
}
|
||||
|
||||
Future<void> _loadImageInfo() async {
|
||||
final file = File(widget.imagePath);
|
||||
final file = File(_workingImagePath);
|
||||
final decodedImage = await decodeImageFromList(await file.readAsBytes());
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
@@ -254,7 +271,7 @@ class _CropScreenState extends State<CropScreen> {
|
||||
// On passe de 0.95 à 0.85 pour matcher parfaitement avec l'appareil photo !
|
||||
_cropSize = math.min(displayWidth, displayHeight)* 0.85;
|
||||
|
||||
if (_scale == 1.0 && _offset == Offset.zero && _rotation == 0.0) {
|
||||
if (_scale == 1.0 && _offset == Offset.zero) {
|
||||
_initializeImagePosition();
|
||||
}
|
||||
|
||||
@@ -273,7 +290,7 @@ class _CropScreenState extends State<CropScreen> {
|
||||
..rotateZ(_rotation * (math.pi / 180)),
|
||||
alignment: Alignment.center,
|
||||
child: Image.file(
|
||||
File(widget.imagePath),
|
||||
File(_workingImagePath),
|
||||
fit: BoxFit.contain,
|
||||
width: _viewportSize.width,
|
||||
height: _viewportSize.height,
|
||||
@@ -358,18 +375,10 @@ class _CropScreenState extends State<CropScreen> {
|
||||
displayWidth = _viewportSize.height * imageAspect;
|
||||
}
|
||||
|
||||
final minDisplayDim = math.min(displayWidth, displayHeight);
|
||||
|
||||
// 2. Calcul du scale initial basé sur la dimension de l'image (et non du viewport)
|
||||
_scale = widget.initialScale ?? 1.0;
|
||||
if (_scale < 1.0 && widget.initialScale == null) _scale = 1.0;
|
||||
|
||||
// 3. Réinitialisation propre de l'offset au centre de la zone d'affichage
|
||||
if (widget.initialOffset != null) {
|
||||
_offset = widget.initialOffset!;
|
||||
} else {
|
||||
_offset = Offset.zero; // Force l'image à se centrer parfaitement sur la croix verte
|
||||
}
|
||||
// On repart TOUJOURS de l'image entière, centrée, sans zoom :
|
||||
// c'est ce qui empêche tout cumul de recadrage entre les allers-retours.
|
||||
_scale = 1.0;
|
||||
_offset = Offset.zero;
|
||||
}
|
||||
|
||||
void _onScaleStart(ScaleStartDetails details) {
|
||||
@@ -402,9 +411,10 @@ class _CropScreenState extends State<CropScreen> {
|
||||
_scale = savedScale;
|
||||
_offset = savedOffset;
|
||||
|
||||
// AJOUT DE LA DECOUPE ET DU REDRESSEMENT GÉOMÉTRIQUE PHYSIQUE
|
||||
// Le crop est TOUJOURS calculé sur l'image originale, jamais sur un
|
||||
// résultat déjà recadré.
|
||||
final croppedImagePath = await _cropService.cropToSquare(
|
||||
widget.imagePath,
|
||||
_workingImagePath,
|
||||
cropRect,
|
||||
rotationDegrees: _rotation,
|
||||
);
|
||||
@@ -419,12 +429,13 @@ class _CropScreenState extends State<CropScreen> {
|
||||
MaterialPageRoute(
|
||||
builder: (_) => AnalysisScreen(
|
||||
imagePath: croppedImagePath,
|
||||
// On fait suivre l'ORIGINAL et la rotation choisie, pour pouvoir
|
||||
// revenir au centrage sans jamais re-recadrer le résultat.
|
||||
originalImagePath: _workingImagePath,
|
||||
cropRotation: _rotation,
|
||||
targetType: widget.targetType,
|
||||
initialCenterX: targetCenterX,
|
||||
initialCenterY: targetCenterY,
|
||||
cropScale: 1.0,
|
||||
cropOffset: Offset.zero,
|
||||
cropRotation: 0.0,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user