From ab45786d0ca4978e84d179716c6a2e002d7d5db2 Mon Sep 17 00:00:00 2001 From: qguillaume Date: Wed, 27 May 2026 12:16:35 +0200 Subject: [PATCH] fix(analysis): appliquer la transformation globale sur le GestureDetector pour restaurer le drag-and-drop --- lib/features/analysis/analysis_screen.dart | 187 ++++++++++----------- 1 file changed, 93 insertions(+), 94 deletions(-) diff --git a/lib/features/analysis/analysis_screen.dart b/lib/features/analysis/analysis_screen.dart index 9df82f02..0d5a32db 100644 --- a/lib/features/analysis/analysis_screen.dart +++ b/lib/features/analysis/analysis_screen.dart @@ -403,120 +403,119 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> { maxScale: 10.0, boundaryMargin: const EdgeInsets.all(double.infinity), panEnabled: _movingShotId == null, - child: GestureDetector( - onDoubleTapDown: (TapDownDetails details) { - final RenderBox? box = _imageKey.currentContext?.findRenderObject() as RenderBox?; - if (box == null) return; + child: Transform( + // CORRECTION GÉOMÉTRIQUE : On déplace le Transform ICI pour englober le GestureDetector et le Stack d'un coup ! + 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; - 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); - } - }, - 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; + if (closestShot != null) { + _showShotDetails(context, provider, closestShot); } - } + }, + onLongPressStart: (LongPressStartDetails details) { + final RenderBox? box = _imageKey.currentContext?.findRenderObject() as RenderBox?; + if (box == null) return; - if (closestShot != null) { - setState(() { - _movingShotId = closestShot!.id; - }); - } - }, - onLongPressMoveUpdate: (LongPressMoveUpdateDetails details) { - if (_movingShotId == null) return; + final localOffset = box.globalToLocal(details.globalPosition); + final relX = localOffset.dx / box.size.width; + final relY = localOffset.dy / box.size.height; - final RenderBox? box = _imageKey.currentContext?.findRenderObject() as RenderBox?; - if (box == null) return; + if (provider.shots.isEmpty) return; - final adjustedGlobalPosition = details.globalPosition + const Offset(-25, -35); - final localOffset = box.globalToLocal(adjustedGlobalPosition); + Shot? closestShot; + double minDistance = double.infinity; + const double dragTolerance = 0.06; - final relX = (localOffset.dx / box.size.width).clamp(0.0, 1.0); - final relY = (localOffset.dy / box.size.height).clamp(0.0, 1.0); + for (final shot in provider.shots) { + final dx = shot.x - relX; + final dy = shot.y - relY; + final distance = math.sqrt(dx * dx + dy * dy); - provider.updateShotPosition(_movingShotId!, relX, relY); - }, - onLongPressEnd: (_) { - if (_movingShotId != null) { - setState(() { - _movingShotId = null; - }); - } - }, - child: Stack( - children: [ - // FIX CRITIQUE PLOTTING : L'image de fond doit appliquer exactement le même angle de rotation qu'en calibration ! - 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: Image.file( + 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, // Remplace BoxFit.fill pour s'aligner sur la Calibration + 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), - ), - ], + 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), + ), + ], + ), ), ), ); } - Widget _buildActionButtons(BuildContext context, AnalysisProvider provider) { return const Column(children: [Row(children: [])]); }