Skip to content

Commit

Permalink
more line chart work
Browse files Browse the repository at this point in the history
  • Loading branch information
ctacke committed Sep 8, 2023
1 parent 5b3dfeb commit 7da7207
Show file tree
Hide file tree
Showing 12 changed files with 369 additions and 213 deletions.
18 changes: 18 additions & 0 deletions Source/MF.Charts.slnf
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"solution": {
"path": "Meadow.Foundation.sln",
"projects": [
"..\\..\\MQTTnet\\Source\\MQTTnet\\MQTTnet.csproj",
"..\\..\\Meadow.Contracts\\Source\\Meadow.Contracts\\Meadow.Contracts.csproj",
"..\\..\\Meadow.Core\\source\\Meadow.Core\\Meadow.Core.csproj",
"..\\..\\Meadow.Core\\source\\implementations\\f7\\Meadow.F7\\Meadow.F7.csproj",
"..\\..\\Meadow.Core\\source\\implementations\\windows\\Meadow.Windows\\Meadow.Windows.csproj",
"..\\..\\Meadow.Logging\\Source\\Meadow.Logging\\lib\\Meadow.Logging.csproj",
"..\\..\\Meadow.Modbus\\src\\Meadow.Modbus\\Meadow.Modbus.csproj",
"..\\..\\Meadow.Units\\Source\\Meadow.Units\\Meadow.Units.csproj",
"Meadow.Foundation.Core\\Meadow.Foundation.Core.csproj",
"Meadow.Foundation.Libraries_and_Frameworks\\Graphics.MicroGraphics\\Driver\\Graphics.MicroGraphics.csproj",
"Meadow.Foundation.Libraries_and_Frameworks\\Graphics.MicroLayout\\Driver\\Graphics.MicroLayout.csproj"
]
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace Meadow.Foundation.Graphics.MicroLayout;

Expand Down Expand Up @@ -30,43 +28,4 @@ public static LineChartSeries ToLineChartSeries(this double[,] xyData)
return series;
}

public static double MinX(this LineChartSeries series)
{
return (series.Points.Count == 0) ? 0 : series.Points.Min(p => p.X);
}

public static double MaxX(this LineChartSeries series)
{
return (series.Points.Count() == 0) ? 0 : series.Points.Max(p => p.X);
}

public static double MinY(this LineChartSeries series)
{
return (series.Points.Count() == 0) ? 0 : series.Points.Min(p => p.Y);
}

public static double MaxY(this LineChartSeries series)
{
return (series.Points.Count() == 0) ? 0 : series.Points.Max(p => p.Y);
}

public static double MinX(this IEnumerable<LineSeriesPoint> points)
{
return (points.Count() == 0) ? 0 : points.Min(p => p.X);
}

public static double MaxX(this IEnumerable<LineSeriesPoint> points)
{
return (points.Count() == 0) ? 0 : points.Max(p => p.X);
}

public static double MinY(this IEnumerable<LineSeriesPoint> points)
{
return (points.Count() == 0) ? 0 : points.Min(p => p.Y);
}

