feat: retrait cercle et encadre + decalage centrage de la cible pris en compte
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,6 +11,7 @@
|
|||||||
.svn/
|
.svn/
|
||||||
.swiftpm/
|
.swiftpm/
|
||||||
migrate_working_dir/
|
migrate_working_dir/
|
||||||
|
.claude/
|
||||||
|
|
||||||
# IntelliJ related
|
# IntelliJ related
|
||||||
*.iml
|
*.iml
|
||||||
|
|||||||
@@ -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),
|
||||||
@@ -935,68 +916,3 @@ 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;
|
|
||||||
}
|
|
||||||
@@ -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(
|
||||||
|
|||||||
Reference in New Issue
Block a user