mirror of
https://github.com/ppy/osu.git
synced 2024-12-13 03:35:35 +08:00
Reworked linear line check, and optimized scaled flat slider test
This commit is contained in:
parent
fff52be59a
commit
b2c4e0e951
@ -6,6 +6,7 @@
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
@ -68,108 +69,52 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
AddAssert("slider length shrunk", () => slider.Path.Distance < distanceBefore);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Timeout(4000)] //Catches crashes in other threads, but not ideal. Hopefully there is a improvement to this.
|
||||
public void TestScalingSliderFlat(
|
||||
[Values(0, 1, 2, 3)] int typeInt
|
||||
)
|
||||
{
|
||||
Slider slider = null!;
|
||||
|
||||
switch (typeInt)
|
||||
{
|
||||
case 0:
|
||||
AddStep("Add linear slider", () =>
|
||||
{
|
||||
slider = new Slider { StartTime = EditorClock.CurrentTime, Position = new Vector2(300) };
|
||||
|
||||
PathControlPoint[] points =
|
||||
{
|
||||
new PathControlPoint(new Vector2(0), PathType.LINEAR),
|
||||
new PathControlPoint(new Vector2(50, 100)),
|
||||
};
|
||||
|
||||
slider.Path = new SliderPath(points);
|
||||
EditorBeatmap.Add(slider);
|
||||
});
|
||||
break;
|
||||
|
||||
case 1:
|
||||
AddStep("Add perfect curve slider", () =>
|
||||
{
|
||||
slider = new Slider { StartTime = EditorClock.CurrentTime, Position = new Vector2(300) };
|
||||
|
||||
PathControlPoint[] points =
|
||||
{
|
||||
new PathControlPoint(new Vector2(0), PathType.PERFECT_CURVE),
|
||||
new PathControlPoint(new Vector2(50, 25)),
|
||||
new PathControlPoint(new Vector2(25, 100)),
|
||||
};
|
||||
|
||||
slider.Path = new SliderPath(points);
|
||||
EditorBeatmap.Add(slider);
|
||||
});
|
||||
break;
|
||||
|
||||
case 3:
|
||||
AddStep("Add catmull slider", () =>
|
||||
{
|
||||
slider = new Slider { StartTime = EditorClock.CurrentTime, Position = new Vector2(300) };
|
||||
|
||||
PathControlPoint[] points =
|
||||
{
|
||||
new PathControlPoint(new Vector2(0), PathType.CATMULL),
|
||||
new PathControlPoint(new Vector2(50, 25)),
|
||||
new PathControlPoint(new Vector2(25, 80)),
|
||||
new PathControlPoint(new Vector2(40, 100)),
|
||||
};
|
||||
|
||||
slider.Path = new SliderPath(points);
|
||||
EditorBeatmap.Add(slider);
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
AddStep("Add bezier slider", () =>
|
||||
{
|
||||
slider = new Slider { StartTime = EditorClock.CurrentTime, Position = new Vector2(300) };
|
||||
|
||||
PathControlPoint[] points =
|
||||
{
|
||||
new PathControlPoint(new Vector2(0), PathType.BEZIER),
|
||||
new PathControlPoint(new Vector2(50, 25)),
|
||||
new PathControlPoint(new Vector2(25, 80)),
|
||||
new PathControlPoint(new Vector2(40, 100)),
|
||||
};
|
||||
|
||||
slider.Path = new SliderPath(points);
|
||||
EditorBeatmap.Add(slider);
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
AddAssert("ensure object placed", () => EditorBeatmap.HitObjects.Count == 1);
|
||||
|
||||
moveMouse(new Vector2(300));
|
||||
AddStep("select slider", () => InputManager.Click(MouseButton.Left));
|
||||
AddStep("slider is valid", () => slider.Path.GetSegmentEnds()); //To run ensureValid();
|
||||
|
||||
SelectionBoxDragHandle dragHandle = null!;
|
||||
AddStep("store drag handle", () => dragHandle = Editor.ChildrenOfType<SelectionBoxDragHandle>().Skip(1).First());
|
||||
AddAssert("is dragHandle not null", () => dragHandle != null);
|
||||
|
||||
AddStep("move mouse to handle", () => InputManager.MoveMouseTo(dragHandle));
|
||||
AddStep("begin drag", () => InputManager.PressButton(MouseButton.Left));
|
||||
moveMouse(new Vector2(0, 300));
|
||||
AddStep("end drag", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
AddStep("move mouse to handle", () => InputManager.MoveMouseTo(dragHandle));
|
||||
AddStep("begin drag", () => InputManager.PressButton(MouseButton.Left));
|
||||
moveMouse(new Vector2(0, 300)); //Should crash here if broken, although doesn't count as failed...
|
||||
AddStep("end drag", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
}
|
||||
|
||||
private void moveMouse(Vector2 pos) =>
|
||||
AddStep($"move mouse to {pos}", () => InputManager.MoveMouseTo(playfield.ToScreenSpace(pos)));
|
||||
}
|
||||
[TestFixture]
|
||||
public class TestSliderNearLinearScaling
|
||||
{
|
||||
[Test]
|
||||
public void TestScalingSliderFlat()
|
||||
{
|
||||
Slider sliderPerfect = new Slider
|
||||
{
|
||||
Position = new Vector2(300),
|
||||
Path = new SliderPath(
|
||||
[
|
||||
new PathControlPoint(new Vector2(0), PathType.PERFECT_CURVE),
|
||||
new PathControlPoint(new Vector2(50, 25)),
|
||||
new PathControlPoint(new Vector2(25, 100)),
|
||||
])
|
||||
};
|
||||
|
||||
Slider sliderBezier = new Slider
|
||||
{
|
||||
Position = new Vector2(300),
|
||||
Path = new SliderPath(
|
||||
[
|
||||
new PathControlPoint(new Vector2(0), PathType.BEZIER),
|
||||
new PathControlPoint(new Vector2(50, 25)),
|
||||
new PathControlPoint(new Vector2(25, 100)),
|
||||
])
|
||||
};
|
||||
|
||||
scaleSlider(sliderPerfect, new Vector2(0.000001f, 1));
|
||||
scaleSlider(sliderBezier, new Vector2(0.000001f, 1));
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
Assert.True(Precision.AlmostEquals(sliderPerfect.Path.PositionAt(i / 100.0f), sliderBezier.Path.PositionAt(i / 100.0f)));
|
||||
}
|
||||
}
|
||||
|
||||
private void scaleSlider(Slider slider, Vector2 scale)
|
||||
{
|
||||
for (int i = 0; i < slider.Path.ControlPoints.Count; i++)
|
||||
{
|
||||
slider.Path.ControlPoints[i].Position *= scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -269,38 +269,6 @@ namespace osu.Game.Rulesets.Objects
|
||||
pathCache.Validate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the array of vectors is almost straight.
|
||||
/// </summary>
|
||||
/// <para>
|
||||
/// The angle is first obtained based on the farthest vector from the first,
|
||||
/// then we find the angle of each vector from the first,
|
||||
/// and calculate the distance between the two angle vectors.
|
||||
/// We than scale this distance to the distance from the first vector
|
||||
/// (or by 10 if the distance is smaller),
|
||||
/// and if it is greater than acceptableDifference, we return false.
|
||||
/// </para>
|
||||
private static bool isAlmostStraight(Vector2[] vectors, float acceptableDifference = 0.1f)
|
||||
{
|
||||
if (vectors.Length <= 2 || vectors.All(x => x == vectors.First())) return true;
|
||||
|
||||
Vector2 first = vectors.First();
|
||||
Vector2 farthest = vectors.MaxBy(x => Vector2.Distance(first, x));
|
||||
|
||||
Vector2 angle = Vector2.Normalize(farthest - first);
|
||||
|
||||
foreach (Vector2 vector in vectors)
|
||||
{
|
||||
if (vector == first)
|
||||
continue;
|
||||
|
||||
if (Math.Max(10.0f, Vector2.Distance(vector, first)) * Vector2.Distance(Vector2.Normalize(vector - first), angle) > acceptableDifference)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void calculatePath()
|
||||
{
|
||||
calculatedPath.Clear();
|
||||
@ -325,10 +293,6 @@ namespace osu.Game.Rulesets.Objects
|
||||
var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1);
|
||||
var segmentType = ControlPoints[start].Type ?? PathType.LINEAR;
|
||||
|
||||
//If a segment is almost straight, treat it as linear.
|
||||
if (segmentType != PathType.LINEAR && isAlmostStraight(segmentVertices.ToArray()))
|
||||
segmentType = PathType.LINEAR;
|
||||
|
||||
// No need to calculate path when there is only 1 vertex
|
||||
if (segmentVertices.Length == 1)
|
||||
calculatedPath.Add(segmentVertices[0]);
|
||||
@ -366,6 +330,13 @@ namespace osu.Game.Rulesets.Objects
|
||||
if (subControlPoints.Length != 3)
|
||||
break;
|
||||
|
||||
//If a curve's theta range almost equals zero, the radius needed to have more than a
|
||||
//floating point error difference is very large and results in a nearly straight path.
|
||||
//Calculate it via a bezier aproximation instead.
|
||||
//0.0005 corresponds with a radius of 8000 to have a more than 0.001 shift in the X value
|
||||
if (Math.Abs(new CircularArcProperties(subControlPoints).ThetaRange) <= 0.0005d)
|
||||
break;
|
||||
|
||||
List<Vector2> subPath = PathApproximator.CircularArcToPiecewiseLinear(subControlPoints);
|
||||
|
||||
// If for some reason a circular arc could not be fit to the 3 given points, fall back to a numerically stable bezier approximation.
|
||||
|
Loading…
Reference in New Issue
Block a user