diff --git a/osu.Game/Rulesets/Objects/BezierApproximator.cs b/osu.Game/Rulesets/Objects/BezierApproximator.cs index a1803e32f7..011526339e 100644 --- a/osu.Game/Rulesets/Objects/BezierApproximator.cs +++ b/osu.Game/Rulesets/Objects/BezierApproximator.cs @@ -1,29 +1,77 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using System.Collections.Generic; using OpenTK; namespace osu.Game.Rulesets.Objects { - public readonly ref struct BezierApproximator + public class BezierApproximator : IApproximator { - private readonly int count; - private readonly ReadOnlySpan controlPoints; - private readonly Vector2[] subdivisionBuffer1; - private readonly Vector2[] subdivisionBuffer2; - private const float tolerance = 0.25f; private const float tolerance_sq = tolerance * tolerance; - public BezierApproximator(ReadOnlySpan controlPoints) - { - this.controlPoints = controlPoints; - count = controlPoints.Length; + private int count; + private Vector2[] subdivisionBuffer1; + private Vector2[] subdivisionBuffer2; + /// + /// Creates a piecewise-linear approximation of a bezier curve, by adaptively repeatedly subdividing + /// the control points until their approximation error vanishes below a given threshold. + /// + /// A list of vectors representing the piecewise-linear approximation. + public List Approximate(List controlPoints) + { + count = controlPoints.Count; subdivisionBuffer1 = new Vector2[count]; subdivisionBuffer2 = new Vector2[count * 2 - 1]; + + List output = new List(); + + if (count == 0) + return output; + + Stack toFlatten = new Stack(); + Stack freeBuffers = new Stack(); + + // "toFlatten" contains all the curves which are not yet approximated well enough. + // We use a stack to emulate recursion without the risk of running into a stack overflow. + // (More specifically, we iteratively and adaptively refine our curve with a + // Depth-first search + // over the tree resulting from the subdivisions we make.) + toFlatten.Push(controlPoints.ToArray()); + + Vector2[] leftChild = subdivisionBuffer2; + + while (toFlatten.Count > 0) + { + Vector2[] parent = toFlatten.Pop(); + if (isFlatEnough(parent)) + { + // If the control points we currently operate on are sufficiently "flat", we use + // an extension to De Casteljau's algorithm to obtain a piecewise-linear approximation + // of the bezier curve represented by our control points, consisting of the same amount + // of points as there are control points. + approximate(parent, output); + freeBuffers.Push(parent); + continue; + } + + // If we do not yet have a sufficiently "flat" (in other words, detailed) approximation we keep + // subdividing the curve we are currently operating on. + Vector2[] rightChild = freeBuffers.Count > 0 ? freeBuffers.Pop() : new Vector2[count]; + subdivide(parent, leftChild, rightChild); + + // We re-use the buffer of the parent for one of the children, so that we save one allocation per iteration. + for (int i = 0; i < count; ++i) + parent[i] = leftChild[i]; + + toFlatten.Push(rightChild); + toFlatten.Push(parent); + } + + output.Add(controlPoints[count - 1]); + return output; } /// @@ -92,60 +140,5 @@ namespace osu.Game.Rulesets.Objects output.Add(p); } } - - /// - /// Creates a piecewise-linear approximation of a bezier curve, by adaptively repeatedly subdividing - /// the control points until their approximation error vanishes below a given threshold. - /// - /// A list of vectors representing the piecewise-linear approximation. - public List CreateBezier() - { - List output = new List(); - - if (count == 0) - return output; - - Stack toFlatten = new Stack(); - Stack freeBuffers = new Stack(); - - // "toFlatten" contains all the curves which are not yet approximated well enough. - // We use a stack to emulate recursion without the risk of running into a stack overflow. - // (More specifically, we iteratively and adaptively refine our curve with a - // Depth-first search - // over the tree resulting from the subdivisions we make.) - toFlatten.Push(controlPoints.ToArray()); - - Vector2[] leftChild = subdivisionBuffer2; - - while (toFlatten.Count > 0) - { - Vector2[] parent = toFlatten.Pop(); - if (isFlatEnough(parent)) - { - // If the control points we currently operate on are sufficiently "flat", we use - // an extension to De Casteljau's algorithm to obtain a piecewise-linear approximation - // of the bezier curve represented by our control points, consisting of the same amount - // of points as there are control points. - approximate(parent, output); - freeBuffers.Push(parent); - continue; - } - - // If we do not yet have a sufficiently "flat" (in other words, detailed) approximation we keep - // subdividing the curve we are currently operating on. - Vector2[] rightChild = freeBuffers.Count > 0 ? freeBuffers.Pop() : new Vector2[count]; - subdivide(parent, leftChild, rightChild); - - // We re-use the buffer of the parent for one of the children, so that we save one allocation per iteration. - for (int i = 0; i < count; ++i) - parent[i] = leftChild[i]; - - toFlatten.Push(rightChild); - toFlatten.Push(parent); - } - - output.Add(controlPoints[count - 1]); - return output; - } } } diff --git a/osu.Game/Rulesets/Objects/CatmullApproximator.cs b/osu.Game/Rulesets/Objects/CatmullApproximator.cs index 78f8e471f3..624f5fc9ab 100644 --- a/osu.Game/Rulesets/Objects/CatmullApproximator.cs +++ b/osu.Game/Rulesets/Objects/CatmullApproximator.cs @@ -1,40 +1,32 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using System.Collections.Generic; using OpenTK; namespace osu.Game.Rulesets.Objects { - public readonly ref struct CatmullApproximator + public class CatmullApproximator : IApproximator { /// /// The amount of pieces to calculate for each controlpoint quadruplet. /// private const int detail = 50; - private readonly ReadOnlySpan controlPoints; - - public CatmullApproximator(ReadOnlySpan controlPoints) - { - this.controlPoints = controlPoints; - } - /// /// Creates a piecewise-linear approximation of a Catmull-Rom spline. /// /// A list of vectors representing the piecewise-linear approximation. - public List CreateCatmull() + public List Approximate(List controlPoints) { - var result = new List((controlPoints.Length - 1) * detail * 2); + var result = new List(); - for (int i = 0; i < controlPoints.Length - 1; i++) + for (int i = 0; i < controlPoints.Count - 1; i++) { var v1 = i > 0 ? controlPoints[i - 1] : controlPoints[i]; var v2 = controlPoints[i]; - var v3 = i < controlPoints.Length - 1 ? controlPoints[i + 1] : v2 + v2 - v1; - var v4 = i < controlPoints.Length - 2 ? controlPoints[i + 2] : v3 + v3 - v2; + var v3 = i < controlPoints.Count - 1 ? controlPoints[i + 1] : v2 + v2 - v1; + var v4 = i < controlPoints.Count - 2 ? controlPoints[i + 2] : v3 + v3 - v2; for (int c = 0; c < detail; c++) { diff --git a/osu.Game/Rulesets/Objects/CircularArcApproximator.cs b/osu.Game/Rulesets/Objects/CircularArcApproximator.cs index 28d7442aaf..201c6296ba 100644 --- a/osu.Game/Rulesets/Objects/CircularArcApproximator.cs +++ b/osu.Game/Rulesets/Objects/CircularArcApproximator.cs @@ -8,23 +8,19 @@ using OpenTK; namespace osu.Game.Rulesets.Objects { - public readonly ref struct CircularArcApproximator + public class CircularArcApproximator : IApproximator { private const float tolerance = 0.1f; - private readonly ReadOnlySpan controlPoints; - - public CircularArcApproximator(ReadOnlySpan controlPoints) - { - this.controlPoints = controlPoints; - } - /// /// Creates a piecewise-linear approximation of a circular arc curve. /// /// A list of vectors representing the piecewise-linear approximation. - public List CreateArc() + public List Approximate(List controlPoints) { + if (controlPoints.Count != 3) + throw new ArgumentException("Must have 3 control points to perform circular arc approximation.", nameof(controlPoints)); + Vector2 a = controlPoints[0]; Vector2 b = controlPoints[1]; Vector2 c = controlPoints[2]; diff --git a/osu.Game/Rulesets/Objects/IApproximator.cs b/osu.Game/Rulesets/Objects/IApproximator.cs new file mode 100644 index 0000000000..f865172bb4 --- /dev/null +++ b/osu.Game/Rulesets/Objects/IApproximator.cs @@ -0,0 +1,13 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using OpenTK; + +namespace osu.Game.Rulesets.Objects +{ + public interface IApproximator + { + List Approximate(List controlPoints); + } +} diff --git a/osu.Game/Rulesets/Objects/LinearApproximator.cs b/osu.Game/Rulesets/Objects/LinearApproximator.cs new file mode 100644 index 0000000000..a2c2dd5a93 --- /dev/null +++ b/osu.Game/Rulesets/Objects/LinearApproximator.cs @@ -0,0 +1,13 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using OpenTK; + +namespace osu.Game.Rulesets.Objects +{ + public class LinearApproximator : IApproximator + { + public List Approximate(List controlpoints) => controlpoints; + } +}