swharden / SWHarden.com

The personal website of Scott W Harden
https://swharden.com
MIT License
4 stars 0 forks source link

Blog post idea: sine wave with evenly spaced points #26

Open swharden opened 5 months ago

swharden commented 5 months ago
original fancy
image image
(double[] xs, double[] ys) = GetSineWave();

int targetMarkerCount = 50;
(double[] xs2, double[] ys2) = GetEvenlySpacedPoints2D(xs, ys, targetMarkerCount);
(double[] xs3, double[] ys3) = GetEvenlySpacedPointsX(xs, ys, targetMarkerCount);

Plot(xs2, ys2, "Evenly Spaced in 2D Space");
Plot(xs3, ys3, "Evenly Spaced X Positions");

The code isn't efficient, but it works. It's susceptible to floating point accumulation errors if too many points are used. There may be a better way to do this...

static void Plot(double[] xs, double[] ys, string title)
{
    ScottPlot.Plot plt = new();
    plt.ScaleFactor = 1.5f;
    var rule = new ScottPlot.AxisRules.SquareZoomOut(plt.Axes.Bottom, plt.Axes.Left);
    plt.Axes.Rules.Add(rule);
    var sp = plt.Add.Scatter(xs, ys);
    sp.MarkerSize = 5;
    sp.MarkerStyle.Shape = ScottPlot.MarkerShape.OpenCircle;
    plt.Title(title);
    string saveAs = Path.GetFullPath(title.Replace(" ", "") + ".png");
    plt.SavePng(saveAs, 600, 600);
    Console.WriteLine(saveAs);
}

static (double[] xs, double[] ys) GetSineWave(int count = 10_000, double oscillations = 1)
{
    double[] xs = new double[count];
    double[] ys = new double[count];
    for (int i = 0; i < count; i++)
    {
        double fraction = (double)i / (count - 1);
        double x = fraction * Math.PI * 2 * oscillations;
        xs[i] = x / (oscillations * Math.PI * 2);
        ys[i] = Math.Sin(x) / 2 + .5;
    }
    return (xs, ys);
}

static (double[] xs, double[] ys) GetEvenlySpacedPointsX(double[] xs, double[] ys, int targetMarkerCount)
{
    int[] indexes = Enumerable
    .Range(0, targetMarkerCount)
    .Select(x => (int)(xs.Length * x / (double)targetMarkerCount))
    .ToArray();
    double[] xs2 = indexes.Select(x => xs[x]).Append(xs.Last()).ToArray();
    double[] ys2 = indexes.Select(x => ys[x]).Append(ys.Last()).ToArray();
    return (xs2, ys2);
}

static (double[] xs, double[] ys) GetEvenlySpacedPoints2D(double[] xs, double[] ys, int targetMarkerCount)
{
    if (xs.Length != ys.Length)
        throw new ArgumentException("xs and ys must have same length");

    // walk the points and add markers at certain distances
    double[] distances = new double[xs.Length - 1];
    for (int i = 1; i < xs.Length; i++)
    {
        double dX = xs[i] - xs[i - 1];
        double dY = ys[i] - ys[i - 1];
        double d = Math.Sqrt(dX * dX + dY * dY);
        distances[i - 1] = d;
    }
    double totalDistance = distances.Sum();

    List<double> xs2 = [xs.First()];
    List<double> ys2 = [ys.First()];

    double travelTarget = totalDistance / targetMarkerCount;
    double travel = 0;
    for (int i = 1; i < xs.Length; i++)
    {
        travel += distances[i - 1];
        if (travel > travelTarget)
        {
            double overshoot = travel - travelTarget;
            travel = overshoot;
            xs2.Add(xs[i]);
            ys2.Add(ys[i]);
        }
    }

    return (xs2.ToArray(), ys2.ToArray());
}