1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-13 16:13:34 +08:00

Generalize Bezier curves to BSplines of Nth degree

This commit is contained in:
Thomas Müller-Höhne 2023-11-08 19:43:54 +09:00 committed by cs
parent 9df8fc14e7
commit 926636cc03
54 changed files with 372 additions and 305 deletions

View File

@ -140,7 +140,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
AddStep("update hit object path", () =>
{
hitObject.Path = new SliderPath(PathType.PerfectCurve, new[]
hitObject.Path = new SliderPath(PathType.PERFECTCURVE, new[]
{
Vector2.Zero,
new Vector2(100, 100),
@ -190,16 +190,16 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
[Test]
public void TestVertexResampling()
{
addBlueprintStep(100, 100, new SliderPath(PathType.PerfectCurve, new[]
addBlueprintStep(100, 100, new SliderPath(PathType.PERFECTCURVE, new[]
{
Vector2.Zero,
new Vector2(100, 100),
new Vector2(50, 200),
}), 0.5);
AddAssert("1 vertex per 1 nested HO", () => getVertices().Count == hitObject.NestedHitObjects.Count);
AddAssert("slider path not yet changed", () => hitObject.Path.ControlPoints[0].Type == PathType.PerfectCurve);
AddAssert("slider path not yet changed", () => hitObject.Path.ControlPoints[0].Type == PathType.PERFECTCURVE);
addAddVertexSteps(150, 150);
AddAssert("slider path change to linear", () => hitObject.Path.ControlPoints[0].Type == PathType.Linear);
AddAssert("slider path change to linear", () => hitObject.Path.ControlPoints[0].Type == PathType.LINEAR);
}
private void addBlueprintStep(double time, float x, SliderPath sliderPath, double velocity) => AddStep("add selection blueprint", () =>

View File

@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.Catch.Tests
} while (rng.Next(2) != 0);
int length = sliderPath.ControlPoints.Count - start + 1;
sliderPath.ControlPoints[start].Type = length <= 2 ? PathType.Linear : length == 3 ? PathType.PerfectCurve : PathType.Bezier;
sliderPath.ControlPoints[start].Type = length <= 2 ? PathType.LINEAR : length == 3 ? PathType.PERFECTCURVE : PathType.BEZIER;
} while (rng.Next(3) != 0);
if (rng.Next(5) == 0)
@ -215,7 +215,7 @@ namespace osu.Game.Rulesets.Catch.Tests
foreach (var point in sliderPath.ControlPoints)
{
Assert.That(point.Type, Is.EqualTo(PathType.Linear).Or.Null);
Assert.That(point.Type, Is.EqualTo(PathType.LINEAR).Or.Null);
Assert.That(sliderStartY + point.Position.Y, Is.InRange(0, JuiceStreamPath.OSU_PLAYFIELD_HEIGHT));
}

View File

@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods
var stream = new JuiceStream
{
StartTime = 1000,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(100, 0),

View File

@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods
{
X = CatchPlayfield.CENTER_X,
StartTime = 3000,
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, Vector2.UnitY * 200 })
Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, Vector2.UnitY * 200 })
}
}
}

View File

@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Catch.Tests
beatmap.HitObjects.Add(new JuiceStream
{
X = CatchPlayfield.CENTER_X - width / 2,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(width, 0)

View File

@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Catch.Tests
new JuiceStream
{
StartTime = 1000,
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(0, -192) }),
Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(0, -192) }),
X = CatchPlayfield.WIDTH / 2
}
}

View File

@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Catch.Tests
{
X = xCoords,
StartTime = playfieldTime + 1000,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(0, 200)

View File

@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Catch.Tests
new JuiceStream
{
X = CatchPlayfield.CENTER_X,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(0, 100)

View File

@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
path.ConvertFromSliderPath(sliderPath, hitObject.Velocity);
// If the original slider path has non-linear type segments, resample the vertices at nested hit object times to reduce the number of vertices.
if (sliderPath.ControlPoints.Any(p => p.Type != null && p.Type != PathType.Linear))
if (sliderPath.ControlPoints.Any(p => p.Type != null && p.Type != PathType.LINEAR))
{
path.ResampleVertices(hitObject.NestedHitObjects
.Skip(1).TakeWhile(h => !(h is Fruit)) // Only droplets in the first span are used.

View File

@ -236,7 +236,7 @@ namespace osu.Game.Rulesets.Catch.Objects
for (int i = 1; i < vertices.Count; i++)
{
sliderPath.ControlPoints[^1].Type = PathType.Linear;
sliderPath.ControlPoints[^1].Type = PathType.LINEAR;
float deltaX = vertices[i].X - lastPosition.X;
double length = (vertices[i].Time - currentTime) * velocity;

View File

@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
Position = new Vector2(420, 240),
Path = new SliderPath(new[]
{
new PathControlPoint(new Vector2(0, 0), PathType.Linear),
new PathControlPoint(new Vector2(0, 0), PathType.LINEAR),
new PathControlPoint(new Vector2(-100, 0))
}),
}
@ -128,7 +128,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
Position = playfield_centre,
Path = new SliderPath(new[]
{
new PathControlPoint(new Vector2(0, 0), PathType.Linear),
new PathControlPoint(new Vector2(0, 0), PathType.LINEAR),
new PathControlPoint(new Vector2(0, -playfield_centre.Y + 5))
}),
}
@ -149,7 +149,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
Position = playfield_centre,
Path = new SliderPath(new[]
{
new PathControlPoint(new Vector2(0, 0), PathType.Linear),
new PathControlPoint(new Vector2(0, 0), PathType.LINEAR),
new PathControlPoint(new Vector2(0, -playfield_centre.Y + 5))
}),
StackHeight = 5
@ -171,7 +171,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
Position = new Vector2(0, 0),
Path = new SliderPath(new[]
{
new PathControlPoint(new Vector2(0, 0), PathType.Linear),
new PathControlPoint(new Vector2(0, 0), PathType.LINEAR),
new PathControlPoint(playfield_centre)
}),
}
@ -192,7 +192,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
Position = playfield_centre,
Path = new SliderPath(new[]
{
new PathControlPoint(new Vector2(0, 0), PathType.Linear),
new PathControlPoint(new Vector2(0, 0), PathType.LINEAR),
new PathControlPoint(-playfield_centre)
}),
}
@ -214,7 +214,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
Path = new SliderPath(new[]
{
// Circular arc shoots over the top of the screen.
new PathControlPoint(new Vector2(0, 0), PathType.PerfectCurve),
new PathControlPoint(new Vector2(0, 0), PathType.PERFECTCURVE),
new PathControlPoint(new Vector2(-100, -200)),
new PathControlPoint(new Vector2(100, -200))
}),

View File

@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
mergeSelection();
AddAssert("slider created", () => circle1 is not null && circle2 is not null && sliderCreatedFor(
(pos: circle1.Position, pathType: PathType.Linear),
(pos: circle1.Position, pathType: PathType.LINEAR),
(pos: circle2.Position, pathType: null)));
AddStep("undo", () => Editor.Undo());
@ -73,11 +73,11 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
var controlPoints = slider.Path.ControlPoints;
(Vector2, PathType?)[] args = new (Vector2, PathType?)[controlPoints.Count + 2];
args[0] = (circle1.Position, PathType.Linear);
args[0] = (circle1.Position, PathType.LINEAR);
for (int i = 0; i < controlPoints.Count; i++)
{
args[i + 1] = (controlPoints[i].Position + slider.Position, i == controlPoints.Count - 1 ? PathType.Linear : controlPoints[i].Type);
args[i + 1] = (controlPoints[i].Position + slider.Position, i == controlPoints.Count - 1 ? PathType.LINEAR : controlPoints[i].Type);
}
args[^1] = (circle2.Position, null);
@ -172,7 +172,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
mergeSelection();
AddAssert("slider created", () => circle1 is not null && circle2 is not null && sliderCreatedFor(
(pos: circle1.Position, pathType: PathType.Linear),
(pos: circle1.Position, pathType: PathType.LINEAR),
(pos: circle2.Position, pathType: null)));
AddAssert("samples exist", sliderSampleExist);
@ -227,7 +227,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
mergeSelection();
AddAssert("slider created", () => circle1 is not null && circle2 is not null && sliderCreatedFor(
(pos: circle1.Position, pathType: PathType.Linear),
(pos: circle1.Position, pathType: PathType.LINEAR),
(pos: circle2.Position, pathType: null)));
}

View File

@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
PathControlPoint[] points =
{
new PathControlPoint(new Vector2(0), PathType.PerfectCurve),
new PathControlPoint(new Vector2(0), PathType.PERFECTCURVE),
new PathControlPoint(new Vector2(-100, 0)),
new PathControlPoint(new Vector2(100, 20))
};

View File

