// Perform polynomial fit using the given discrete points
var xCoords = _originalPoints.Select(p => (double)p.X).ToArray();
var yCoords = _originalPoints.Select(p => (double)p.Y).ToArray();
int maxOrder = Math.Min(5, xCoords.Length - 1);
double[] coefficients = Fit.Polynomial(xCoords, yCoords, maxOrder);
Polynomial evalutaion follows the form c0 + c1*x + c2*x^2 + ... + ck*x^k. A method can be written to compute this efficiently with out explicit iteratoin over powers:
static double EvaluatePolynomial(double[] coeffs, double x)
{
double result = 0.0;
double power = 1.0;
foreach (double c in coeffs)
{
result += c * power;
power *= x;
}
return result;
}
To generate points along the fitted curve over the drawing area width:
_fittedPoints.Clear();
int step = 5;
for (int px = 0; px <= width; px += step)
{
double yVal = EvaluatePolynomial(coefficients, px);
_fittedPoints.Add(new SKPoint(px, (float)yVal));
}
Calculate R² to quantify fit quality:
double[] predictedY = _originalPoints
.Select(p => EvaluatePolynomial(coefficients, p.X))
.ToArray();
double rSq = GoodnessOfFit.RSquared(yCoords, predictedY);
The complete Windows Forms example integrates SkiaSharp control and MathNet:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using MathNet.Numerics;
using SkiaSharp;
using SkiaSharp.Views.Desktop;
public class CurveFitForm : Form
{
private readonly Point[] _rawPoints =
{
new Point(16, 60),
new Point(40, 20),
new Point(52, 47),
new Point(63, 85),
new Point(87, 26)
};
private List<SKPoint> _originalPts = new();
private List<SKPoint> _fittedPts = new();
private SKControl _canvas;
private Panel _container;
private double _rSquared;
private int _polyOrder;
public CurveFitForm()
{
_container = new Panel();
_canvas = new SKControl { Dock = DockStyle.Fill };
_canvas.PaintSurface += OnPaintSurface;
_container.Controls.Add(_canvas);
Controls.Add(_container);
Load += (_, _) => { FitAndDraw(); };
SizeChanged += (_, _) => { LayoutCanvas(); FitAndDraw(); };
}
private void LayoutCanvas()
{
_container.Left = 10;
_container.Top = 10;
_container.Width = ClientSize.Width - 20;
_container.Height = ClientSize.Height - 20;
}
private void FitAndDraw()
{
float w = _container.Width;
float h = _container.Height;
_originalPts = _rawPoints.Select(p =>
new SKPoint(p.X / 100f * w, (100 - p.Y) / 100f * h)).ToList();
double[] xs = _originalPts.Select(p => (double)p.X).ToArray();
double[] ys = _originalPts.Select(p => (double)p.Y).ToArray();
_polyOrder = Math.Min(5, xs.Length - 1);
double[] coeffs = Fit.Polynomial(xs, ys, _polyOrder);
_fittedPts.Clear();
for (int x = 0; x <= w; x += 5)
{
double y = EvaluatePolynomial(coeffs, x);
_fittedPts.Add(new SKPoint(x, (float)y));
}
double[] predicted = _originalPts
.Select(p => EvaluatePolynomial(coeffs, p.X)).ToArray();
_rSquared = GoodnessOfFit.RSquared(ys, predicted);
_canvas.Refresh();
}
static double EvaluatePolynomial(double[] c, double x)
{
double sum = 0;
double xpow = 1;
foreach (double coeff in c)
{
sum += coeff * xpow;
xpow *= x;
}
return sum;
}
private void OnPaintSurface(object sender, SKPaintSurfaceEventArgs e)
{
SKCanvas canvas = e.Surface.Canvas;
canvas.Clear(new SKColor(211, 211, 211));
DrawFittedCurve(canvas);
DrawDataPoints(canvas);
DrawStatsText(canvas);
}
private void DrawFittedCurve(SKCanvas canvas)
{
if (_fittedPts.Count == 0) return;
using SKPaint paint = new()
{
Style = SKPaintStyle.Stroke,
StrokeWidth = 3,
IsAntialias = true,
StrokeCap = SKStrokeCap.Round,
Color = SKColors.Aqua
};
using SKPath path = new();
path.MoveTo(_fittedPts[0]);
for (int i = 1; i < _fittedPts.Count; i++)
path.LineTo(_fittedPts[i]);
canvas.DrawPath(path, paint);
}
private void DrawDataPoints(SKCanvas canvas)
{
using SKPaint paint = new()
{
Style = SKPaintStyle.Stroke,
StrokeWidth = 8,
IsAntialias = true,
StrokeCap = SKStrokeCap.Round,
Color = SKColors.Red
};
foreach (var pt in _originalPts)
canvas.DrawPoint(pt, paint);
}
private void DrawStatsText(SKCanvas canvas)
{
using SKPaint paint = new()
{
Color = SKColors.Black,
IsAntialias = true,
TextSize = 16
};
string msg = $"{_polyOrder}‑order fit, R² = {_rSquared:F4}";
canvas.DrawText(msg, 5, paint.TextSize + 5, paint);
}
}