Skip to content

Commit

Permalink
Merge pull request ppy#6056 from OliBomby/bezier-approximation-tensor
Browse files Browse the repository at this point in the history
Add method to convert piecewise linear curves to spline control points
  • Loading branch information
bdach authored Dec 7, 2023
2 parents 5a45622 + a3f3fa6 commit 44f2f93
Show file tree
Hide file tree
Showing 4 changed files with 711 additions and 0 deletions.
43 changes: 43 additions & 0 deletions osu.Framework.Benchmarks/BenchmarkPiecewiseLinearToBezier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using osu.Framework.Utils;
using osuTK;

namespace osu.Framework.Benchmarks
{
public class BenchmarkPiecewiseLinearToBezier : BenchmarkTest
{
private Vector2[] inputPath = null!;

[Params(5, 25)]
public int NumControlPoints;

[Params(5, 200)]
public int NumTestPoints;

[Params(0, 100, 200)]
public int MaxIterations;

public override void SetUp()
{
base.SetUp();

Vector2[] points = new Vector2[5];
points[0] = new Vector2(50, 250);
points[1] = new Vector2(150, 230);
points[2] = new Vector2(100, 150);
points[3] = new Vector2(200, 80);
points[4] = new Vector2(250, 50);
inputPath = PathApproximator.LagrangePolynomialToPiecewiseLinear(points).ToArray();
}

[Benchmark]
public List<Vector2> PiecewiseLinearToBezier()
{
return PathApproximator.PiecewiseLinearToBezier(inputPath, NumControlPoints, NumTestPoints, MaxIterations);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Lines;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Utils;
using osu.Framework.Testing;
using osuTK;
using osuTK.Graphics;

namespace osu.Framework.Tests.Visual.Drawables
{
public partial class TestScenePiecewiseLinearToBSpline : GridTestScene
{
private int numControlPoints = 5;
private int degree = 2;
private int numTestPoints = 100;
private int maxIterations = 100;
private float learningRate = 8;
private float b1 = 0.8f;
private float b2 = 0.99f;

private readonly List<DoubleApproximatedPathTest> doubleApproximatedPathTests = new List<DoubleApproximatedPathTest>();

public TestScenePiecewiseLinearToBSpline()
: base(2, 2)
{
doubleApproximatedPathTests.Add(new DoubleApproximatedPathTest(PathApproximator.BezierToPiecewiseLinear));
Cell(0).AddRange(new[]
{
createLabel(nameof(PathApproximator.BezierToPiecewiseLinear)),
new ApproximatedPathTest(PathApproximator.BezierToPiecewiseLinear),
doubleApproximatedPathTests[^1],
});

doubleApproximatedPathTests.Add(new DoubleApproximatedPathTest(PathApproximator.CatmullToPiecewiseLinear));
Cell(1).AddRange(new[]
{
createLabel(nameof(PathApproximator.CatmullToPiecewiseLinear)),
new ApproximatedPathTest(PathApproximator.CatmullToPiecewiseLinear),
doubleApproximatedPathTests[^1],
});

doubleApproximatedPathTests.Add(new DoubleApproximatedPathTest(PathApproximator.CircularArcToPiecewiseLinear));
Cell(2).AddRange(new[]
{
createLabel(nameof(PathApproximator.CircularArcToPiecewiseLinear)),
new ApproximatedPathTest(PathApproximator.CircularArcToPiecewiseLinear),
doubleApproximatedPathTests[^1],
});

doubleApproximatedPathTests.Add(new DoubleApproximatedPathTest(PathApproximator.LagrangePolynomialToPiecewiseLinear));
Cell(3).AddRange(new[]
{
createLabel(nameof(PathApproximator.LagrangePolynomialToPiecewiseLinear)),
new ApproximatedPathTest(PathApproximator.LagrangePolynomialToPiecewiseLinear),
doubleApproximatedPathTests[^1],
});

AddSliderStep($"{nameof(numControlPoints)}", 3, 25, 5, v =>
{
numControlPoints = v;
updateTests();
});

AddSliderStep($"{nameof(degree)}", 1, 5, 3, v =>
{
degree = v;
updateTests();
});

AddSliderStep($"{nameof(numTestPoints)}", 10, 200, 100, v =>
{
numTestPoints = v;
updateTests();
});

AddSliderStep($"{nameof(maxIterations)}", 0, 200, 10, v =>
{
maxIterations = v;
updateTests();
});

AddSliderStep($"{nameof(learningRate)}", 0, 10, 8f, v =>
{
learningRate = v;
updateTests();
});

AddSliderStep($"{nameof(b1)}", 0, 0.999f, 0.8f, v =>
{
b1 = v;
updateTests();
});

AddSliderStep($"{nameof(b2)}", 0, 0.999f, 0.99f, v =>
{
b2 = v;
updateTests();
});

AddStep("Enable optimization", () =>
{
foreach (var test in doubleApproximatedPathTests)
test.OptimizePath = true;
updateTests();
});
}

private void updateTests()
{
foreach (var test in doubleApproximatedPathTests)
{
test.NumControlPoints = numControlPoints;
test.Degree = degree;
test.NumTestPoints = numTestPoints;
test.MaxIterations = maxIterations;
test.LearningRate = learningRate;
test.B1 = b1;
test.B2 = b2;
test.UpdatePath();
}
}

private Drawable createLabel(string text) => new SpriteText
{
Text = text + "ToBSpline",
Font = new FontUsage(size: 20),
Colour = Color4.White,
};

public delegate List<Vector2> ApproximatorFunc(ReadOnlySpan<Vector2> controlPoints);

private partial class ApproximatedPathTest : SmoothPath
{
public ApproximatedPathTest(ApproximatorFunc approximator)
{
Vector2[] points = new Vector2[5];
points[0] = new Vector2(50, 250);
points[1] = new Vector2(150, 230);
points[2] = new Vector2(100, 150);
points[3] = new Vector2(200, 80);
points[4] = new Vector2(250, 50);

AutoSizeAxes = Axes.None;
RelativeSizeAxes = Axes.Both;
PathRadius = 2;
Vertices = approximator(points);
Colour = Color4.White;
}
}

private partial class DoubleApproximatedPathTest : SmoothPath
{
private readonly Vector2[] inputPath;

public int NumControlPoints { get; set; }

public int Degree { get; set; }

public int NumTestPoints { get; set; }

public int MaxIterations { get; set; }

public float LearningRate { get; set; }

public float B1 { get; set; }

public float B2 { get; set; }

public bool OptimizePath { get; set; }

public DoubleApproximatedPathTest(ApproximatorFunc approximator)
{
Vector2[] points = new Vector2[5];
points[0] = new Vector2(50, 250);
points[1] = new Vector2(150, 230);
points[2] = new Vector2(100, 150);
points[3] = new Vector2(200, 80);
points[4] = new Vector2(250, 50);

AutoSizeAxes = Axes.None;
RelativeSizeAxes = Axes.Both;
PathRadius = 2;
Colour = Color4.Magenta;
inputPath = approximator(points).ToArray();
}

public void UpdatePath()
{
if (!OptimizePath) return;

var controlPoints = PathApproximator.PiecewiseLinearToBSpline(inputPath, NumControlPoints, Degree, NumTestPoints, MaxIterations, LearningRate, B1, B2);
Vertices = PathApproximator.BSplineToPiecewiseLinear(controlPoints.ToArray(), Degree);
}
}
}
}
Loading

0 comments on commit 44f2f93

Please sign in to comment.