@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
{
createVisualiser(true);
addControlPointStep(new Vector2(200), PathType.Bezier);
addControlPointStep(new Vector2(200), PathType.BEZIER);
addControlPointStep(new Vector2(300));
addControlPointStep(new Vector2(500, 300));
addControlPointStep(new Vector2(700, 200));
@ -63,9 +63,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
AddStep("select control point", () => visualiser.Pieces[1].IsSelected.Value = true);
addContextMenuItemStep("Perfect curve");
assertControlPointPathType(0, PathType.Bezier);
assertControlPointPathType(1, PathType.PerfectCurve);
assertControlPointPathType(3, PathType.Bezier);
assertControlPointPathType(0, PathType.BEZIER);
assertControlPointPathType(1, PathType.PERFECTCURVE);
assertControlPointPathType(3, PathType.BEZIER);
}
[Test]
@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
{
createVisualiser(true);
addControlPointStep(new Vector2(200), PathType.Bezier);
addControlPointStep(new Vector2(200), PathType.BEZIER);
addControlPointStep(new Vector2(300));
addControlPointStep(new Vector2(500, 300));
addControlPointStep(new Vector2(700, 200));
@ -83,8 +83,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
AddStep("select control point", () => visualiser.Pieces[2].IsSelected.Value = true);
addContextMenuItemStep("Perfect curve");
assertControlPointPathType(0, PathType.Bezier);
assertControlPointPathType(2, PathType.PerfectCurve);
assertControlPointPathType(0, PathType.BEZIER);
assertControlPointPathType(2, PathType.PERFECTCURVE);
assertControlPointPathType(4, null);
}
@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
{
createVisualiser(true);
addControlPointStep(new Vector2(200), PathType.Bezier);
addControlPointStep(new Vector2(200), PathType.BEZIER);
addControlPointStep(new Vector2(300));
addControlPointStep(new Vector2(500, 300));
addControlPointStep(new Vector2(700, 200));
@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
AddStep("select control point", () => visualiser.Pieces[3].IsSelected.Value = true);
addContextMenuItemStep("Perfect curve");
assertControlPointPathType(0, PathType.Bezier);
assertControlPointPathType(0, PathType.BEZIER);
AddAssert("point 3 is not inherited", () => slider.Path.ControlPoints[3].Type != null);
}
@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
{
createVisualiser(true);
addControlPointStep(new Vector2(200), PathType.Linear);
addControlPointStep(new Vector2(200), PathType.LINEAR);
addControlPointStep(new Vector2(300));
addControlPointStep(new Vector2(500, 300));
addControlPointStep(new Vector2(700, 200));
@ -123,9 +123,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
AddStep("select control point", () => visualiser.Pieces[1].IsSelected.Value = true);
addContextMenuItemStep("Perfect curve");
assertControlPointPathType(0, PathType.Linear);
assertControlPointPathType(1, PathType.PerfectCurve);
assertControlPointPathType(3, PathType.Linear);
assertControlPointPathType(0, PathType.LINEAR);
assertControlPointPathType(1, PathType.PERFECTCURVE);
assertControlPointPathType(3, PathType.LINEAR);
}
[Test]
@ -133,18 +133,18 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
{
createVisualiser(true);
addControlPointStep(new Vector2(200), PathType.Bezier);
addControlPointStep(new Vector2(300), PathType.PerfectCurve);
addControlPointStep(new Vector2(200), PathType.BEZIER);
addControlPointStep(new Vector2(300), PathType.PERFECTCURVE);
addControlPointStep(new Vector2(500, 300));
addControlPointStep(new Vector2(700, 200), PathType.Bezier);
addControlPointStep(new Vector2(700, 200), PathType.BEZIER);
addControlPointStep(new Vector2(500, 100));
moveMouseToControlPoint(3);
AddStep("select control point", () => visualiser.Pieces[3].IsSelected.Value = true);
addContextMenuItemStep("Inherit");
assertControlPointPathType(0, PathType.Bezier);
assertControlPointPathType(1, PathType.Bezier);
assertControlPointPathType(0, PathType.BEZIER);
assertControlPointPathType(1, PathType.BEZIER);
assertControlPointPathType(3, null);
}

View File

@ -38,9 +38,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
Position = new Vector2(256, 192),
Path = new SliderPath(new[]
{
new PathControlPoint(Vector2.Zero, PathType.PerfectCurve),
new PathControlPoint(Vector2.Zero, PathType.PERFECTCURVE),
new PathControlPoint(new Vector2(150, 150)),
new PathControlPoint(new Vector2(300, 0), PathType.PerfectCurve),
new PathControlPoint(new Vector2(300, 0), PathType.PERFECTCURVE),
new PathControlPoint(new Vector2(400, 0)),
new PathControlPoint(new Vector2(400, 150))
})
@ -182,7 +182,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left));
assertControlPointPosition(1, new Vector2(150, 50));
assertControlPointType(0, PathType.PerfectCurve);
assertControlPointType(0, PathType.PERFECTCURVE);
}
[Test]
@ -210,7 +210,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
AddAssert("three control point pieces selected", () => this.ChildrenOfType<PathControlPointPiece<Slider>>().Count(piece => piece.IsSelected.Value) == 3);
assertControlPointPosition(2, new Vector2(450, 50));
assertControlPointType(2, PathType.PerfectCurve);
assertControlPointType(2, PathType.PERFECTCURVE);
assertControlPointPosition(3, new Vector2(550, 50));
@ -249,7 +249,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
AddAssert("slider moved", () => Precision.AlmostEquals(slider.Position, new Vector2(256, 192) + new Vector2(150, 50)));
assertControlPointPosition(0, Vector2.Zero);
assertControlPointType(0, PathType.PerfectCurve);
assertControlPointType(0, PathType.PERFECTCURVE);
assertControlPointPosition(1, new Vector2(0, 100));
@ -272,7 +272,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left));
assertControlPointPosition(1, new Vector2(400, 0.01f));
assertControlPointType(0, PathType.Bezier);
assertControlPointType(0, PathType.BEZIER);
}
[Test]
@ -282,13 +282,13 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
AddStep("hold", () => InputManager.PressButton(MouseButton.Left));
addMovementStep(new Vector2(400, 0.01f));
assertControlPointType(0, PathType.Bezier);
assertControlPointType(0, PathType.BEZIER);
addMovementStep(new Vector2(150, 50));
AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left));
assertControlPointPosition(1, new Vector2(150, 50));
assertControlPointType(0, PathType.PerfectCurve);
assertControlPointType(0, PathType.PERFECTCURVE);
}
[Test]
@ -298,32 +298,32 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
AddStep("hold", () => InputManager.PressButton(MouseButton.Left));
addMovementStep(new Vector2(350, 0.01f));
assertControlPointType(2, PathType.Bezier);
assertControlPointType(2, PathType.BEZIER);
addMovementStep(new Vector2(150, 150));
AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left));
assertControlPointPosition(4, new Vector2(150, 150));
assertControlPointType(2, PathType.PerfectCurve);
assertControlPointType(2, PathType.PERFECTCURVE);
}
[Test]
public void TestDragControlPointPathAfterChangingType()
{
AddStep("change type to bezier", () => slider.Path.ControlPoints[2].Type = PathType.Bezier);
AddStep("change type to bezier", () => slider.Path.ControlPoints[2].Type = PathType.BEZIER);
AddStep("add point", () => slider.Path.ControlPoints.Add(new PathControlPoint(new Vector2(500, 10))));
AddStep("change type to perfect", () => slider.Path.ControlPoints[3].Type = PathType.PerfectCurve);
AddStep("change type to perfect", () => slider.Path.ControlPoints[3].Type = PathType.PERFECTCURVE);
moveMouseToControlPoint(4);
AddStep("hold", () => InputManager.PressButton(MouseButton.Left));
assertControlPointType(3, PathType.PerfectCurve);
assertControlPointType(3, PathType.PERFECTCURVE);
addMovementStep(new Vector2(350, 0.01f));
AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left));
assertControlPointPosition(4, new Vector2(350, 0.01f));
assertControlPointType(3, PathType.Bezier);
assertControlPointType(3, PathType.BEZIER);
}
private void addMovementStep(Vector2 relativePosition)

View File

@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
PathControlPoint[] points =
{
new PathControlPoint(new Vector2(0), PathType.Linear),
new PathControlPoint(new Vector2(0), PathType.LINEAR),
new PathControlPoint(new Vector2(100, 0)),
};
@ -82,7 +82,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
PathControlPoint[] points =
{
new PathControlPoint(new Vector2(0), PathType.Linear),
new PathControlPoint(new Vector2(0), PathType.LINEAR),
new PathControlPoint(new Vector2(100, 0)),
};
@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
PathControlPoint[] points =
{
new PathControlPoint(new Vector2(0), PathType.PerfectCurve),
new PathControlPoint(new Vector2(0), PathType.PERFECTCURVE),
new PathControlPoint(new Vector2(100, 0)),
new PathControlPoint(new Vector2(0, 10))
};
@ -165,7 +165,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
PathControlPoint[] points =
{
new PathControlPoint(new Vector2(0), PathType.Linear),
new PathControlPoint(new Vector2(0), PathType.LINEAR),
new PathControlPoint(new Vector2(0, 50)),
new PathControlPoint(new Vector2(0, 100))
};

View File

