Toggle for sheetbot-like behavior of radar graphs

Man this is so jank
This commit is contained in:
dan63047 2024-06-10 14:55:32 +03:00
parent 6e9ecbf48e
commit c7475e8d5c
4 changed files with 284 additions and 9 deletions

View File

@ -1,6 +1,7 @@
import 'dart:io'; import 'dart:io';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tetra_stats/views/settings_view.dart' show subtitleStyle;
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/gen/strings.g.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
@ -19,6 +20,7 @@ class CustomizationView extends StatefulWidget {
class CustomizationState extends State<CustomizationView> { class CustomizationState extends State<CustomizationView> {
late SharedPreferences prefs; late SharedPreferences prefs;
late bool oskKagariGimmick; late bool oskKagariGimmick;
late bool sheetbotRadarGraphs;
void changeColor(Color color) { void changeColor(Color color) {
setState(() => pickerColor = color); setState(() => pickerColor = color);
@ -47,6 +49,11 @@ class CustomizationState extends State<CustomizationView> {
} else { } else {
oskKagariGimmick = true; oskKagariGimmick = true;
} }
if (prefs.getBool("sheetbotRadarGraphs") != null) {
sheetbotRadarGraphs = prefs.getBool("sheetbotRadarGraphs")!;
} else {
sheetbotRadarGraphs = false;
}
} }
ThemeData getTheme(BuildContext context, Color color){ ThemeData getTheme(BuildContext context, Color color){
@ -64,7 +71,7 @@ class CustomizationState extends State<CustomizationView> {
} }
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(t.settings), title: Text(t.customization),
), ),
backgroundColor: Colors.black, backgroundColor: Colors.black,
body: SafeArea( body: SafeArea(
@ -105,12 +112,20 @@ class CustomizationState extends State<CustomizationView> {
// subtitle: Text("Not implemented"), // subtitle: Text("Not implemented"),
// ), // ),
ListTile(title: Text(t.oskKagari), ListTile(title: Text(t.oskKagari),
subtitle: Text(t.oskKagariDescription), subtitle: Text(t.oskKagariDescription, style: subtitleStyle),
trailing: Switch(value: oskKagariGimmick, onChanged: (bool value){ trailing: Switch(value: oskKagariGimmick, onChanged: (bool value){
prefs.setBool("oskKagariGimmick", value); prefs.setBool("oskKagariGimmick", value);
setState(() { setState(() {
oskKagariGimmick = value; oskKagariGimmick = value;
}); });
}),),
ListTile(title: Text("Sheetbot-like behavior for radar graphs"),
subtitle: Text(t.oskKagariDescription, style: subtitleStyle),
trailing: Switch(value: sheetbotRadarGraphs, onChanged: (bool value){
prefs.setBool("sheetbotRadarGraphs", value);
setState(() {
sheetbotRadarGraphs = value;
});
}),) }),)
], ],
)), )),

View File

@ -1,10 +1,269 @@
import 'dart:math';
import 'package:fl_chart/fl_chart.dart'; import 'package:fl_chart/fl_chart.dart';
import 'package:fl_chart/src/chart/radar_chart/radar_chart_painter.dart';
import 'package:fl_chart/src/chart/radar_chart/radar_chart_renderer.dart';
import 'package:fl_chart/src/chart/base/base_chart/base_chart_painter.dart';
import 'package:fl_chart/src/utils/canvas_wrapper.dart';
import 'package:fl_chart/src/utils/utils.dart';
import 'package:tetra_stats/main.dart' show prefs;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/utils/numers_formats.dart'; import 'package:tetra_stats/utils/numers_formats.dart';
class MyRadarChartPainter extends RadarChartPainter{
MyRadarChartPainter() : super() {
_backgroundPaint = Paint()
..style = PaintingStyle.fill
..isAntiAlias = true;
_borderPaint = Paint()..style = PaintingStyle.stroke;
_gridPaint = Paint()..style = PaintingStyle.stroke;
_tickPaint = Paint()..style = PaintingStyle.stroke;
_graphPaint = Paint();
_graphBorderPaint = Paint();
_graphPointPaint = Paint();
_ticksTextPaint = TextPainter();
_titleTextPaint = TextPainter();
sheetbotRadarGraphs = prefs.getBool("sheetbotRadarGraphs")??false;
}
late Paint _borderPaint;
late Paint _backgroundPaint;
late Paint _gridPaint;
late Paint _tickPaint;
late Paint _graphPaint;
late Paint _graphBorderPaint;
late Paint _graphPointPaint;
late TextPainter _ticksTextPaint;
late TextPainter _titleTextPaint;
late bool sheetbotRadarGraphs;
@override
double getChartCenterValue(RadarChartData data) {
final dataSetMaxValue = sheetbotRadarGraphs ? max(data.maxEntry.value, data.minEntry.value.abs()) : data.maxEntry.value;
final dataSetMinValue = data.minEntry.value;
final tickSpace = getSpaceBetweenTicks(data);
final centerValue = (dataSetMinValue < 0 && sheetbotRadarGraphs) ? 0.0 : dataSetMinValue;
return dataSetMaxValue == dataSetMinValue
? getDefaultChartCenterValue()
: centerValue;
}
@override
double getSpaceBetweenTicks(RadarChartData data) {
final defaultCenterValue = getDefaultChartCenterValue();
final dataSetMaxValue = sheetbotRadarGraphs ? max(data.maxEntry.value, data.minEntry.value.abs()) : data.maxEntry.value;
final dataSetMinValue = (data.minEntry.value < 0 && sheetbotRadarGraphs) ? 0.0 : data.minEntry.value;
final tickSpace = sheetbotRadarGraphs ? dataSetMaxValue / data.tickCount : (dataSetMaxValue - dataSetMinValue) / data.tickCount;
final defaultTickSpace =
(dataSetMaxValue - defaultCenterValue) / (data.tickCount + 1);
return dataSetMaxValue == dataSetMinValue ? defaultTickSpace : tickSpace;
}
@override
double getScaledPoint(RadarEntry point, double radius, RadarChartData data) {
final centerValue = getChartCenterValue(data);
final distanceFromPointToCenter = point.value - centerValue;
final distanceFromMaxToCenter = max(data.maxEntry.value, data.minEntry.value.abs()) - centerValue;
if (distanceFromMaxToCenter == 0) {
return radius * distanceFromPointToCenter / 0.001;
}
return radius * distanceFromPointToCenter / distanceFromMaxToCenter;
}
@override
double getFirstTickValue(RadarChartData data) {
final defaultCenterValue = getDefaultChartCenterValue();
final dataSetMaxValue = sheetbotRadarGraphs ? max(data.maxEntry.value, data.minEntry.value.abs()) : data.maxEntry.value;
final dataSetMinValue = (data.minEntry.value < 0 && sheetbotRadarGraphs) ? 0.0 : data.minEntry.value;
return dataSetMaxValue == dataSetMinValue
? (dataSetMaxValue - defaultCenterValue) / (data.tickCount + 1) +
defaultCenterValue
: dataSetMinValue;
}
@override
void drawTicks(
BuildContext context,
CanvasWrapper canvasWrapper,
PaintHolder<RadarChartData> holder,
) {
final data = holder.data;
final size = canvasWrapper.size;
final centerX = radarCenterX(size);
final centerY = radarCenterY(size);
final centerOffset = Offset(centerX, centerY);
/// controls Radar chart size
final radius = radarRadius(size);
_backgroundPaint.color = data.radarBackgroundColor;
_borderPaint
..color = data.radarBorderData.color
..strokeWidth = data.radarBorderData.width;
if (data.radarShape == RadarShape.circle) {
/// draw radar background
canvasWrapper
..drawCircle(centerOffset, radius, _backgroundPaint)
/// draw radar border
..drawCircle(centerOffset, radius, _borderPaint);
} else {
final path =
_generatePolygonPath(centerX, centerY, radius, data.titleCount);
/// draw radar background
canvasWrapper
..drawPath(path, _backgroundPaint)
/// draw radar border
..drawPath(path, _borderPaint);
}
final tickSpace = getSpaceBetweenTicks(data);
final ticks = <double>[];
var tickValue = getFirstTickValue(data);
for (var i = 0; i <= data.tickCount; i++) {
ticks.add(tickValue);
tickValue += tickSpace;
}
final tickDistance = radius / (ticks.length-1);
_tickPaint
..color = data.tickBorderData.color
..strokeWidth = data.tickBorderData.width;
/// draw radar ticks
ticks.sublist(1, ticks.length).asMap().forEach(
(index, tick) {
final tickRadius = tickDistance * (index + 1);
if (data.radarShape == RadarShape.circle) {
canvasWrapper.drawCircle(centerOffset, tickRadius, _tickPaint);
} else {
canvasWrapper.drawPath(
_generatePolygonPath(centerX, centerY, tickRadius, data.titleCount),
_tickPaint,
);
}
_ticksTextPaint
..text = TextSpan(
text: percentage.format(tick),
style: Utils().getThemeAwareTextStyle(context, data.ticksTextStyle),
)
..textDirection = TextDirection.ltr
..layout(maxWidth: size.width);
canvasWrapper.drawText(
_ticksTextPaint,
Offset(centerX + 5, centerY - tickRadius - _ticksTextPaint.height/2),
);
},
);
}
Path _generatePolygonPath(
double centerX,
double centerY,
double radius,
int count,
) {
final path = Path()..moveTo(centerX, centerY - radius);
final angle = (2 * pi) / count;
for (var index = 0; index < count; index++) {
final xAngle = cos(angle * index - pi / 2);
final yAngle = sin(angle * index - pi / 2);
path.lineTo(centerX + radius * xAngle, centerY + radius * yAngle);
}
path.lineTo(centerX, centerY - radius);
return path;
}
}
class MyRadarChartLeaf extends RadarChartLeaf{
MyRadarChartLeaf({required super.data, required super.targetData});
@override
RenderRadarChart createRenderObject(BuildContext context) => MyRenderRadarChart(
context,
data,
targetData,
MediaQuery.of(context).textScaler,
);
}
class MyRenderRadarChart extends RenderRadarChart{
MyRenderRadarChart(super.context, super.data, super.targetData, super.textScaler);
@override
RadarChartPainter painter = MyRadarChartPainter();
}
class MyRadarChart extends ImplicitlyAnimatedWidget {
const MyRadarChart(
this.data, {
super.key,
Duration swapAnimationDuration = const Duration(milliseconds: 150),
Curve swapAnimationCurve = Curves.linear,
}) : super(
duration: swapAnimationDuration,
curve: swapAnimationCurve,
);
/// Determines how the [RadarChart] should be look like.
final RadarChartData data;
@override
RadarChartState createState() => RadarChartState();
}
class RadarChartState extends AnimatedWidgetBaseState<MyRadarChart> {
/// we handle under the hood animations (implicit animations) via this tween,
/// it lerps between the old [RadarChartData] to the new one.
RadarChartDataTween? _radarChartDataTween;
@override
Widget build(BuildContext context) {
final showingData = _getDate();
return MyRadarChartLeaf(
data: _radarChartDataTween!.evaluate(animation),
targetData: showingData,
);
}
RadarChartData _getDate() {
return widget.data;
}
@override
void forEachTween(TweenVisitor<dynamic> visitor) {
_radarChartDataTween = visitor(
_radarChartDataTween,
widget.data,
(dynamic value) =>
RadarChartDataTween(begin: value as RadarChartData, end: widget.data),
) as RadarChartDataTween?;
}
}
class Graphs extends StatelessWidget{ class Graphs extends StatelessWidget{
const Graphs( const Graphs(
this.apm, this.apm,
this.pps, this.pps,
@ -37,7 +296,7 @@ class Graphs extends StatelessWidget{
child: SizedBox( child: SizedBox(
height: 310, height: 310,
width: 310, width: 310,
child: RadarChart( child: MyRadarChart(
RadarChartData( RadarChartData(
radarShape: RadarShape.polygon, radarShape: RadarShape.polygon,
tickCount: 4, tickCount: 4,
@ -114,7 +373,7 @@ class Graphs extends StatelessWidget{
child: SizedBox( child: SizedBox(
height: 310, height: 310,
width: 310, width: 310,
child: RadarChart( child: MyRadarChart(
RadarChartData( RadarChartData(
radarShape: RadarShape.polygon, radarShape: RadarShape.polygon,
tickCount: 4, tickCount: 4,
@ -169,7 +428,7 @@ class Graphs extends StatelessWidget{
child: SizedBox( child: SizedBox(
height: 310, height: 310,
width: 310, width: 310,
child: RadarChart( child: MyRadarChart(
RadarChartData( RadarChartData(
radarShape: RadarShape.polygon, radarShape: RadarShape.polygon,
tickCount: 4, tickCount: 4,

View File

@ -3,7 +3,7 @@ import 'package:intl/intl.dart';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:syncfusion_flutter_gauges/gauges.dart'; import 'package:syncfusion_flutter_gauges/gauges.dart';
import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/main.dart'; import 'package:tetra_stats/main.dart' show prefs;
import 'package:tetra_stats/utils/colors_functions.dart'; import 'package:tetra_stats/utils/colors_functions.dart';
import 'package:tetra_stats/utils/numers_formats.dart'; import 'package:tetra_stats/utils/numers_formats.dart';
import 'package:tetra_stats/widgets/gauget_num.dart'; import 'package:tetra_stats/widgets/gauget_num.dart';

View File

@ -1,5 +1,6 @@
import 'package:fl_chart/fl_chart.dart'; import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tetra_stats/widgets/graphs.dart' show MyRadarChart;
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/gen/strings.g.dart';
@ -31,7 +32,7 @@ class VsGraphs extends StatelessWidget{
child: SizedBox( child: SizedBox(
height: 310, height: 310,
width: 310, width: 310,
child: RadarChart( child: MyRadarChart(
RadarChartData( RadarChartData(
radarShape: RadarShape.polygon, radarShape: RadarShape.polygon,
tickCount: 4, tickCount: 4,
@ -134,7 +135,7 @@ class VsGraphs extends StatelessWidget{
child: SizedBox( child: SizedBox(
height: 310, height: 310,
width: 310, width: 310,
child: RadarChart( child: MyRadarChart(
RadarChartData( RadarChartData(
radarShape: RadarShape.polygon, radarShape: RadarShape.polygon,
tickCount: 4, tickCount: 4,
@ -211,7 +212,7 @@ class VsGraphs extends StatelessWidget{
child: SizedBox( child: SizedBox(
height: 310, height: 310,
width: 310, width: 310,
child: RadarChart( child: MyRadarChart(
RadarChartData( RadarChartData(
radarShape: RadarShape.polygon, radarShape: RadarShape.polygon,
tickCount: 4, tickCount: 4,