Add ScottPlot.WinForms using Manage NuGet packages.

Form1.cs

using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using ScottPlot;

namespace Spline
{
    public partial class Form1 : Form
    {
        public static class Cubic
        {
            /// <summary>
            /// Generate a smooth (interpolated) curve that follows the path of the given X/Y points
            /// </summary>
            public static (double[] xs, double[] ys) InterpolateXY(double[] xs, double[] ys, int count)
            {
                if (xs is null || ys is null || xs.Length != ys.Length)
                    throw new ArgumentException($"{nameof(xs)} and {nameof(ys)} must have same length");

                int inputPointCount = xs.Length;
                double[] inputDistances = new double[inputPointCount];
                for (int i = 1; i < inputPointCount; i++)
                {
                    double dx = xs[i] - xs[i - 1];
                    double dy = ys[i] - ys[i - 1];
                    double distance = Math.Sqrt(dx * dx + dy * dy);
                    inputDistances[i] = inputDistances[i - 1] + distance;
                }

                double meanDistance = inputDistances.Last() / (count - 1);
                double[] evenDistances = Enumerable.Range(0, count).Select(x => x * meanDistance).ToArray();
                double[] xsOut = Interpolate(inputDistances, xs, evenDistances);
                double[] ysOut = Interpolate(inputDistances, ys, evenDistances);
                return (xsOut, ysOut);
            }

            private static double[] Interpolate(double[] xOrig, double[] yOrig, double[] xInterp)
            {
                (double[] a, double[] b) = FitMatrix(xOrig, yOrig);

                double[] yInterp = new double[xInterp.Length];
                for (int i = 0; i < yInterp.Length; i++)
                {
                    int j;
                    for (j = 0; j < xOrig.Length - 2; j++)
                        if (xInterp[i] <= xOrig[j + 1])
                            break;

                    double dx = xOrig[j + 1] - xOrig[j];
                    double t = (xInterp[i] - xOrig[j]) / dx;
                    double y = (1 - t) * yOrig[j] + t * yOrig[j + 1] +
                        t * (1 - t) * (a[j] * (1 - t) + b[j] * t);
                    yInterp[i] = y;
                }
                return yInterp;
            }
            private static (double[] a, double[] b) FitMatrix(double[] x, double[] y)
            {
                int n = x.Length;
                double[] a = new double[n - 1];
                double[] b = new double[n - 1];
                double[] r = new double[n];
                double[] A = new double[n];
                double[] B = new double[n];
                double[] C = new double[n];

                double dx1, dx2, dy1, dy2;

                dx1 = x[1] - x[0];
                C[0] = 1.0f / dx1;
                B[0] = 2.0f * C[0];
                r[0] = 3 * (y[1] - y[0]) / (dx1 * dx1);

                for (int i = 1; i < n - 1; i++)
                {
                    dx1 = x[i] - x[i - 1];
                    dx2 = x[i + 1] - x[i];
                    A[i] = 1.0f / dx1;
                    C[i] = 1.0f / dx2;
                    B[i] = 2.0f * (A[i] + C[i]);
                    dy1 = y[i] - y[i - 1];
                    dy2 = y[i + 1] - y[i];
                    r[i] = 3 * (dy1 / (dx1 * dx1) + dy2 / (dx2 * dx2));
                }

                dx1 = x[n - 1] - x[n - 2];
                dy1 = y[n - 1] - y[n - 2];
                A[n - 1] = 1.0f / dx1;
                B[n - 1] = 2.0f * A[n - 1];
                r[n - 1] = 3 * (dy1 / (dx1 * dx1));

                double[] cPrime = new double[n];
                cPrime[0] = C[0] / B[0];
                for (int i = 1; i < n; i++)
                    cPrime[i] = C[i] / (B[i] - cPrime[i - 1] * A[i]);

                double[] dPrime = new double[n];
                dPrime[0] = r[0] / B[0];
                for (int i = 1; i < n; i++)
                    dPrime[i] = (r[i] - dPrime[i - 1] * A[i]) / (B[i] - cPrime[i - 1] * A[i]);

                double[] k = new double[n];
                k[n - 1] = dPrime[n - 1];
                for (int i = n - 2; i >= 0; i--)
                    k[i] = dPrime[i] - cPrime[i] * k[i + 1];

                for (int i = 1; i < n; i++)
                {
                    dx1 = x[i] - x[i - 1];
                    dy1 = y[i] - y[i - 1];
                    a[i - 1] = k[i - 1] * dx1 - dy1;
                    b[i - 1] = -k[i] * dx1 + dy1;
                }

                return (a, b);
            }
        }

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Random rand = new(1268);
            int pointCount = 15;
            double[] xs1 = DataGen.Consecutive(pointCount);
            double[] ys1 = new double[pointCount];
            for (int i = 1; i < pointCount; i++)
            {
                //xs1[i] = xs1[i - 1] + rand.NextDouble() - .5;
                ys1[i] = ys1[i - 1] + rand.NextDouble() - .5;
            }

            // Use cubic interpolation to smooth the original data
            (double[] xs2, double[] ys2) = Cubic.InterpolateXY(xs1, ys1, 200);

            // Plot the original vs. interpolated data
            var plt = new ScottPlot.Plot(900, 500);
            plt.AddScatter(xs1, ys1, label: "original", markerSize: 7);
            plt.AddScatter(xs2, ys2, label: "interpolated", markerSize: 3);
            plt.Style(Style.Seaborn);
            plt.Title("Cubic Spline");
            plt.XLabel("x axis");
            plt.YLabel("y axis");
            plt.Legend();
            plt.SaveFig("interpolation.png");
            pictureBox1.ImageLocation = "interpolation.png";
        }
    }
}

Discover more from Tips and Hints for Aerospace Engineers

Subscribe now to keep reading and get access to the full archive.

Continue reading