@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
assertPlaced(true);
assertLength(200);
assertControlPointCount(2);
assertControlPointType(0, PathType.Linear);
assertControlPointType(0, PathType.LINEAR);
}
[Test]
@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
assertPlaced(true);
assertControlPointCount(2);
assertControlPointType(0, PathType.Linear);
assertControlPointType(0, PathType.LINEAR);
}
[Test]
@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
assertPlaced(true);
assertControlPointCount(3);
assertControlPointPosition(1, new Vector2(100, 0));
assertControlPointType(0, PathType.PerfectCurve);
assertControlPointType(0, PathType.PERFECTCURVE);
}
[Test]
@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
assertControlPointCount(4);
assertControlPointPosition(1, new Vector2(100, 0));
assertControlPointPosition(2, new Vector2(100, 100));
assertControlPointType(0, PathType.Bezier);
assertControlPointType(0, PathType.BEZIER);
}
[Test]
@ -131,8 +131,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
assertPlaced(true);
assertControlPointCount(3);
assertControlPointPosition(1, new Vector2(100, 0));
assertControlPointType(0, PathType.Linear);
assertControlPointType(1, PathType.Linear);
assertControlPointType(0, PathType.LINEAR);
assertControlPointType(1, PathType.LINEAR);
}
[Test]
@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
assertPlaced(true);
assertControlPointCount(2);
assertControlPointType(0, PathType.Linear);
assertControlPointType(0, PathType.LINEAR);
assertLength(100);
}
@ -172,7 +172,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
assertPlaced(true);
assertControlPointCount(3);
assertControlPointType(0, PathType.PerfectCurve);
assertControlPointType(0, PathType.PERFECTCURVE);
}
[Test]
@ -196,7 +196,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
assertPlaced(true);
assertControlPointCount(4);
assertControlPointType(0, PathType.Bezier);
assertControlPointType(0, PathType.BEZIER);
}
[Test]
@ -216,8 +216,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
assertControlPointCount(3);
assertControlPointPosition(1, new Vector2(100, 0));
assertControlPointPosition(2, new Vector2(100));
assertControlPointType(0, PathType.Linear);
assertControlPointType(1, PathType.Linear);
assertControlPointType(0, PathType.LINEAR);
assertControlPointType(1, PathType.LINEAR);
}
[Test]
@ -240,8 +240,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
assertControlPointCount(4);
assertControlPointPosition(1, new Vector2(100, 0));
assertControlPointPosition(2, new Vector2(100));
assertControlPointType(0, PathType.Linear);
assertControlPointType(1, PathType.PerfectCurve);
assertControlPointType(0, PathType.LINEAR);
assertControlPointType(1, PathType.PERFECTCURVE);
}
[Test]
@ -269,8 +269,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
assertControlPointPosition(2, new Vector2(100));
assertControlPointPosition(3, new Vector2(200, 100));
assertControlPointPosition(4, new Vector2(200));
assertControlPointType(0, PathType.PerfectCurve);
assertControlPointType(2, PathType.PerfectCurve);
assertControlPointType(0, PathType.PERFECTCURVE);
assertControlPointType(2, PathType.PERFECTCURVE);
}
[Test]
@ -287,7 +287,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
assertPlaced(true);
assertLength(200);
assertControlPointCount(2);
assertControlPointType(0, PathType.Linear);
assertControlPointType(0, PathType.LINEAR);
}
[Test]
@ -306,7 +306,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
assertPlaced(true);
assertControlPointCount(3);
assertControlPointType(0, PathType.Bezier);
assertControlPointType(0, PathType.BEZIER);
}
[Test]
@ -326,7 +326,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
assertPlaced(true);
assertControlPointCount(3);
assertControlPointType(0, PathType.PerfectCurve);
assertControlPointType(0, PathType.PERFECTCURVE);
}
[Test]
@ -347,7 +347,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
assertPlaced(true);
assertControlPointCount(3);
assertControlPointType(0, PathType.PerfectCurve);
assertControlPointType(0, PathType.PERFECTCURVE);
}
[Test]
@ -368,7 +368,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
assertPlaced(true);
assertControlPointCount(3);
assertControlPointType(0, PathType.Bezier);
assertControlPointType(0, PathType.BEZIER);
}
[Test]
@ -385,7 +385,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
assertPlaced(true);
assertControlPointCount(3);
assertControlPointType(0, PathType.PerfectCurve);
assertControlPointType(0, PathType.PERFECTCURVE);
}
private void addMovementStep(Vector2 position) => AddStep($"move mouse to {position}", () => InputManager.MoveMouseTo(InputManager.ToScreenSpace(position)));

View File

@ -22,12 +22,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
private readonly PathControlPoint[][] paths =
{
createPathSegment(
PathType.PerfectCurve,
PathType.PERFECTCURVE,
new Vector2(200, -50),
new Vector2(250, 0)
),
createPathSegment(
PathType.Linear,
PathType.LINEAR,
new Vector2(100, 0),
new Vector2(100, 100)
)

View File

@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
slider = new Slider
{
Position = new Vector2(256, 192),
Path = new SliderPath(PathType.Bezier, new[]
Path = new SliderPath(PathType.BEZIER, new[]
{
Vector2.Zero,
new Vector2(150, 150),

View File

@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
{
ControlPoints =
{
new PathControlPoint(Vector2.Zero, PathType.PerfectCurve),
new PathControlPoint(Vector2.Zero, PathType.PERFECTCURVE),
new PathControlPoint(new Vector2(136, 205)),
new PathControlPoint(new Vector2(-4, 226))
}
@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
{
OsuSelectionHandler selectionHandler;
AddAssert("first control point perfect", () => slider.Path.ControlPoints[0].Type == PathType.PerfectCurve);
AddAssert("first control point perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECTCURVE);
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
AddStep("rotate 90 degrees ccw", () =>
@ -190,7 +190,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
selectionHandler.HandleRotation(-90);
});
AddAssert("first control point still perfect", () => slider.Path.ControlPoints[0].Type == PathType.PerfectCurve);
AddAssert("first control point still perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECTCURVE);
}
[Test]
@ -223,7 +223,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
{
OsuSelectionHandler selectionHandler;
AddAssert("first control point perfect", () => slider.Path.ControlPoints[0].Type == PathType.PerfectCurve);
AddAssert("first control point perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECTCURVE);
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
AddStep("flip slider horizontally", () =>
@ -232,7 +232,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
selectionHandler.OnPressed(new KeyBindingPressEvent<GlobalAction>(InputManager.CurrentState, GlobalAction.EditorFlipVertically));
});
AddAssert("first control point still perfect", () => slider.Path.ControlPoints[0].Type == PathType.PerfectCurve);
AddAssert("first control point still perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECTCURVE);
}
[Test]

View File

@ -45,9 +45,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
Position = new Vector2(0, 50),
Path = new SliderPath(new[]
{
new PathControlPoint(Vector2.Zero, PathType.PerfectCurve),
new PathControlPoint(Vector2.Zero, PathType.PERFECTCURVE),
new PathControlPoint(new Vector2(150, 150)),
new PathControlPoint(new Vector2(300, 0), PathType.PerfectCurve),
new PathControlPoint(new Vector2(300, 0), PathType.PERFECTCURVE),
new PathControlPoint(new Vector2(400, 0)),
new PathControlPoint(new Vector2(400, 150))
})
@ -73,20 +73,20 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
AddAssert("slider split", () => slider is not null && EditorBeatmap.HitObjects.Count == 2 &&
sliderCreatedFor((Slider)EditorBeatmap.HitObjects[0], 0, EditorBeatmap.HitObjects[1].StartTime - split_gap,
(new Vector2(0, 50), PathType.PerfectCurve),
(new Vector2(0, 50), PathType.PERFECTCURVE),
(new Vector2(150, 200), null),
(new Vector2(300, 50), null)
) && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[1], slider.StartTime, endTime + split_gap,
(new Vector2(300, 50), PathType.PerfectCurve),
(new Vector2(300, 50), PathType.PERFECTCURVE),
(new Vector2(400, 50), null),
(new Vector2(400, 200), null)
));
AddStep("undo", () => Editor.Undo());
AddAssert("original slider restored", () => EditorBeatmap.HitObjects.Count == 1 && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[0], 0, endTime,
(new Vector2(0, 50), PathType.PerfectCurve),
(new Vector2(0, 50), PathType.PERFECTCURVE),
(new Vector2(150, 200), null),
(new Vector2(300, 50), PathType.PerfectCurve),
(new Vector2(300, 50), PathType.PERFECTCURVE),
(new Vector2(400, 50), null),
(new Vector2(400, 200), null)
));
@ -104,11 +104,11 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
Position = new Vector2(0, 50),
Path = new SliderPath(new[]
{
new PathControlPoint(Vector2.Zero, PathType.PerfectCurve),
new PathControlPoint(Vector2.Zero, PathType.PERFECTCURVE),
new PathControlPoint(new Vector2(150, 150)),
new PathControlPoint(new Vector2(300, 0), PathType.Bezier),
new PathControlPoint(new Vector2(300, 0), PathType.BEZIER),
new PathControlPoint(new Vector2(400, 0)),
new PathControlPoint(new Vector2(400, 150), PathType.Catmull),
new PathControlPoint(new Vector2(400, 150), PathType.CATMULL),
new PathControlPoint(new Vector2(300, 200)),
new PathControlPoint(new Vector2(400, 250))
})
@ -139,15 +139,15 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
AddAssert("slider split", () => slider is not null && EditorBeatmap.HitObjects.Count == 3 &&
sliderCreatedFor((Slider)EditorBeatmap.HitObjects[0], 0, EditorBeatmap.HitObjects[1].StartTime - split_gap,
(new Vector2(0, 50), PathType.PerfectCurve),
(new Vector2(0, 50), PathType.PERFECTCURVE),
(new Vector2(150, 200), null),
(new Vector2(300, 50), null)
) && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[1], EditorBeatmap.HitObjects[0].GetEndTime() + split_gap, slider.StartTime - split_gap,
(new Vector2(300, 50), PathType.Bezier),
(new Vector2(300, 50), PathType.BEZIER),
(new Vector2(400, 50), null),
(new Vector2(400, 200), null)
) && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[2], EditorBeatmap.HitObjects[1].GetEndTime() + split_gap, endTime + split_gap * 2,
(new Vector2(400, 200), PathType.Catmull),
(new Vector2(400, 200), PathType.CATMULL),
(new Vector2(300, 250), null),
(new Vector2(400, 300), null)
));
@ -165,9 +165,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
Position = new Vector2(0, 50),
Path = new SliderPath(new[]
{
new PathControlPoint(Vector2.Zero, PathType.PerfectCurve),
new PathControlPoint(Vector2.Zero, PathType.PERFECTCURVE),
new PathControlPoint(new Vector2(150, 150)),
new PathControlPoint(new Vector2(300, 0), PathType.PerfectCurve),
new PathControlPoint(new Vector2(300, 0), PathType.PERFECTCURVE),
new PathControlPoint(new Vector2(400, 0)),
new PathControlPoint(new Vector2(400, 150))
})

View File

@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
PathControlPoint[] points =
{
new PathControlPoint(new Vector2(0), PathType.Linear),
new PathControlPoint(new Vector2(0), PathType.LINEAR),
new PathControlPoint(new Vector2(100, 0)),
};

View File

@ -81,12 +81,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
new Slider
{
StartTime = 3200,
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(100, 0), })
Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(100, 0), })
},
new Slider
{
StartTime = 5200,
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(100, 0), })
Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(100, 0), })
}
}
},
@ -105,12 +105,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
new Slider
{
StartTime = 1000,
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(100, 0), })
Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(100, 0), })
},
new Slider
{
StartTime = 4000,
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(100, 0), })
Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(100, 0), })
},
}
},
@ -140,7 +140,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
{
StartTime = 3000,
Position = new Vector2(156, 242),
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(200, 0), })
Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(200, 0), })
},
new Spinner
{

View File

@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
var slider = new Slider
{
StartTime = 1000,
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(100, 0), })
Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(100, 0), })
};
CreateHitObjectTest(new HitObjectTestData(slider), shouldMiss);

