feat: retrait cercle et encadre + decalage centrage de la cible pris en compte

This commit is contained in:
qguillaume
2026-06-11 19:40:30 +02:00
parent 2107f187b6
commit b4226216eb
3 changed files with 8 additions and 95 deletions

1
.gitignore vendored
View File

@@ -11,6 +11,7 @@
.svn/ .svn/
.swiftpm/ .swiftpm/
migrate_working_dir/ migrate_working_dir/
.claude/
# IntelliJ related # IntelliJ related
*.iml *.iml

View File

@@ -469,30 +469,11 @@ class _CaptureScreenState extends State<CaptureScreen>
), ),
), ),
// 1.bis Cercle dessiné autour de la cible circulaire détectée // 2. Coins du cadre et mire centrale (sans le cadre rectangulaire)
if (_targetResult != null && _targetResult!.success)
Center(
child: AspectRatio(
aspectRatio: 3 / 4,
child: CustomPaint(
painter: _TargetCirclePainter(
target: _targetResult!,
color: _targetReady ? frameColor : Colors.white,
highlighted: _targetReady,
),
),
),
),
// 2. Cadre de visée avec coins et mire centrale
Center( Center(
child: Container( child: SizedBox(
width: MediaQuery.of(context).size.width * 0.85, width: MediaQuery.of(context).size.width * 0.85,
height: MediaQuery.of(context).size.width * 0.85, height: MediaQuery.of(context).size.width * 0.85,
decoration: BoxDecoration(
border: Border.all(color: frameColor, width: 2.0),
borderRadius: BorderRadius.circular(16),
),
child: Stack( child: Stack(
children: [ children: [
_buildCameraCorner(TopLeft: true, color: frameColor), _buildCameraCorner(TopLeft: true, color: frameColor),
@@ -934,69 +915,4 @@ class _CaptureScreenState extends State<CaptureScreen>
), ),
); );
} }
}
// ─────────────────────────────────────────────────────────────────────────
// Painter qui dessine un cercle autour de la cible circulaire détectée.
// Le centre et le rayon viennent d'OpenCV (coordonnées normalisées 0..1).
// Blanc tant que la cible n'est pas bien cadrée, couleur d'état sinon.
// ─────────────────────────────────────────────────────────────────────────
class _TargetCirclePainter extends CustomPainter {
final TargetDetectionResult target;
final Color color;
final bool highlighted;
_TargetCirclePainter({
required this.target,
required this.color,
required this.highlighted,
});
@override
void paint(Canvas canvas, Size size) {
// Centre en pixels dans la zone de dessin
final double cx = target.centerX * size.width;
final double cy = target.centerY * size.height;
// radius est normalisé par min(largeur, hauteur) côté OpenCV
final double r = target.radius * math.min(size.width, size.height);
final Paint stroke = Paint()
..style = PaintingStyle.stroke
..strokeWidth = highlighted ? 3.5 : 2.0
..color = color;
// Cercle principal autour de la cible
canvas.drawCircle(Offset(cx, cy), r, stroke);
// Petite croix au centre détecté
final Paint cross = Paint()
..strokeWidth = 2.0
..color = color;
const double k = 10;
canvas.drawLine(Offset(cx - k, cy), Offset(cx + k, cy), cross);
canvas.drawLine(Offset(cx, cy - k), Offset(cx, cy + k), cross);
// Étiquette "CIBLE" quand elle est bien cadrée
if (highlighted) {
final tp = TextPainter(
text: TextSpan(
text: ' CIBLE ',
style: TextStyle(
color: Colors.black,
fontSize: 12,
fontWeight: FontWeight.bold,
backgroundColor: color,
),
),
textDirection: TextDirection.ltr,
)..layout();
tp.paint(canvas, Offset(cx - tp.width / 2, (cy - r - 20).clamp(0.0, size.height)));
}
}
@override
bool shouldRepaint(_TargetCirclePainter old) =>
old.target != target ||
old.color != color ||
old.highlighted != highlighted;
} }

View File

@@ -389,18 +389,14 @@ class _CropScreenState extends State<CropScreen> {
Future<void> _onCropConfirm() async { Future<void> _onCropConfirm() async {
setState(() => _isLoading = true); setState(() => _isLoading = true);
try { try {
// CORRECTIF ZOOM : On sauvegarde et réinitialise le zoom avant de calculer // La zone de découpe doit refléter le DÉPLACEMENT (pan) et la ROTATION,
// la zone de découpe pour ne pas transmettre le zoom aux écrans suivants // mais PAS le ZOOM. On neutralise donc temporairement _scale à 1.0 pour
// le calcul, tout en conservant _offset. La rotation est appliquée à part
// par cropToSquare(rotationDegrees: _rotation).
final savedScale = _scale; final savedScale = _scale;
final savedOffset = _offset;
_scale = 1.0; _scale = 1.0;
_offset = Offset.zero;
final cropRect = _calculateCropRect(); final cropRect = _calculateCropRect();
_scale = savedScale; // on restaure pour l'affichage (retour arrière)
// On restaure pour l'affichage (au cas où on revient en arrière)
_scale = savedScale;
_offset = savedOffset;
// AJOUT DE LA DECOUPE ET DU REDRESSEMENT GÉOMÉTRIQUE PHYSIQUE // AJOUT DE LA DECOUPE ET DU REDRESSEMENT GÉOMÉTRIQUE PHYSIQUE
final croppedImagePath = await _cropService.cropToSquare( final croppedImagePath = await _cropService.cropToSquare(