mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 10:52:53 +08:00
Merge pull request #24581 from OliBomby/fix-segment-ends
Fix `SliderPath.GetSegmentEnds`
This commit is contained in:
commit
90bef267c8
@ -185,6 +185,37 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddStep("shorten to -10 length", () => path.ExpectedDistance.Value = -10);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGetSegmentEnds()
|
||||
{
|
||||
var positions = new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(100, 0),
|
||||
new Vector2(100),
|
||||
new Vector2(200, 100),
|
||||
};
|
||||
double[] distances = { 100d, 200d, 300d };
|
||||
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(positions.Select(p => new PathControlPoint(p, PathType.Linear))));
|
||||
AddAssert("segment ends are correct", () => path.GetSegmentEnds(), () => Is.EqualTo(distances.Select(d => d / 300)));
|
||||
AddAssert("segment end positions recovered", () => path.GetSegmentEnds().Select(p => path.PositionAt(p)), () => Is.EqualTo(positions.Skip(1)));
|
||||
|
||||
AddStep("lengthen last segment", () => path.ExpectedDistance.Value = 400);
|
||||
AddAssert("segment ends are correct", () => path.GetSegmentEnds(), () => Is.EqualTo(distances.Select(d => d / 400)));
|
||||
AddAssert("segment end positions recovered", () => path.GetSegmentEnds().Select(p => path.PositionAt(p)), () => Is.EqualTo(positions.Skip(1)));
|
||||
|
||||
AddStep("shorten last segment", () => path.ExpectedDistance.Value = 150);
|
||||
AddAssert("segment ends are correct", () => path.GetSegmentEnds(), () => Is.EqualTo(distances.Select(d => d / 150)));
|
||||
// see remarks in `GetSegmentEnds()` xmldoc (`SliderPath.PositionAt()` clamps progress to [0,1]).
|
||||
AddAssert("segment end positions not recovered", () => path.GetSegmentEnds().Select(p => path.PositionAt(p)), () => Is.EqualTo(new[]
|
||||
{
|
||||
positions[1],
|
||||
new Vector2(100, 50),
|
||||
new Vector2(100, 50),
|
||||
}));
|
||||
}
|
||||
|
||||
private List<PathControlPoint> createSegment(PathType type, params Vector2[] controlPoints)
|
||||
{
|
||||
var points = controlPoints.Select(p => new PathControlPoint { Position = p }).ToList();
|
||||
|
@ -40,11 +40,13 @@ namespace osu.Game.Rulesets.Objects
|
||||
|
||||
private readonly List<Vector2> calculatedPath = new List<Vector2>();
|
||||
private readonly List<double> cumulativeLength = new List<double>();
|
||||
private readonly List<int> segmentEnds = new List<int>();
|
||||
private readonly Cached pathCache = new Cached();
|
||||
|
||||
private double calculatedLength;
|
||||
|
||||
private readonly List<int> segmentEnds = new List<int>();
|
||||
private double[] segmentEndDistances = Array.Empty<double>();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="SliderPath"/>.
|
||||
/// </summary>
|
||||
@ -196,13 +198,28 @@ namespace osu.Game.Rulesets.Objects
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the progress values at which segments of the path end.
|
||||
/// Returns the progress values at which (control point) segments of the path end.
|
||||
/// Ranges from 0 (beginning of the path) to 1 (end of the path) to infinity (beyond the end of the path).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="PositionAt"/> truncates the progression values to [0,1],
|
||||
/// so you can't use this method in conjunction with that one to retrieve the positions of segment ends beyond the end of the path.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <para>
|
||||
/// In case <see cref="Distance"/> is less than <see cref="CalculatedDistance"/>,
|
||||
/// the last segment ends after the end of the path, hence it returns a value greater than 1.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// In case <see cref="Distance"/> is greater than <see cref="CalculatedDistance"/>,
|
||||
/// the last segment ends before the end of the path, hence it returns a value less than 1.
|
||||
/// </para>
|
||||
/// </example>
|
||||
public IEnumerable<double> GetSegmentEnds()
|
||||
{
|
||||
ensureValid();
|
||||
|
||||
return segmentEnds.Select(i => cumulativeLength[i] / calculatedLength);
|
||||
return segmentEndDistances.Select(d => d / Distance);
|
||||
}
|
||||
|
||||
private void invalidate()
|
||||
@ -251,8 +268,11 @@ namespace osu.Game.Rulesets.Objects
|
||||
calculatedPath.Add(t);
|
||||
}
|
||||
|
||||
// Remember the index of the segment end
|
||||
segmentEnds.Add(calculatedPath.Count - 1);
|
||||
if (i > 0)
|
||||
{
|
||||
// Remember the index of the segment end
|
||||
segmentEnds.Add(calculatedPath.Count - 1);
|
||||
}
|
||||
|
||||
// Start the new segment at the current vertex
|
||||
start = i;
|
||||
@ -298,6 +318,14 @@ namespace osu.Game.Rulesets.Objects
|
||||
cumulativeLength.Add(calculatedLength);
|
||||
}
|
||||
|
||||
// Store the distances of the segment ends now, because after shortening the indices may be out of range
|
||||
segmentEndDistances = new double[segmentEnds.Count];
|
||||
|
||||
for (int i = 0; i < segmentEnds.Count; i++)
|
||||
{
|
||||
segmentEndDistances[i] = cumulativeLength[segmentEnds[i]];
|
||||
}
|
||||
|
||||
if (ExpectedDistance.Value is double expectedDistance && calculatedLength != expectedDistance)
|
||||
{
|
||||
// In osu-stable, if the last two control points of a slider are equal, extension is not performed.
|
||||
@ -319,10 +347,6 @@ namespace osu.Game.Rulesets.Objects
|
||||
{
|
||||
cumulativeLength.RemoveAt(cumulativeLength.Count - 1);
|
||||
calculatedPath.RemoveAt(pathEndIndex--);
|
||||
|
||||
// Shorten the last segment to the expected distance
|
||||
if (segmentEnds.Count > 0)
|
||||
segmentEnds[^1]--;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user