View File

@ -26,9 +26,9 @@ namespace osu.Game.Rulesets.Osu.Tests
{
ControlPoints =
{
new PathControlPoint(new Vector2(), PathType.Linear),
new PathControlPoint(new Vector2(-64, -128), PathType.Linear), // absolute position: (64, 0)
new PathControlPoint(new Vector2(-128, 0), PathType.Linear) // absolute position: (0, 128)
new PathControlPoint(new Vector2(), PathType.LINEAR),
new PathControlPoint(new Vector2(-64, -128), PathType.LINEAR), // absolute position: (64, 0)
new PathControlPoint(new Vector2(-128, 0), PathType.LINEAR) // absolute position: (0, 128)
}
},
RepeatCount = 1

View File

@ -167,7 +167,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = Time.Current + 500,
Position = new Vector2(250),
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(0, 100),

View File

@ -264,7 +264,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = time_slider,
Position = positionSlider,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(50, 0),
@ -308,7 +308,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = time_slider,
Position = positionSlider,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(50, 0),
@ -391,7 +391,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = time_slider,
Position = positionSlider,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(25, 0),
@ -428,7 +428,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = time_first_slider,
Position = positionFirstSlider,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(25, 0),
@ -438,7 +438,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = time_second_slider,
Position = positionSecondSlider,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(25, 0),
@ -521,7 +521,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = time_first_slider,
Position = positionFirstSlider,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(25, 0),
@ -531,7 +531,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = time_second_slider,
Position = positionSecondSlider,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(25, 0),
@ -571,7 +571,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = time_first_slider,
Position = positionFirstSlider,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(25, 0),
@ -581,7 +581,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = time_second_slider,
Position = positionSecondSlider,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(25, 0),

View File

