mirror of
https://github.com/ppy/osu
synced 2024-12-25 00:02:48 +00:00
Merge pull request #24527 from Pasi4K5/fix-slider-reversing
Fix sliders being reversed incorrectly in the editor
This commit is contained in:
commit
aa5680a8aa
110
osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderReversal.cs
Normal file
110
osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderReversal.cs
Normal file
@ -0,0 +1,110 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
{
|
||||
public partial class TestSceneSliderReversal : TestSceneOsuEditor
|
||||
{
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(Ruleset.Value, false);
|
||||
|
||||
private readonly PathControlPoint[][] paths =
|
||||
{
|
||||
createPathSegment(
|
||||
PathType.PerfectCurve,
|
||||
new Vector2(200, -50),
|
||||
new Vector2(250, 0)
|
||||
),
|
||||
createPathSegment(
|
||||
PathType.Linear,
|
||||
new Vector2(100, 0),
|
||||
new Vector2(100, 100)
|
||||
)
|
||||
};
|
||||
|
||||
private static PathControlPoint[] createPathSegment(PathType type, params Vector2[] positions)
|
||||
{
|
||||
return positions.Select(p => new PathControlPoint
|
||||
{
|
||||
Position = p
|
||||
}).Prepend(new PathControlPoint
|
||||
{
|
||||
Type = type
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
private Slider selectedSlider => (Slider)EditorBeatmap.SelectedHitObjects[0];
|
||||
|
||||
[TestCase(0, 250)]
|
||||
[TestCase(0, 200)]
|
||||
[TestCase(1, 120)]
|
||||
[TestCase(1, 80)]
|
||||
public void TestSliderReversal(int pathIndex, double length)
|
||||
{
|
||||
var controlPoints = paths[pathIndex];
|
||||
|
||||
Vector2 oldStartPos = default;
|
||||
Vector2 oldEndPos = default;
|
||||
double oldDistance = default;
|
||||
var oldControlPointTypes = controlPoints.Select(p => p.Type);
|
||||
|
||||
AddStep("Add slider", () =>
|
||||
{
|
||||
var slider = new Slider
|
||||
{
|
||||
Position = new Vector2(OsuPlayfield.BASE_SIZE.X / 2, OsuPlayfield.BASE_SIZE.Y / 2),
|
||||
Path = new SliderPath(controlPoints)
|
||||
{
|
||||
ExpectedDistance = { Value = length }
|
||||
}
|
||||
};
|
||||
|
||||
EditorBeatmap.Add(slider);
|
||||
|
||||
oldStartPos = slider.Position;
|
||||
oldEndPos = slider.EndPosition;
|
||||
oldDistance = slider.Path.Distance;
|
||||
});
|
||||
|
||||
AddStep("Select slider", () =>
|
||||
{
|
||||
var slider = (Slider)EditorBeatmap.HitObjects[0];
|
||||
EditorBeatmap.SelectedHitObjects.Add(slider);
|
||||
});
|
||||
|
||||
AddStep("Reverse slider", () =>
|
||||
{
|
||||
InputManager.PressKey(Key.LControl);
|
||||
InputManager.Key(Key.G);
|
||||
InputManager.ReleaseKey(Key.LControl);
|
||||
});
|
||||
|
||||
AddAssert("Slider has correct length", () =>
|
||||
Precision.AlmostEquals(selectedSlider.Path.Distance, oldDistance));
|
||||
|
||||
AddAssert("Slider has correct start position", () =>
|
||||
Vector2.Distance(selectedSlider.Position, oldEndPos) < 1);
|
||||
|
||||
AddAssert("Slider has correct end position", () =>
|
||||
Vector2.Distance(selectedSlider.EndPosition, oldStartPos) < 1);
|
||||
|
||||
AddAssert("Control points have correct types", () =>
|
||||
{
|
||||
var newControlPointTypes = selectedSlider.Path.ControlPoints.Select(p => p.Type).ToArray();
|
||||
|
||||
return oldControlPointTypes.Take(newControlPointTypes.Length).SequenceEqual(newControlPointTypes);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
@ -25,6 +26,53 @@ namespace osu.Game.Rulesets.Objects
|
||||
/// <param name="sliderPath">The <see cref="SliderPath"/>.</param>
|
||||
/// <param name="positionalOffset">The positional offset of the resulting path. It should be added to the start position of this path.</param>
|
||||
public static void Reverse(this SliderPath sliderPath, out Vector2 positionalOffset)
|
||||
{
|
||||
var controlPoints = sliderPath.ControlPoints;
|
||||
|
||||
var inheritedLinearPoints = controlPoints.Where(p => sliderPath.PointsInSegment(p)[0].Type == PathType.Linear && p.Type is null).ToList();
|
||||
|
||||
// Inherited points after a linear point, as well as the first control point if it inherited,
|
||||
// should be treated as linear points, so their types are temporarily changed to linear.
|
||||
inheritedLinearPoints.ForEach(p => p.Type = PathType.Linear);
|
||||
|
||||
double[] segmentEnds = sliderPath.GetSegmentEnds().ToArray();
|
||||
|
||||
// Remove segments after the end of the slider.
|
||||
for (int numSegmentsToRemove = segmentEnds.Count(se => se >= 1) - 1; numSegmentsToRemove > 0 && controlPoints.Count > 0;)
|
||||
{
|
||||
if (controlPoints.Last().Type is not null)
|
||||
{
|
||||
numSegmentsToRemove--;
|
||||
segmentEnds = segmentEnds[..^1];
|
||||
}
|
||||
|
||||
controlPoints.RemoveAt(controlPoints.Count - 1);
|
||||
}
|
||||
|
||||
// Restore original control point types.
|
||||
inheritedLinearPoints.ForEach(p => p.Type = null);
|
||||
|
||||
// Recalculate middle perfect curve control points at the end of the slider path.
|
||||
if (controlPoints.Count >= 3 && controlPoints[^3].Type == PathType.PerfectCurve && controlPoints[^2].Type is null && segmentEnds.Any())
|
||||
{
|
||||
double lastSegmentStart = segmentEnds.Length > 1 ? segmentEnds[^2] : 0;
|
||||
double lastSegmentEnd = segmentEnds[^1];
|
||||
|
||||
var circleArcPath = new List<Vector2>();
|
||||
sliderPath.GetPathToProgress(circleArcPath, lastSegmentStart / lastSegmentEnd, 1);
|
||||
|
||||
controlPoints[^2].Position = circleArcPath[circleArcPath.Count / 2];
|
||||
}
|
||||
|
||||
sliderPath.reverseControlPoints(out positionalOffset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reverses the order of the provided <see cref="SliderPath"/>'s <see cref="PathControlPoint"/>s.
|
||||
/// </summary>
|
||||
/// <param name="sliderPath">The <see cref="SliderPath"/>.</param>
|
||||
/// <param name="positionalOffset">The positional offset of the resulting path. It should be added to the start position of this path.</param>
|
||||
private static void reverseControlPoints(this SliderPath sliderPath, out Vector2 positionalOffset)
|
||||
{
|
||||
var points = sliderPath.ControlPoints.ToArray();
|
||||
positionalOffset = sliderPath.PositionAt(1);
|
||||
|
Loading…
Reference in New Issue
Block a user