mirror of https://github.com/ppy/osu
Merge pull request #27734 from smoogipoo/fix-catmull-bulbs
Fix bulbs on Catmull sliders
This commit is contained in:
commit
b559bec49f
|
@ -41,7 +41,7 @@ public double Duration
|
|||
|
||||
public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t);
|
||||
|
||||
private readonly SliderPath path = new SliderPath();
|
||||
private readonly SliderPath path = new SliderPath { OptimiseCatmull = true };
|
||||
|
||||
public SliderPath Path
|
||||
{
|
||||
|
|
|
@ -42,6 +42,17 @@ public class SliderPath
|
|||
private readonly List<double> cumulativeLength = new List<double>();
|
||||
private readonly Cached pathCache = new Cached();
|
||||
|
||||
/// <summary>
|
||||
/// Any additional length of the path which was optimised out during piecewise approximation, but should still be considered as part of <see cref="calculatedLength"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is a hack for Catmull paths.
|
||||
/// </remarks>
|
||||
private double optimisedLength;
|
||||
|
||||
/// <summary>
|
||||
/// The final calculated length of the path.
|
||||
/// </summary>
|
||||
private double calculatedLength;
|
||||
|
||||
private readonly List<int> segmentEnds = new List<int>();
|
||||
|
@ -123,6 +134,24 @@ public double CalculatedDistance
|
|||
}
|
||||
}
|
||||
|
||||
private bool optimiseCatmull;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to optimise Catmull path segments, usually resulting in removing bulbs around stacked knots.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This changes the path shape and should therefore not be used.
|
||||
/// </remarks>
|
||||
public bool OptimiseCatmull
|
||||
{
|
||||
get => optimiseCatmull;
|
||||
set
|
||||
{
|
||||
optimiseCatmull = value;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the slider path until a given progress that ranges from 0 (beginning of the slider)
|
||||
/// to 1 (end of the slider) and stores the generated path in the given list.
|
||||
|
@ -244,6 +273,7 @@ private void calculatePath()
|
|||
{
|
||||
calculatedPath.Clear();
|
||||
segmentEnds.Clear();
|
||||
optimisedLength = 0;
|
||||
|
||||
if (ControlPoints.Count == 0)
|
||||
return;
|
||||
|
@ -269,6 +299,7 @@ private void calculatePath()
|
|||
else if (segmentVertices.Length > 1)
|
||||
{
|
||||
List<Vector2> subPath = calculateSubPath(segmentVertices, segmentType);
|
||||
|
||||
// Skip the first vertex if it is the same as the last vertex from the previous segment
|
||||
bool skipFirst = calculatedPath.Count > 0 && subPath.Count > 0 && calculatedPath.Last() == subPath[0];
|
||||
|
||||
|
@ -295,6 +326,7 @@ private List<Vector2> calculateSubPath(ReadOnlySpan<Vector2> subControlPoints, P
|
|||
return PathApproximator.LinearToPiecewiseLinear(subControlPoints);
|
||||
|
||||
case SplineType.PerfectCurve:
|
||||
{
|
||||
if (subControlPoints.Length != 3)
|
||||
break;
|
||||
|
||||
|
@ -305,9 +337,58 @@ private List<Vector2> calculateSubPath(ReadOnlySpan<Vector2> subControlPoints, P
|
|||
break;
|
||||
|
||||
return subPath;
|
||||
}
|
||||
|
||||
case SplineType.Catmull:
|
||||
return PathApproximator.CatmullToPiecewiseLinear(subControlPoints);
|
||||
{
|
||||
List<Vector2> subPath = PathApproximator.CatmullToPiecewiseLinear(subControlPoints);
|
||||
|
||||
if (!OptimiseCatmull)
|
||||
return subPath;
|
||||
|
||||
// At draw time, osu!stable optimises paths by only keeping piecewise segments that are 6px apart.
|
||||
// For the most part we don't care about this optimisation, and its additional heuristics are hard to reproduce in every implementation.
|
||||
//
|
||||
// However, it matters for Catmull paths which form "bulbs" around sequential knots with identical positions,
|
||||
// so we'll apply a very basic form of the optimisation here and return a length representing the optimised portion.
|
||||
// The returned length is important so that the optimisation doesn't cause the path to get extended to match the value of ExpectedDistance.
|
||||
|
||||
List<Vector2> optimisedPath = new List<Vector2>(subPath.Count);
|
||||
|
||||
Vector2? lastStart = null;
|
||||
double lengthRemovedSinceStart = 0;
|
||||
|
||||
for (int i = 0; i < subPath.Count; i++)
|
||||
{
|
||||
if (lastStart == null)
|
||||
{
|
||||
optimisedPath.Add(subPath[i]);
|
||||
lastStart = subPath[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
Debug.Assert(i > 0);
|
||||
|
||||
double distFromStart = Vector2.Distance(lastStart.Value, subPath[i]);
|
||||
lengthRemovedSinceStart += Vector2.Distance(subPath[i - 1], subPath[i]);
|
||||
|
||||
// See PathApproximator.catmull_detail.
|
||||
const int catmull_detail = 50;
|
||||
const int catmull_segment_length = catmull_detail * 2;
|
||||
|
||||
// Either 6px from the start, the last vertex at every knot, or the end of the path.
|
||||
if (distFromStart > 6 || (i + 1) % catmull_segment_length == 0 || i == subPath.Count - 1)
|
||||
{
|
||||
optimisedPath.Add(subPath[i]);
|
||||
optimisedLength += lengthRemovedSinceStart - distFromStart;
|
||||
|
||||
lastStart = null;
|
||||
lengthRemovedSinceStart = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return optimisedPath;
|
||||
}
|
||||
}
|
||||
|
||||
return PathApproximator.BSplineToPiecewiseLinear(subControlPoints, type.Degree ?? subControlPoints.Length);
|
||||
|
@ -315,7 +396,7 @@ private List<Vector2> calculateSubPath(ReadOnlySpan<Vector2> subControlPoints, P
|
|||
|
||||
private void calculateLength()
|
||||
{
|
||||
calculatedLength = 0;
|
||||
calculatedLength = optimisedLength;
|
||||
cumulativeLength.Clear();
|
||||
cumulativeLength.Add(0);
|
||||
|
||||
|
|
Loading…
Reference in New Issue