@ -219,7 +219,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = Time.Current + time_offset,
Position = new Vector2(239, 176),
Path = new SliderPath(PathType.PerfectCurve, new[]
Path = new SliderPath(PathType.PERFECTCURVE, new[]
{
Vector2.Zero,
new Vector2(154, 28),
@ -255,7 +255,7 @@ namespace osu.Game.Rulesets.Osu.Tests
SliderVelocityMultiplier = speedMultiplier,
StartTime = Time.Current + time_offset,
Position = new Vector2(0, -(distance / 2)),
Path = new SliderPath(PathType.PerfectCurve, new[]
Path = new SliderPath(PathType.PERFECTCURVE, new[]
{
Vector2.Zero,
new Vector2(0, distance),
@ -273,7 +273,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = Time.Current + time_offset,
Position = new Vector2(-max_length / 2, 0),
Path = new SliderPath(PathType.PerfectCurve, new[]
Path = new SliderPath(PathType.PERFECTCURVE, new[]
{
Vector2.Zero,
new Vector2(max_length / 2, max_length / 2),
@ -293,7 +293,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = Time.Current + time_offset,
Position = new Vector2(-max_length / 2, 0),
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(max_length * 0.375f, max_length * 0.18f),
@ -316,7 +316,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = Time.Current + time_offset,
Position = new Vector2(-max_length / 2, 0),
Path = new SliderPath(PathType.Bezier, new[]
Path = new SliderPath(PathType.BEZIER, new[]
{
Vector2.Zero,
new Vector2(max_length * 0.375f, max_length * 0.18f),
@ -338,7 +338,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = Time.Current + time_offset,
Position = new Vector2(0, 0),
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(-max_length / 2, 0),
@ -365,7 +365,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = Time.Current + time_offset,
Position = new Vector2(-max_length / 4, 0),
Path = new SliderPath(PathType.Catmull, new[]
Path = new SliderPath(PathType.CATMULL, new[]
{
Vector2.Zero,
new Vector2(max_length * 0.125f, max_length * 0.125f),

View File

@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Tests
Position = new Vector2(256, 192),
IndexInCurrentCombo = 0,
StartTime = Time.Current,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(150, 100),
@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Tests
Position = new Vector2(256, 192),
ComboIndex = 1,
StartTime = dho.HitObject.StartTime,
Path = new SliderPath(PathType.Bezier, new[]
Path = new SliderPath(PathType.BEZIER, new[]
{
Vector2.Zero,
new Vector2(150, 100),
@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.Osu.Tests
Position = new Vector2(256, 192),
IndexInCurrentCombo = 0,
StartTime = Time.Current,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(150, 100),

View File

@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Tests
StartTime = time_slider_start,
Position = new Vector2(0, 0),
SliderVelocityMultiplier = velocity,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(followCircleRadius, 0),

View File

@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Osu.Tests
Position = new Vector2(0, 0),
SliderVelocityMultiplier = 10f,
RepeatCount = repeatCount,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(sliderLength, 0),
@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Tests
Position = new Vector2(0, 0),
SliderVelocityMultiplier = 10f,
RepeatCount = repeatCount,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(sliderLength, 0),
@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Osu.Tests
StartTime = time_slider_start,
Position = new Vector2(0, 0),
SliderVelocityMultiplier = 10f,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(slider_path_length * 10, 0),
@ -478,7 +478,7 @@ namespace osu.Game.Rulesets.Osu.Tests
StartTime = time_slider_start,
Position = new Vector2(0, 0),
SliderVelocityMultiplier = 0.1f,
Path = new SliderPath(PathType.PerfectCurve, new[]
Path = new SliderPath(PathType.PERFECTCURVE, new[]
{
Vector2.Zero,
new Vector2(slider_path_length, 0),

View File

@ -217,7 +217,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = 3000,
Position = new Vector2(100, 100),
Path = new SliderPath(PathType.PerfectCurve, new[]
Path = new SliderPath(PathType.PERFECTCURVE, new[]
{
Vector2.Zero,
new Vector2(300, 200)
@ -227,7 +227,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = 13000,
Position = new Vector2(100, 100),
Path = new SliderPath(PathType.PerfectCurve, new[]
Path = new SliderPath(PathType.PERFECTCURVE, new[]
{
Vector2.Zero,
new Vector2(300, 200)
@ -238,7 +238,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = 23000,
Position = new Vector2(100, 100),
Path = new SliderPath(PathType.PerfectCurve, new[]
Path = new SliderPath(PathType.PERFECTCURVE, new[]
{
Vector2.Zero,
new Vector2(300, 200)

View File

@ -196,7 +196,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = time_slider,
Position = positionSlider,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(25, 0),
@ -238,7 +238,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = time_slider,
Position = positionSlider,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(25, 0),
@ -318,7 +318,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = time_slider,
Position = positionSlider,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(25, 0),
@ -352,7 +352,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = time_first_slider,
Position = positionFirstSlider,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(25, 0),
@ -362,7 +362,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = time_second_slider,
Position = positionSecondSlider,
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(25, 0),

View File

@ -221,11 +221,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
/// </summary>
private void updatePathType()
{
if (ControlPoint.Type != PathType.PerfectCurve)
if (ControlPoint.Type != PathType.PERFECTCURVE)
return;
if (PointsInSegment.Count > 3)
ControlPoint.Type = PathType.Bezier;
ControlPoint.Type = PathType.BEZIER;
if (PointsInSegment.Count != 3)
return;
@ -233,7 +233,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
ReadOnlySpan<Vector2> points = PointsInSegment.Select(p => p.Position).ToArray();
RectangleF boundingBox = PathApproximator.CircularArcBoundingBox(points);
if (boundingBox.Width >= 640 || boundingBox.Height >= 480)
ControlPoint.Type = PathType.Bezier;
ControlPoint.Type = PathType.BEZIER;
}
/// <summary>
@ -256,18 +256,22 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
private Color4 getColourFromNodeType()
{
if (!(ControlPoint.Type is PathType pathType))
if (ControlPoint.Type is not PathType pathType)
return colours.Yellow;
switch (pathType)
{
case PathType.Catmull:
case { SplineType: SplineType.Catmull }:
return colours.SeaFoam;
case PathType.Bezier:
return colours.Pink;
case { SplineType: SplineType.BSpline, Degree: null }:
return colours.PinkLighter;
case PathType.PerfectCurve:
case { SplineType: SplineType.BSpline, Degree: >= 1 }:
int idx = Math.Clamp(pathType.Degree.Value, 0, 3);
return new[] { colours.PinkDarker, colours.PinkDark, colours.Pink, colours.PinkLight }[idx];
case { SplineType: SplineType.PerfectCurve }:
return colours.PurpleDark;
default:

View File

@ -242,18 +242,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
{
int indexInSegment = piece.PointsInSegment.IndexOf(piece.ControlPoint);
switch (type)
if (type.HasValue && type.Value.SplineType == SplineType.PerfectCurve)
{
case PathType.PerfectCurve:
// Can't always create a circular arc out of 4 or more points,
// so we split the segment into one 3-point circular arc segment
// and one segment of the previous type.
int thirdPointIndex = indexInSegment + 2;
// Can't always create a circular arc out of 4 or more points,
// so we split the segment into one 3-point circular arc segment
// and one segment of the previous type.
int thirdPointIndex = indexInSegment + 2;
if (piece.PointsInSegment.Count > thirdPointIndex + 1)
piece.PointsInSegment[thirdPointIndex].Type = piece.PointsInSegment[0].Type;
break;
if (piece.PointsInSegment.Count > thirdPointIndex + 1)
piece.PointsInSegment[thirdPointIndex].Type = piece.PointsInSegment[0].Type;
}
hitObject.Path.ExpectedDistance.Value = null;
@ -370,10 +367,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
curveTypeItems.Add(createMenuItemForPathType(null));
// todo: hide/disable items which aren't valid for selected points
curveTypeItems.Add(createMenuItemForPathType(PathType.Linear));
curveTypeItems.Add(createMenuItemForPathType(PathType.PerfectCurve));
curveTypeItems.Add(createMenuItemForPathType(PathType.Bezier));
curveTypeItems.Add(createMenuItemForPathType(PathType.Catmull));
curveTypeItems.Add(createMenuItemForPathType(PathType.LINEAR));
curveTypeItems.Add(createMenuItemForPathType(PathType.PERFECTCURVE));
curveTypeItems.Add(createMenuItemForPathType(PathType.BEZIER));
curveTypeItems.Add(createMenuItemForPathType(PathType.BSpline(3)));
curveTypeItems.Add(createMenuItemForPathType(PathType.CATMULL));
var menuItems = new List<MenuItem>
{

View File

@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
{
RelativeSizeAxes = Axes.Both;
HitObject.Path.ControlPoints.Add(segmentStart = new PathControlPoint(Vector2.Zero, PathType.Linear));
HitObject.Path.ControlPoints.Add(segmentStart = new PathControlPoint(Vector2.Zero, PathType.LINEAR));
currentSegmentLength = 1;
}
@ -128,7 +128,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
Debug.Assert(lastPoint != null);
segmentStart = lastPoint;
segmentStart.Type = PathType.Linear;
segmentStart.Type = PathType.LINEAR;
currentSegmentLength = 1;
}
@ -173,15 +173,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
{
case 1:
case 2:
segmentStart.Type = PathType.Linear;
segmentStart.Type = PathType.LINEAR;
break;
case 3:
segmentStart.Type = PathType.PerfectCurve;
segmentStart.Type = PathType.PERFECTCURVE;
break;
default:
segmentStart.Type = PathType.Bezier;
segmentStart.Type = PathType.BEZIER;
break;
}
}
@ -195,7 +195,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
{
HitObject.Path.ControlPoints.Add(cursor = new PathControlPoint { Position = Vector2.Zero });
// The path type should be adjusted in the progression of updatePathType() (Linear -> PC -> Bezier).
// The path type should be adjusted in the progression of updatePathType() (LINEAR -> PC -> BEZIER).
currentSegmentLength++;
updatePathType();
}
@ -210,7 +210,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
HitObject.Path.ControlPoints.Remove(cursor);
cursor = null;
// The path type should be adjusted in the reverse progression of updatePathType() (Bezier -> PC -> Linear).
// The path type should be adjusted in the reverse progression of updatePathType() (BEZIER -> PC -> LINEAR).
currentSegmentLength--;
updatePathType();
}

View File

@ -320,7 +320,7 @@ namespace osu.Game.Rulesets.Osu.Edit
if (mergedHitObject.Path.ControlPoints.Count == 0)
{
mergedHitObject.Path.ControlPoints.Add(new PathControlPoint(Vector2.Zero, PathType.Linear));
mergedHitObject.Path.ControlPoints.Add(new PathControlPoint(Vector2.Zero, PathType.LINEAR));
}
// Merge all the selected hit objects into one slider path.
@ -350,7 +350,7 @@ namespace osu.Game.Rulesets.Osu.Edit
// Turn the last control point into a linear type if this is the first merging circle in a sequence, so the subsequent control points can be inherited path type.
if (!lastCircle)
{
mergedHitObject.Path.ControlPoints.Last().Type = PathType.Linear;
mergedHitObject.Path.ControlPoints.Last().Type = PathType.LINEAR;
}
mergedHitObject.Path.ControlPoints.Add(new PathControlPoint(selectedMergeableObject.Position - mergedHitObject.Position));

View File

@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
double IHasDistance.Distance => Duration * Velocity;
SliderPath IHasPath.Path
=> new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(1) }, ((IHasDistance)this).Distance / LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER);
=> new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(1) }, ((IHasDistance)this).Distance / LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER);
#endregion
}

View File

@ -663,7 +663,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
assertObjectHasBanks(hitObjects[9], HitSampleInfo.BANK_DRUM, HitSampleInfo.BANK_NORMAL);
}
void assertObjectHasBanks(HitObject hitObject, string normalBank, string? additionsBank = null)
static void assertObjectHasBanks(HitObject hitObject, string normalBank, string? additionsBank = null)
{
Assert.AreEqual(normalBank, hitObject.Samples[0].Bank);
@ -808,14 +808,14 @@ namespace osu.Game.Tests.Beatmaps.Formats
var first = ((IHasPath)decoded.HitObjects[0]).Path;
Assert.That(first.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
Assert.That(first.ControlPoints[0].Type, Is.EqualTo(PathType.PerfectCurve));
Assert.That(first.ControlPoints[0].Type, Is.EqualTo(PathType.PERFECTCURVE));
Assert.That(first.ControlPoints[1].Position, Is.EqualTo(new Vector2(161, -244)));
Assert.That(first.ControlPoints[1].Type, Is.EqualTo(null));
// ReSharper disable once HeuristicUnreachableCode
// weird one, see https://youtrack.jetbrains.com/issue/RIDER-70159.
Assert.That(first.ControlPoints[2].Position, Is.EqualTo(new Vector2(376, -3)));
Assert.That(first.ControlPoints[2].Type, Is.EqualTo(PathType.Bezier));
Assert.That(first.ControlPoints[2].Type, Is.EqualTo(PathType.BEZIER));
Assert.That(first.ControlPoints[3].Position, Is.EqualTo(new Vector2(68, 15)));
Assert.That(first.ControlPoints[3].Type, Is.EqualTo(null));
Assert.That(first.ControlPoints[4].Position, Is.EqualTo(new Vector2(259, -132)));
@ -827,7 +827,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
var second = ((IHasPath)decoded.HitObjects[1]).Path;
Assert.That(second.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
Assert.That(second.ControlPoints[0].Type, Is.EqualTo(PathType.PerfectCurve));
Assert.That(second.ControlPoints[0].Type, Is.EqualTo(PathType.PERFECTCURVE));
Assert.That(second.ControlPoints[1].Position, Is.EqualTo(new Vector2(161, -244)));
Assert.That(second.ControlPoints[1].Type, Is.EqualTo(null));
Assert.That(second.ControlPoints[2].Position, Is.EqualTo(new Vector2(376, -3)));
@ -837,14 +837,14 @@ namespace osu.Game.Tests.Beatmaps.Formats
var third = ((IHasPath)decoded.HitObjects[2]).Path;
Assert.That(third.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
Assert.That(third.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier));
Assert.That(third.ControlPoints[0].Type, Is.EqualTo(PathType.BEZIER));
Assert.That(third.ControlPoints[1].Position, Is.EqualTo(new Vector2(0, 192)));
Assert.That(third.ControlPoints[1].Type, Is.EqualTo(null));
Assert.That(third.ControlPoints[2].Position, Is.EqualTo(new Vector2(224, 192)));
Assert.That(third.ControlPoints[2].Type, Is.EqualTo(null));
Assert.That(third.ControlPoints[3].Position, Is.EqualTo(new Vector2(224, 0)));
Assert.That(third.ControlPoints[3].Type, Is.EqualTo(PathType.Bezier));
Assert.That(third.ControlPoints[3].Type, Is.EqualTo(PathType.BEZIER));
Assert.That(third.ControlPoints[4].Position, Is.EqualTo(new Vector2(224, -192)));
Assert.That(third.ControlPoints[4].Type, Is.EqualTo(null));
Assert.That(third.ControlPoints[5].Position, Is.EqualTo(new Vector2(480, -192)));
@ -856,7 +856,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
var fourth = ((IHasPath)decoded.HitObjects[3]).Path;
Assert.That(fourth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
Assert.That(fourth.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier));
Assert.That(fourth.ControlPoints[0].Type, Is.EqualTo(PathType.BEZIER));
Assert.That(fourth.ControlPoints[1].Position, Is.EqualTo(new Vector2(1, 1)));
Assert.That(fourth.ControlPoints[1].Type, Is.EqualTo(null));
Assert.That(fourth.ControlPoints[2].Position, Is.EqualTo(new Vector2(2, 2)));
@ -870,7 +870,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
var fifth = ((IHasPath)decoded.HitObjects[4]).Path;
Assert.That(fifth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
Assert.That(fifth.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier));
Assert.That(fifth.ControlPoints[0].Type, Is.EqualTo(PathType.BEZIER));
Assert.That(fifth.ControlPoints[1].Position, Is.EqualTo(new Vector2(1, 1)));
Assert.That(fifth.ControlPoints[1].Type, Is.EqualTo(null));
Assert.That(fifth.ControlPoints[2].Position, Is.EqualTo(new Vector2(2, 2)));
@ -881,7 +881,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.That(fifth.ControlPoints[4].Type, Is.EqualTo(null));
Assert.That(fifth.ControlPoints[5].Position, Is.EqualTo(new Vector2(4, 4)));
Assert.That(fifth.ControlPoints[5].Type, Is.EqualTo(PathType.Bezier));
Assert.That(fifth.ControlPoints[5].Type, Is.EqualTo(PathType.BEZIER));
Assert.That(fifth.ControlPoints[6].Position, Is.EqualTo(new Vector2(5, 5)));
Assert.That(fifth.ControlPoints[6].Type, Is.EqualTo(null));
@ -889,12 +889,12 @@ namespace osu.Game.Tests.Beatmaps.Formats
var sixth = ((IHasPath)decoded.HitObjects[5]).Path;
Assert.That(sixth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
Assert.That(sixth.ControlPoints[0].Type == PathType.Bezier);
Assert.That(sixth.ControlPoints[0].Type == PathType.BEZIER);
Assert.That(sixth.ControlPoints[1].Position, Is.EqualTo(new Vector2(75, 145)));
Assert.That(sixth.ControlPoints[1].Type == null);
Assert.That(sixth.ControlPoints[2].Position, Is.EqualTo(new Vector2(170, 75)));
Assert.That(sixth.ControlPoints[2].Type == PathType.Bezier);
Assert.That(sixth.ControlPoints[2].Type == PathType.BEZIER);
Assert.That(sixth.ControlPoints[3].Position, Is.EqualTo(new Vector2(300, 145)));
Assert.That(sixth.ControlPoints[3].Type == null);
Assert.That(sixth.ControlPoints[4].Position, Is.EqualTo(new Vector2(410, 20)));
@ -904,12 +904,12 @@ namespace osu.Game.Tests.Beatmaps.Formats
var seventh = ((IHasPath)decoded.HitObjects[6]).Path;
Assert.That(seventh.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
Assert.That(seventh.ControlPoints[0].Type == PathType.PerfectCurve);
Assert.That(seventh.ControlPoints[0].Type == PathType.PERFECTCURVE);
Assert.That(seventh.ControlPoints[1].Position, Is.EqualTo(new Vector2(75, 145)));
Assert.That(seventh.ControlPoints[1].Type == null);
Assert.That(seventh.ControlPoints[2].Position, Is.EqualTo(new Vector2(170, 75)));
Assert.That(seventh.ControlPoints[2].Type == PathType.PerfectCurve);
Assert.That(seventh.ControlPoints[2].Type == PathType.PERFECTCURVE);
Assert.That(seventh.ControlPoints[3].Position, Is.EqualTo(new Vector2(300, 145)));
Assert.That(seventh.ControlPoints[3].Type == null);
Assert.That(seventh.ControlPoints[4].Position, Is.EqualTo(new Vector2(410, 20)));
@ -1016,7 +1016,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
var controlPoints = ((IHasPath)decoded.HitObjects[0]).Path.ControlPoints;
Assert.That(controlPoints.Count, Is.EqualTo(6));
Assert.That(controlPoints.Single(c => c.Type != null).Type, Is.EqualTo(PathType.Catmull));
Assert.That(controlPoints.Single(c => c.Type != null).Type, Is.EqualTo(PathType.CATMULL));
}
}
@ -1032,9 +1032,9 @@ namespace osu.Game.Tests.Beatmaps.Formats
var controlPoints = ((IHasPath)decoded.HitObjects[0]).Path.ControlPoints;
Assert.That(controlPoints.Count, Is.EqualTo(4));
Assert.That(controlPoints[0].Type, Is.EqualTo(PathType.Catmull));
Assert.That(controlPoints[1].Type, Is.EqualTo(PathType.Catmull));
Assert.That(controlPoints[2].Type, Is.EqualTo(PathType.Catmull));
Assert.That(controlPoints[0].Type, Is.EqualTo(PathType.CATMULL));
Assert.That(controlPoints[1].Type, Is.EqualTo(PathType.CATMULL));
Assert.That(controlPoints[2].Type, Is.EqualTo(PathType.CATMULL));
Assert.That(controlPoints[3].Type, Is.Null);
}
}
@ -1051,7 +1051,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
var controlPoints = ((IHasPath)decoded.HitObjects[0]).Path.ControlPoints;
Assert.That(controlPoints.Count, Is.EqualTo(4));
Assert.That(controlPoints[0].Type, Is.EqualTo(PathType.Catmull));
Assert.That(controlPoints[0].Type, Is.EqualTo(PathType.CATMULL));
Assert.That(controlPoints[0].Position, Is.EqualTo(Vector2.Zero));
Assert.That(controlPoints[1].Type, Is.Null);
Assert.That(controlPoints[1].Position, Is.Not.EqualTo(Vector2.Zero));

View File

@ -77,7 +77,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
compareBeatmaps(decoded, decodedAfterEncode);
ControlPointInfo removeLegacyControlPointTypes(ControlPointInfo controlPointInfo)
static ControlPointInfo removeLegacyControlPointTypes(ControlPointInfo controlPointInfo)
{
// emulate non-legacy control points by cloning the non-legacy portion.
// the assertion is that the encoder can recreate this losslessly from hitobject data.
@ -125,10 +125,10 @@ namespace osu.Game.Tests.Beatmaps.Formats
Position = new Vector2(0.6f),
Path = new SliderPath(new[]
{
new PathControlPoint(Vector2.Zero, PathType.Bezier),
new PathControlPoint(Vector2.Zero, PathType.BEZIER),
new PathControlPoint(new Vector2(0.5f)),
new PathControlPoint(new Vector2(0.51f)), // This is actually on the same position as the previous one in legacy beatmaps (truncated to int).
new PathControlPoint(new Vector2(1f), PathType.Bezier),
new PathControlPoint(new Vector2(1f), PathType.BEZIER),
new PathControlPoint(new Vector2(2f))
})
},

View File

@ -162,7 +162,7 @@ namespace osu.Game.Tests.Editing
{
new PathControlPoint(Vector2.Zero),
new PathControlPoint(Vector2.One),
new PathControlPoint(new Vector2(2), PathType.Bezier),
new PathControlPoint(new Vector2(2), PathType.BEZIER),
new PathControlPoint(new Vector2(3)),
}, 50)
},
@ -179,7 +179,7 @@ namespace osu.Game.Tests.Editing
StartTime = 2000,
Path = new SliderPath(new[]
{
new PathControlPoint(Vector2.Zero, PathType.Bezier),
new PathControlPoint(Vector2.Zero, PathType.BEZIER),
new PathControlPoint(new Vector2(4)),
new PathControlPoint(new Vector2(5)),
}, 100)

View File

@ -72,7 +72,7 @@ namespace osu.Game.Tests.Visual.Editing
ControlPoints =
{
new PathControlPoint(),
new PathControlPoint(new Vector2(100, 0), PathType.Bezier)
new PathControlPoint(new Vector2(100, 0), PathType.BEZIER)
}
}
};

View File

@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Editing
new Slider
{
Position = new Vector2(128, 256),
Path = new SliderPath(PathType.Linear, new[]
Path = new SliderPath(PathType.LINEAR, new[]
{
Vector2.Zero,
new Vector2(216, 0),

View File

@ -114,23 +114,25 @@ namespace osu.Game.Tests.Visual.Gameplay
{
}
[TestCase(PathType.Linear)]
[TestCase(PathType.Bezier)]
[TestCase(PathType.Catmull)]
[TestCase(PathType.PerfectCurve)]
public void TestSingleSegment(PathType type)
=> AddStep("create path", () => path.ControlPoints.AddRange(createSegment(type, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
[TestCase(SplineType.Linear, null)]
[TestCase(SplineType.BSpline, null)]
[TestCase(SplineType.BSpline, 3)]
[TestCase(SplineType.Catmull, null)]
[TestCase(SplineType.PerfectCurve, null)]
public void TestSingleSegment(SplineType splineType, int? degree)
=> AddStep("create path", () => path.ControlPoints.AddRange(createSegment(new PathType { SplineType = splineType, Degree = degree }, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
[TestCase(PathType.Linear)]
[TestCase(PathType.Bezier)]
[TestCase(PathType.Catmull)]
[TestCase(PathType.PerfectCurve)]
public void TestMultipleSegment(PathType type)
[TestCase(SplineType.Linear, null)]
[TestCase(SplineType.BSpline, null)]
[TestCase(SplineType.BSpline, 3)]
[TestCase(SplineType.Catmull, null)]
[TestCase(SplineType.PerfectCurve, null)]
public void TestMultipleSegment(SplineType splineType, int? degree)
{
AddStep("create path", () =>
{
path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero));
path.ControlPoints.AddRange(createSegment(type, new Vector2(0, 100), new Vector2(100), Vector2.Zero));
path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero));
path.ControlPoints.AddRange(createSegment(new PathType { SplineType = splineType, Degree = degree }, new Vector2(0, 100), new Vector2(100), Vector2.Zero));
});
}
@ -139,9 +141,9 @@ namespace osu.Game.Tests.Visual.Gameplay
{
AddStep("create path", () =>
{
path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(100, 0)));
path.ControlPoints.AddRange(createSegment(PathType.Bezier, new Vector2(100, 0), new Vector2(150, 30), new Vector2(100, 100)));
path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, new Vector2(100, 100), new Vector2(25, 50), Vector2.Zero));
path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(100, 0)));
path.ControlPoints.AddRange(createSegment(PathType.BEZIER, new Vector2(100, 0), new Vector2(150, 30), new Vector2(100, 100)));
path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, new Vector2(100, 100), new Vector2(25, 50), Vector2.Zero));
});
}
@ -157,7 +159,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
AddStep("create path", () =>
{
path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(width / 2, height), new Vector2(width, 0)));
path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, Vector2.Zero, new Vector2(width / 2, height), new Vector2(width, 0)));
});
}
@ -170,11 +172,11 @@ namespace osu.Game.Tests.Visual.Gameplay
switch (points)
{
case 2:
path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100)));
path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, Vector2.Zero, new Vector2(0, 100)));
break;
case 4:
path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
break;
}
});

View File

@ -88,7 +88,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
HitWindows = new HitWindows(),
StartTime = t += spacing,
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, Vector2.UnitY * 200 }),
Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, Vector2.UnitY * 200 }),
Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_WHISTLE, HitSampleInfo.BANK_SOFT) },
},
});

