diff --git a/lib/features/analysis/analysis_screen.dart b/lib/features/analysis/analysis_screen.dart index 70f4b6fd..83ae4605 100644 --- a/lib/features/analysis/analysis_screen.dart +++ b/lib/features/analysis/analysis_screen.dart @@ -479,123 +479,114 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> { maxScale: 10.0, boundaryMargin: const EdgeInsets.all(double.infinity), panEnabled: _movingShotId == null, - child: Transform( - transform: Matrix4.identity() - ..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: GestureDetector( - onDoubleTapDown: (TapDownDetails details) { - final RenderBox? box = - _imageKey.currentContext?.findRenderObject() as RenderBox?; - if (box == null) return; + // CORRECTIF : Le widget Transform forçant le zoom/décalage initial a été supprimé d'ici. + // L'image s'affiche maintenant de manière brute et entière, à plat. + child: GestureDetector( + onDoubleTapDown: (TapDownDetails details) { + final RenderBox? box = + _imageKey.currentContext?.findRenderObject() as RenderBox?; + if (box == null) return; - final localOffset = box.globalToLocal(details.globalPosition); - final relX = localOffset.dx / box.size.width; - final relY = localOffset.dy / box.size.height; + final localOffset = box.globalToLocal(details.globalPosition); + final relX = localOffset.dx / box.size.width; + final relY = localOffset.dy / box.size.height; - if (provider.shots.isEmpty) return; + if (provider.shots.isEmpty) return; - Shot? closestShot; - double minDistance = double.infinity; - const double clickTolerance = 0.05; + Shot? closestShot; + double minDistance = double.infinity; + const double clickTolerance = 0.05; - for (final shot in provider.shots) { - final dx = shot.x - relX; - final dy = shot.y - relY; - final distance = math.sqrt(dx * dx + dy * dy); + for (final shot in provider.shots) { + final dx = shot.x - relX; + final dy = shot.y - relY; + final distance = math.sqrt(dx * dx + dy * dy); - if (distance < minDistance && distance < clickTolerance) { - minDistance = distance; - closestShot = shot; - } + if (distance < minDistance && distance < clickTolerance) { + minDistance = distance; + closestShot = shot; } + } - if (closestShot != null) { - _showShotDetails(context, provider, closestShot); + if (closestShot != null) { + _showShotDetails(context, provider, closestShot); + } + }, + onLongPressStart: (LongPressStartDetails details) { + final RenderBox? box = + _imageKey.currentContext?.findRenderObject() as RenderBox?; + if (box == null) return; + + final localOffset = box.globalToLocal(details.globalPosition); + final relX = localOffset.dx / box.size.width; + final relY = localOffset.dy / box.size.height; + + if (provider.shots.isEmpty) return; + + Shot? closestShot; + double minDistance = double.infinity; + const double dragTolerance = 0.06; + + for (final shot in provider.shots) { + final dx = shot.x - relX; + final dy = shot.y - relY; + final distance = math.sqrt(dx * dx + dy * dy); + + if (distance < minDistance && distance < dragTolerance) { + minDistance = distance; + closestShot = shot; } - }, - onLongPressStart: (LongPressStartDetails details) { - final RenderBox? box = - _imageKey.currentContext?.findRenderObject() as RenderBox?; - if (box == null) return; + } - final localOffset = box.globalToLocal(details.globalPosition); - final relX = localOffset.dx / box.size.width; - final relY = localOffset.dy / box.size.height; + if (closestShot != null) { + setState(() { + _movingShotId = closestShot!.id; + }); + } + }, + onLongPressMoveUpdate: (LongPressMoveUpdateDetails details) { + if (_movingShotId == null) return; - if (provider.shots.isEmpty) return; + final RenderBox? box = + _imageKey.currentContext?.findRenderObject() as RenderBox?; + if (box == null) return; - Shot? closestShot; - double minDistance = double.infinity; - const double dragTolerance = 0.06; + final adjustedGlobalPosition = + details.globalPosition + const Offset(-25, -35); + final localOffset = box.globalToLocal(adjustedGlobalPosition); - for (final shot in provider.shots) { - final dx = shot.x - relX; - final dy = shot.y - relY; - final distance = math.sqrt(dx * dx + dy * dy); + final relX = (localOffset.dx / box.size.width).clamp(0.0, 1.0); + final relY = (localOffset.dy / box.size.height).clamp(0.0, 1.0); - if (distance < minDistance && distance < dragTolerance) { - minDistance = distance; - closestShot = shot; - } - } - - if (closestShot != null) { - setState(() { - _movingShotId = closestShot!.id; - }); - } - }, - onLongPressMoveUpdate: (LongPressMoveUpdateDetails details) { - if (_movingShotId == null) return; - - final RenderBox? box = - _imageKey.currentContext?.findRenderObject() as RenderBox?; - if (box == null) return; - - final adjustedGlobalPosition = - details.globalPosition + const Offset(-25, -35); - final localOffset = box.globalToLocal(adjustedGlobalPosition); - - final relX = (localOffset.dx / box.size.width).clamp(0.0, 1.0); - final relY = (localOffset.dy / box.size.height).clamp(0.0, 1.0); - - provider.updateShotPosition(_movingShotId!, relX, relY); - }, - onLongPressEnd: (_) { - if (_movingShotId != null) { - setState(() { - _movingShotId = null; - }); - } - }, - child: Stack( - children: [ - Image.file( - File(provider.imagePath!), - key: _imageKey, - fit: BoxFit.contain, - ), - TargetOverlay( - targetCenterX: provider.targetCenterX, - targetCenterY: provider.targetCenterY, - targetRadius: provider.targetRadius, - targetType: provider.targetType!, - shots: provider.shots, - showRings: true, - zoomScale: _currentZoomScale, - onShotTapped: (shot) => - _showShotDetails(context, provider, shot), - onAddShot: (relX, relY) => provider.addShot(relX, relY), - ), - ], - ), + provider.updateShotPosition(_movingShotId!, relX, relY); + }, + onLongPressEnd: (_) { + if (_movingShotId != null) { + setState(() { + _movingShotId = null; + }); + } + }, + child: Stack( + children: [ + Image.file( + File(provider.imagePath!), + key: _imageKey, + fit: BoxFit.contain, + ), + TargetOverlay( + targetCenterX: provider.targetCenterX, + targetCenterY: provider.targetCenterY, + targetRadius: provider.targetRadius, + targetType: provider.targetType!, + shots: provider.shots, + showRings: true, + zoomScale: _currentZoomScale, + onShotTapped: (shot) => + _showShotDetails(context, provider, shot), + onAddShot: (relX, relY) => provider.addShot(relX, relY), + ), + ], ), ), ); diff --git a/lib/features/analysis/widgets/target_overlay.dart b/lib/features/analysis/widgets/target_overlay.dart index 6ab23613..38188f6a 100644 --- a/lib/features/analysis/widgets/target_overlay.dart +++ b/lib/features/analysis/widgets/target_overlay.dart @@ -5,6 +5,7 @@ /// d'impacts et la sélection d'impacts existants. library; +import 'dart:math' as math; import 'package:flutter/material.dart'; import '../../../core/theme/app_theme.dart'; import '../../../data/models/shot.dart'; @@ -211,8 +212,8 @@ class _TargetOverlayPainter extends CustomPainter { final prevMultiplier = i == 0 ? 0.0 : (ringRadii != null && ringRadii!.length == ringCount) - ? ringRadii![i - 1] - : i / ringCount; + ? ringRadii![i - 1] + : i / ringCount; final zoneRadius = maxRadius * (currentMultiplier + prevMultiplier) / 2; final score = 10 - i; @@ -260,7 +261,10 @@ class _TargetOverlayPainter extends CustomPainter { void _drawGroupingCircle(Canvas canvas, Size size) { final centerX = groupingCenterX! * size.width; final centerY = groupingCenterY! * size.height; - final diameter = groupingDiameter! * size.width.clamp(0, size.height); + + // CORRECTIF : Utilisation de math.min pour éviter les écrasements d'aspect ratio asymétriques + final minDim = math.min(size.width, size.height); + final diameter = groupingDiameter! * minDim; // Draw filled circle final fillPaint = Paint() @@ -381,4 +385,4 @@ class _TargetOverlayPainter extends CustomPainter { zoomScale != oldDelegate.zoomScale || showRings != oldDelegate.showRings; } -} +} \ No newline at end of file