public static double MaxY(this IEnumerable<LineSeriesPoint> points)
{
return (points.Count() == 0) ? 0 : points.Max(p => p.Y);
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,76 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq;

namespace Meadow.Foundation.Graphics.MicroLayout;

/// <summary>
/// An X/Y Line chart
/// </summary>
public class DisplayLineChart : DisplayControl
{
public static Color DefaultAxisColor = Color.Gray;
public static Color DefaultAxisLabelColor = Color.White;
public static Color DefaultBackgroundColor = Color.Black;

private const int DefaultMargin = 5;
private const int DefaultAxisStroke = 5;
private const int DefaultAxisStroke = 4;

public static Color DefaultAxisColor = Color.WhiteSmoke;
public static Color DefaultBackgroundColor = Color.Black;
private IFont _axisFont;

/// <summary>
/// When true, Y-value origin (zero) is always displayed, otherwise the Y axis is scaled based on the data range.
/// </summary>
public bool AlwaysShowYOrigin { get; set; } = true;
/// <summary>
/// The IFont used to for displaying axis labels
/// </summary>
public IFont? AxisFont { get; set; }
/// <summary>
/// The chart's background color
/// </summary>
public Color BackgroundColor { get; set; } = DefaultBackgroundColor;
/// <summary>
/// The color used to draw the chart axes lines
/// </summary>
public Color AxisColor { get; set; } = DefaultAxisColor;
public List<LineChartSeries> Series { get; set; } = new();
/// <summary>
/// The color used to draw the chart axes labels
/// </summary>
public Color AxisLabelColor { get; set; } = DefaultAxisLabelColor;
/// <summary>
/// The width of the axes lines
/// </summary>
public int AxisStroke { get; set; } = DefaultAxisStroke;

// public bool ShowXAxisLabels { get; set; }
/// <summary>
/// When true, Y-axis labels will be shown
/// </summary>
public bool ShowYAxisLabels { get; set; }
/// <summary>
/// The collection of data series to plot
/// </summary>
public LineChartSeriesCollection Series { get; set; } = new();

private double VerticalScale { get; set; } // pixels per Y units
private double YMinimumValue { get; set; } // Y units at bottom of chart
private double YMaximumValue { get; set; } // Y units at top of chart
private double XAxisYIntercept { get; set; }
private int XAxisScaledPosition { get; set; }
private double HorizontalScale { get; set; } // pixels per X units
private double HorizontalMinimum { get; set; } // X units at left of chart
private int ChartAreaHeight { get; set; }
private int ChartAreaWidth { get; set; }
private int ChartAreaLeft { get; set; }
private int ChartAreaTop { get; set; }
private int ChartAreaBottom { get; set; }

/// <summary>
/// Creates a DisplayLineChart instance
/// </summary>
/// <param name="left">The control's left position</param>
/// <param name="top">The control's top position</param>
/// <param name="width">The control's width</param>
/// <param name="height">The control's height</param>
public DisplayLineChart(int left, int top, int width, int height)
: base(left, top, width, height)
{
Expand All @@ -26,111 +82,167 @@ public override void ApplyTheme(DisplayTheme theme)

protected override void OnDraw(MicroGraphics graphics)
{
graphics.Clear(BackgroundColor);
graphics.DrawRectangle(Left, Top, Width, Height, BackgroundColor, true);

ChartAreaTop = Top + DefaultMargin;
ChartAreaBottom = Bottom - DefaultMargin;

// determine overall min/max
var minX = Series.Min(s => s.MinX());
var maxX = Series.Max(s => s.MaxX());
var minX = Series.Min(s => s.Points.MinX);
var maxX = Series.Max(s => s.Points.MaxX);


if (AlwaysShowYOrigin)
{
var min = Series.Min(s => s.MinY());
var max = Series.Max(s => s.MaxY());
var min = Series.Min(s => s.Points.MinY);
var max = Series.Max(s => s.Points.MaxY);

if (min > 0)
{
VerticalMinimum = 0;
VerticalMaximum = max;
YMinimumValue = 0;
YMaximumValue = max;
}
else if (max < 0)
{
VerticalMinimum = min;
VerticalMaximum = 0;
YMinimumValue = min;
YMaximumValue = 0;
}
else
{
VerticalMinimum = min;
VerticalMaximum = max;
YMinimumValue = min;
YMaximumValue = max;
}
}
else
{
VerticalMinimum = Series.Min(s => s.MinY());
VerticalMaximum = Series.Max(s => s.MaxY());
YMinimumValue = Series.Min(s => s.Points.MinY);
YMaximumValue = Series.Max(s => s.Points.MaxY);
}

ChartAreaHeight = this.Height - (2 * DefaultMargin) - (DefaultAxisStroke / 2);
VerticalScale = ChartAreaHeight / (VerticalMaximum - VerticalMinimum); // pixels per vertical unit
VerticalScale = ChartAreaHeight / (YMaximumValue - YMinimumValue); // pixels per vertical unit

DrawXAxis(graphics, VerticalMinimum, VerticalMaximum);
DrawXAxis(graphics, YMinimumValue, YMaximumValue);
DrawYAxis(graphics);

foreach (var series in Series)
{
DrawSeries(graphics, series);
}

DrawAxisLabels(graphics);

graphics.Show();
}

public bool AlwaysShowYOrigin { get; set; } = true;
private double VerticalScale { get; set; } // pixels per Y units
private double VerticalMinimum { get; set; } // Y units at bottom of chart
private double VerticalMaximum { get; set; } // Y units at top of chart
private double HorizontalScale { get; set; } // pixels per X units
private double HorizontalMinimum { get; set; } // X units at left of chart
private int ChartAreaHeight { get; set; }
private void DrawAxisLabels(MicroGraphics graphics)
{
var font = GetAxisFont();

if (ShowYAxisLabels)
{
// axis label
if (XAxisYIntercept != YMinimumValue)
{
graphics.DrawText(
x: DefaultMargin,
y: XAxisScaledPosition - (font.Height / 2), // centered on tick
color: AxisLabelColor,
text: XAxisYIntercept.ToString("0.0"),
font: font);
}

// max label
graphics.DrawText(
x: DefaultMargin,
y: ChartAreaTop + font.Height,
color: AxisLabelColor,
text: YMaximumValue.ToString("0.0"),
font: font);

// min label
graphics.DrawText(
x: DefaultMargin,
y: ChartAreaBottom - font.Height,
color: AxisLabelColor,
text: YMinimumValue.ToString("0.0"),
font: font);
}
}

private void DrawXAxis(MicroGraphics graphics, double minY, double maxY)
{
int scaledYOffset;

if (minY < 0 && maxY > 0)
{
// axis is at 0
XAxisYIntercept = 0;

scaledYOffset = Bottom - DefaultMargin - DefaultAxisStroke + (int)(minY * VerticalScale);
XAxisScaledPosition = Bottom - DefaultMargin - DefaultAxisStroke + (int)(minY * VerticalScale);
}
else
{

// axis at min Y
scaledYOffset = Bottom - DefaultMargin - DefaultAxisStroke;
XAxisYIntercept = YMinimumValue;

XAxisScaledPosition = Bottom - DefaultMargin - DefaultAxisStroke;
}

// for now it's a fixed line at the bottom
graphics.Stroke = DefaultAxisStroke;
graphics.DrawLine(
Left + DefaultMargin,
scaledYOffset,
XAxisScaledPosition,
Right - DefaultMargin,
scaledYOffset,
XAxisScaledPosition,
AxisColor);
}

private IFont GetAxisFont()
{
if (AxisFont == null)
{
_axisFont = new Font8x16();
}
else
{
_axisFont = AxisFont;
}

return _axisFont;
}

private void DrawYAxis(MicroGraphics graphics)
{
var leftMargin = DefaultMargin + AxisStroke;

if (ShowYAxisLabels)
{
// TODO: this needs to be label-based
leftMargin += GetAxisFont().Width * 4;
}

// TODO: deal with chart with negative values

ChartAreaLeft = leftMargin;
ChartAreaWidth = Width - ChartAreaLeft - DefaultMargin;

// for now it's a fixed line at the left
graphics.Stroke = DefaultAxisStroke;
graphics.DrawLine(
Left + DefaultMargin,
ChartAreaLeft,
Top + DefaultMargin,
Left + DefaultMargin,
ChartAreaLeft,
Bottom - DefaultMargin,
AxisColor);
}

private void DrawSeries(MicroGraphics graphics, LineChartSeries series)
{
var chartWidth = Width - (2 * DefaultMargin);

var minX = series.MinX();
var minY = series.MinY();
var xRange = series.MaxX() - minX;
var yRange = series.MaxY(); // - minY; // assuming axis at 0 right now
var minX = series.Points.MinX;
var minY = series.Points.MinY;
var xRange = series.Points.MaxX - minX;
var yRange = series.Points.MaxY; // - minY; // assuming axis at 0 right now

LineSeriesPoint lastPoint = new LineSeriesPoint();
var first = true;
Expand All @@ -139,8 +251,8 @@ private void DrawSeries(MicroGraphics graphics, LineChartSeries series)

foreach (var point in series.Points)
{
var scaledX = (int)((point.X / xRange) * chartWidth) + DefaultMargin;
var scaledY = Bottom - DefaultMargin - DefaultAxisStroke / 2 - (int)((point.Y - VerticalMinimum) * VerticalScale);
var scaledX = ChartAreaLeft + (int)(point.X / xRange * ChartAreaWidth) + DefaultMargin;
var scaledY = Bottom - DefaultMargin - (DefaultAxisStroke / 2) - (int)((point.Y - YMinimumValue) * VerticalScale);

if (series.ShowLines)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System.Collections.Generic;

namespace Meadow.Foundation.Graphics.MicroLayout;
namespace Meadow.Foundation.Graphics.MicroLayout;

public class LineChartSeries
{
Expand All @@ -12,5 +10,5 @@ public class LineChartSeries
public Color PointColor { get; set; }
public int PointSize { get; set; }

public List<LineSeriesPoint> Points { get; set; } = new();
public LineSeriesPointCollection Points { get; set; } = new();
}
Loading

0 comments on commit 7da7207

Please sign in to comment.