make graphs scalable to handle values greater than the heigh of the canvas

This commit is contained in:
TAJAOUART Mounir 2023-02-09 17:51:47 +01:00
parent 0b2caaaf87
commit 0cdb3db45f
2 changed files with 84 additions and 53 deletions

View File

@ -34,38 +34,40 @@ class MyHomePage extends StatefulWidget {
class _MyHomePageState extends State<MyHomePage> { class _MyHomePageState extends State<MyHomePage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final stream = getDataStream().asBroadcastStream();
return Scaffold( return Scaffold(
backgroundColor: Colors.black, backgroundColor: Colors.black,
appBar: AppBar( appBar: AppBar(
title: Text(widget.title), title: Text(widget.title),
), ),
body: Column( body: SizedBox(
children: [ width: double.maxFinite,
Flexible( child: Column(
child: Container( children: [
color: Colors.red, Expanded(
width: MediaQuery.of(context).size.width, child: Container(
height: MediaQuery.of(context).size.width, color: Colors.green,
child: RealTimeGraph( child: RealTimeGraph(
stream: getDataStream(), stream: stream,
),
), ),
), ),
), const SizedBox(
const SizedBox( height: 32,
height: 32, child: Divider(color: Colors.grey,),
), ),
Flexible( Expanded(
child: Container( child: Container(
color: Colors.red, color: Colors.green,
width: MediaQuery.of(context).size.width, child: RealTimeGraph(
height: MediaQuery.of(context).size.width, stream: stream,
child: RealTimeGraph( displayMode: ChartDisplay.points,
stream: getDataStream(), ),
displayMode: ChartDisplay.points,
), ),
), ),
), ],
], ),
), ),
); );
} }
@ -75,7 +77,7 @@ class _MyHomePageState extends State<MyHomePage> {
Stream<double> getDataStream() { Stream<double> getDataStream() {
return Stream.periodic(const Duration(milliseconds: 500), (_) { return Stream.periodic(const Duration(milliseconds: 500), (_) {
return Random().nextInt(300).toDouble(); return Random().nextInt(500).toDouble();
}); });
} }
} }

View File

@ -1,5 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:ui'; import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -73,9 +73,9 @@ class RealTimeGraphState extends State<RealTimeGraph>
width: constraints.maxHeight, width: constraints.maxHeight,
child: CustomPaint( child: CustomPaint(
painter: widget.displayMode == ChartDisplay.points painter: widget.displayMode == ChartDisplay.points
? _PointsGraphPainter( ? _PointGraphPainter(
data: _data, data: _data,
pointsSpacing: widget.pointsSpacing, pointSpacing: 3,
) )
: _LineGraphPainter( : _LineGraphPainter(
data: _data, data: _data,
@ -100,47 +100,64 @@ class RealTimeGraphState extends State<RealTimeGraph>
} }
} }
class _PointsGraphPainter extends CustomPainter { class _PointGraphPainter extends CustomPainter {
final List<Point<double>> data; final List<Point<double>> data;
final double pointSpacing;
_PointsGraphPainter({ _PointGraphPainter({required this.data, required this.pointSpacing});
required this.data,
required this.pointsSpacing,
});
final double pointsSpacing;
@override @override
void paint(Canvas canvas, Size size) { void paint(Canvas canvas, Size size) {
final paint = Paint() final paint = Paint()
..style = PaintingStyle.stroke ..style = PaintingStyle.fill
..strokeWidth = 1.5 ..strokeWidth = 1.5
..color = Colors.white; ..color = Colors.white;
// Find the maximum y value in the data
double maxY = 0;
// Calculate the scaling factor for the y values
double yScale = 1;
// Iterate over the data points and add intermediate points if necessary // Iterate over the data points and add intermediate points if necessary
if (data.isNotEmpty) {
maxY = data.map((point) => point.y).reduce(max);
yScale = (maxY > size.height) ? (size.height / maxY) : 1;
}
for (int i = 0; i < data.length - 1; i++) { for (int i = 0; i < data.length - 1; i++) {
final y1 = size.height - data[i].y; double y1 = data[i].y * yScale;
final x1 = data[i].x + size.width; double x1 = data[i].x + size.width;
final y2 = size.height - data[i + 1].y; double y2 = data[i + 1].y * yScale;
final x2 = data[i + 1].x + size.width; double x2 = data[i + 1].x + size.width;
final yDiff = (y2 - y1).abs(); double yDiff = (y2 - y1).abs();
// If the difference in y values is small, add intermediate points // If the difference in y values is small, add intermediate points
if (yDiff >= pointsSpacing) { if (yDiff >= pointSpacing) {
int numOfIntermediatePoints = (yDiff / pointsSpacing).round(); int numOfIntermediatePoints = (yDiff / pointSpacing).round();
final yInterval = (y2 - y1) / numOfIntermediatePoints; double yInterval = (y2 - y1) / numOfIntermediatePoints;
final xInterval = (x2 - x1) / numOfIntermediatePoints; double xInterval = (x2 - x1) / numOfIntermediatePoints;
for (int j = 0; j <= numOfIntermediatePoints; j++) { for (int j = 0; j <= numOfIntermediatePoints; j++) {
final intermediateY = y1 + yInterval * j; double intermediateY = y1 + yInterval * j;
final intermediateX = x1 + xInterval * j; double intermediateX = x1 + xInterval * j;
canvas.drawPoints( canvas.drawCircle(
PointMode.points, Offset(intermediateX, size.height - intermediateY),
[Offset(intermediateX, intermediateY)], 1,
paint, paint,
); );
} }
} }
canvas.drawCircle(
Offset(x1, size.height - y1),
1,
paint,
);
} }
canvas.drawCircle(
Offset(data.last.x + size.width, size.height - data.last.y * yScale),
1,
paint,
);
} }
@override @override
@ -159,16 +176,28 @@ class _LineGraphPainter extends CustomPainter {
..strokeWidth = 1.5 ..strokeWidth = 1.5
..color = Colors.white; ..color = Colors.white;
Path path = Path(); Path path = Path();
// Find the maximum y value in the data
double maxY = 0;
// Calculate the scaling factor for the y values
double yScale = 1;
// Iterate over the data points and add intermediate points if necessary // Iterate over the data points and add intermediate points if necessary
if (data.isNotEmpty) { if (data.isNotEmpty) {
path.moveTo(data.first.x + size.width, size.height - data.first.y); maxY = data.map((point) => point.y).reduce(max);
yScale = (maxY > size.height) ? (size.height / maxY) : 1;
path.moveTo(
data.first.x + size.width,
(size.height - data.first.y * yScale),
);
} }
for (int i = 0; i < data.length - 1; i++) { for (int i = 0; i < data.length - 1; i++) {
double y = size.height - data[i].y; double y = data[i + 1].y * yScale;
double x = data[i].x + size.width; double x = data[i + 1].x + size.width;
path.lineTo(x, y);
path.lineTo(x, size.height - y);
} }
canvas.drawPath(path, paint); canvas.drawPath(path, paint);
} }