Toggle for sheetbot-like behavior of radar graphs
Man this is so jank
This commit is contained in:
parent
6e9ecbf48e
commit
c7475e8d5c
|
@ -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;
|
||||||
|
});
|
||||||
}),)
|
}),)
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue