correction du centrage des vecteurs de zone (cercle) pour le plotting

This commit is contained in:
2026-02-21 10:22:18 +01:00
parent 2e81f4b69e
commit d4cb179fde
4 changed files with 298 additions and 58 deletions

View File

@@ -53,6 +53,7 @@ class AnalysisProvider extends ChangeNotifier {
double _targetCenterX = 0.5;
double _targetCenterY = 0.5;
double _targetRadius = 0.4;
double _targetInnerRadius = 0.04;
int _ringCount = 10;
List<double>? _ringRadii; // Individual ring radii multipliers
double _imageAspectRatio = 1.0; // width / height
@@ -83,6 +84,7 @@ class AnalysisProvider extends ChangeNotifier {
double get targetCenterX => _targetCenterX;
double get targetCenterY => _targetCenterY;
double get targetRadius => _targetRadius;
double get targetInnerRadius => _targetInnerRadius;
int get ringCount => _ringCount;
List<double>? get ringRadii =>
_ringRadii != null ? List.unmodifiable(_ringRadii!) : null;
@@ -138,6 +140,7 @@ class AnalysisProvider extends ChangeNotifier {
_targetCenterX = 0.5;
_targetCenterY = 0.5;
_targetRadius = 0.4;
_targetInnerRadius = 0.04;
// Initialize empty shots list
_shots = [];
@@ -160,6 +163,7 @@ class AnalysisProvider extends ChangeNotifier {
_targetCenterX = result.centerX;
_targetCenterY = result.centerY;
_targetRadius = result.radius;
_targetInnerRadius = result.radius * 0.1;
// Create shots from detected impacts
_shots = result.impacts.map((impact) {
@@ -488,12 +492,14 @@ class AnalysisProvider extends ChangeNotifier {
void adjustTargetPosition(
double centerX,
double centerY,
double innerRadius,
double radius, {
int? ringCount,
List<double>? ringRadii,
}) {
_targetCenterX = centerX;
_targetCenterY = centerY;
_targetInnerRadius = innerRadius;
_targetRadius = radius;
if (ringCount != null) {
_ringCount = ringCount;
@@ -520,7 +526,12 @@ class AnalysisProvider extends ChangeNotifier {
final result = await _opencvTargetService.detectTarget(_imagePath!);
if (result.success) {
adjustTargetPosition(result.centerX, result.centerY, result.radius);
adjustTargetPosition(
result.centerX,
result.centerY,
result.radius * 0.1,
result.radius,
);
return true;
}
return false;
@@ -687,6 +698,7 @@ class AnalysisProvider extends ChangeNotifier {
_targetCenterX = 0.5;
_targetCenterY = 0.5;
_targetRadius = 0.4;
_targetInnerRadius = 0.04;
_ringCount = 10;
_ringRadii = null;
_imageAspectRatio = 1.0;

View File

@@ -275,7 +275,7 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
child: Column(
children: [
// Auto-calibrate button
/*SizedBox(
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: () async {
@@ -334,7 +334,7 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
foregroundColor: Colors.white,
),
),
),*/
),
const SizedBox(height: 16),
// Ring count slider
Row(
@@ -361,6 +361,7 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
provider.adjustTargetPosition(
provider.targetCenterX,
provider.targetCenterY,
provider.targetInnerRadius,
provider.targetRadius,
ringCount: value.round(),
);
@@ -411,6 +412,7 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
provider.adjustTargetPosition(
provider.targetCenterX,
provider.targetCenterY,
provider.targetInnerRadius,
value,
ringCount: provider.ringCount,
);
@@ -535,6 +537,7 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
initialCenterX: provider.targetCenterX,
initialCenterY: provider.targetCenterY,
initialRadius: provider.targetRadius,
initialInnerRadius: provider.targetInnerRadius,
initialRingCount: provider.ringCount,
initialRingRadii: provider.ringRadii,
targetType: provider.targetType!,
@@ -542,6 +545,7 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
(
centerX,
centerY,
innerRadius,
radius,
ringCount, {
List<double>? ringRadii,
@@ -549,6 +553,7 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
provider.adjustTargetPosition(
centerX,
centerY,
innerRadius,
radius,
ringCount: ringCount,
ringRadii: ringRadii,

View File

@@ -13,16 +13,26 @@ class TargetCalibration extends StatefulWidget {
final double initialCenterX;
final double initialCenterY;
final double initialRadius;
final double initialInnerRadius;
final int initialRingCount;
final TargetType targetType;
final List<double>? initialRingRadii;
final Function(double centerX, double centerY, double radius, int ringCount, {List<double>? ringRadii}) onCalibrationChanged;
final Function(
double centerX,
double centerY,
double innerRadius,
double radius,
int ringCount, {
List<double>? ringRadii,
})
onCalibrationChanged;
const TargetCalibration({
super.key,
required this.initialCenterX,
required this.initialCenterY,
required this.initialRadius,
required this.initialInnerRadius,
this.initialRingCount = 10,
required this.targetType,
this.initialRingRadii,
@@ -37,11 +47,13 @@ class _TargetCalibrationState extends State<TargetCalibration> {
late double _centerX;
late double _centerY;
late double _radius;
late double _innerRadius;
late int _ringCount;
late List<double> _ringRadii;
bool _isDraggingCenter = false;
bool _isDraggingRadius = false;
bool _isDraggingInnerRadius = false;
@override
void initState() {
@@ -49,28 +61,57 @@ class _TargetCalibrationState extends State<TargetCalibration> {
_centerX = widget.initialCenterX;
_centerY = widget.initialCenterY;
_radius = widget.initialRadius;
_innerRadius = widget.initialInnerRadius;
_ringCount = widget.initialRingCount;
_initRingRadii();
}
void _initRingRadii() {
if (widget.initialRingRadii != null && widget.initialRingRadii!.length == _ringCount) {
if (widget.initialRingRadii != null &&
widget.initialRingRadii!.length == _ringCount) {
_ringRadii = List.from(widget.initialRingRadii!);
} else {
// Initialize with default proportional radii
_ringRadii = List.generate(_ringCount, (i) => (i + 1) / _ringCount);
// Initialize with default proportional radii interpolated between inner and outer
_ringRadii = List.generate(_ringCount, (i) {
if (_ringCount <= 1) return 1.0;
final ratio = _innerRadius / _radius;
return ratio + (1.0 - ratio) * i / (_ringCount - 1);
});
}
}
@override
void didUpdateWidget(TargetCalibration oldWidget) {
super.didUpdateWidget(oldWidget);
bool shouldReinit = false;
if (widget.initialCenterX != oldWidget.initialCenterX &&
!_isDraggingCenter) {
_centerX = widget.initialCenterX;
}
if (widget.initialCenterY != oldWidget.initialCenterY &&
!_isDraggingCenter) {
_centerY = widget.initialCenterY;
}
if (widget.initialRingCount != oldWidget.initialRingCount) {
_ringCount = widget.initialRingCount;
_initRingRadii();
shouldReinit = true;
}
if (widget.initialRadius != oldWidget.initialRadius && !_isDraggingRadius) {
_radius = widget.initialRadius;
shouldReinit = true;
}
if (widget.initialInnerRadius != oldWidget.initialInnerRadius &&
!_isDraggingInnerRadius) {
_innerRadius = widget.initialInnerRadius;
shouldReinit = true;
}
if (widget.initialRingRadii != oldWidget.initialRingRadii) {
shouldReinit = true;
}
if (shouldReinit) {
_initRingRadii();
}
}
@@ -90,11 +131,13 @@ class _TargetCalibrationState extends State<TargetCalibration> {
centerX: _centerX,
centerY: _centerY,
radius: _radius,
innerRadius: _innerRadius,
ringCount: _ringCount,
ringRadii: _ringRadii,
targetType: widget.targetType,
isDraggingCenter: _isDraggingCenter,
isDraggingRadius: _isDraggingRadius,
isDraggingInnerRadius: _isDraggingInnerRadius,
),
),
);
@@ -109,21 +152,42 @@ class _TargetCalibrationState extends State<TargetCalibration> {
// Check if tapping on center handle
final distToCenter = _distance(tapX, tapY, _centerX, _centerY);
// Check if tapping on radius handle (on the right edge of the outermost circle)
// Check if tapping on outer radius handle
final minDim = math.min(size.width, size.height);
final outerRadius = _radius * (_ringRadii.isNotEmpty ? _ringRadii.last : 1.0);
final outerRadius = _radius;
final radiusHandleX = _centerX + outerRadius * minDim / size.width;
final radiusHandleY = _centerY;
final distToRadiusHandle = _distance(tapX, tapY, radiusHandleX.clamp(0.0, 1.0), radiusHandleY.clamp(0.0, 1.0));
final distToOuterHandle = _distance(
tapX,
tapY,
radiusHandleX.clamp(0.0, 1.0),
radiusHandleY.clamp(0.0, 1.0),
);
// Check if tapping on inner radius handle (top edge of innermost circle)
final actualInnerRadius = _innerRadius;
final innerHandleX = _centerX;
final innerHandleY = _centerY - actualInnerRadius * minDim / size.height;
final distToInnerHandle = _distance(
tapX,
tapY,
innerHandleX.clamp(0.0, 1.0),
innerHandleY.clamp(0.0, 1.0),
);
// Increase touch target size slightly for handles
if (distToCenter < 0.05) {
setState(() {
_isDraggingCenter = true;
});
} else if (distToRadiusHandle < 0.05) {
} else if (distToOuterHandle < 0.05) {
setState(() {
_isDraggingRadius = true;
});
} else if (distToInnerHandle < 0.05) {
setState(() {
_isDraggingInnerRadius = true;
});
} else if (distToCenter < _radius + 0.02) {
// Tapping inside the target - move center
setState(() {
@@ -143,19 +207,36 @@ class _TargetCalibrationState extends State<TargetCalibration> {
_centerX = _centerX + deltaX;
_centerY = _centerY + deltaY;
} else if (_isDraggingRadius) {
// Adjust outer radius (scales all rings proportionally)
// Adjust outer radius
final newRadius = _radius + deltaX * (size.width / minDim);
_radius = newRadius.clamp(0.05, 3.0);
_radius = newRadius.clamp(math.max(0.05, _innerRadius + 0.01), 3.0);
_initRingRadii(); // Recalculate linear separation
} else if (_isDraggingInnerRadius) {
// Adjust inner radius (sliding up reduces Y, so deltaY is negative when growing. Thus we subtract deltaY)
final newInnerRadius = _innerRadius - deltaY * (size.height / minDim);
_innerRadius = newInnerRadius.clamp(
0.01,
math.max(0.01, _radius - 0.01),
);
_initRingRadii(); // Recalculate linear separation
}
});
widget.onCalibrationChanged(_centerX, _centerY, _radius, _ringCount, ringRadii: _ringRadii);
widget.onCalibrationChanged(
_centerX,
_centerY,
_innerRadius,
_radius,
_ringCount,
ringRadii: _ringRadii,
);
}
void _onPanEnd() {
setState(() {
_isDraggingCenter = false;
_isDraggingRadius = false;
_isDraggingInnerRadius = false;
});
}
@@ -170,21 +251,25 @@ class _CalibrationPainter extends CustomPainter {
final double centerX;
final double centerY;
final double radius;
final double innerRadius;
final int ringCount;
final List<double> ringRadii;
final TargetType targetType;
final bool isDraggingCenter;
final bool isDraggingRadius;
final bool isDraggingInnerRadius;
_CalibrationPainter({
required this.centerX,
required this.centerY,
required this.radius,
required this.innerRadius,
required this.ringCount,
required this.ringRadii,
required this.targetType,
required this.isDraggingCenter,
required this.isDraggingRadius,
required this.isDraggingInnerRadius,
});
@override
@@ -192,6 +277,7 @@ class _CalibrationPainter extends CustomPainter {
final centerPx = Offset(centerX * size.width, centerY * size.height);
final minDim = size.width < size.height ? size.width : size.height;
final baseRadiusPx = radius * minDim;
final innerRadiusPx = innerRadius * minDim;
if (targetType == TargetType.concentric) {
_drawConcentricZones(canvas, size, centerPx, baseRadiusPx);
@@ -199,17 +285,42 @@ class _CalibrationPainter extends CustomPainter {
_drawSilhouetteZones(canvas, size, centerPx, baseRadiusPx);
}
// Fullscreen crosshairs when dragging center
if (isDraggingCenter) {
final crosshairLinePaint = Paint()
..color = AppTheme.successColor.withValues(alpha: 0.5)
..strokeWidth = 1;
canvas.drawLine(
Offset(0, centerPx.dy),
Offset(size.width, centerPx.dy),
crosshairLinePaint,
);
canvas.drawLine(
Offset(centerPx.dx, 0),
Offset(centerPx.dx, size.height),
crosshairLinePaint,
);
}
// Draw center handle
_drawCenterHandle(canvas, centerPx);
// Draw radius handle (for outer ring)
_drawRadiusHandle(canvas, size, centerPx, baseRadiusPx);
// Draw inner radius handle
_drawInnerRadiusHandle(canvas, size, centerPx, innerRadiusPx);
// Draw instructions
_drawInstructions(canvas, size);
}
void _drawConcentricZones(Canvas canvas, Size size, Offset center, double baseRadius) {
void _drawConcentricZones(
Canvas canvas,
Size size,
Offset center,
double baseRadius,
) {
// Generate colors for zones
List<Color> zoneColors = [];
for (int i = 0; i < ringCount; i++) {
@@ -235,7 +346,9 @@ class _CalibrationPainter extends CustomPainter {
// Draw from outside to inside
for (int i = ringCount - 1; i >= 0; i--) {
final ringRadius = ringRadii.length > i ? ringRadii[i] : (i + 1) / ringCount;
final ringRadius = ringRadii.length > i
? ringRadii[i]
: (i + 1) / ringCount;
final zoneRadius = baseRadius * ringRadius;
zonePaint.color = zoneColors[i];
@@ -244,12 +357,12 @@ class _CalibrationPainter extends CustomPainter {
}
// Draw zone labels (only if within visible area)
final textPainter = TextPainter(
textDirection: TextDirection.ltr,
);
final textPainter = TextPainter(textDirection: TextDirection.ltr);
for (int i = 0; i < ringCount; i++) {
final ringRadius = ringRadii.length > i ? ringRadii[i] : (i + 1) / ringCount;
final ringRadius = ringRadii.length > i
? ringRadii[i]
: (i + 1) / ringCount;
final prevRingRadius = i > 0
? (ringRadii.length > i - 1 ? ringRadii[i - 1] : i / ringCount)
: 0.0;
@@ -268,9 +381,7 @@ class _CalibrationPainter extends CustomPainter {
color: Colors.white.withValues(alpha: 0.9),
fontSize: 12,
fontWeight: FontWeight.bold,
shadows: const [
Shadow(color: Colors.black, blurRadius: 2),
],
shadows: const [Shadow(color: Colors.black, blurRadius: 2)],
),
);
textPainter.layout();
@@ -278,14 +389,24 @@ class _CalibrationPainter extends CustomPainter {
// Draw label on the right side of each zone
final labelY = center.dy - textPainter.height / 2;
if (labelY >= 0 && labelY <= size.height) {
textPainter.paint(canvas, Offset(labelX - textPainter.width / 2, labelY));
textPainter.paint(
canvas,
Offset(labelX - textPainter.width / 2, labelY),
);
}
}
}
void _drawSilhouetteZones(Canvas canvas, Size size, Offset center, double radius) {
void _drawSilhouetteZones(
Canvas canvas,
Size size,
Offset center,
double radius,
) {
// Simplified silhouette zones
final paint = Paint()..style = PaintingStyle.stroke..strokeWidth = 2;
final paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 2;
// Draw silhouette outline (simplified as rectangle for now)
final silhouetteWidth = radius * 0.8;
@@ -293,7 +414,11 @@ class _CalibrationPainter extends CustomPainter {
paint.color = Colors.green.withValues(alpha: 0.5);
canvas.drawRect(
Rect.fromCenter(center: center, width: silhouetteWidth, height: silhouetteHeight),
Rect.fromCenter(
center: center,
width: silhouetteWidth,
height: silhouetteHeight,
),
paint,
);
}
@@ -316,17 +441,36 @@ class _CalibrationPainter extends CustomPainter {
final crossPaint = Paint()
..color = isDraggingCenter ? AppTheme.successColor : AppTheme.primaryColor
..strokeWidth = 2;
canvas.drawLine(Offset(center.dx - 20, center.dy), Offset(center.dx - 8, center.dy), crossPaint);
canvas.drawLine(Offset(center.dx + 8, center.dy), Offset(center.dx + 20, center.dy), crossPaint);
canvas.drawLine(Offset(center.dx, center.dy - 20), Offset(center.dx, center.dy - 8), crossPaint);
canvas.drawLine(Offset(center.dx, center.dy + 8), Offset(center.dx, center.dy + 20), crossPaint);
canvas.drawLine(
Offset(center.dx - 20, center.dy),
Offset(center.dx - 8, center.dy),
crossPaint,
);
canvas.drawLine(
Offset(center.dx + 8, center.dy),
Offset(center.dx + 20, center.dy),
crossPaint,
);
canvas.drawLine(
Offset(center.dx, center.dy - 20),
Offset(center.dx, center.dy - 8),
crossPaint,
);
canvas.drawLine(
Offset(center.dx, center.dy + 8),
Offset(center.dx, center.dy + 20),
crossPaint,
);
}
void _drawRadiusHandle(Canvas canvas, Size size, Offset center, double baseRadius) {
void _drawRadiusHandle(
Canvas canvas,
Size size,
Offset center,
double baseRadius,
) {
// Radius handle on the right edge of the outermost ring
final outerRingRadius = ringRadii.isNotEmpty ? ringRadii.last : 1.0;
final actualRadius = baseRadius * outerRingRadius;
final actualHandleX = center.dx + actualRadius;
final actualHandleX = center.dx + baseRadius;
final clampedHandleX = actualHandleX.clamp(20.0, size.width - 20);
final clampedHandleY = center.dy.clamp(20.0, size.height - 20);
final handlePos = Offset(clampedHandleX, clampedHandleY);
@@ -376,7 +520,7 @@ class _CalibrationPainter extends CustomPainter {
// Label
final textPainter = TextPainter(
text: const TextSpan(
text: 'RAYON',
text: 'EXT.',
style: TextStyle(
color: Colors.white,
fontSize: 8,
@@ -392,6 +536,78 @@ class _CalibrationPainter extends CustomPainter {
);
}
void _drawInnerRadiusHandle(
Canvas canvas,
Size size,
Offset center,
double innerRadiusPx,
) {
// Inner radius handle on the top edge of the innermost ring
final actualHandleY = center.dy - innerRadiusPx;
final clampedHandleX = center.dx.clamp(20.0, size.width - 20);
final clampedHandleY = actualHandleY.clamp(20.0, size.height - 20);
final handlePos = Offset(clampedHandleX, clampedHandleY);
final isClamped = actualHandleY < 20.0;
final paint = Paint()
..color = isDraggingInnerRadius
? AppTheme.successColor
: (isClamped ? Colors.orange : Colors.purpleAccent)
..style = PaintingStyle.fill;
// Draw handle
canvas.drawCircle(handlePos, 14, paint);
// Up/Down arrow indicators
final arrowPaint = Paint()
..color = Colors.white
..strokeWidth = 2
..style = PaintingStyle.stroke;
// Up arrow
canvas.drawLine(
Offset(handlePos.dx, handlePos.dy - 4),
Offset(handlePos.dx - 4, handlePos.dy - 8),
arrowPaint,
);
canvas.drawLine(
Offset(handlePos.dx, handlePos.dy - 4),
Offset(handlePos.dx + 4, handlePos.dy - 8),
arrowPaint,
);
// Down arrow
canvas.drawLine(
Offset(handlePos.dx, handlePos.dy + 4),
Offset(handlePos.dx - 4, handlePos.dy + 8),
arrowPaint,
);
canvas.drawLine(
Offset(handlePos.dx, handlePos.dy + 4),
Offset(handlePos.dx + 4, handlePos.dy + 8),
arrowPaint,
);
// Label
final textPainter = TextPainter(
text: const TextSpan(
text: 'INT.',
style: TextStyle(
color: Colors.white,
fontSize: 8,
fontWeight: FontWeight.bold,
),
),
textDirection: TextDirection.ltr,
);
textPainter.layout();
textPainter.paint(
canvas,
Offset(handlePos.dx - textPainter.width / 2, handlePos.dy - 24),
);
}
void _drawInstructions(Canvas canvas, Size size) {
const instruction = 'Deplacez le centre ou ajustez le rayon';
@@ -418,9 +634,11 @@ class _CalibrationPainter extends CustomPainter {
return centerX != oldDelegate.centerX ||
centerY != oldDelegate.centerY ||
radius != oldDelegate.radius ||
innerRadius != oldDelegate.innerRadius ||
ringCount != oldDelegate.ringCount ||
isDraggingCenter != oldDelegate.isDraggingCenter ||
isDraggingRadius != oldDelegate.isDraggingRadius ||
isDraggingInnerRadius != oldDelegate.isDraggingInnerRadius ||
ringRadii != oldDelegate.ringRadii;
}
}