View File

@ -52,59 +52,68 @@ namespace osu.Game.Tests.Visual.Gameplay
{
}
[TestCase(PathType.Linear)]
[TestCase(PathType.Bezier)]
[TestCase(PathType.Catmull)]
[TestCase(PathType.PerfectCurve)]
public void TestSingleSegment(PathType type)
=> AddStep("create path", () => path.ControlPoints.AddRange(createSegment(type, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
[TestCase(SplineType.Linear, null)]
[TestCase(SplineType.BSpline, null)]
[TestCase(SplineType.BSpline, 3)]
[TestCase(SplineType.Catmull, null)]
[TestCase(SplineType.PerfectCurve, null)]
public void TestSingleSegment(SplineType splineType, int? degree)
=> AddStep("create path", () => path.ControlPoints.AddRange(createSegment(
new PathType { SplineType = splineType, Degree = degree },
Vector2.Zero,
new Vector2(0, 100),
new Vector2(100),
new Vector2(0, 200),
new Vector2(200)
)));
[TestCase(PathType.Linear)]
[TestCase(PathType.Bezier)]
[TestCase(PathType.Catmull)]
[TestCase(PathType.PerfectCurve)]
public void TestMultipleSegment(PathType type)
[TestCase(SplineType.Linear, null)]
[TestCase(SplineType.BSpline, null)]
[TestCase(SplineType.BSpline, 3)]
[TestCase(SplineType.Catmull, null)]
[TestCase(SplineType.PerfectCurve, null)]
public void TestMultipleSegment(SplineType splineType, int? degree)
{
AddStep("create path", () =>
{
path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero));
path.ControlPoints.AddRange(createSegment(type, new Vector2(0, 100), new Vector2(100), Vector2.Zero));
path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero));
path.ControlPoints.AddRange(createSegment(new PathType { SplineType = splineType, Degree = degree }, new Vector2(0, 100), new Vector2(100), Vector2.Zero));
});
}
[Test]
public void TestAddControlPoint()
{
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100))));
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100))));
AddStep("add point", () => path.ControlPoints.Add(new PathControlPoint { Position = new Vector2(100) }));
}
[Test]
public void TestInsertControlPoint()
{
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(100))));
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(100))));
AddStep("insert point", () => path.ControlPoints.Insert(1, new PathControlPoint { Position = new Vector2(0, 100) }));
}
[Test]
public void TestRemoveControlPoint()
{
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
AddStep("remove second point", () => path.ControlPoints.RemoveAt(1));
}
[Test]
public void TestChangePathType()
{
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
AddStep("change type to bezier", () => path.ControlPoints[0].Type = PathType.Bezier);
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
AddStep("change type to bezier", () => path.ControlPoints[0].Type = PathType.BEZIER);
}
[Test]
public void TestAddSegmentByChangingType()
{
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))));
AddStep("change second point type to bezier", () => path.ControlPoints[1].Type = PathType.Bezier);
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))));
AddStep("change second point type to bezier", () => path.ControlPoints[1].Type = PathType.BEZIER);
}
[Test]
@ -112,8 +121,8 @@ namespace osu.Game.Tests.Visual.Gameplay
{
AddStep("create path", () =>
{
path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
path.ControlPoints[1].Type = PathType.Bezier;
path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
path.ControlPoints[1].Type = PathType.BEZIER;
});
AddStep("change second point type to null", () => path.ControlPoints[1].Type = null);
@ -124,8 +133,8 @@ namespace osu.Game.Tests.Visual.Gameplay
{
AddStep("create path", () =>
{
path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
path.ControlPoints[1].Type = PathType.Bezier;
path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
path.ControlPoints[1].Type = PathType.BEZIER;
});
AddStep("remove second point", () => path.ControlPoints.RemoveAt(1));
@ -140,11 +149,11 @@ namespace osu.Game.Tests.Visual.Gameplay
switch (points)
{
case 2:
path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100)));
path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, Vector2.Zero, new Vector2(0, 100)));
break;
case 4:
path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
break;
}
});
@ -153,35 +162,35 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestLengthenLastSegment()
{
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
AddStep("lengthen last segment", () => path.ExpectedDistance.Value = 300);
}
[Test]
public void TestShortenLastSegment()
{
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
AddStep("shorten last segment", () => path.ExpectedDistance.Value = 150);
}
[Test]
public void TestShortenFirstSegment()
{
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
AddStep("shorten first segment", () => path.ExpectedDistance.Value = 50);
}
[Test]
public void TestShortenToZeroLength()
{
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
AddStep("shorten to 0 length", () => path.ExpectedDistance.Value = 0);
}
[Test]
public void TestShortenToNegativeLength()
{
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
AddStep("shorten to -10 length", () => path.ExpectedDistance.Value = -10);
}
@ -197,7 +206,7 @@ namespace osu.Game.Tests.Visual.Gameplay
};
double[] distances = { 100d, 200d, 300d };
AddStep("create path", () => path.ControlPoints.AddRange(positions.Select(p => new PathControlPoint(p, PathType.Linear))));
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)));

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
@ -437,7 +438,7 @@ namespace osu.Game.Beatmaps.Formats
// Explicit segments have a new format in which the type is injected into the middle of the control point string.
// To preserve compatibility with osu-stable as much as possible, explicit segments with the same type are converted to use implicit segments by duplicating the control point.
// One exception are consecutive perfect curves, which aren't supported in osu!stable and can lead to decoding issues if encoded as implicit segments
bool needsExplicitSegment = point.Type != lastType || point.Type == PathType.PerfectCurve;
bool needsExplicitSegment = point.Type != lastType || point.Type == PathType.PERFECTCURVE;
// Another exception to this is when the last two control points of the last segment were duplicated. This is not a scenario supported by osu!stable.
// Lazer does not add implicit segments for the last two control points of _any_ explicit segment, so an explicit segment is forced in order to maintain consistency with the decoder.
@ -455,19 +456,23 @@ namespace osu.Game.Beatmaps.Formats
{
switch (point.Type)
{
case PathType.Bezier:
case { SplineType: SplineType.BSpline, Degree: > 0 }:
writer.Write($"B{point.Type.Value.Degree}|");
break;
case { SplineType: SplineType.BSpline, Degree: <= 0 }:
writer.Write("B|");
break;
case PathType.Catmull:
case { SplineType: SplineType.Catmull }:
writer.Write("C|");
break;
case PathType.PerfectCurve:
case { SplineType: SplineType.PerfectCurve }:
writer.Write("P|");
break;
case PathType.Linear:
case { SplineType: SplineType.Linear }:
writer.Write("L|");
break;
}

View File

@ -78,10 +78,10 @@ namespace osu.Game.Database
// wherein the last control point of an otherwise-single-segment slider path has a different type than previous,
// which would lead to sliders being mangled when exported back to stable.
// normally, that would be handled by the `BezierConverter.ConvertToModernBezier()` call below,
// which outputs a slider path containing only Bezier control points,
// which outputs a slider path containing only BEZIER control points,
// but a non-inherited last control point is (rightly) not considered to be starting a new segment,
// therefore it would fail to clear the `CountSegments() <= 1` check.
// by clearing explicitly we both fix the issue and avoid unnecessary conversions to Bezier.
// by clearing explicitly we both fix the issue and avoid unnecessary conversions to BEZIER.
if (hasPath.Path.ControlPoints.Count > 1)
hasPath.Path.ControlPoints[^1].Type = null;

View File

@ -68,26 +68,26 @@ namespace osu.Game.Rulesets.Objects
// The current vertex ends the segment
var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1);
var segmentType = controlPoints[start].Type ?? PathType.Linear;
var segmentType = controlPoints[start].Type ?? PathType.LINEAR;
switch (segmentType)
{
case PathType.Catmull:
case { SplineType: SplineType.Catmull }:
result.AddRange(from segment in ConvertCatmullToBezierAnchors(segmentVertices) from v in segment select v + position);
break;
case PathType.Linear:
case { SplineType: SplineType.Linear }:
result.AddRange(from segment in ConvertLinearToBezierAnchors(segmentVertices) from v in segment select v + position);
break;
case PathType.PerfectCurve:
case { SplineType: SplineType.PerfectCurve }:
result.AddRange(ConvertCircleToBezierAnchors(segmentVertices).Select(v => v + position));
break;
default:
if (segmentType.Degree != null)
throw new NotImplementedException("BSpline conversion of arbitrary degree is not implemented.");
foreach (Vector2 v in segmentVertices)
{
result.Add(v + position);
@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Objects
}
/// <summary>
/// Converts a path of control points to an identical path using only Bezier type control points.
/// Converts a path of control points to an identical path using only BEZIER type control points.
/// </summary>
/// <param name="controlPoints">The control points of the path.</param>
/// <returns>The list of bezier control points.</returns>
@ -124,38 +124,38 @@ namespace osu.Game.Rulesets.Objects
// The current vertex ends the segment
var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1);
var segmentType = controlPoints[start].Type ?? PathType.Linear;
var segmentType = controlPoints[start].Type ?? PathType.LINEAR;
switch (segmentType)
{
case PathType.Catmull:
case { SplineType: SplineType.Catmull }:
foreach (var segment in ConvertCatmullToBezierAnchors(segmentVertices))
{
for (int j = 0; j < segment.Length - 1; j++)
{
result.Add(new PathControlPoint(segment[j], j == 0 ? PathType.Bezier : null));
result.Add(new PathControlPoint(segment[j], j == 0 ? PathType.BEZIER : null));
}
}
break;
case PathType.Linear:
case { SplineType: SplineType.Linear }:
foreach (var segment in ConvertLinearToBezierAnchors(segmentVertices))
{
for (int j = 0; j < segment.Length - 1; j++)
{
result.Add(new PathControlPoint(segment[j], j == 0 ? PathType.Bezier : null));
result.Add(new PathControlPoint(segment[j], j == 0 ? PathType.BEZIER : null));
}
}
break;
case PathType.PerfectCurve:
case { SplineType: SplineType.PerfectCurve }:
var circleResult = ConvertCircleToBezierAnchors(segmentVertices);
for (int j = 0; j < circleResult.Length - 1; j++)
{
result.Add(new PathControlPoint(circleResult[j], j == 0 ? PathType.Bezier : null));
result.Add(new PathControlPoint(circleResult[j], j == 0 ? PathType.BEZIER : null));
}
break;
@ -163,7 +163,7 @@ namespace osu.Game.Rulesets.Objects
default:
for (int j = 0; j < segmentVertices.Length - 1; j++)
{
result.Add(new PathControlPoint(segmentVertices[j], j == 0 ? PathType.Bezier : null));
result.Add(new PathControlPoint(segmentVertices[j], j == 0 ? segmentType : null));
}
break;

View File

@ -224,16 +224,18 @@ namespace osu.Game.Rulesets.Objects.Legacy
{
default:
case 'C':
return PathType.Catmull;
return new PathType(SplineType.Catmull);
case 'B':
return PathType.Bezier;
if (input.Length > 1 && int.TryParse(input.Substring(1), out int degree) && degree > 0)
return new PathType { SplineType = SplineType.BSpline, Degree = degree };
return new PathType(SplineType.BSpline);
case 'L':
return PathType.Linear;
return new PathType(SplineType.Linear);
case 'P':
return PathType.PerfectCurve;
return new PathType(SplineType.PerfectCurve);
}
}
@ -320,14 +322,14 @@ namespace osu.Game.Rulesets.Objects.Legacy
readPoint(endPoint, offset, out vertices[^1]);
// Edge-case rules (to match stable).
if (type == PathType.PerfectCurve)
if (type == PathType.PERFECTCURVE)
{
if (vertices.Length != 3)
type = PathType.Bezier;
type = PathType.BEZIER;
else if (isLinear(vertices))
{
// osu-stable special-cased colinear perfect curves to a linear path
type = PathType.Linear;
type = PathType.LINEAR;
}
}
@ -349,10 +351,10 @@ namespace osu.Game.Rulesets.Objects.Legacy
if (vertices[endIndex].Position != vertices[endIndex - 1].Position)
continue;
// Legacy Catmull sliders don't support multiple segments, so adjacent Catmull segments should be treated as a single one.
// Legacy CATMULL sliders don't support multiple segments, so adjacent CATMULL segments should be treated as a single one.
// Importantly, this is not applied to the first control point, which may duplicate the slider path's position
// resulting in a duplicate (0,0) control point in the resultant list.
if (type == PathType.Catmull && endIndex > 1 && FormatVersion < LegacyBeatmapEncoder.FIRST_LAZER_VERSION)
if (type == PathType.CATMULL && endIndex > 1 && FormatVersion < LegacyBeatmapEncoder.FIRST_LAZER_VERSION)
continue;
// The last control point of each segment is not allowed to start a new implicit segment.

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using Microsoft.Toolkit.HighPerformance;
using Newtonsoft.Json;
using osu.Framework.Bindables;
using osu.Framework.Caching;
@ -260,7 +261,7 @@ namespace osu.Game.Rulesets.Objects
// The current vertex ends the segment
var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1);
var segmentType = ControlPoints[start].Type ?? PathType.Linear;
var segmentType = ControlPoints[start].Type ?? PathType.LINEAR;
// No need to calculate path when there is only 1 vertex
if (segmentVertices.Length == 1)
@ -288,12 +289,12 @@ namespace osu.Game.Rulesets.Objects
private List<Vector2> calculateSubPath(ReadOnlySpan<Vector2> subControlPoints, PathType type)
{
switch (type)
switch (type.SplineType)
{
case PathType.Linear:
case SplineType.Linear:
return PathApproximator.ApproximateLinear(subControlPoints);
case PathType.PerfectCurve:
case SplineType.PerfectCurve:
if (subControlPoints.Length != 3)
break;
@ -305,11 +306,11 @@ namespace osu.Game.Rulesets.Objects
return subPath;
case PathType.Catmull:
case SplineType.Catmull:
return PathApproximator.ApproximateCatmull(subControlPoints);
}
return PathApproximator.ApproximateBezier(subControlPoints);
return PathApproximator.ApproximateBSpline(subControlPoints, type.Degree ?? subControlPoints.Length);
}
private void calculateLength()

