fix(analysis): appliquer la rotation du crop sur l'image en modes calibration et plotting

This commit is contained in:
qguillaume
2026-05-27 12:11:00 +02:00
parent 04c1204116
commit 95e62abe47
2 changed files with 47 additions and 26 deletions

View File

@@ -50,6 +50,9 @@ class AnalysisProvider extends ChangeNotifier {
String? _imagePath; String? _imagePath;
TargetType? _targetType; TargetType? _targetType;
// AJOUT PROTECTION DU PLOTTING : Stockage permanent de la rotation du Crop
double _cropRotation = 0.0;
// Target detection results // Target detection results
double _targetCenterX = 0.5; double _targetCenterX = 0.5;
double _targetCenterY = 0.5; double _targetCenterY = 0.5;
@@ -82,6 +85,7 @@ class AnalysisProvider extends ChangeNotifier {
String? get errorMessage => _errorMessage; String? get errorMessage => _errorMessage;
String? get imagePath => _imagePath; String? get imagePath => _imagePath;
TargetType? get targetType => _targetType; TargetType? get targetType => _targetType;
double get cropRotation => _cropRotation; // Getter pour le Plotting
double get targetCenterX => _targetCenterX; double get targetCenterX => _targetCenterX;
double get targetCenterY => _targetCenterY; double get targetCenterY => _targetCenterY;
double get targetRadius => _targetRadius; double get targetRadius => _targetRadius;
@@ -112,6 +116,12 @@ class AnalysisProvider extends ChangeNotifier {
? _correctedImagePath ? _correctedImagePath
: _imagePath; : _imagePath;
/// Modifie et mémorise la rotation de l'image pour le Plotting
void setCropRotation(double rotation) {
_cropRotation = rotation;
notifyListeners();
}
/// Analyze an image /// Analyze an image
/// ///
/// [autoAnalyze] determines if we should run automatic detection immediately. /// [autoAnalyze] determines if we should run automatic detection immediately.
@@ -778,6 +788,7 @@ class AnalysisProvider extends ChangeNotifier {
_errorMessage = null; _errorMessage = null;
_imagePath = null; _imagePath = null;
_targetType = null; _targetType = null;
_cropRotation = 0.0; // Reset de la rotation
_targetCenterX = 0.5; _targetCenterX = 0.5;
_targetCenterY = 0.5; _targetCenterY = 0.5;
_targetRadius = 0.4; _targetRadius = 0.4;

View File

@@ -23,11 +23,8 @@ import '../crop/crop_screen.dart';
import '../capture/capture_screen.dart'; import '../capture/capture_screen.dart';
import 'widgets/target_overlay.dart'; import 'widgets/target_overlay.dart';
import 'widgets/target_calibration.dart'; import 'widgets/target_calibration.dart';
import 'widgets/target_overlay.dart';
import 'widgets/score_card.dart'; import 'widgets/score_card.dart';
import 'widgets/grouping_stats.dart'; import 'widgets/grouping_stats.dart';
import '../capture/capture_screen.dart'; // Si ton écran principal s'appelle comme ça
import '../crop/crop_screen.dart';
class AnalysisScreen extends StatelessWidget { class AnalysisScreen extends StatelessWidget {
final String imagePath; final String imagePath;
@@ -57,12 +54,20 @@ class AnalysisScreen extends StatelessWidget {
: null; : null;
return ChangeNotifierProvider( return ChangeNotifierProvider(
create: (context) => AnalysisProvider( create: (context) {
detectionService: context.read<TargetDetectionService>(), final p = AnalysisProvider(
scoreCalculatorService: context.read<ScoreCalculatorService>(), detectionService: context.read<TargetDetectionService>(),
groupingAnalyzerService: context.read<GroupingAnalyzerService>(), scoreCalculatorService: context.read<ScoreCalculatorService>(),
sessionRepository: context.read<SessionRepository>(), groupingAnalyzerService: context.read<GroupingAnalyzerService>(),
)..analyzeImage(imagePath, targetType, autoAnalyze: false, manualCenter: manualCenterOffset), sessionRepository: context.read<SessionRepository>(),
);
// Sauvegarde de l'angle de rotation d'origine directement dans l'état global
if (cropRotation != null) {
p.setCropRotation(cropRotation!);
}
p.analyzeImage(imagePath, targetType, autoAnalyze: false, manualCenter: manualCenterOffset);
return p;
},
child: _AnalysisScreenContent( child: _AnalysisScreenContent(
cropScale: cropScale, cropScale: cropScale,
cropOffset: cropOffset, cropOffset: cropOffset,
@@ -207,7 +212,7 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
), ),
), ),
// SECTION CORRIGÉE : Utilise maintenant widget.cropRotation sans aucune erreur ! // SECTION CORRIGÉE : Utilise maintenant la réactivité du provider globale
AspectRatio( AspectRatio(
aspectRatio: provider.imageAspectRatio, aspectRatio: provider.imageAspectRatio,
child: _isCalibrating child: _isCalibrating
@@ -217,19 +222,15 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
ClipRect( ClipRect(
child: Builder( child: Builder(
builder: (context) { builder: (context) {
final double rotationAngle = widget.cropRotation ?? 0.0;
return Transform( return Transform(
transform: Matrix4.identity() transform: Matrix4.identity()
..translate(widget.cropOffset?.dx ?? 0.0, widget.cropOffset?.dy ?? 0.0) ..setTranslationRaw(widget.cropOffset?.dx ?? 0.0, widget.cropOffset?.dy ?? 0.0, 0.0)
..scale(1.0, 1.0), // Zoom ignoré au plotting pour rester en vue globale ..scale(1.0, 1.0) // Zoom ignoré au plotting pour rester en vue globale
..rotateZ((provider.cropRotation) * (math.pi / 180)), // FIX : Utilisation du provider dynamique
alignment: Alignment.center, alignment: Alignment.center,
child: Transform.rotate( child: Image.file(
angle: rotationAngle * (math.pi / 180), File(provider.imagePath!),
child: Image.file( fit: BoxFit.contain,
File(provider.imagePath!),
fit: BoxFit.contain,
),
), ),
); );
} }
@@ -486,10 +487,18 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
}, },
child: Stack( child: Stack(
children: [ children: [
Image.file( // FIX CRITIQUE PLOTTING : L'image de fond doit appliquer exactement le même angle de rotation qu'en calibration !
File(provider.imagePath!), Transform(
key: _imageKey, transform: Matrix4.identity()
fit: BoxFit.fill, ..setTranslationRaw(widget.cropOffset?.dx ?? 0.0, widget.cropOffset?.dy ?? 0.0, 0.0)
..scale(1.0, 1.0)
..rotateZ((provider.cropRotation) * (math.pi / 180)),
alignment: Alignment.center,
child: Image.file(
File(provider.imagePath!),
key: _imageKey,
fit: BoxFit.contain, // Remplace BoxFit.fill pour s'aligner sur la Calibration
),
), ),
TargetOverlay( TargetOverlay(
targetCenterX: provider.targetCenterX, targetCenterX: provider.targetCenterX,
@@ -568,6 +577,7 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
), ),
); );
} }
void _showSaveSessionDialog(BuildContext context, AnalysisProvider provider) { void _showSaveSessionDialog(BuildContext context, AnalysisProvider provider) {
showDialog( showDialog(
context: context, context: context,
@@ -645,7 +655,7 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
weaponName: sessionProvider.currentWeapon, weaponName: sessionProvider.currentWeapon,
weaponId: sessionProvider.currentWeaponId, weaponId: sessionProvider.currentWeaponId,
distance: sessionProvider.distance, distance: sessionProvider.distance,
date: sessionProvider.sessionDate, // <-- CORRECTION : Prise en compte de la date ici aussi ! date: sessionProvider.sessionDate,
); );
if (context.mounted) { if (context.mounted) {
@@ -667,4 +677,4 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
), ),
); );
} }
} }