import 'package:flutter/material.dart'; import 'package:latlong2/latlong.dart' hide Path; import '../../domain/waypoint/flat_waypoint_list.dart'; /// Kompaktes, nicht interaktives Hoehenprofil in der Fusszeile /// (HTML-Demonstrator: #altProfile/buildProfileSvg()). Fuer die grosse, /// per Drag editierbare Ansicht siehe die Altitude-Tab im /// WaypointListPanel. class MiniAltitudeProfile extends StatelessWidget { const MiniAltitudeProfile({ super.key, required this.waypoints, required this.legClimbBad, required this.vertexBad, required this.activeIndex, }); final List waypoints; final List legClimbBad; final List vertexBad; final int activeIndex; static const _distance = Distance(roundResult: false); @override Widget build(BuildContext context) { return SizedBox( height: 56, width: double.infinity, child: waypoints.length < 2 ? const Center( child: Text( 'Altitude profile', style: TextStyle(color: Colors.white38, fontSize: 11), ), ) : CustomPaint( size: Size.infinite, painter: _MiniProfilePainter( waypoints: waypoints, legClimbBad: legClimbBad, vertexBad: vertexBad, activeIndex: activeIndex, ), ), ); } } class _MiniProfilePainter extends CustomPainter { const _MiniProfilePainter({ required this.waypoints, required this.legClimbBad, required this.vertexBad, required this.activeIndex, }); final List waypoints; final List legClimbBad; final List vertexBad; final int activeIndex; static const _pad = 4.0; @override void paint(Canvas canvas, Size size) { final n = waypoints.length; final boxW = size.width - 16; final boxH = size.height - 4; final cum = [0]; for (var i = 1; i < n; i++) { final a = LatLng(waypoints[i - 1].lat, waypoints[i - 1].lon); final b = LatLng(waypoints[i].lat, waypoints[i].lon); cum.add(cum[i - 1] + MiniAltitudeProfile._distance(a, b)); } final totalD = cum.last == 0 ? 1.0 : cum.last; final alts = [for (final w in waypoints) w.altitudeM]; var altMin = alts.reduce((a, b) => a < b ? a : b); var altMax = alts.reduce((a, b) => a > b ? a : b); if (altMax - altMin < 10) { altMax += 5; altMin -= 5; } double xAt(int idx) => _pad + (cum[idx] / totalD) * (boxW - 2 * _pad); double yAt(double alt) => boxH - _pad - ((alt - altMin) / (altMax - altMin)) * (boxH - 2 * _pad); final areaPath = Path()..moveTo(xAt(0), boxH - _pad); for (var i = 0; i < n; i++) { areaPath.lineTo(xAt(i), yAt(alts[i])); } areaPath.lineTo(xAt(n - 1), boxH - _pad); areaPath.close(); canvas.drawPath( areaPath, Paint()..color = const Color(0xFF4D9DFF).withValues(alpha: 0.18), ); for (var i = 0; i < n - 1; i++) { final bad = i < legClimbBad.length && legClimbBad[i]; canvas.drawLine( Offset(xAt(i), yAt(alts[i])), Offset(xAt(i + 1), yAt(alts[i + 1])), Paint() ..color = bad ? const Color(0xFFE2574A) : Colors.white ..strokeWidth = 2, ); } for (var i = 0; i < n; i++) { final active = i == activeIndex; final bad = i < vertexBad.length && vertexBad[i]; final color = active ? const Color(0xFF4D9DFF) : (bad ? const Color(0xFFE2574A) : Colors.white); canvas.drawCircle( Offset(xAt(i), yAt(alts[i])), active ? 4.5 : 2.4, Paint()..color = color, ); if (active) { canvas.drawCircle( Offset(xAt(i), yAt(alts[i])), 4.5, Paint() ..color = Colors.white ..style = PaintingStyle.stroke ..strokeWidth = 1.5, ); } } } @override bool shouldRepaint(covariant _MiniProfilePainter oldDelegate) => oldDelegate.waypoints != waypoints || oldDelegate.legClimbBad != legClimbBad || oldDelegate.vertexBad != vertexBad || oldDelegate.activeIndex != activeIndex; }