Skip to content

Commit

Permalink
improved paths viewer
Browse files Browse the repository at this point in the history
  • Loading branch information
mootw committed Jan 25, 2024
1 parent 2663a7e commit 9e872ed
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 65 deletions.
11 changes: 7 additions & 4 deletions app/lib/screens/analysis/events_heatmaps.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@ class AnalysisEventsHeatmap extends StatelessWidget {
body: ListView(
children: [
Text("Autos", style: Theme.of(context).textTheme.titleMedium),
AutoPathsViewer(
PathsViewer(
paths: [
for (final match in data.event.matches.values)
for (final robot in match.robot.entries)
match.robot[robot.key]!.timelineInterpolated
.where((element) => element.isInAuto)
.toList()
(
label: '${match.description} ${robot.key}',
path: match.robot[robot.key]!.timelineInterpolated
.where((element) => element.isInAuto)
.toList()
)
],
),
for (final eventType in data.event.config.matchscouting.events) ...[
Expand Down
15 changes: 9 additions & 6 deletions app/lib/screens/analysis/match_preview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -201,16 +201,19 @@ class _AnalysisMatchPreviewState extends State<AnalysisMatchPreview> {
Text("Autos",
style:
Theme.of(context).textTheme.titleMedium),
AutoPathsViewer(
PathsViewer(
size: 300,
paths: [
for (final match
in data.event.teamRecordedMatches(team))
match.value.robot[team.toString()]!
.timelineInterpolatedRedNormalized(
data.event.config.fieldStyle)
.where((element) => element.isInAuto)
.toList()
(
label: match.value.description,
path: match.value.robot[team.toString()]!
.timelineInterpolatedRedNormalized(
data.event.config.fieldStyle)
.where((element) => element.isInAuto)
.toList()
)
],
),
],
Expand Down
2 changes: 1 addition & 1 deletion app/lib/screens/configure_source.dart
Original file line number Diff line number Diff line change
Expand Up @@ -180,4 +180,4 @@ Future<String?> createNewEvent(BuildContext context) async {
}

FRCEvent get emptyNewEvent => FRCEvent(
config: EventConfig(name: 'Event Name', team: 6749, fieldImage: ''));
config: const EventConfig(name: 'Event Name', team: 6749, fieldImage: ''));
13 changes: 8 additions & 5 deletions app/lib/screens/match_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,15 @@ class _MatchPageState extends State<MatchPage> {
children: [
const SizedBox(height: 16),
Text("Autos", style: Theme.of(context).textTheme.titleMedium),
AutoPathsViewer(
PathsViewer(
paths: [
for (final robot in match.robot.values)
robot.timelineInterpolated
.where((element) => element.isInAuto)
.toList()
for (final robot in match.robot.entries)
(
label: robot.key,
path: robot.value.timelineInterpolated
.where((element) => element.isInAuto)
.toList()
)
],
),
],
Expand Down
16 changes: 10 additions & 6 deletions app/lib/screens/view_team_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -178,17 +178,21 @@ class _TeamViewPageState extends State<TeamViewPage> {
const SizedBox(height: 16),
Text("Autos",
style: Theme.of(context).textTheme.titleMedium),
AutoPathsViewer(
PathsViewer(
// Make it larger since its the team page so BIG
size: 600,
paths: [
for (final match in data.event
.teamRecordedMatches(widget.teamNumber))
match.value.robot[widget.teamNumber.toString()]!
.timelineInterpolatedRedNormalized(
data.event.config.fieldStyle)
.where((element) => element.isInAuto)
.toList()
(
label: match.value.description,
path: match
.value.robot[widget.teamNumber.toString()]!
.timelineInterpolatedRedNormalized(
data.event.config.fieldStyle)
.where((element) => element.isInAuto)
.toList()
)
],
),
],
Expand Down
137 changes: 94 additions & 43 deletions app/lib/widgets/fieldwidget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ const double robotFieldProportion = robotSizeMeters / fieldWidthMeters;
const double largeFieldSize = 355;
const double smallFieldSize = 255;


/// used on the scouting pages
class FieldPositionSelector extends StatelessWidget {
const FieldPositionSelector(
Expand Down Expand Up @@ -267,7 +266,7 @@ class FieldHeatMap extends StatelessWidget {
child: FieldMap(
//size: size,
children: [
Container(color: Colors.black26),
Container(color: Colors.black12),
CustomPaint(
size: Size.infinite,
painter: HeatMap(events: events),
Expand All @@ -280,76 +279,130 @@ class FieldHeatMap extends StatelessWidget {
}

class FullScreenFieldSelector extends StatelessWidget {

final Widget child;
final Widget? showAbove;

const FullScreenFieldSelector({super.key, required this.child});
const FullScreenFieldSelector(
{super.key, required this.child, this.showAbove});

@override
Widget build(BuildContext context) {
if (showAbove != null) {
return Stack(
children: [
InkWell(
child: child,
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => Scaffold(
appBar: AppBar(),
body: Stack(
children: [
child,
showAbove!,
],
),
)));
},
),
showAbove!,
],
);
}
return InkWell(
child: child,
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => Scaffold(
appBar: AppBar(),
body: child,
)));
builder: (context) => Scaffold(
appBar: AppBar(),
body: child,
)));
},
);
}
}

class AutoPathsViewer extends StatelessWidget {
final List<List<MatchEvent>> paths;
class PathsViewer extends StatefulWidget {
final List<({String label, List<MatchEvent> path})> paths;
final bool emphasizeStartPoint;
final bool eventLabels;
final bool useRedNormalized;
final double size;

const AutoPathsViewer(
const PathsViewer(
{super.key,
required this.paths,
this.size = largeFieldSize,
this.emphasizeStartPoint = true,
this.useRedNormalized = true,
this.eventLabels = true});
this.useRedNormalized = true});

@override
State<PathsViewer> createState() => _PathsViewerState();
}

class _PathsViewerState extends State<PathsViewer> {
int filterIndex = -1;

@override
Widget build(BuildContext context) {
final List<({String label, List<MatchEvent> path})> filteredPaths;
if (filterIndex == -1) {
filteredPaths = widget.paths;
} else {
filteredPaths = [widget.paths[filterIndex]];
}

return ConstrainedBox(
constraints: BoxConstraints.loose(Size(size, size / 2)),
constraints: BoxConstraints.loose(Size(widget.size, widget.size / 2)),
child: FullScreenFieldSelector(
showAbove: Align(
alignment: Alignment.bottomLeft,
child: Container(
color: Colors.black87,
height: 32,
child: ListView(scrollDirection: Axis.horizontal, children: [
TextButton(
onPressed: () => setState(() {
filterIndex = -1;
}),
child: const Text("All")),
for (final (idx, item) in widget.paths.indexed)
TextButton(
onPressed: () => setState(() {
filterIndex = idx;
}),
child: Text(
item.label,
style: TextStyle(
color: getColorFromIndex(widget.paths.indexOf(item))),
)),
]),
),
),
child: FieldMap(children: [
Container(color: Colors.black26),
for (final match in paths) ...[
Container(color: Colors.black12),
for (final path in filteredPaths) ...[
CustomPaint(
size: Size.infinite,
painter: MapLine(
emphasizeStartPoint: emphasizeStartPoint,
color: getColorFromIndex(paths.indexOf(match)),
events: match,
eventLabels: eventLabels),
emphasizeStartPoint: widget.emphasizeStartPoint,
color: getColorFromIndex(filteredPaths.indexOf(path)),
events: path.path),
),
],
Align(
alignment: Alignment.bottomRight,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (eventLabels)
for (final match in paths)
for (final event
in match.where((event) => !event.isPositionEvent))
Text(
event.getLabelFromConfig(
context.watch<DataProvider>().event.config),
style: TextStyle(
color: getColorFromIndex(paths.indexOf(match)),
fontSize: 10,
backgroundColor: Colors.black87)),
],
),
// Event Labels
Stack(
children: [
for (final match in filteredPaths)
for (final event
in match.path.where((event) => !event.isPositionEvent))
Align(
alignment: Alignment(event.position.x, -event.position.y),
child: Text(
'${event.time}.${event.getLabelFromConfig(context.watch<DataProvider>().event.config)}',
style: const TextStyle(
fontSize: 11, backgroundColor: Colors.black26)),
),
],
),
]),
),
Expand Down Expand Up @@ -415,12 +468,10 @@ class MapLine extends CustomPainter {
Color color;
List<MatchEvent> events;
bool emphasizeStartPoint;
bool eventLabels;

MapLine(
{required this.events,
required this.emphasizeStartPoint,
required this.eventLabels,
this.color = Colors.green});

@override
Expand All @@ -430,7 +481,7 @@ class MapLine extends CustomPainter {
}
Paint p = Paint();
p.color = color;
p.strokeWidth = 2;
p.strokeWidth = 3;
p.strokeCap = StrokeCap.round;
p.style = PaintingStyle.stroke;

Expand All @@ -451,7 +502,7 @@ class MapLine extends CustomPainter {
p.style = PaintingStyle.fill;

if (emphasizeStartPoint) {
canvas.drawCircle(Offset(startingPosition[0], startingPosition[1]), 5, p);
canvas.drawCircle(Offset(startingPosition[0], startingPosition[1]), 6, p);
}
}

Expand Down

0 comments on commit 9e872ed

Please sign in to comment.