View File

@ -29,11 +29,11 @@ namespace osu.Game.Rulesets.Objects
{
var controlPoints = sliderPath.ControlPoints;
var inheritedLinearPoints = controlPoints.Where(p => sliderPath.PointsInSegment(p)[0].Type == PathType.Linear && p.Type is null).ToList();
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);
inheritedLinearPoints.ForEach(p => p.Type = PathType.LINEAR);
double[] segmentEnds = sliderPath.GetSegmentEnds().ToArray();
@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Objects
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())
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];

View File

@ -1,13 +1,59 @@
// 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;
using System.Diagnostics;
namespace osu.Game.Rulesets.Objects.Types
{
public enum PathType
public enum SplineType
{
Catmull,
Bezier,
BSpline,
Linear,
PerfectCurve
}
public struct PathType
{
public static readonly PathType CATMULL = new PathType(SplineType.Catmull);
public static readonly PathType BEZIER = new PathType(SplineType.BSpline);
public static readonly PathType LINEAR = new PathType(SplineType.Linear);
public static readonly PathType PERFECTCURVE = new PathType(SplineType.PerfectCurve);
/// <summary>
/// The type of the spline that should be used to interpret the control points of the path.
/// </summary>
public SplineType SplineType { get; init; }
/// <summary>
/// The degree of a BSpline. Unused if <see cref="SplineType"/> is not <see cref="SplineType.BSpline"/>.
/// Null means the degree is equal to the number of control points, 1 means linear, 2 means quadratic, etc.
/// </summary>
public int? Degree { get; init; }
public PathType(SplineType splineType)
{
SplineType = splineType;
Degree = null;
}
public override int GetHashCode()
=> HashCode.Combine(SplineType, Degree);
public override bool Equals(object? obj)
=> obj is PathType pathType && this == pathType;
public static bool operator ==(PathType a, PathType b)
=> a.SplineType == b.SplineType && a.Degree == b.Degree;
public static bool operator !=(PathType a, PathType b)
=> a.SplineType != b.SplineType || a.Degree != b.Degree;
public static PathType BSpline(int degree)
{
Debug.Assert(degree > 0);
return new PathType { SplineType = SplineType.BSpline, Degree = degree };
}
}
}

