linechart inbuilt curve smoothing.
MODIFY
WalletWasabi.Fluent/Controls/LineChart.Properties.cs
WalletWasabi.Fluent/Controls/LineChart.Properties.cs
@@ -46,6 +46,9 @@ namespace WalletWasabi.Fluent.Controls
public static readonly StyledProperty<double> AreaMinViableWidthProperty =
AvaloniaProperty.Register<LineChart, double>(nameof(AreaMinViableWidth), double.MinValue);
+ public static readonly StyledProperty<bool> SmoothCurveProperty =
+ AvaloniaProperty.Register<LineChart, bool>(nameof(SmoothCurve));
+
// XAxis
public static readonly StyledProperty<IList<double>?> XAxisValuesProperty =
@@ -451,6 +454,12 @@ namespace WalletWasabi.Fluent.Controls
set => SetValue(AreaMinViableWidthProperty, value);
}
+ public bool SmoothCurve
+ {
+ get => GetValue(SmoothCurveProperty);
+ set => SetValue(SmoothCurveProperty, value);
+ }
+
// XAxis
public IList<double>? XAxisValues

MODIFY
WalletWasabi.Fluent/Controls/LineChart.cs
WalletWasabi.Fluent/Controls/LineChart.cs
@@ -10,6 +10,7 @@ using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Media;
using WalletWasabi.Fluent.Helpers;
+using WalletWasabi.Fluent.MathNet;
namespace WalletWasabi.Fluent.Controls
{
@@ -197,6 +198,49 @@ namespace WalletWasabi.Fluent.Controls
return state;
}
+ private static IEnumerable<(double x, double y)> SplineInterpolate(double[] xs, double[] ys)
+ {
+ const int Divisions = 256;
+
+ if (xs.Length > 2)
+ {
+ var spline = CubicSpline.InterpolatePchipSorted(xs, ys);
+
+ for (var i = 0; i < xs.Length - 1; i++)
+ {
+ var a = xs[i];
+ var b = xs[i + 1];
+ var range = b - a;
+ var step = range / Divisions;
+
+ var t0 = xs[i];
+
+ var xt0 = spline.Interpolate(xs[i]);
+
+ yield return (t0, xt0);
+
+ for (var t = a + step; t < b; t += step)
+ {
+ var xt = spline.Interpolate(t);
+
+ yield return (t, xt);
+ }
+ }
+
+ var tn = xs[^1];
+ var xtn = spline.Interpolate(xs[^1]);
+
+ yield return (tn, xtn);
+ }
+ else
+ {
+ for (var i = 0; i < xs.Length; i++)
+ {
+ yield return (xs[i], ys[i]);
+ }
+ }
+ }
+
private void SetStateAreaPoints(LineChartState state)
{
var xAxisValues = XAxisValues;
@@ -222,8 +266,6 @@ namespace WalletWasabi.Fluent.Controls
.Select(y => ScaleVertical(y, yAxisValuesLogScaledMax, state.AreaHeight))
.ToList();
- state.Points = new Point[yAxisValues.Count];
-
var xAxisValuesEnumerable = xAxisValues as IEnumerable<double>;
switch (XAxisPlotMode)
@@ -248,6 +290,14 @@ namespace WalletWasabi.Fluent.Controls
break;
}
+ if (SmoothCurve)
+ {
+ var interpolated = SplineInterpolate(xAxisValuesEnumerable.ToArray(), yAxisValuesScaled.ToArray());
+
+ state.Points = interpolated.Select(pt => new Point(pt.x, pt.y)).ToArray();
+ }
+ else
+ {
using (var enumerator = xAxisValuesEnumerable.GetEnumerator())
{
for (var i = 0; i < yAxisValuesScaled.Count; i++)
@@ -257,6 +307,7 @@ namespace WalletWasabi.Fluent.Controls
}
}
}
+ }
private void SetStateXAxisLabels(LineChartState state)
{

linechart inbuilt curve smoothing.