View File

@ -246,13 +246,13 @@ namespace osu.Game.Screens.Play.HUD
barPath = new SliderPath(new[]
{
new PathControlPoint(new Vector2(0, 0), PathType.Linear),
new PathControlPoint(new Vector2(curveStart - curve_smoothness, 0), PathType.Bezier),
new PathControlPoint(new Vector2(0, 0), PathType.LINEAR),
new PathControlPoint(new Vector2(curveStart - curve_smoothness, 0), PathType.BEZIER),
new PathControlPoint(new Vector2(curveStart, 0)),
new PathControlPoint(new Vector2(curveStart, 0) + diagonalDir * curve_smoothness, PathType.Linear),
new PathControlPoint(new Vector2(curveEnd, BarHeight.Value) - diagonalDir * curve_smoothness, PathType.Bezier),
new PathControlPoint(new Vector2(curveStart, 0) + diagonalDir * curve_smoothness, PathType.LINEAR),
new PathControlPoint(new Vector2(curveEnd, BarHeight.Value) - diagonalDir * curve_smoothness, PathType.BEZIER),
new PathControlPoint(new Vector2(curveEnd, BarHeight.Value)),
new PathControlPoint(new Vector2(curveEnd + curve_smoothness, BarHeight.Value), PathType.Linear),
new PathControlPoint(new Vector2(curveEnd + curve_smoothness, BarHeight.Value), PathType.LINEAR),
new PathControlPoint(new Vector2(barLength, BarHeight.Value)),
});